// Copyright 2014, Max Planck Society.
// Distributed under the BSD 3-Clause license.
// (See accompanying file LICENSE.txt or copy at
// http://opensource.org/licenses/BSD-3-Clause)

/*!@file
 * This file includes the tests for the grassmann pca
 */

#include <boost/test/unit_test.hpp>

#include <test/test_main.hpp>

#include <include/grassmann_pca.hpp>
#include <include/private/boost_ublas_row_iterator.hpp>

// data stored into a matrix
#include <boost/numeric/ublas/io.hpp>

// boost chrono
#include <boost/chrono/include.hpp>
#include <fstream>

BOOST_FIXTURE_TEST_SUITE(grassmann_pca_test_suite, fixture_simple_matrix_creation)

BOOST_AUTO_TEST_CASE(returns_false_for_inapropriate_inputs)
{
  using namespace grassmann_averages_pca;
  namespace ub = boost::numeric::ublas;

  typedef grassmann_pca< ub::vector<double> > grassmann_pca_t;

  grassmann_pca_t instance;

  typedef details::ublas_helpers::row_iter<const matrix_t> const_row_iter_t;
  typedef ub::vector<double> data_t;

  std::vector<data_t> basis_vectors(dimensions);
  const int max_iterations = 1000;

  BOOST_CHECK(!instance.batch_process(
    max_iterations,
    dimensions,
    const_row_iter_t(mat_data, 2),
    const_row_iter_t(mat_data, 0),
    basis_vectors.begin()));

  BOOST_CHECK(!instance.batch_process(
    max_iterations,
    dimensions,
    const_row_iter_t(mat_data, 2),
    const_row_iter_t(mat_data, 0),
    basis_vectors.begin()));
}

BOOST_AUTO_TEST_CASE(check_centering_not_called)
{
  // in this implementation, the centering should not be called
  // the observer throws an exception that is intercepted by the test here.
  using namespace grassmann_averages_pca;
  namespace ub = boost::numeric::ublas;
  typedef ub::vector<double> data_t;

  typedef test_mean_observer<data_t> observer_t;

  typedef grassmann_pca< ub::vector<double>, observer_t > grassmann_pca_t;

  grassmann_pca_t instance;

  observer_t observer;

  BOOST_CHECK(instance.set_observer(&observer));
  BOOST_CHECK(instance.set_centering(false));

  typedef details::ublas_helpers::row_iter<const matrix_t> const_row_iter_t;

  std::vector<data_t> basis_vectors(dimensions);
  const int max_iterations = 10;

  BOOST_CHECK_NO_THROW(instance.batch_process(
                       max_iterations,
                       dimensions,
                       const_row_iter_t(mat_data, 0),
                       const_row_iter_t(mat_data, 10),
                       basis_vectors.begin()));

}

BOOST_AUTO_TEST_CASE(check_centering_of_data)
{
  // checks that the multithreaded centering is performing well
  // the observer throws an exception that is intercepted by the test here.
  using namespace grassmann_averages_pca;
  namespace ub = boost::numeric::ublas;
  typedef ub::vector<double> data_t;

  typedef test_mean_observer<data_t> observer_t;

  typedef grassmann_pca< ub::vector<double>, observer_t > grassmann_pca_t;

  grassmann_pca_t instance;

  observer_t observer;

  BOOST_CHECK(instance.set_observer(&observer));
  BOOST_CHECK(instance.set_centering(true));

  typedef details::ublas_helpers::row_iter<const matrix_t> const_row_iter_t;

  std::vector<data_t> basis_vectors(dimensions);
  const int max_iterations = 1000;

  BOOST_CHECK_THROW(instance.batch_process(
                      max_iterations,
                      dimensions,
                      const_row_iter_t(mat_data, 0),
                      const_row_iter_t(mat_data, 10),
                      basis_vectors.begin()),
                    observer_t::s_signal_exception);

  for(int j = 0; j < dimensions; j++)
  {
    double acc = 0;
    for(int i = 0; i < 10; i++)
    {
      acc += mat_data(i, j);
    }

    BOOST_CHECK_CLOSE(acc / 10, observer.mean(j), 1E-3);
  }

}

BOOST_AUTO_TEST_CASE(smoke_and_orthogonality_tests)
{
  using namespace grassmann_averages_pca;
  using namespace grassmann_averages_pca::details::ublas_helpers;
  namespace ub = boost::numeric::ublas;
  typedef boost::chrono::steady_clock clock_type;

  typedef grassmann_pca< ub::vector<double> > grassmann_pca_t;
  grassmann_pca_t instance;
  typedef row_iter<const matrix_t> const_row_iter_t;

  typedef ub::vector<double> data_t;

  std::vector<data_t> basis_vectors(dimensions);
  const int max_iterations = 1000;

  clock_type::duration elapsed;
  if(DATA_DIMENSION == 5)
  {
    const double initial_point[] = {0.2097, 0.3959, 0.5626, 0.2334, 0.6545};
    BOOST_REQUIRE_EQUAL(dimensions, sizeof(initial_point)/sizeof(initial_point[0])); // just in case

    ub::vector<double> vec_initial_point(dimensions);
    for(int i = 0; i < dimensions; i++)
    {
      vec_initial_point(i) = initial_point[i];
    }

    std::vector< ub::vector<double> > v_init_points(dimensions, vec_initial_point);
    clock_type::time_point start = clock_type::now();
    BOOST_CHECK(instance.batch_process(
      max_iterations,
      dimensions,
      const_row_iter_t(mat_data, 0),
      const_row_iter_t(mat_data, mat_data.size1()),
      basis_vectors.begin(),
      &v_init_points));
    elapsed = clock_type::now() - start;
  }
  else
  {
    clock_type::time_point start = clock_type::now();
    BOOST_CHECK(instance.batch_process(
      max_iterations,
      dimensions,
      const_row_iter_t(mat_data, 0),
      const_row_iter_t(mat_data, mat_data.size1()),
      basis_vectors.begin()));
    elapsed = clock_type::now() - start;
  }

  std::cout << "processing " << nb_elements << " elements "
            << "in " << boost::chrono::duration_cast<boost::chrono::microseconds>(elapsed) << std::endl;

  // testing the output sizes
  BOOST_REQUIRE_EQUAL(basis_vectors.size(), dimensions);
  for(int i = 0; i < dimensions; i++)
  {
    BOOST_TEST_CHECKPOINT("testing basis vector size for vector " << i);
    BOOST_REQUIRE_EQUAL(basis_vectors[i].size(), dimensions);
  }

  if(DATA_DIMENSION <= 5)
  {
    BOOST_TEST_MESSAGE("Generated basis vectors are:");

    for(int i = 0; i < dimensions; i++)
    {
      BOOST_TEST_MESSAGE("vector " << i << " :" << basis_vectors[i]);
    }
  }

  // testing orthogonality of all basis vectors
  for(int i = 0; i < dimensions-1; i++)
  {
    for(int j = i + 1; j < dimensions; j++)
    {
      BOOST_CHECK_LE(ub::inner_prod(basis_vectors[i], basis_vectors[j]), 1E-6);
    }
  }

  // testing unitarity of all basis vectors
  for(int i = 0; i < dimensions; i++)
  {
    BOOST_CHECK_CLOSE(ub::inner_prod(basis_vectors[i], basis_vectors[i]), 1, 1E-6);
  }

  if(DATA_DIMENSION == 5)
  {
    // testing against the matlab output, the script being given by Sorent Hauberg, and the init between
    // each dimension iteration being given by the vector "initial_point" above. Each column represents
    // an basis vector.
    static const double matlab_data[] = {
      -0.0318,    0.0564,   -0.0290,   -0.0136,    0.9974,
       0.0242,    0.0061,    0.9993,   -0.0013,    0.0295,
      -0.0118,    0.9982,   -0.0041,    0.0153,   -0.0567,
      -0.0307,   -0.0149,    0.0017,    0.9993,    0.0136,
       0.9987,    0.0130,   -0.0252,    0.0305,    0.0308,
    };

    for(int i = 0; i < dimensions; i++)
    {
      ub::vector<double> current_matlab_vector(dimensions);
      for(int j = 0; j < dimensions; j++)
      {
        current_matlab_vector(j) = matlab_data[i + j*dimensions];
      }
      BOOST_TEST_CHECKPOINT("iteration " << i);
      BOOST_CHECK_LE(ub::norm_2(basis_vectors[i] - current_matlab_vector), 1E-3);
      //std::cout << "computed = " << basis_vectors[i] << std::endl;
      //std::cout << "matlab = " << current_matlab_vector << std::endl;
    }
  }

}

BOOST_AUTO_TEST_CASE(smoke_and_orthogonality_tests_several_workers)
{
  // this test uses several worker threads, but should provide exactly the same values at the previous test.
  // its body is almost the same.
  using namespace grassmann_averages_pca;
  using namespace grassmann_averages_pca::details::ublas_helpers;
  namespace ub = boost::numeric::ublas;
  typedef boost::chrono::steady_clock clock_type;

  typedef grassmann_pca< ub::vector<double> > grassmann_pca_t;
  grassmann_pca_t instance;
  typedef row_iter<const matrix_t> const_row_iter_t;

  typedef ub::vector<double> data_t;

  std::vector<data_t> basis_vectors(DATA_DIMENSION == 5 ? dimensions : 5);
  const int max_iterations = 1000;

  BOOST_CHECK(instance.set_nb_processors(7)); // each chunk is floor(1000/7) = 142. Last chunk is 148. Just to test sthg different from 10.

  clock_type::duration elapsed;
  if(DATA_DIMENSION == 5)
  {
    const double initial_point[] = {0.2097, 0.3959, 0.5626, 0.2334, 0.6545};
    BOOST_REQUIRE_EQUAL(dimensions, sizeof(initial_point)/sizeof(initial_point[0])); // just in case

    ub::vector<double> vec_initial_point(dimensions);
    for(int i = 0; i < dimensions; i++)
    {
      vec_initial_point(i) = initial_point[i];
    }

    std::vector< ub::vector<double> > v_init_points(dimensions, vec_initial_point);

    // setting the number of workers

    // main call
    clock_type::time_point start = clock_type::now();
    BOOST_CHECK(instance.batch_process(
      max_iterations,
      dimensions,
      const_row_iter_t(mat_data, 0),
      const_row_iter_t(mat_data, mat_data.size1()),
      basis_vectors.begin(),
      &v_init_points));
    elapsed = clock_type::now() - start;
  }
  else
  {
    clock_type::time_point start = clock_type::now();
    BOOST_CHECK(instance.batch_process(
      max_iterations,
      5,
      const_row_iter_t(mat_data, 0),
      const_row_iter_t(mat_data, mat_data.size1()),
      basis_vectors.begin()));
    elapsed = clock_type::now() - start;
  }

  std::cout << "processing " << nb_elements << " elements "
            << "in " << boost::chrono::duration_cast<boost::chrono::microseconds>(elapsed) << std::endl;

  // testing the output sizes
  BOOST_REQUIRE_EQUAL(basis_vectors.size(), DATA_DIMENSION == 5 ? dimensions : 5);
  for(int i = 0; i < basis_vectors.size(); i++)
  {
    BOOST_TEST_CHECKPOINT("testing basis vector size for vector " << i);
    BOOST_REQUIRE_EQUAL(basis_vectors[i].size(), dimensions);
  }

  if(DATA_DIMENSION <= 5)
  {
    BOOST_TEST_MESSAGE("Generated basis vectors are:");

    for(int i = 0; i < dimensions; i++)
    {
      BOOST_TEST_MESSAGE("vector " << i << " :" << basis_vectors[i]);
    }
  }

  // testing orthogonality of all basis vectors
  for(int i = 0; i < basis_vectors.size()-1; i++)
  {
    for(int j = i + 1; j < basis_vectors.size(); j++)
    {
      BOOST_CHECK_LE(ub::inner_prod(basis_vectors[i], basis_vectors[j]), 1E-6);
    }
  }

  for(int i = 0; i < basis_vectors.size(); i++)
  {
    BOOST_CHECK_CLOSE(ub::inner_prod(basis_vectors[i], basis_vectors[i]), 1, 1E-6);
  }

  if(DATA_DIMENSION == 5)
  {
    // testing against the matlab output, the script being given by Sorent Hauberg, and the init between
    // each dimension iteration being given by the vector "initial_point" above. Each column represents
    // an basis vector.
    static const double matlab_data[] = {
      -0.0318,    0.0564,   -0.0290,   -0.0136,    0.9974,
       0.0242,    0.0061,    0.9993,   -0.0013,    0.0295,
      -0.0118,    0.9982,   -0.0041,    0.0153,   -0.0567,
      -0.0307,   -0.0149,    0.0017,    0.9993,    0.0136,
       0.9987,    0.0130,   -0.0252,    0.0305,    0.0308,
    };

    for(int i = 0; i < dimensions; i++)
    {
      ub::vector<double> current_matlab_vector(dimensions);
      for(int j = 0; j < dimensions; j++)
      {
        current_matlab_vector(j) = matlab_data[i + j*dimensions];
      }
      BOOST_TEST_CHECKPOINT("iteration " << i);
      BOOST_CHECK_LE(ub::norm_2(basis_vectors[i] - current_matlab_vector), 1E-3);
    }
  }

}

#if 0
BOOST_AUTO_TEST_CASE(checking_against_matlab)
{
  using namespace grassmann_averages_pca;
  using namespace grassmann_averages_pca::ublas_adaptor;
  namespace ub = boost::numeric::ublas;

  typedef grassmann_pca< ub::vector<double> > grassmann_pca_t;
  grassmann_pca_t instance;
  typedef row_iter<const matrix_t> const_row_iter_t;

  typedef boost::numeric::ublas::vector<double> data_t;

  std::vector<data_t> temporary_data(nb_elements);
  std::vector<data_t> basis_vectors(dimensions);
  const int max_iterations = 1000;

  BOOST_CHECK(instance.batch_process(
    max_iterations,
    dimensions,
    const_row_iter_t(mat_data, 0),
    const_row_iter_t(mat_data, mat_data.size1()),
    temporary_data.begin(),
    basis_vectors.begin()));

  BOOST_TEST_MESSAGE("Generated basis vectors are:");

  for(int i = 0; i < dimensions; i++)
  {
    BOOST_TEST_MESSAGE("vector " << i << " :" << basis_vectors[i]);
  }
}
#endif

BOOST_AUTO_TEST_SUITE_END();


#ifndef __VSIM_CAMERA_HPP__
#define __VSIM_CAMERA_HPP__

#include <string>
#include <vector>
#include <memory>
#include <map>

#include <Eigen/Core>

namespace vsim {

// Abstract camera

struct Viewport {
    Viewport() = default ;
    size_t x_ = 0, y_ = 0, width_, height_ ;
};

class Camera {
public:
    enum Type { Perspective, Orthographic } ;

    Camera(Type t): type_(t), mat_(Eigen::Matrix4f::Identity()) {}
    virtual ~Camera() {}

    void setViewport(size_t w, size_t h) {
        vp_.width_ = w ; vp_.height_ = h ;
    }

    void setViewport(size_t x, size_t y, size_t w, size_t h) {
        vp_.width_ = w ; vp_.height_ = h ;
        vp_.x_ = x ; vp_.y_ = y ;
    }

    void setViewTransform(const Eigen::Matrix4f &vt) {
        mat_ = vt ;
    }

    Type type() const { return type_ ; }
    Eigen::Matrix4f getViewMatrix() const { return mat_ ; }
    const Viewport &getViewport() const { return vp_ ; }

    void lookAt(const Eigen::Vector3f &eye, const Eigen::Vector3f &center, float roll = 0);
    void lookAt(const Eigen::Vector3f &eye, const Eigen::Vector3f &center, const Eigen::Vector3f &up);
protected:

    Type type_ ;
    Eigen::Matrix4f mat_ ; // view transformation
    Viewport vp_ ;
};

// Perspective camera

class PerspectiveCamera: public Camera {
public:
    PerspectiveCamera(float aspect, float yfov, float znear = 0.01, float zfar = 10.0):
        Camera(Perspective), aspect_(aspect), yfov_(yfov), znear_(znear), zfar_(zfar) {
    }

    void setAspectRatio(float asp) {
        aspect_ = asp ;
    }

    Eigen::Matrix4f projectionMatrix() const ;
    float zNear() const { return znear_ ; }
    float zFar() const { return zfar_ ; }

protected:

    float yfov_, aspect_, znear_, zfar_ ;
};

// Orthographic camera

struct OrthographicCamera: public Camera {

    OrthographicCamera(): Camera(Orthographic) {}

    float xmag_, ymag_, znear_, zfar_ ;
};

}
#endif


// Copyright (c) 2018 Graphcore Ltd. All rights reserved.
#define BOOST_TEST_MODULE ReduceMaxClassSparse
#include <poplar/Engine.hpp>
#include <poplibs_support/TestDevice.hpp>

#include "poplibs_support/Algorithm.hpp"
#include "poplibs_test/Util.hpp"
#include "popnn/codelets.hpp"
#include "poputil/VertexTemplates.hpp"

#include <boost/program_options.hpp>
#include <boost/random.hpp>
#include <cstdint>

using namespace poplar;
using namespace poplar::program;
using namespace poplibs_test::util;
using namespace poputil;
using namespace poplibs_support;

namespace {

static void modelVertex(const std::vector<double> &activations,
                        const std::vector<std::uint64_t> &labels,
                        double &maxAct, std::uint64_t &maxIndex) {
  std::uint64_t maxI = 0;
  double maxV = activations[maxI];
  for (std::size_t i = 1; i < activations.size(); ++i) {
    if (activations[i] > maxV) {
      maxV = activations[i];
      maxI = i;
    }
  }
  maxAct = maxV;
  maxIndex = labels[maxI];
}

} // end anonymous namespace

static bool doTest(const DeviceType &deviceType, const Type &inputType,
                   const Type &labelType, unsigned size) {
  auto device = createTestDevice(deviceType);
  auto &target = device.getTarget();
  Graph graph(target);
  popnn::addCodelets(graph);

  auto activations = graph.addVariable(inputType, {size}, "activations");
  auto labels = graph.addVariable(labelType, {size}, "labels");
  auto maxAct = graph.addVariable(inputType, {}, "maxValuePartials");
  auto maxIndex = graph.addVariable(labelType, {}, "maxIndexPartials");
  graph.setTileMapping(activations, 0);
  graph.setTileMapping(labels, 0);
  graph.setTileMapping(maxAct, 0);
  graph.setTileMapping(maxIndex, 0);

  Sequence uploadProg, downloadProg;
  std::vector<std::pair<std::string, char *>> tmap;
  auto rawHostActivations = allocateHostMemoryForTensor(
      activations, "activations", graph, uploadProg, downloadProg, tmap);
  auto rawHostLabels = allocateHostMemoryForTensor(
      labels, "labels", graph, uploadProg, downloadProg, tmap);
  auto rawHostMaxAct = allocateHostMemoryForTensor(
      maxAct, "maxValuePartial", graph, uploadProg, downloadProg, tmap);
  auto rawHostMaxIndex = allocateHostMemoryForTensor(
      maxIndex, "maxIndexPartial", graph, uploadProg, downloadProg, tmap);

  std::mt19937 randomEngine;
  std::vector<double> hostActivations(size);
  if (inputType == FLOAT) {
    boost::random::uniform_real_distribution<float> randDist;
    for (auto &a : hostActivations) {
      a = randDist(randomEngine);
    }
  } else {
    boost::random::uniform_int_distribution<int> randDist;
    for (auto &a : hostActivations) {
      a = static_cast<double>(randDist(randomEngine));
    }
  }
  copy(target, hostActivations.data(), size, inputType,
       rawHostActivations.get());

  std::vector<std::uint64_t> hostLabels(size);
  {
    boost::random::uniform_int_distribution<std::uint64_t> randDist(0, 500);
    for (auto &l : hostLabels) {
      l = randDist(randomEngine);
    }
  }
  copy(target, hostLabels.data(), size, labelType, rawHostLabels.get());

  auto cs = graph.addComputeSet();
  auto v = graph.addVertex(
      cs, templateVertex("popnn::ReduceMaxClassSparse", inputType, labelType));
  graph.setTileMapping(v, 0);

  graph.connect(v["activations"], activations);
  graph.connect(v["labels"], labels);
  graph.connect(v["maxValue"], maxAct);
  graph.connect(v["maxIndex"], maxIndex);

  Engine e(std::move(graph), Sequence(uploadProg, Execute(cs), downloadProg));
  attachStreams(e, tmap);
  device.bind([&](const Device &d) { e.loadAndRun(d); });

  double modelAct;
  std::uint64_t modelIndex;
  modelVertex(hostActivations, hostLabels, modelAct, modelIndex);

  double hostMaxAct;
  std::uint64_t hostMaxIndex;
  copy(target, inputType, rawHostMaxAct.get(), &hostMaxAct, 1);
  copy(target, labelType, rawHostMaxIndex.get(), &hostMaxIndex, 1);

  bool success = true;
  success &=
      checkIsClose("maxValue", &hostMaxAct, {1}, &modelAct, 1, 0.1, 1e-20);
  success &= checkEqual("maxIndex", &hostMaxIndex, {1}, &modelIndex, 1);
  return success;
}

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

  DeviceType deviceType;
  Type labelType = UNSIGNED_INT;
  Type activationType = HALF;
  unsigned size;
  po::options_description desc("Options");
  // clang-format off
  desc.add_options()
    ("help", "Print help")
    ("device-type",
     po::value<DeviceType>(&deviceType)->required(),
     "Device Type")
    ("activation-type",
      po::value<Type>(&activationType)->required(),
     "Activation type")
    ("label-type",
      po::value<Type>(&labelType)->required(),
     "Label type")
    ("size",
     po::value<unsigned>(&size)->required(),
     "Total size to process with vertex");
  // clang-format on

  po::variables_map vm;
  try {
    po::store(po::parse_command_line(argc, argv, desc), vm);
    if (vm.count("help")) {
      std::cout << desc << "\n\n";
      return 1;
    }
    po::notify(vm);
  } catch (std::exception &e) {
    std::cerr << "error: " << e.what() << "\n";
    return 1;
  }

  if (!doTest(deviceType, activationType, labelType, size))
    return 1;
  return 0;
}


/*
Copyright 2020 Dennis Rohde

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#pragma once

#include <unordered_map>

#include <boost/chrono/include.hpp>

#include "random.hpp"
#include "curve.hpp"
#include "frechet.hpp"
#include "dynamic_time_warping.hpp"
#include "simplification.hpp"

namespace Clustering {

struct Distance_Matrix : public std::vector<std::vector<distance_t>> {
    Distance_Matrix() {}
    Distance_Matrix(const curve_number_t n, const curve_number_t m) : std::vector<std::vector<distance_t>>(n, std::vector<distance_t>(m, -1.0)) {}

    void print() const {
        for (const auto &row : *this) {
            for (const auto elem : row) {
                std::cout << elem << " ";
            }
            std::cout << std::endl;
        }
    }
};

class Cluster_Assignment : public std::vector<std::vector<curve_number_t>> {
public:
    inline Cluster_Assignment(const curve_number_t k = 0) : std::vector<std::vector<curve_number_t>>(k, std::vector<curve_number_t>()) {}

    inline curve_number_t count(const curve_number_t i) const {
        return operator[](i).size();
    }

    inline curve_number_t get(const curve_number_t i, const curve_number_t j) const {
        return operator[](i)[j];
    }

};

inline distance_t _cheap_dist(const curve_number_t i, const curve_number_t j, const Curves &in, const Curves &simplified_in, Distance_Matrix &distances) {
    if (distances[i][j] < 0) {
        const auto dist = Frechet::Continuous::distance(in[i], simplified_in[j]);
        distances[i][j] = dist.value;
    }
    return distances[i][j];
}

inline curve_number_t _nearest_center(const curve_number_t i, const Curves &in, const Curves &simplified_in, const std::vector<curve_number_t> &centers, Distance_Matrix &distances) {
    const auto infty = std::numeric_limits<distance_t>::infinity();
    // cost for curve is infinity
    auto min_cost = infty;
    curve_number_t nearest = 0;

    // except there is a center with smaller cost, then choose the one with smallest cost
    for (curve_number_t j = 0; j < centers.size(); ++j) {
        if (_cheap_dist(i, centers[j], in, simplified_in, distances) < min_cost) {
            min_cost = _cheap_dist(i, centers[j], in, simplified_in, distances);
            nearest = j;
        }
    }
    return nearest;
}

inline distance_t _curve_cost(const curve_number_t i, const Curves &in, const Curves &simplified_in, const std::vector<curve_number_t> &centers, Distance_Matrix &distances) {
    return _cheap_dist(i, centers[_nearest_center(i, in, simplified_in, centers, distances)], in, simplified_in, distances);
}

inline distance_t _center_cost_sum(const Curves &in, const Curves &simplified_in, const std::vector<curve_number_t> &centers, Distance_Matrix &distances) {
    distance_t cost = 0;

    // for all curves
    for (curve_number_t i = 0; i < in.size(); ++i) {
        const auto min_cost_elem = _curve_cost(i, in, simplified_in, centers, distances);
        cost += min_cost_elem;
    }
    return cost;
}

inline Cluster_Assignment _cluster_assignment(const Curves &in, const Curves &simplified_in, const std::vector<curve_number_t> &centers, Distance_Matrix &distances) {
    const auto k = centers.size();
    Cluster_Assignment result(centers.size());

    if (k == 0) return result;

    for (curve_number_t i = 0; i < in.size(); ++i) result[_nearest_center(i, in, simplified_in, centers, distances)].push_back(i);

    return result;
}

struct Clustering_Result {
    Curves centers;
    distance_t value;
    double running_time;
    Cluster_Assignment assignment;

    inline Curve& get(const curve_number_t i) {
        return centers[i];
    }
    inline curve_number_t size() const {
        return centers.size();
    }

    inline Curves::const_iterator cbegin() const {
        return centers.cbegin();
    }

    inline Curves::const_iterator cend() const {
        return centers.cend();
    }

    inline void compute_assignment(const Curves &in) {
        Distance_Matrix distances(in.size(), centers.size());
        std::vector<curve_number_t> center_indices = std::vector<curve_number_t>(centers.size(), 0);
        for (curve_size_t i = 1; i < centers.size(); ++i) center_indices[i] = i;
        assignment = _cluster_assignment(in, centers, center_indices, distances);
    }
};

Clustering_Result gonzalez(const curve_number_t num_centers, const curve_size_t ell, const Curves &in, Distance_Matrix &distances, const bool arya = false, const Curves &center_domain = Curves(), const bool random_start_center = true) {

    const auto start = boost::chrono::process_real_cpu_clock::now();
    Clustering_Result result;

    if (in.empty()) return result;

    std::vector<curve_number_t> centers;
    const Curves &simplified_in = center_domain;

    if (center_domain.empty()) {
        Curves simplified_in_self(in.number(), ell, in.dimensions());

        for (curve_number_t i = 0; i < in.size(); ++i) {
            Simplification::Subcurve_Shortcut_Graph graph(const_cast<Curve&>(in[i]));
            auto simplified_curve = graph.weak_minimum_error_simplification(ell);
            simplified_curve.set_name("Simplification of " + in[i].get_name());
            simplified_in_self[i] = simplified_curve;
        }
        const_cast<Curves&>(simplified_in) = simplified_in_self;
    }

    if (random_start_center) {

        Random::Uniform_Random_Generator<double> ugen;
        const curve_number_t r =  std::floor(simplified_in.size() * ugen.get());
        centers.push_back(r);

    } else centers.push_back(0);

    distance_t curr_maxdist = 0;
    curve_number_t curr_maxcurve = 0;
    distance_t curr_curve_cost;

    if (distances.empty()) distances = Distance_Matrix(in.size(), simplified_in.size());

    {
        // remaining centers
        for (curve_number_t i = 1; i < num_centers; ++i) {

            curr_maxdist = 0;
            curr_maxcurve = 0;
            {

                // all curves
                for (curve_number_t j = 0; j < in.size(); ++j) {

                    curr_curve_cost = _curve_cost(j, in, simplified_in, centers, distances);

                    if (curr_curve_cost > curr_maxdist) {
                        curr_maxdist = curr_curve_cost;
                        curr_maxcurve = j;
                    }

                }
                #if DEBUG
                std::cout << "found center no. " << i+1 << std::endl;
                #endif

                centers.push_back(curr_maxcurve);
            }
        }
    }

    if (arya) {

        auto cost = _center_cost_sum(in, simplified_in, centers, distances);
        auto approxcost = cost;
        auto gamma = 1/(3 * num_centers * in.size());
        auto found = true;

        // try to improve current solution
        while (found) {
            found = false;

            // go through all centers
            for (curve_number_t i = 0; i < num_centers; ++i) {
                auto curr_centers = centers;

                // check if there is a better center among all other curves
                for (curve_number_t j = 0; j < simplified_in.size(); ++j) {
                    // continue if curve is already part of center set
                    if (std::find(curr_centers.begin(), curr_centers.end(), j) != curr_centers.end()) continue;

                    // swap
                    curr_centers[i] = j;
                    // new cost
                    const auto curr_cost = _center_cost_sum(in, simplified_in, curr_centers, distances);
                    // check if improvement is done
                    if (curr_cost < cost - gamma * approxcost) {
                        cost = curr_cost;
                        centers = curr_centers;
                        found = true;
                    }
                }
            }
        }
        curr_maxdist = cost;
    }

    Curves simpl_centers;
    for (const auto center: centers) simpl_centers.push_back(simplified_in[center]);

    auto end = boost::chrono::process_real_cpu_clock::now();
    result.centers = simpl_centers;
    result.value = curr_maxdist;
    result.running_time = (end-start).count() / 1000000000.0;
    return result;
}

Clustering_Result arya(const curve_number_t num_centers, const curve_size_t ell, const Curves &in, Distance_Matrix &distances, const Curves &center_domain = Curves()) {
    return gonzalez(num_centers, ell, in, distances, true, center_domain, false);
}

Clustering_Result one_median_sampling(const curve_size_t ell, const Curves &in, const double epsilon, const Curves &center_domain = Curves()) {
    const auto start = boost::chrono::process_real_cpu_clock::now();
    Clustering_Result result;
    std::vector<curve_number_t> centers;
    const Curves &simplified_in = center_domain;

    if (center_domain.empty()) {
        Curves simplified_in_self(in.number(), ell, in.dimensions());

        for (curve_number_t i = 0; i < in.size(); ++i) {
            Simplification::Subcurve_Shortcut_Graph graph(const_cast<Curve&>(in[i]));
            auto simplified_curve = graph.weak_minimum_error_simplification(ell);
            simplified_curve.set_name("Simplification of " + in[i].get_name());
            simplified_in_self[i] = simplified_curve;
        }
        const_cast<Curves&>(simplified_in) = simplified_in_self;
    }

    const auto n = in.size();

    const auto s = std::ceil(60);
    const auto t = std::ceil(std::log(60)/(epsilon*epsilon));

    Random::Uniform_Random_Generator<double> ugen;

    const auto candidates = ugen.get(s);
    const auto witnesses = ugen.get(t);

    Distance_Matrix distances = Distance_Matrix(in.size(), in.size());

    curve_number_t best_candidate = 0;
    distance_t best_objective_value = std::numeric_limits<distance_t>::infinity();

    for (curve_number_t i = 0; i < candidates.size(); ++i) {

        const curve_number_t candidate = std::floor(candidates[i] * n);
        distance_t objective = 0;

        for (curve_number_t j = 0; j < witnesses.size(); ++j) {
            const curve_number_t witness = std::floor(witnesses[j] * n);

            _cheap_dist(witness, candidate, in, simplified_in, distances);
            objective += distances[witness][candidate];
        }

        if (objective < best_objective_value) {
            best_candidate = candidate;
            best_objective_value = objective;
        }
    }
    centers.push_back(best_candidate);

    auto end = boost::chrono::process_real_cpu_clock::now();
    result.centers.push_back(simplified_in[centers[0]]);
    result.value = _center_cost_sum(in, simplified_in, centers, distances);
    result.running_time = (end-start).count() / 1000000000.0;
    return result;
}

Clustering_Result one_median_exhaustive(const curve_size_t ell, const Curves &in, const Curves &center_domain = Curves()) {
    const auto start = boost::chrono::process_real_cpu_clock::now();
    Clustering_Result result;
    std::vector<curve_number_t> centers;
    const Curves &simplified_in = center_domain;

    if (center_domain.empty()) {
        Curves simplified_in_self(in.number(), ell, in.dimensions());

        for (curve_number_t i = 0; i < in.size(); ++i) {
            Simplification::Subcurve_Shortcut_Graph graph(const_cast<Curve&>(in[i]));
            auto simplified_curve = graph.weak_minimum_error_simplification(ell);
            simplified_curve.set_name("Simplification of " + in[i].get_name());
            simplified_in_self[i] = simplified_curve;
        }
        const_cast<Curves&>(simplified_in) = simplified_in_self;
    }

    const auto n = in.size();

    Distance_Matrix distances = Distance_Matrix(in.size(), in.size());

    curve_number_t best_candidate = 0;
    distance_t best_objective_value = std::numeric_limits<distance_t>::infinity();

    for (curve_number_t j = 0; j < in.size(); ++j) {

        distance_t objective = 0;

        for (curve_number_t i = 0; i < in.size(); ++i) {
            _cheap_dist(i, j, in, simplified_in, distances);
            objective += distances[i][j];
        }

        if (objective < best_objective_value) {
            best_candidate = j;
            best_objective_value = objective;
        }
    }
    centers.push_back(best_candidate);

    auto end = boost::chrono::process_real_cpu_clock::now();
    result.centers.push_back(simplified_in[centers[0]]);
    result.value = best_objective_value;
    result.running_time = (end-start).count() / 1000000000.0;
    return result;
}

Clustering_Result two_two_dtw_one_two_median(const Curves &in, const bool with_assignment = false) {
    const auto start = boost::chrono::process_real_cpu_clock::now();
    Clustering_Result result;

    const auto n = in.size();

    std::vector<std::vector<bool>> markings = std::vector<std::vector<bool>>(n, std::vector<bool>(in.get_m(), false));
    std::vector<curve_size_t> svert = std::vector<curve_size_t>(n, 0), evert = std::vector<curve_size_t>(n, 0);

    Points S1(in.dimensions()), S2(in.dimensions());
    Point mu1(in.dimensions()), mu2(in.dimensions());

    for (curve_number_t i = 0; i < n; ++i) {
        S1.push_back(in[i][svert[i]]);
        markings[i][svert[i]] = true;
        ++svert[i];
        evert[i] = in[i].size() - 1;
        S2.push_back(in[i][evert[i]]);
        markings[i][evert[i]] = true;
        --evert[i];
    }

    mu1 = S1.centroid();
    mu2 = S2.centroid();

    const auto infty = std::numeric_limits<distance_t>::infinity();

    bool done = false;
    distance_t d1 = infty, d2 = infty, dist = infty;
    curve_number_t c1 = 0, c2 = 0;

    while (not done) {
        d1 = infty;
        d2 = infty;

        for (curve_size_t i = 0; i < in.size(); ++i) {
            if (not markings[i][svert[i]]) {
                dist = in[i][svert[i]].dist_sqr(mu1);
                if (dist < d1) {
                    d1 = dist;
                    c1 = i;
                }
            }
            if (not markings[i][evert[i]]) {
                dist = in[i][evert[i]].dist_sqr(mu2);
                if (dist < d2) {
                    d2 = dist;
                    c2 = i;
                }
            }
        }

        if (d1 < d2) {
            //std::cout << "S1 add " << c1 << "." << svert[c1] << std::endl;
            S1.push_back(in[c1][svert[c1]]);
            markings[c1][svert[c1]] = true;
            ++svert[c1];
            mu1 = S1.centroid();
            done = false;
        }
        else if (d2 < infty) {
            //std::cout << "S2 add " << c2 << "." << evert[c2] << std::endl;
            S2.push_back(in[c2][evert[c2]]);
            markings[c2][evert[c2]] = true;
            --evert[c2];
            mu2 = S2.centroid();
            done = false;
        } else done = true;

    }

    Curve center_curve(mu1.dimensions(), "center curve");
    center_curve.push_back(mu1);
    center_curve.push_back(mu2);

    distance_t cost = 0;

    for (const auto &p : S1) cost += p.dist(mu1);
    for (const auto &p : S2) cost += p.dist(mu2);

    auto end = boost::chrono::process_real_cpu_clock::now();
    result.centers.push_back(center_curve);
    result.value = cost;
    result.running_time = (end-start).count() / 1000000000.0;
    return result;
}

Clustering_Result two_two_dtw_one_two_median_exact(const Curves &in, const bool with_assignment = false) {
    const auto start = boost::chrono::process_real_cpu_clock::now();
    Clustering_Result result;
    Curve best_center(in.dimensions());
    const auto infty = std::numeric_limits<distance_t>::infinity();

    curve_size_t n = 1;
    std::vector<curve_size_t> pointers = std::vector<curve_size_t>(in.size(), 0),
                                divisors = std::vector<curve_size_t>(in.size(), 0);
    distance_t best = infty, cost = 0;
    Points S1(in.dimensions()), S2(in.dimensions());

    for (curve_number_t i = 0; i < in.size(); ++i) {
        n *= in[i].complexity() - 1;
        if (i == 0) divisors[i] = in[0].complexity() - 1;
        else if (i == 1) divisors[i] = in[0].complexity() - 1;
        else divisors[i] = divisors[i-1] * (in[i].complexity() - 1);
    }

    const auto onepercent = n / 100;

    int currperc = 0;

    for (curve_size_t i = 0; i < n; ++i) {

        if (onepercent > 0) {
            if (i / onepercent > currperc) {
                currperc = i / onepercent;
                std::cout << currperc << "% done" << std::endl;
            }
        }

        pointers[0] = i % divisors[0];
        for (curve_number_t j = 1; j < in.size(); ++j) {
            pointers[j] = (i / divisors[j]) % (in[j].complexity() - 1);
        }

        S1.clear();
        S2.clear();

        for (curve_number_t j = 0; j < in.size(); ++j) {
            for (curve_size_t k = 0; k < in[j].complexity(); ++ k) {
                if (k <= pointers[j]) {
                    S1.push_back(in[j][k]);
                    if (k == in[j].complexity() - 1) std::cerr << "error!!" << std::endl;
                }
                else S2.push_back(in[j][k]);
            }
        }

        auto mu1 = S1.centroid();
        auto mu2 = S2.centroid();

        Curve center_curve(mu1.dimensions(), "optimal center curve");
        center_curve.push_back(mu1);
        center_curve.push_back(mu2);

        cost = 0;

        for (curve_number_t j = 0; j < in.size(); ++j) {
            const auto dist = Dynamic_Time_Warping::Discrete::distance(center_curve, in[j]);
            cost += dist.value;
        }

        if (cost < best) {
            best = cost;
            best_center = center_curve;
        }
    }

    auto end = boost::chrono::process_real_cpu_clock::now();
    result.centers.push_back(best_center);
    result.value = best;
    result.running_time = (end-start).count() / 1000000000.0;
    return result;
}

}


/*
 * LSST Data Management System
 * Copyright 2008, 2009, 2010 LSST Corporation.
 *
 * This product includes software developed by the
 * LSST Project (http://www.lsst.org/).
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the LSST License Statement and
 * the GNU General Public License along with this program.  If not,
 * see <http://www.lsstcorp.org/LegalNotices/>.
 */
#include <cstdint>
#include <cmath>
#include <string>
#include <boost/format.hpp>
#include "lsst/pex/exceptions.h"
#include "lsst/afw/math/Statistics.h"
#include "lsst/afw/image/Image.h"
#include "lsst/afw/image/MaskedImage.h"
#include "lsst/afw/detection/Threshold.h"

namespace lsst {
namespace afw {
namespace detection {

Threshold::ThresholdType Threshold::parseTypeString(std::string const& typeStr) {
    if (typeStr.compare("bitmask") == 0) {
        return Threshold::BITMASK;
    } else if (typeStr.compare("value") == 0) {
        return Threshold::VALUE;
    } else if (typeStr.compare("stdev") == 0) {
        return Threshold::STDEV;
    } else if (typeStr.compare("variance") == 0) {
        return Threshold::VARIANCE;
    } else if (typeStr.compare("pixel_stdev") == 0) {
        return Threshold::PIXEL_STDEV;
    } else {
        throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
                          (boost::format("Unsupported Threshold type: %s") % typeStr).str());
    }
}

std::string Threshold::getTypeString(ThresholdType const& type) {
    if (type == VALUE) {
        return "value";
    } else if (type == STDEV) {
        return "stdev";
    } else if (type == VARIANCE) {
        return "variance";
    } else {
        throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
                          (boost::format("Unsopported Threshold type: %d") % type).str());
    }
}

double Threshold::getValue(const double param) const {
    switch (_type) {
        case STDEV:
            if (param <= 0) {
                throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
                                  (boost::format("St. dev. must be > 0: %g") % param).str());
            }
            return _value * param;
        case VALUE:
        case BITMASK:
        case PIXEL_STDEV:
            return _value;
        case VARIANCE:
            if (param <= 0) {
                throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
                                  (boost::format("Variance must be > 0: %g") % param).str());
            }
            return _value * std::sqrt(param);
        default:
            throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
                              (boost::format("Unsupported type: %d") % _type).str());
    }
}

template <typename ImageT>
double Threshold::getValue(ImageT const& image) const {
    double param = -1;  // Parameter for getValue()
    if (_type == STDEV || _type == VARIANCE) {
        math::Statistics stats = math::makeStatistics(image, math::STDEVCLIP);
        double const sd = stats.getValue(math::STDEVCLIP);

        if (_type == VARIANCE) {
            param = sd * sd;
        } else {
            param = sd;
        }
    }
    return getValue(param);
}

Threshold createThreshold(double const value, std::string const typeStr, bool const polarity) {
    return Threshold(value, Threshold::parseTypeString(typeStr), polarity);
}

//
// Explicit instantiations
//
#define INSTANTIATE(TYPE)                                                          \
    template double Threshold::getValue(image::TYPE<unsigned short> const&) const; \
    template double Threshold::getValue(image::TYPE<int> const&) const;            \
    template double Threshold::getValue(image::TYPE<float> const&) const;          \
    template double Threshold::getValue(image::TYPE<double> const&) const;         \
    template double Threshold::getValue(image::TYPE<std::uint64_t> const&) const;

#ifndef DOXYGEN
INSTANTIATE(Image);
INSTANTIATE(MaskedImage);
#endif
}  // namespace detection
}  // namespace afw
}  // namespace lsst


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

#include <climits>
//#include <iostream>

#include <boost/numeric/conversion/converter.hpp>
#include "BConverter.hh"

template<typename T, typename S> T BConverter::round_to_even(const S& x)
{
    typedef boost::numeric::conversion_traits<T, S> Traits;
    typedef boost::numeric::def_overflow_handler OverflowHandler;
    typedef boost::numeric::RoundEven<typename Traits::source_type> Rounder;
    typedef boost::numeric::converter<T, S, Traits, OverflowHandler, Rounder> Converter;
    return Converter::convert(x);
}

short BConverter::shortnorm( float v, float center, float extent ) // static
{
    float f = (v - center)/extent ;
    return std::abs(f) > 1 ? SHRT_MIN : round_to_even<short, float>( 32767.0f * f ) ;
}

#define fitsInShort(x) !(((((x) & 0xffff8000) >> 15) + 1) & 0x1fffe)
#define iround(x) ((x)>=0?(int)((x)+0.5):(int)((x)-0.5))

short BConverter::shortnorm_old( float v, float center, float extent )  // static
{
    // range of short is -32768 to 32767
    // Expect no positions out of range, as constrained by the geometry are bouncing on,
    // but getting times beyond the range eg 0.:100 ns is expected
    //
    int inorm = iround(32767.0f * (v - center)/extent ) ;    // linear scaling into -1.f:1.f * float(SHRT_MAX)
    return fitsInShort(inorm) ? short(inorm) : SHRT_MIN  ;
}

unsigned char BConverter::my__float2uint_rn_old( float f ) // static
{
    return iround(f);
}

unsigned char BConverter::my__float2uint_rn( float fv ) // static
{
    return BConverter::round_to_even<unsigned char, float>( fv ) ;
}

unsigned char BConverter::my__float2uint_rn_kludge( float fv ) // static
{
    unsigned char uc(0);
    try
    {
        uc = BConverter::my__float2uint_rn(fv ) ;
    }
    catch( boost::numeric::positive_overflow& e  )
    {
        //std::cout << e.what() << std::endl ;
    }
    catch( boost::numeric::negative_overflow& e  )
    {
        //std::cout << e.what() << std::endl ;
    }
    return uc ;
}

template BRAP_API int   BConverter::round_to_even(const float& x);
template BRAP_API short BConverter::round_to_even(const float& x);
template BRAP_API unsigned char BConverter::round_to_even(const float& x);


// ----------------------------------------------------------------------------
// -                        Open3D: www.open3d.org                            -
// ----------------------------------------------------------------------------
// The MIT License (MIT)
//
// Copyright (c) 2019 www.open3d.org
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
// ----------------------------------------------------------------------------

#include "Open3D/Geometry/Geometry3D.h"

#include <Eigen/Dense>
#include <numeric>

#include "Open3D/Utility/Console.h"

namespace open3d {
namespace geometry {

Eigen::Vector3d Geometry3D::ComputeMinBound(
        const std::vector<Eigen::Vector3d>& points) const {
    if (points.empty()) {
        return Eigen::Vector3d(0.0, 0.0, 0.0);
    }
    return std::accumulate(
            points.begin(), points.end(), points[0],
            [](const Eigen::Vector3d& a, const Eigen::Vector3d& b) {
                return a.array().min(b.array()).matrix();
            });
}

Eigen::Vector3d Geometry3D::ComputeMaxBound(
        const std::vector<Eigen::Vector3d>& points) const {
    if (points.empty()) {
        return Eigen::Vector3d(0.0, 0.0, 0.0);
    }
    return std::accumulate(
            points.begin(), points.end(), points[0],
            [](const Eigen::Vector3d& a, const Eigen::Vector3d& b) {
                return a.array().max(b.array()).matrix();
            });
}
Eigen::Vector3d Geometry3D::ComputeCenter(
        const std::vector<Eigen::Vector3d>& points) const {
    Eigen::Vector3d center(0, 0, 0);
    if (points.empty()) {
        return center;
    }
    center = std::accumulate(points.begin(), points.end(), center);
    center /= double(points.size());
    return center;
}

void Geometry3D::ResizeAndPaintUniformColor(
        std::vector<Eigen::Vector3d>& colors,
        const size_t size,
        const Eigen::Vector3d& color) const {
    colors.resize(size);
    Eigen::Vector3d clipped_color = color;
    if (color.minCoeff() < 0 || color.maxCoeff() > 1) {
        utility::LogWarning(
                "invalid color in PaintUniformColor, clipping to [0, 1]");
        clipped_color = clipped_color.array()
                                .max(Eigen::Vector3d(0, 0, 0).array())
                                .matrix();
        clipped_color = clipped_color.array()
                                .min(Eigen::Vector3d(1, 1, 1).array())
                                .matrix();
    }
    for (size_t i = 0; i < size; i++) {
        colors[i] = clipped_color;
    }
}

void Geometry3D::TransformPoints(const Eigen::Matrix4d& transformation,
                                 std::vector<Eigen::Vector3d>& points) const {
    for (auto& point : points) {
        Eigen::Vector4d new_point =
                transformation *
                Eigen::Vector4d(point(0), point(1), point(2), 1.0);
        point = new_point.head<3>() / new_point(3);
    }
}

void Geometry3D::TransformNormals(const Eigen::Matrix4d& transformation,
                                  std::vector<Eigen::Vector3d>& normals) const {
    for (auto& normal : normals) {
        Eigen::Vector4d new_normal =
                transformation *
                Eigen::Vector4d(normal(0), normal(1), normal(2), 0.0);
        normal = new_normal.head<3>();
    }
}

void Geometry3D::TranslatePoints(const Eigen::Vector3d& translation,
                                 std::vector<Eigen::Vector3d>& points,
                                 bool relative) const {
    Eigen::Vector3d transform = translation;
    if (!relative) {
        transform -= ComputeCenter(points);
    }
    for (auto& point : points) {
        point += transform;
    }
}

void Geometry3D::ScalePoints(const double scale,
                             std::vector<Eigen::Vector3d>& points,
                             const Eigen::Vector3d& center) const {
    for (auto& point : points) {
        point = (point - center) * scale + center;
    }
}

void Geometry3D::RotatePoints(const Eigen::Matrix3d& R,
                              std::vector<Eigen::Vector3d>& points,
                              const Eigen::Vector3d& center) const {
    for (auto& point : points) {
        point = R * (point - center) + center;
    }
}

void Geometry3D::RotateNormals(const Eigen::Matrix3d& R,
                               std::vector<Eigen::Vector3d>& normals) const {
    for (auto& normal : normals) {
        normal = R * normal;
    }
}

Eigen::Matrix3d Geometry3D::GetRotationMatrixFromXYZ(
        const Eigen::Vector3d& rotation) {
    return open3d::utility::RotationMatrixX(rotation(0)) *
           open3d::utility::RotationMatrixY(rotation(1)) *
           open3d::utility::RotationMatrixZ(rotation(2));
}

Eigen::Matrix3d Geometry3D::GetRotationMatrixFromYZX(
        const Eigen::Vector3d& rotation) {
    return open3d::utility::RotationMatrixY(rotation(0)) *
           open3d::utility::RotationMatrixZ(rotation(1)) *
           open3d::utility::RotationMatrixX(rotation(2));
}

Eigen::Matrix3d Geometry3D::GetRotationMatrixFromZXY(
        const Eigen::Vector3d& rotation) {
    return open3d::utility::RotationMatrixZ(rotation(0)) *
           open3d::utility::RotationMatrixX(rotation(1)) *
           open3d::utility::RotationMatrixY(rotation(2));
}

Eigen::Matrix3d Geometry3D::GetRotationMatrixFromXZY(
        const Eigen::Vector3d& rotation) {
    return open3d::utility::RotationMatrixX(rotation(0)) *
           open3d::utility::RotationMatrixZ(rotation(1)) *
           open3d::utility::RotationMatrixY(rotation(2));
}

Eigen::Matrix3d Geometry3D::GetRotationMatrixFromZYX(
        const Eigen::Vector3d& rotation) {
    return open3d::utility::RotationMatrixZ(rotation(0)) *
           open3d::utility::RotationMatrixY(rotation(1)) *
           open3d::utility::RotationMatrixX(rotation(2));
}

Eigen::Matrix3d Geometry3D::GetRotationMatrixFromYXZ(
        const Eigen::Vector3d& rotation) {
    return open3d::utility::RotationMatrixY(rotation(0)) *
           open3d::utility::RotationMatrixX(rotation(1)) *
           open3d::utility::RotationMatrixZ(rotation(2));
}

Eigen::Matrix3d Geometry3D::GetRotationMatrixFromAxisAngle(
        const Eigen::Vector3d& rotation) {
    const double phi = rotation.norm();
    return Eigen::AngleAxisd(phi, rotation / phi).toRotationMatrix();
}

Eigen::Matrix3d Geometry3D::GetRotationMatrixFromQuaternion(
        const Eigen::Vector4d& rotation) {
    return Eigen::Quaterniond(rotation(0), rotation(1), rotation(2),
                              rotation(3))
            .normalized()
            .toRotationMatrix();
}

}  // namespace geometry
}  // namespace open3d


// Copyright (c) 2019 fortiss GmbH, Julian Bernhard, Klemens Esterle, Patrick Hart, Tobias Kessler
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.

#ifndef MODULES_MODELS_DYNAMIC_INTEGRATION_HPP_
#define MODULES_MODELS_DYNAMIC_INTEGRATION_HPP_
#include <Eigen/Core>

namespace modules {
namespace models {
namespace dynamic {

inline State euler_int(const DynamicModel& model,
                const State &x,
                const Input &u,
                float dt) {
  return x + dt * model.StateSpaceModel(x, u);
}

inline State rk4(const DynamicModel& model, const State &x, const Input &u, float dt) {
  return x;
}

}  // namespace dynamic
}  // namespace models
}  // namespace modules

#endif  // MODULES_MODELS_DYNAMIC_INTEGRATION_HPP_


//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2012   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#include <boost/simd/ieee/include/functions/frexp.hpp>
#include <boost/simd/include/constants/one.hpp>
#include <boost/simd/include/constants/half.hpp>
#include <boost/dispatch/functor/meta/call.hpp>
#include <boost/dispatch/meta/as_integer.hpp>
#include <boost/fusion/include/vector_tie.hpp>

#include <nt2/sdk/unit/module.hpp>
#include <nt2/sdk/unit/tests/relation.hpp>
#include <nt2/sdk/unit/tests/type_expr.hpp>

NT2_TEST_CASE_TPL( frexp, BOOST_SIMD_SIMD_REAL_TYPES)
{
  using boost::simd::frexp;
  using boost::simd::tag::frexp_;

  typedef typename boost::dispatch::meta::as_integer<T,signed>::type iT;

  NT2_TEST_TYPE_IS( (typename boost::dispatch::meta::call<frexp_(T)>::type)
                  , (std::pair<T,iT>)
                  );

  {
    iT e;
    T  m;

    frexp(boost::simd::One<T>(), m, e);
    NT2_TEST_EQUAL(m, boost::simd::Half<T>());
    NT2_TEST_EQUAL(e, boost::simd::One<iT>());
  }

  {
    iT e;
    T  m;

    m = frexp(boost::simd::One<T>(), e);
    NT2_TEST_EQUAL(m, boost::simd::Half<T>());
    NT2_TEST_EQUAL(e, boost::simd::One<iT>());
  }

  {
    iT e;
    T  m;

    boost::fusion::vector_tie(m,e) = frexp(boost::simd::One<T>());
    NT2_TEST_EQUAL(m, boost::simd::Half<T>());
    NT2_TEST_EQUAL(e, boost::simd::One<iT>());
  }

  {
    std::pair<T,iT> p;

    p = frexp(boost::simd::One<T>());
    NT2_TEST_EQUAL(p.first  , boost::simd::Half<T>());
    NT2_TEST_EQUAL(p.second , boost::simd::One<iT>());
  }
}


//
// Created by tusa on 15/9/20.
//

#ifndef KERRKITAEV_CONSTS_HPP
#define KERRKITAEV_CONSTS_HPP

#include <boost/math/constants/constants.hpp>
#include <vector>
#include <complex>
#include <thread>
#include <string>

class CONSTS {

public:
    CONSTS() {

        for (int i = 0; i < this->N+1; i++) {
            kIndAll.push_back(i);
        }
        /*for (int i = 0; i < this->N / 2; i++) {
            kIndHalf.push_back(i);
        }*/

        int tmp1 = (int) std::thread::hardware_concurrency();
        int tmp2 = 12;
        this->threadNum = tmp1 > tmp2 ? tmp1 : tmp2;
    }

public:

    int N = 1500;//fermion number
    double Nd = (double) N;//N in double, to facilitate floating point calculation
    double dk = 2 * M_PI / Nd;

    /*
     * indices of momentum space
     * */
    std::vector<int> kIndAll;//0,1,...,N-1, N in total

    /*
     * Parameters before the quench
     * */
    double mu0 = -6;
    double t0 = 1.0;
    double d0 = -1.0;
    /*
     * Parameters after the quench
     * */
    double mu1 = -6;
    double t1 = t0;
    double d1 = d0;

    double lmd = 0;//nonlinearity strength
    /*
     * consts for time*/

    int R = 160;//small time step number

    int Q = 1000;//large time step number
    double dt = 0.00025;//small time step
    double ds = (double) R * dt;//large time step

    double tol = 1e-16;
    double cutOff = 1.4;

    int threadNum;//= std::thread::hardware_concurrency();

    std::string dir="/home/disk2/Documents/cppCode/kerrKitaev/testkNum6/kNum"+boost::lexical_cast<std::string>(N);

    double timeAxisParts=10;

};

#endif //KERRKITAEV_CONSTS_HPP


//
// Created by dominik on 30.06.21.
//

# include <vector>
#include <cassert>
#include <chrono>
#include <iostream>
#include <boost/multi_array.hpp>

using namespace boost;
using std::chrono::high_resolution_clock;
using std::chrono::duration_cast;
using std::chrono::duration;
using std::chrono::milliseconds;

void init_shell_matrix(multi_array_ref<int, 2> &shells, size_t num_atoms, size_t num_pairs) {
    size_t distributed_pairs {0};
    for (size_t i = 0; i < num_atoms; i++) {
        for (size_t j = i ; j < num_atoms; j++) {
            int shell_value;
            if (i == j) shell_value = 0;
            else if (distributed_pairs < num_pairs) {
                shell_value = i*num_atoms + j;
                distributed_pairs++;
            } else {
                shell_value = -1;
            }
            shells[i][j] = shell_value;
            shells[j][i] = shell_value;
        }
    }
    std::cout << "distributed_pairs = " << distributed_pairs << std::endl;
}

typedef std::tuple<size_t, size_t, size_t> AtomPair;
typedef std::vector<AtomPair> AtomPairList;
typedef std::vector<uint8_t> Configuration;

AtomPairList calculate_pair_list(multi_array_ref<int, 2> &shells, size_t num_atoms) {
    AtomPairList result;
    for (size_t i = 0; i < num_atoms; i++) {
        for (size_t j = i; j < num_atoms; j++) {
            if (shells[i][j] > 0)  result.push_back(std::make_tuple(i, j, static_cast<size_t>(shells[i][j])));
        }
    }

    return result;
}

size_t loop_naive(const configuration_t &configuration, multi_array_ref<int, 2> &shells, size_t num_loops = 10000) {
    uint8_t first_species, second_species;
    int pair_shell;
    size_t pair_counts {0};
    for (size_t loop = 0; loop < num_loops; loop++) {
        //std::cout << "loop_benchmark::loop_naive() - " << loop << std::endl;
        for (size_t i = 0; i < configuration.size() ; i++) {
            first_species = configuration[i];
            for (size_t j = i + 1; j < configuration.size() ; j++) {
                pair_shell = shells[i][j];
                if (pair_shell < 0) continue;
                second_species = configuration[j];
                pair_counts+= first_species * second_species;
            }
        }
    }
    //std::cout << "loop_benchmark::loop_naive() - " << pair_counts << std::endl;
    return pair_counts;
}

size_t loop_vector(const configuration_t &configuration, const AtomPairList &pairs, size_t num_loops = 10000) {
    uint8_t first_species, second_species;
    size_t pair_counts {0};
    size_t i, j, shell;
    for (size_t loop = 0; loop < num_loops; loop++) {
        for (const AtomPair &pair: pairs) {
            std::tie(i, j, shell) = pair;
            first_species = configuration[i];
            second_species = configuration[j];
            pair_counts+= first_species * second_species;
        }
    }
    //std::cout << "loop_benchmark::loop_vector() - " << pair_counts << std::endl;
    return pair_counts;
}

void compare_loops(size_t num_atoms, size_t frac = 2, size_t num_loops = 10000, std::string prefix = "\t") {
    size_t num_pairs {(num_atoms * (num_atoms -1))/frac};
    // std::cout << " NUM PAIRS = " << num_pairs << "(" << frac << ")" << std::endl;
    multi_array<int, 2> shells(boost::extents[num_atoms][num_atoms]);
    multi_array_ref<int, 2> shell_ref(shells.data(), boost::extents[num_atoms][num_atoms]);

    init_shell_matrix(shell_ref, num_atoms, num_pairs);
    AtomPairList pair_list(calculate_pair_list(shell_ref, num_atoms));
    assert(pair_list.size() == num_pairs);

    configuration_t conf(num_atoms);
    std::iota(conf.begin(), conf.end(), 1);

    auto t0 = high_resolution_clock::now();
    auto result_naive = loop_naive(conf, shell_ref, num_loops);
    auto t1 = high_resolution_clock::now();
    duration<double, std::milli>  time_naive = t1 - t0;
    auto loop_time_naive = time_naive.count() *1000 /num_loops;
    std::cout << prefix << "loop_benchmark::loop_naive() - " << loop_time_naive << " µs/cycle" << std::endl;

    t0 = high_resolution_clock::now();
    auto result_vector = loop_vector(conf, pair_list, num_loops);
    t1 = high_resolution_clock::now();
    duration<double, std::milli>  time_vector = t1 - t0;
    auto loop_time_vector = time_vector.count() *1000 /num_loops;
    std::cout << prefix << "loop_benchmark::loop_vector() - " << loop_time_vector << " µs/cycle" << std::endl;
    std::cout << prefix << "loop_naive()/loop_vector() - " << loop_time_naive / loop_time_vector<< std::endl;
    assert(result_naive == result_vector);
}

int main(int argc, char *argv[]) {
    std::vector<size_t> fractions {2, 3, 5, 6, 9, 10, 15, 20, 100, 200};
    for (size_t &frac : fractions) {
        compare_loops(500, frac, 20000);
    }

}


/**

Copyright (c) 2016, Aumann Florian, Borella Jocelyn, Heller Florian, Meißner Pascal, Schleicher Ralf, Stöckle Patrick, Stroh Daniel, Trautmann Jeremias, Walter Milena, Wittenbeck Valerij
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

4. The use is explicitly not permitted to any application which deliberately try to kill or do harm to any living creature.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef MYMATHHELPER_HPP_
#define MYMATHHELPER_HPP_

#include "robot_model_services/typedef.hpp"
#include <boost/math/special_functions/binomial.hpp>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int.hpp>
#include <boost/random/uniform_real.hpp>
#include <boost/random/normal_distribution.hpp>
#include <boost/random/variate_generator.hpp>
#include <boost/foreach.hpp>
#include <ros/ros.h>
#include <set>
#include <vector>
#include <geometry_msgs/Quaternion.h>

namespace robot_model_services {
	/*!
	 * \brief MathHelper unites the generally needed math operations.
	 * \author Ralf Schleicher
	 * \date 2014
	 * \version 1.0
	 * \copyright GNU Public License
	 */
	class MathHelper {
	private:
		/*!
		 * \return the randomness generator.
		 */
		static boost::mt19937& getRandomnessGenerator();
	public:
		/*!
		 * \brief converts cartesian coordinates to sphere coordinates
		 * \param cartestian any cartesian coordinates
		 * \return sphere coordinates (radius,inclinate,azimuth)
		 */
		static SimpleSphereCoordinates convertC2S(const SimpleVector3 &cartesian);

		/*!
		 * \brief converts cartesian coordinates to sphere coordinates (lightweight)
		 * \param cartestian any cartesian coordinates
		 * \param sphere (out) sphere coordinates (radius,inclinate,azimuth)
		 */
		static void convertC2S(const SimpleVector3 &cartesian, SimpleSphereCoordinates &sphere);

		/*!
		 * \brief converts sphere coordinates to cartesian coordinates (lightweight)
		 * \param sphere any sphere coordinates (radius,inclinate,azimuth)
		 * \return cartesian coordinates
		 */
		static SimpleVector3 convertS2C(const SimpleSphereCoordinates &sphere);

		/*!
		 * \brief converts sphere coordinates to cartesian coordinates (lightweight)
		 * \param sphere any sphere coordinates (radius,inclinate,azimuth)
		 * \param cartesian (out) cartesian coordinates
		 */
		static void convertS2C(const SimpleSphereCoordinates &sphere, SimpleVector3 &cartesian);

		static SimpleVector3 getVisualAxis(const SimpleQuaternion &orientation);

		static void getVisualAxis(const SimpleQuaternion &orientation, SimpleVector3 &resultAxis);

		/*!
		 * \param value [in]
		 * \return the signum of value
		 */
		static double getSignum(const double &value);

		/*!
		 * \param idx [in] the index to project to 0
		 * \param X [in] the 3d vector used for this action.
		 * \return the 3d vector after projection
		 */
		static SimpleVector3 getProjection(const std::size_t &idx, const SimpleVector3 &X);

		/*!
		 * \param X [in] the first 3d vector
		 * \param Y [in] the second 3d vector
         * \return the cosinus between the two vectors.
		 */
		static Precision getCosinus(const SimpleVector3 &X, const SimpleVector3 &Y);

        /*!
         * \param X [in] the first 3d vector
         * \param Y [in] the second 3d vector
         * \return the angle between the two vectors
         */
        static Precision getAngle(const SimpleVector3 &X, const SimpleVector3 &Y);

        /*!
		 * \param firstAngle [in] the first angle
		 * \param secondAngle [in] the second angle
		 * \return the minimum angle difference.
		 */
		static Precision getMinimumAngleDifference(const Precision &firstAngle, const Precision &secondAngle);

		/*!
		 * \param min [in] the minumum integer
		 * \param max [in] the maximum integer
		 * \return a random integer in the range [min, max]
		 */
		static int getRandomInteger(const int &min, const int &max);

		/*!
		 * \param mean [in] the mean value
		 * \param standardDeviation [in] the standard deviation to use.
		 * \return a normal distributed random number.
		 */
		static Precision getRandomNumber(const Precision &mean, const Precision &standardDeviation);

		/*!
		 * \param mean [in] the mean value in X dimensions
		 * \param standardDeviation [in] the standard deviation to use in X dimensions.
		 * \return a normal distributed random vector.
		 */
		static SimpleVectorX getRandomVector(const SimpleVectorX &mean, const SimpleVectorX &standardDeviation);

		/*!
		 * \return a random rotation quaternion
		 */
		static SimpleQuaternion getRandomQuaternion();

		/*!
		 * \param heading [in] the heading angle
		 * \param attitude [in] the attitude angle
		 * \param bank [in] the bank angle
		 * \return the corresponding quaternion
		 */
		static SimpleQuaternion getQuaternionByAngles(const Precision &heading, const Precision &attitude, const Precision &bank);

		/*!
		 * \param numberOfPoints [in] the number of points to use.
		 * \return an array of orientations.
		 */
		static SimpleQuaternionCollectionPtr getOrientationsOnUnitSphere(const int &numberOfPoints);

		/*!
		 * \param input in radians
		 * \return input in degrees
		 */
		static double radToDeg(double input);

		/*!
		 * \param input in degrees
		 * \return input in radians
		 */
		static double degToRad(double input);

		static double getDotProduct(SimpleVector3 v1, SimpleVector3 v2);

		template<typename Set> static void printSet(boost::shared_ptr<Set> &setPtr) {
			std::cout << "\t{ ";
			BOOST_FOREACH(typename Set::value_type value, *setPtr) {
				std::cout << value << ", ";
			}
			std::cout << "}" << std::endl;
		}

		template<typename Set> static void printPowerSet(boost::shared_ptr<std::set<boost::shared_ptr<Set> > > &powerSetPtr) {
			std::cout << "{ " << std::endl;
			BOOST_FOREACH(boost::shared_ptr<Set> subSetPtr, *powerSetPtr) {
				printSet(subSetPtr);
			}
			std::cout << "} " << std::endl;
			std::cout << powerSetPtr->size() << " Items" << std::endl;
		}

		template<typename Set> static boost::shared_ptr<std::set<boost::shared_ptr<Set> > > powerSet(const boost::shared_ptr<Set> &setPtr) {
			boost::shared_ptr<std::set<boost::shared_ptr<Set> > > powerSetPtr(new std::set<boost::shared_ptr<Set> >);

			std::size_t value = 0;
			assert(setPtr->size() <= 31);

			std::size_t limit = (1 << setPtr->size());
			for (std::size_t counter = 0; counter < limit; ++counter) {
				boost::shared_ptr<Set> subSetPtr(new Set);
				std::size_t idx = 0;
				for (typename Set::iterator setIter = setPtr->begin(); setIter != setPtr->end();  ++setIter, ++idx) {
					if ( (value & (1 << idx)) != 0 ) {
						subSetPtr->insert(*setIter);
					}
				}
				powerSetPtr->insert(subSetPtr);

				// the value
				++value;
			}

			return powerSetPtr;
		}

		template<typename PowerSet> static boost::shared_ptr<PowerSet> filterCardinalityPowerSet(const boost::shared_ptr<PowerSet> &powerSetPtr, const std::size_t min, const std::size_t max) {
			boost::shared_ptr<PowerSet> resultPowerSetPtr(new PowerSet);

			for (typename PowerSet::iterator powerSetIter = powerSetPtr->begin(); powerSetIter != powerSetPtr->end(); ++powerSetIter) {
				if ((*powerSetIter)->size() < min || (*powerSetIter)->size() > max) {
					continue;
				}

				resultPowerSetPtr->insert(*powerSetIter);
			}

			return resultPowerSetPtr;
		}

		template<typename PowerSet> static boost::shared_ptr<PowerSet> filterCardinalityPowerSet(const boost::shared_ptr<PowerSet> &setPtr, const std::size_t min) {
			return filterCardinalityPowerSet<PowerSet>(setPtr, min, setPtr->size());
		}
	};
}

#endif /* MYMATHHELPER_HPP_ */


#ifndef STAN_MATH_REV_MAT_FUN_COV_EXP_QUAD_HPP
#define STAN_MATH_REV_MAT_FUN_COV_EXP_QUAD_HPP

#include <stan/math/rev/core.hpp>
#include <stan/math/rev/scal/fun/value_of.hpp>
#include <stan/math/prim/mat/fun/Eigen.hpp>
#include <stan/math/prim/scal/err/check_not_nan.hpp>
#include <stan/math/prim/scal/err/check_positive.hpp>
#include <stan/math/prim/scal/fun/square.hpp>
#include <stan/math/prim/scal/fun/squared_distance.hpp>
#include <stan/math/prim/scal/fun/exp.hpp>
#include <stan/math/prim/scal/meta/scalar_type.hpp>
#include <boost/math/tools/promotion.hpp>
#include <type_traits>
#include <vector>
#include <cmath>

namespace stan {
namespace math {

/**
 * @deprecated use gp_exp_quad_cov_vari
 */
template <typename T_x, typename T_sigma, typename T_l>
class cov_exp_quad_vari : public vari {
 public:
  const size_t size_;
  const size_t size_ltri_;
  const double l_d_;
  const double sigma_d_;
  const double sigma_sq_d_;
  double* dist_;
  vari* l_vari_;
  vari* sigma_vari_;
  vari** cov_lower_;
  vari** cov_diag_;

  /**
   * @deprecated use gp_exp_quad_cov_vari
   */
  cov_exp_quad_vari(const std::vector<T_x>& x, const T_sigma& sigma,
                    const T_l& l)
      : vari(0.0),
        size_(x.size()),
        size_ltri_(size_ * (size_ - 1) / 2),
        l_d_(value_of(l)),
        sigma_d_(value_of(sigma)),
        sigma_sq_d_(sigma_d_ * sigma_d_),
        dist_(ChainableStack::instance().memalloc_.alloc_array<double>(
            size_ltri_)),
        l_vari_(l.vi_),
        sigma_vari_(sigma.vi_),
        cov_lower_(ChainableStack::instance().memalloc_.alloc_array<vari*>(
            size_ltri_)),
        cov_diag_(
            ChainableStack::instance().memalloc_.alloc_array<vari*>(size_)) {
    double inv_half_sq_l_d = 0.5 / (l_d_ * l_d_);
    size_t pos = 0;
    for (size_t j = 0; j < size_ - 1; ++j) {
      for (size_t i = j + 1; i < size_; ++i) {
        double dist_sq = squared_distance(x[i], x[j]);
        dist_[pos] = dist_sq;
        cov_lower_[pos] = new vari(
            sigma_sq_d_ * std::exp(-dist_sq * inv_half_sq_l_d), false);
        ++pos;
      }
    }
    for (size_t i = 0; i < size_; ++i)
      cov_diag_[i] = new vari(sigma_sq_d_, false);
  }

  virtual void chain() {
    double adjl = 0;
    double adjsigma = 0;

    for (size_t i = 0; i < size_ltri_; ++i) {
      vari* el_low = cov_lower_[i];
      double prod_add = el_low->adj_ * el_low->val_;
      adjl += prod_add * dist_[i];
      adjsigma += prod_add;
    }
    for (size_t i = 0; i < size_; ++i) {
      vari* el = cov_diag_[i];
      adjsigma += el->adj_ * el->val_;
    }
    l_vari_->adj_ += adjl / (l_d_ * l_d_ * l_d_);
    sigma_vari_->adj_ += adjsigma * 2 / sigma_d_;
  }
};

/**
 * @deprecated use gp_exp_quad_cov_vari
 */
template <typename T_x, typename T_l>
class cov_exp_quad_vari<T_x, double, T_l> : public vari {
 public:
  const size_t size_;
  const size_t size_ltri_;
  const double l_d_;
  const double sigma_d_;
  const double sigma_sq_d_;
  double* dist_;
  vari* l_vari_;
  vari** cov_lower_;
  vari** cov_diag_;

  /**
   * @deprecated use gp_exp_quad_cov_vari
   */
  cov_exp_quad_vari(const std::vector<T_x>& x, double sigma, const T_l& l)
      : vari(0.0),
        size_(x.size()),
        size_ltri_(size_ * (size_ - 1) / 2),
        l_d_(value_of(l)),
        sigma_d_(value_of(sigma)),
        sigma_sq_d_(sigma_d_ * sigma_d_),
        dist_(ChainableStack::instance().memalloc_.alloc_array<double>(
            size_ltri_)),
        l_vari_(l.vi_),
        cov_lower_(ChainableStack::instance().memalloc_.alloc_array<vari*>(
            size_ltri_)),
        cov_diag_(
            ChainableStack::instance().memalloc_.alloc_array<vari*>(size_)) {
    double inv_half_sq_l_d = 0.5 / (l_d_ * l_d_);
    size_t pos = 0;
    for (size_t j = 0; j < size_ - 1; ++j) {
      for (size_t i = j + 1; i < size_; ++i) {
        double dist_sq = squared_distance(x[i], x[j]);
        dist_[pos] = dist_sq;
        cov_lower_[pos] = new vari(
            sigma_sq_d_ * std::exp(-dist_sq * inv_half_sq_l_d), false);
        ++pos;
      }
    }
    for (size_t i = 0; i < size_; ++i)
      cov_diag_[i] = new vari(sigma_sq_d_, false);
  }

  virtual void chain() {
    double adjl = 0;

    for (size_t i = 0; i < size_ltri_; ++i) {
      vari* el_low = cov_lower_[i];
      adjl += el_low->adj_ * el_low->val_ * dist_[i];
    }
    l_vari_->adj_ += adjl / (l_d_ * l_d_ * l_d_);
  }
};

/**
 * @deprecated use gp_exp_quad_cov_vari
 */
template <typename T_x>
inline typename std::enable_if<
    std::is_same<typename scalar_type<T_x>::type, double>::value,
    Eigen::Matrix<var, -1, -1> >::type
cov_exp_quad(const std::vector<T_x>& x, const var& sigma, const var& l) {
  return gp_exp_quad_cov(x, sigma, l);
}

/**
 * @deprecated use gp_exp_quad_cov_vari
 */
template <typename T_x>
inline typename std::enable_if<
    std::is_same<typename scalar_type<T_x>::type, double>::value,
    Eigen::Matrix<var, -1, -1> >::type
cov_exp_quad(const std::vector<T_x>& x, double sigma, const var& l) {
  return gp_exp_quad_cov(x, sigma, l);
}

}  // namespace math
}  // namespace stan
#endif


/*!@file
 * @copyright This code is licensed under the 3-clause BSD license.
 *   Copyright ETH Zurich, Laboratory of Physical Chemistry, Reiher Group.
 *   See LICENSE.txt for details.
 */

#include "Molassembler/Graph/EditDistance.h"

// #include "boost/graph/graphviz.hpp"

#include <Eigen/Dense>
#include <queue>
#include <deque>

namespace Scine {
namespace Molassembler {
namespace GraphAlgorithms {

constexpr PrivateGraph::Vertex EditDistanceForest::epsilon;

double EditDistanceForest::HausdorffHeuristic::edgeFunction(
  const PrivateGraph::Vertex u,
  const PrivateGraph::Vertex v
) const {
  double cost = 0;
  for(const PrivateGraph::Edge aEdge : a.edges(u)) {
    double minEdgeCost = costFn.edgeAlteration();
    for(PrivateGraph::Edge bEdge : b.edges(v)) {
      minEdgeCost = std::min(
        minEdgeCost,
        costFn.bondSubstitution(a.bondType(aEdge), b.bondType(bEdge)) / 2.0
      );
    }
    cost += minEdgeCost;
  }

  for(const PrivateGraph::Edge bEdge : b.edges(v)) {
    double minEdgeCost = costFn.edgeAlteration();
    for(PrivateGraph::Edge aEdge : a.edges(u)) {
      minEdgeCost = std::min(
        minEdgeCost,
        costFn.bondSubstitution(a.bondType(aEdge), b.bondType(bEdge)) / 2.0
      );
    }
    cost += minEdgeCost;
  }

  return std::max(
    cost,
    std::fabs(a.degree(u) - b.degree(v)) * costFn.edgeAlteration()
  );
}

double EditDistanceForest::HausdorffHeuristic::nodeFunction(
  const PrivateGraph::Vertex u,
  const PrivateGraph::Vertex v
) const {
  if(u == epsilon) {
    return costFn.vertexAlteration() + b.degree(v) * costFn.edgeAlteration() / 2.0;
  }

  if(v == epsilon) {
    return costFn.vertexAlteration() + a.degree(u) * costFn.edgeAlteration() / 2.0;
  }

  return (
    costFn.elementSubstitution(a.elementType(u), b.elementType(v))
    + edgeFunction(u, v) / 2.0
  ) / 2.0;
}

double EditDistanceForest::HausdorffHeuristic::estimate(
  const unsigned depth,
  const std::unordered_set<PrivateGraph::Vertex>& usedBVertices
) const {
  // Vertices free in a are those greater or equal to depth
  // Vertices free in b are those not in usedBVertices
  std::unordered_map<PrivateGraph::Vertex, double> aCost;
  const PrivateGraph::Vertex aN = a.V();
  for(PrivateGraph::Vertex u = depth; u < aN; ++u) {
    aCost.emplace(u, nodeFunction(u, epsilon));
  }

  std::unordered_map<PrivateGraph::Vertex, double> bCost;
  for(PrivateGraph::Vertex v : b.vertices()) {
    if(usedBVertices.count(v) > 0) {
      continue;
    }
    bCost.emplace(v, nodeFunction(epsilon, v));
  }

  for(auto& aCostPair : aCost) {
    for(auto& bCostPair : bCost) {
      const double cost = precomputedNodeFunction(aCostPair.first, bCostPair.first);
      aCostPair.second = std::min(aCostPair.second, cost);
      bCostPair.second = std::min(bCostPair.second, cost);
    }
  }

  double sum = 0.0;
  for(auto aCostPair : aCost) {
    sum += aCostPair.second;
  }
  for(auto bCostPair : bCost) {
    sum += bCostPair.second;
  }
  return sum;
}

EditDistanceForest::Vertex
EditDistanceForest::search(const PrivateGraph& a, const PrivateGraph& b) {
  const unsigned completeDepth = std::max(a.V(), b.V());

  // A* algorithm
  while(true) {
    // Select the current best path
    const Vertex top = queue.top();
    queue.pop();

    const Traversal traversal = traverse(top);
    std::unordered_set<PrivateGraph::Vertex> bVertices {
      std::begin(traversal.bVertices),
      std::end(traversal.bVertices)
    };
    bVertices.erase(epsilon);
    if(traversal.depth == completeDepth) {
      // Current node is a complete edit path
      return top;
    }

    if(traversal.depth < a.V()) {
      std::unordered_set<unsigned> matchedComponents;
      const auto addBranch = [&](const PrivateGraph::Vertex v) {
        if(bVertices.count(v) > 0) {
          return;
        }
        const unsigned component = bHydrogenComponents.at(v);
        if(matchedComponents.count(component) > 0) {
          return;
        }
        matchedComponents.insert(component);

        const Utils::ElementType ukElementType = a.elementType(traversal.depth);
        /* Calculate edge costs of all implied edges
         * - New vertex mapping between graphs in this loop is depth -> v
         * - If we loop over all pairings in this branch i -> *rIter,
         *   then all implied edges are (i-depth) -> (*rIter-v)
         */
        unsigned impliedEdgeCosts = 0;
        PrivateGraph::Vertex i = 0;
        const auto rEnd = std::rend(traversal.bVertices);
        for(auto rIter = std::rbegin(traversal.bVertices); rIter != rEnd; ++rIter) {
          const auto aBondOption = bondTypeOption(i, traversal.depth, a);
          const auto bBondOption = bondTypeOption(*rIter, v, b);
          if(aBondOption && bBondOption) {
            impliedEdgeCosts += costFn.bondSubstitution(*aBondOption, *bBondOption);
          } else if(static_cast<bool>(aBondOption) ^ static_cast<bool>(bBondOption)) {
            impliedEdgeCosts += costFn.edgeAlteration();
          }

          ++i;
        }
        const unsigned cost = (
          g[top].costSum
          + costFn.elementSubstitution(ukElementType, b.elementType(v))
          + impliedEdgeCosts
        );
        const Vertex newVertex = addVertex(
          VertexData {
            v,
            cost,
            cost + estimate(traversal, bVertices, v)
          },
          top
        );
        queue.push(newVertex);
      };

      const auto preconditionIter = preconditioning.left.find(traversal.depth);
      if(preconditionIter != preconditioning.left.end()) {
        addBranch(preconditionIter->second);
      } else {
        for(const PrivateGraph::Vertex v : b.vertices()) {
          addBranch(v);
        }
        // Vertex deletion
        unsigned impliedEdgeCosts = 0;
        for(PrivateGraph::Vertex i = 0; i < traversal.depth; ++i) {
          const auto aEdgeOption = a.edgeOption(i, traversal.depth);
          if(aEdgeOption) {
            // If the implied edge exists, it has to be deleted
            impliedEdgeCosts += costFn.edgeAlteration();
          }
        }
        const unsigned cost = g[top].costSum + costFn.vertexAlteration() + impliedEdgeCosts;
        const Vertex newVertex = addVertex(
          VertexData {
            epsilon,
            cost,
            cost + estimate(traversal, bVertices, epsilon)
          },
          top
        );
        queue.push(newVertex);
      }
    } else {
      // Add all vertex insertions at once
      Vertex currentForestVertex = top;
      for(PrivateGraph::Vertex v : b.vertices()) {
        if(bVertices.count(v) > 0) {
          continue;
        }

        unsigned impliedEdgeCosts = 0;
        for(const PrivateGraph::Vertex w : bVertices) {
          const auto bEdgeOption = b.edgeOption(v, w);
          if(bEdgeOption) {
            impliedEdgeCosts += costFn.edgeAlteration();
          }
        }
        const unsigned cost = g[currentForestVertex].costSum + costFn.vertexAlteration() + impliedEdgeCosts;
        /* NOTE: No point in estimating here since the final value will be
         * true and no intermediate solutions will be added to the queue, so
         * we skip it
         */
        currentForestVertex = addVertex(
          VertexData {
            v,
            cost,
            static_cast<double>(cost)
          },
          currentForestVertex
        );
        bVertices.insert(v);
      }
      queue.push(currentForestVertex);
    }
  }
}

} // namespace GraphAlgorithms
} // namespace Molassembler
} // namespace Scine


//	Copyright (c) 2013, Andre Gaschler, Quirin Fischer
//	All rights reserved.
//
//	Redistribution and use in source and binary forms, with or without
// modification,
//	are permitted provided that the following conditions are met:
//
//	* Redistributions of source code must retain the above copyright notice,
// this
//	  list of conditions and the following disclaimer.
//
//	* Redistributions in binary form must reproduce the above copyright
// notice, this
//	  list of conditions and the following disclaimer in the documentation
// and/or
//	  other materials provided with the distribution.
//
//	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND
//	ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED
//	WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
//	DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR
//	ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES
//	(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES;
//	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
// AND ON
//	ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
//	(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS
//	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#pragma once

#include <set>
#include <Eigen/StdVector>

#include "../BezierTMesh.hh"

namespace boundingmesh
{
using Real = double;
using Vector3 = Eigen::Matrix<Real, 3, 1>;
using Vector4 = Eigen::Matrix<Real, 4, 1>;
using Matrix33 = Eigen::Matrix<Real, 3, 3>;
using Matrix44 = Eigen::Matrix<Real, 4, 4>;
//using Index = unsigned int;

class EdgeContraction
{
public:
	EdgeContraction();
	EdgeContraction(BezierTMesh::EdgeHandle edge, BezierTMesh::Point new_point, Real cost,
		const Matrix44& qem);

	BezierTMesh::EdgeHandle edge() const;
	BezierTMesh::Point new_point() const;
	Real cost() const;
	const Matrix44& qem() const;

	friend bool operator<(const EdgeContraction& lhs, const EdgeContraction& rhs);
	friend bool operator>(const EdgeContraction& lhs, const EdgeContraction& rhs);

	EIGEN_MAKE_ALIGNED_OPERATOR_NEW

private:
	BezierTMesh::EdgeHandle edge_;
	BezierTMesh::Point new_point_;
	Real cost_;
	Matrix44 qem_;
};

typedef std::multiset<EdgeContraction, std::less<EdgeContraction>,
	Eigen::aligned_allocator<EdgeContraction>>::iterator
	ContractionIterator;

class ContractionIndex
{
public:
	ContractionIndex(BezierTMesh::EdgeHandle index);
	ContractionIndex(BezierTMesh::EdgeHandle index, ContractionIterator contraction);

	ContractionIterator contraction() const;

	friend bool operator<(const ContractionIndex& lhs,
		const ContractionIndex& rhs);
	friend bool operator>(const ContractionIndex& lhs,
		const ContractionIndex& rhs);

	EIGEN_MAKE_ALIGNED_OPERATOR_NEW
private:
	BezierTMesh::EdgeHandle index_;
	ContractionIterator contraction_;
	bool searching_;
};

class ContractionQueue
{
public:
	ContractionQueue();
	unsigned int size();
	const EdgeContraction& first();
	void insert(const EdgeContraction& contraction);
	void remove(BezierTMesh::EdgeHandle edge);

	friend void swap(ContractionQueue& first, ContractionQueue& second);

	EIGEN_MAKE_ALIGNED_OPERATOR_NEW
private:
	std::multiset<EdgeContraction, std::less<EdgeContraction>,
		Eigen::aligned_allocator<EdgeContraction> >
		contractions_;
	std::set<ContractionIndex> indices_;

	friend class Decimator;
};
}  // namespace boundingmesh


#include <cctbx/boost_python/flex_fwd.h>

#include <scitbx/matrix/tensors.h>
#include <cctbx/sgtbx/tensors.h>
#include <scitbx/array_family/versa_matrix.h>
#include <boost/python/class.hpp>
#include <boost/python/args.hpp>
#include <boost/python/return_value_policy.hpp>
#include <boost/python/return_by_value.hpp>

namespace cctbx {
  namespace sgtbx {
    namespace boost_python {

      namespace {

        template <class w_t>
        struct tensor_constraints_wrappers {
          static
            af::versa<int, af::c_grid<2> >
            row_echelon_form_as_versa(w_t const& self)
          {
            return af::mat_const_ref_as_versa(self.row_echelon_form());
          }

          static
            af::versa<double, af::c_grid<2> >
            gradient_sum_matrix_as_versa(w_t const& self)
          {
            return af::mat_const_ref_as_versa(self.gradient_sum_matrix());
          }

          static af::versa<int, af::c_grid<2> > get_indices() {
            const std::vector<std::vector<int> > &indices = w_t::get_indices();
            af::versa<int, af::c_grid<2> > rv(af::c_grid<2>(
              indices.size(), indices[0].size()));
            for (size_t i = 0; i < indices.size(); i++) {
              for (size_t j = 0; j < indices[i].size(); j++) {
                rv(i, j) = indices[i][j];
              }
            }
            return rv;
          }

          static void wrap(const char *name) {
            using namespace boost::python;
            typedef return_value_policy<return_by_value> rbv;
            class_<w_t>(name, no_init)
              .def(init<sgtbx::space_group const&, bool>((
                arg("space_group"),
                arg("reciprocal_space"))))
              .def(init<af::shared<rt_mx> const&, std::size_t, bool>((
                arg("symmetry_matrices"),
                arg("i_first_matrix_to_use"),
                arg("reciprocal_space"))))
              .def("row_echelon_form", row_echelon_form_as_versa)
              .add_property("independent_indices",
                make_getter(&w_t::independent_indices, rbv()))
              .def("gradient_sum_matrix", gradient_sum_matrix_as_versa)
              .def("n_independent_params", &w_t::n_independent_params)
              .def("n_dependent_params", &w_t::n_dependent_params)
              .def("independent_params", &w_t::independent_params,
              (arg("all_params")))
              .def("all_params", &w_t::all_params, (arg("independent_params")))
              .def("independent_gradients", &w_t::independent_gradients,
              (arg("all_gradients")))
              .def("independent_curvatures", &w_t::independent_curvatures,
              (arg("all_curvatures")))
              .def("indices", &get_indices, rbv())
              .staticmethod("indices")
              .def("initialise", &w_t::initialise)
              .staticmethod("initialise")
              .def("cleanup", &w_t::cleanup)
              .staticmethod("cleanup")
              ;
          }
        };

      } // namespace <anoymous>

      void wrap_tensor_constraints() {
        using namespace scitbx::matrix::tensors;
        tensor_constraints_wrappers<tensors::constraints<double, tensor_rank_2<double> > >
          ::wrap("rank_2_tensor_constraints");
        tensor_constraints_wrappers<tensors::constraints<double, tensor_rank_3<double> > >
          ::wrap("rank_3_tensor_constraints");
        tensor_constraints_wrappers<tensors::constraints<double, tensor_rank_4<double> > >
          ::wrap("rank_4_tensor_constraints");
      }

    }
  }
} // namespace cctbx::sgtbx::boost_python


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

  @copyright 2016 NumScale SAS

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

#include <boost/simd/function/interleave_odd.hpp>
#include <boost/simd/function/lo.hpp>
#include <boost/simd/function/shr.hpp>
#include <boost/simd/detail/dispatch/function/overload.hpp>
#include <boost/simd/detail/dispatch/meta/as_integer.hpp>
#include <boost/simd/detail/dispatch/meta/scalar_of.hpp>
#include <boost/simd/detail/dispatch/meta/downgrade.hpp>
#include <boost/simd/detail/traits.hpp>
#include <boost/simd/detail/predef.hpp>
#include <boost/config.hpp>

namespace boost { namespace simd { namespace ext
{
  namespace bd = boost::dispatch;
  BOOST_DISPATCH_OVERLOAD_IF ( hi_
                             , (typename A0, typename X)
                             , (detail::is_native<X>)
                             , bd::cpu_
                             , bs::pack_< bd::type8_<A0>, X>
                             )
  {
    using result_t = bd::as_integer_t<A0,unsigned>;
    using s_t = bd::scalar_of_t<result_t>;

    BOOST_FORCEINLINE result_t operator() ( A0 const& a0) const BOOST_NOEXCEPT
    {
      const s_t half_bits = sizeof(s_t)*(CHAR_BIT/2);
      return lo( shr(a0, half_bits) );
    }
  };

  BOOST_DISPATCH_OVERLOAD_IF ( hi_
                             , (typename A0, typename X)
                             , (detail::is_native<X>)
                             , bd::cpu_
                             , bs::pack_< bd::arithmetic_<A0>, X>
                             )
  {
    using result_t = bd::as_integer_t<A0,unsigned>;
    using down_t = bd::downgrade_t<result_t>;

    BOOST_FORCEINLINE result_t operator() ( A0 const& a0) const BOOST_NOEXCEPT
    {
      #if BOOST_ENDIAN_BIG_BYTE
      return bitwise_cast<result_t>(interleave_even(Zero<down_t>(), bitwise_cast<down_t>(a0)));
      #else
      return bitwise_cast<result_t>(interleave_odd(bitwise_cast<down_t>(a0), Zero<down_t>()));
      #endif
    }
  };
} } }

#endif


// Copyright Louis Dionne 2013-2016
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)

#include <boost/hana.hpp>

#include <boost/hana/assert.hpp>
#include <boost/hana/concept/monoid.hpp>
#include <boost/hana/tuple.hpp>

#include <laws/monoid.hpp>
using namespace boost::hana;

int main() {
    test::TestMonoid<int>{make<tuple_tag>(0,1,2,3,4,5)};
    test::TestMonoid<unsigned int>{make<tuple_tag>(0u,1u,2u,3u,4u,5u)};
    test::TestMonoid<long>{make<tuple_tag>(0l,1l,2l,3l,4l,5l)};
    test::TestMonoid<unsigned long>{make<tuple_tag>(0ul,1ul,2ul,3ul,4ul,5ul)};

    // zero
    static_assert(zero<int>() == 0, "");

    // plus
    static_assert(plus(6, 4) == 6 + 4, "");
}


///////////////////////////////////////////////////////////////
//  Copyright 2012 John Maddock. Distributed under the Boost
//  Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt

#include <boost/multiprecision/cpp_int.hpp>

#include "test_arithmetic.hpp"

template <>
struct related_type<boost::multiprecision::number<boost::multiprecision::rational_adaptor<boost::multiprecision::checked_uint256_t::backend_type> > >
{
   typedef boost::multiprecision::checked_uint256_t type;
};

template <>
struct is_twos_complement_integer<boost::multiprecision::number<boost::multiprecision::rational_adaptor<boost::multiprecision::checked_uint256_t::backend_type> > > : public std::false_type
{};

template <>
struct is_checked_cpp_int<boost::multiprecision::checked_uint256_t> : public std::integral_constant<bool, true>
{};
template <>
struct is_checked_cpp_int<boost::multiprecision::number<boost::multiprecision::rational_adaptor<boost::multiprecision::checked_uint256_t::backend_type> > > : public std::integral_constant<bool, true>
{};

int main()
{
   test<boost::multiprecision::number<boost::multiprecision::rational_adaptor<boost::multiprecision::checked_uint256_t::backend_type> > >();
   return boost::report_errors();
}