/*ckwg +29
 * Copyright 2012 by Kitware, Inc.
 * 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.
 *
 *  * Neither name of Kitware, Inc. nor the names of any contributors may be used
 *    to endorse or promote products derived from this software without specific
 *    prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS 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.
 */

#include "gaussian_smooth.h"

#include <boost/make_shared.hpp>
#include <viscl/core/program_registry.h>
#include <viscl/core/manager.h>

#include <math.h>

extern const char* gaussian_smooth_source;

namespace viscl
{

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

gaussian_smooth::gaussian_smooth()
{
  program = program_registry::inst()->register_program(std::string("gaussian_smooth"),
                                                       gaussian_smooth_source);
  conv_x = make_kernel("convolveHoriz1D");
  conv_y = make_kernel("convolveVert1D");
  queue = manager::inst()->create_queue();
}

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

image gaussian_smooth::smooth(const image &img, float sigma, int kernel_radius) const
{
  int kernel_size = 2*kernel_radius+1;
  float *filter = new float[kernel_size];
  int i = 0;
  float sum=0.0f;
  for (float x = -kernel_radius;  x <= kernel_radius; x++, i++)
  {
    filter[i] = exp( (- x * x) / (2.0f * sigma * sigma));
    sum += filter[i];
  }
  for (i = 0; i < kernel_size; ++i)
  {
    filter[i] /= sum;
  }

  buffer smoothing_kernel = manager::inst()->create_buffer<float>(CL_MEM_READ_ONLY, kernel_size);
  queue->enqueueWriteBuffer(*smoothing_kernel().get(), CL_TRUE, 0, smoothing_kernel.mem_size(), filter);

  size_t ni = img.width(), nj = img.height();
  image working = manager::inst()->create_image(img.format(), CL_MEM_READ_WRITE, ni, nj);
  image result = manager::inst()->create_image(img.format(), CL_MEM_READ_WRITE, ni, nj);

  // Set arguments to kernel
  conv_x->setArg(0, *img().get());
  conv_x->setArg(1, *smoothing_kernel().get());
  conv_x->setArg(2, kernel_radius);
  conv_x->setArg(3, *working().get());

  // Set arguments to kernel
  conv_y->setArg(0, *working().get());
  conv_y->setArg(1, *smoothing_kernel().get());
  conv_y->setArg(2, kernel_radius);
  conv_y->setArg(3, *result().get());

  //Run the kernel on specific ND range
  cl::NDRange global(ni, nj);
  //cl::NDRange local(1,1);

  queue->enqueueNDRangeKernel(*conv_x.get(), cl::NullRange, global, cl::NullRange);
  queue->enqueueBarrier();
  queue->enqueueNDRangeKernel(*conv_y.get(), cl::NullRange, global, cl::NullRange);
  queue->finish();

  delete [] filter;

  return result;
}

}


#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/ArrayView.h>
#include <Corrade/Utility/Arguments.h>
#include <Magnum/GL/Renderer.h>
#include <Magnum/Shaders/VertexColor.h>
#include <mtao/geometry/grid/grid.h>
#include <mtao/opengl/objects/grid.h>
#include <mtao/types.h>

#include <Eigen/Geometry>
#include <algorithm>
#include <iostream>
#include <memory>
#include <mtao/opengl/shaders/vector_field.hpp>

#include "imgui.h"
#include "mtao/geometry/bounding_box.hpp"
#include "mtao/geometry/mesh/boundary_facets.h"
#include "mtao/geometry/mesh/read_obj.hpp"
#include "mtao/opengl/Window.h"
#include "mtao/opengl/drawables.h"

using namespace mtao::opengl;

class MeshViewer : public mtao::opengl::Window3 {
   public:
    enum class Mode : int { Smoothing, LSReinitialization };
    Mode mode = Mode::LSReinitialization;

    float permeability = 100.0;
    float timestep = 1000.0;
    bool animate = false;
    using Vec = mtao::VectorX<GLfloat>;
    using Vec3 = mtao::Vec3f;
    Vec data;
    Vec data_original;
    Vec dx;
    Vec dy;
    Vec signs;
    Eigen::AlignedBox<float, 3> bbox;
    Eigen::SparseMatrix<float> L;

    std::array<int, 3> N{{20, 20, 20}};
    int& NI = N[0];
    int& NJ = N[1];
    int& NK = N[2];
    float scale = 1.0;

    Eigen::SimplicialLDLT<Eigen::SparseMatrix<float>> solver;

    MeshViewer(const Arguments& args)
        : Window3(args),
          _wireframe_shader{
              supportsGeometryShader()
                  ? Magnum::Shaders::MeshVisualizer::Flag::Wireframe
                  : Magnum::Shaders::MeshVisualizer::Flag{}} {
        bbox.min().setConstant(-1);
        bbox.max().setConstant(1);
        mv_drawable =
            new mtao::opengl::Drawable<Magnum::Shaders::MeshVisualizer>{
                grid, _wireframe_shader, drawables()};
        mv_drawable->set_visibility(false);
        ;

        edge_drawable = new mtao::opengl::Drawable<Magnum::Shaders::Flat3D>{
            grid, _flat_shader, drawables()};
        edge_drawable->activate_triangles({});
        edge_drawable->activate_edges();
        grid.setParent(&root());
        vfield_mesh.setParent(&root());
#ifdef FLATIT
        _vf_viewer = new mtao::opengl::Drawable<Magnum::Shaders::Flat3D>{
            vfield_mesh, _flat_shader, drawables()};
#else
        _vf_viewer =
            new mtao::opengl::Drawable<mtao::opengl::VectorFieldShader<3>>{
                vfield_mesh, _vf_shader, drawables()};
#endif
        update();
    }
    void update() {
        // mtao::geometry::grid::Grid3f g(std::array<int,3>{{NI,NJ,NK}});
        auto g = mtao::geometry::grid::Grid3f::from_bbox(
            bbox, std::array<int, 3>{{NI, NJ, NK}});

        grid.set(g);
        {
            vfield_mesh.setVertexBuffer(g.vertices());
            mtao::ColVecs3f V = g.vertices();
            // V.array() -= .5;
            auto x = V.row(0).eval();
            V.row(0) = -V.row(1);
            V.row(1) = x;
            V.row(2).array() -= g.bbox().center().z();
            V.row(2) = -V.row(2).array().pow(3);
            vfield_mesh.setVFieldBuffer(V);
        }

        _vf_viewer->deactivate();
        _vf_viewer->activate_points();
    }
    void do_animation() {}
    void gui() override {
        if (ImGui::InputInt3("N", &NI)) {
            update();
        }
        if (ImGui::SliderFloat3("min", bbox.min().data(), -2, 2)) {
            bbox.min() = (bbox.min().array() < bbox.max().array())
                             .select(bbox.min(), bbox.max());
            update();
        }
        if (ImGui::SliderFloat3("max", bbox.max().data(), -2, 2)) {
            bbox.max() = (bbox.min().array() > bbox.max().array())
                             .select(bbox.min(), bbox.max());
            update();
        }
        if (ImGui::SliderFloat("scale", &scale, 0, 2)) {
            _vf_shader.setScale(scale);
        }
        if (mv_drawable) {
            mv_drawable->gui();
        }
        if (edge_drawable) {
            edge_drawable->gui();
        }
        if (ImGui::Button("Step")) {
            do_animation();
        }
    }
    /*
    void draw() override {
        if(animate) {
            do_animation();
        }
        Magnum::GL::Renderer::disable(Magnum::GL::Renderer::Feature::FaceCulling);
        Window3::draw();
    }
    */
   private:
    Magnum::Shaders::MeshVisualizer _wireframe_shader;
    Magnum::Shaders::Flat3D _flat_shader;
    mtao::opengl::VectorFieldShader<3> _vf_shader;
    mtao::opengl::objects::Mesh<3> vfield_mesh;
    mtao::opengl::objects::Grid<3> grid;
    mtao::opengl::Drawable<Magnum::Shaders::MeshVisualizer>* mv_drawable =
        nullptr;
    mtao::opengl::Drawable<Magnum::Shaders::Flat3D>* edge_drawable = nullptr;
#ifdef FLATIT
    mtao::opengl::Drawable<Magnum::Shaders::Flat3D>* _vf_viewer = nullptr;
#else
    mtao::opengl::Drawable<mtao::opengl::VectorFieldShader<3>>* _vf_viewer =
        nullptr;
#endif
};

MAGNUM_APPLICATION_MAIN(MeshViewer)


#include <iostream>
#include <string>

#include <boost/range/irange.hpp>

using uintmax = std::uintmax_t;

auto solution(std::string seq, uintmax n) {

  const auto size = [&seq] {
    return seq.size();
  };

  for(const auto e : boost::irange(n)) {

    auto tmp = std::string{};

    for(auto i = decltype(e){}, pos = i; i < size(); i = pos) {

      pos = seq.find_first_not_of(seq[i], i + 1);

      if (pos == seq.npos) {
        pos = size();
      }

      tmp.push_back('0' + pos - i);
      tmp.push_back(seq[i]);
    }

    seq = tmp;
  }

  return size();
}

int main() {

    std::cout << solution("1321131112", 50) << std::endl;

    return 0;
}


#include <cstdio>
#include <cstdlib>

#include <iostream>

#include <armadillo>

using namespace std;
using namespace arma;

int main(int argc, char* argv[]) {
  if (argc < 3) {
    cerr << "Usage: " << argv[0] << " num-particles rand-seed\n";
    return EXIT_FAILURE;
  }

  const size_t num_particles = atoi(argv[1]);

  const ulong rand_seed = strtoul(argv[2], nullptr, 10);
  clog << "Using random seed: " << rand_seed << "\n";
  srand(rand_seed);

  const double sigma = pow(2.0, 1.0 / 6.0);
  mat::fixed<3, 3> L = 40.0 * sigma * eye(3, 3);
  L.save("cell.mat", arma::csv_ascii);

  mat x = randu<mat>(3, num_particles);
  mat r = L * x;
  r.save("positions.mat", arma::csv_ascii);

  vec q = 2e1 * randu<vec>(num_particles);
  // vec q = ones<vec>(num_particles);
  for (size_t n = 0; n < num_particles; ++n) if (n % 3 != 0) q[n] = 0.0;
  q.save("charges.mat", arma::csv_ascii);

  return EXIT_SUCCESS;
}


#define NPY_NO_DEPRECATED_API NPY_1_11_API_VERSION

#include <iostream>
#include <stdint.h>
#include <alloca.h>
#include <omp.h>
#include <Eigen/Core>
#include <Python.h>
#include <numpy/ndarrayobject.h>

using namespace Eigen;
using namespace std;

static double extract_double(PyArrayObject *arr, int i) {
    return ((double*) PyArray_DATA(arr))[i];
}

static double extract_double_2d(PyArrayObject *arr, int i, int j) {
    return ((double*) PyArray_DATA(arr))[i * PyArray_DIM(arr, 1) + j];
}

static double extract_int64_t(PyArrayObject *arr, int i) {
    return ((int64_t*) PyArray_DATA(arr))[i];
}

void capsule_cleanup(PyObject *capsule) {
    void *memory = PyCapsule_GetPointer(capsule, NULL);
    delete memory;
}

static PyObject* run(PyObject * module, PyObject * args) {
    //TODO: check if parameters are null.
    //TODO: check that dicts have the required members.
    //TODO: check that all parameters have the right sizes.
    //TODO: i'm not sending any error messages.
    import_array();

    PyObject *data_ptr = NULL, *model_ptr = NULL, *fwd_msgs = NULL;
    PyArrayObject *alldata = NULL, *allresources = NULL, *starts = NULL, *lengths = NULL, *learns = NULL, *forgets = NULL, *guesses = NULL, *slips = NULL, *forward_messages = NULL;
    double prior;
    int parallel;

    if (!PyArg_ParseTuple(args, "OOOi", &data_ptr, &model_ptr, &fwd_msgs, &parallel)) {
        PyErr_SetString(PyExc_ValueError, "Error parsing arguments.");
        return NULL;
    }

    if (!parallel)
        omp_set_num_threads(1);

    int DTYPE = PyArray_ObjectType(fwd_msgs, NPY_FLOAT);
    forward_messages = (PyArrayObject *)PyArray_FROM_OTF(fwd_msgs, DTYPE, NPY_ARRAY_IN_ARRAY);

    char* DM_NAMES[] = {"data", "resources", "starts", "lengths", "learns", "forgets", "guesses", "slips"};
    PyArrayObject** DM_PTRS[] = {&alldata, &allresources, &starts, &lengths, &learns, &forgets, &guesses, &slips};
    for (int i = 0; i < 8; i++) {
        PyObject *dp = PyDict_GetItemString(i < 4 ? data_ptr : model_ptr, DM_NAMES[i]);
        DTYPE = PyArray_ObjectType(dp, (i < 4 ? (i < 1 ? NPY_INT : NPY_INT64) : NPY_FLOAT)); // hack to force correct type
        *DM_PTRS[i] = (PyArrayObject *)PyArray_FROM_OTF(dp, DTYPE, NPY_ARRAY_IN_ARRAY);
    }
    prior = PyFloat_AsDouble(PyDict_GetItemString(model_ptr, "prior"));

    int bigT = (int) PyArray_DIM(alldata, 1), num_subparts = (int) PyArray_DIM(alldata, 0);
    int len_allresources = (int) PyArray_DIM(allresources, 0);
    int num_sequences = (int) PyArray_DIM(starts, 0);
    int len_lengths = (int) PyArray_DIM(lengths, 0);
    int num_resources = (int) PyArray_DIM(learns, 0);

    Array2d initial_distn;
    initial_distn << 1-prior, prior;

    MatrixXd As(2,2*num_resources);
    for (int n=0; n<num_resources; n++) {
        double learn = extract_double(learns, n);
        double forget = extract_double(forgets, n);
        As.col(2*n) << 1-learn, learn;
        As.col(2*n+1) << forget, 1-forget;
    }

    // forward messages
    //numpy::ndarray all_forward_messages = extract<numpy::ndarray>(forward_messages);
    double * forward_messages_temp = new double[2*bigT];
    for (int i=0; i<2; i++) {
        for (int j=0; j<bigT; j++){
            forward_messages_temp[i* bigT +j] = extract_double_2d(forward_messages, i, j);
        }
    }

    //// outputs

    double* all_predictions = new double[2 * bigT];
    Map<Array2Xd,Aligned> predictions(all_predictions,2,bigT);

    /* COMPUTATION */

    #pragma omp parallel for
    for (int sequence_index=0; sequence_index < num_sequences; sequence_index++) {
        // NOTE: -1 because Matlab indexing starts at 1
        int64_t sequence_start = extract_int64_t(starts, sequence_index) - 1;
        int64_t T = extract_int64_t(lengths, sequence_index);

        //int16_t *resources = allresources + sequence_start;
        Map<MatrixXd, Aligned> forward_messages(forward_messages_temp + 2*sequence_start,2,T);
        Map<MatrixXd, Aligned> predictions(all_predictions + 2*sequence_start,2,T);

        predictions.col(0) = initial_distn;
        for (int t=0; t<T-1; t++) {
            int64_t resources_temp = extract_int64_t(allresources, sequence_start + t);
            predictions.col(t+1) = As.block(0,2*(resources_temp-1),2,2) * forward_messages.col(t);
        }
    }

    npy_intp dims[] = {2, bigT};
    PyObject *all_predictions_arr = (PyObject *) PyArray_SimpleNewFromData(2, dims, NPY_DOUBLE, all_predictions);
    PyObject *capsule = PyCapsule_New(all_predictions, NULL, capsule_cleanup);
    PyArray_SetBaseObject((PyArrayObject *) all_predictions_arr, capsule);

    for (int i = 0; i < 8; i++)
        Py_XDECREF(*DM_PTRS[i]);
    Py_XDECREF(forward_messages);

    return(all_predictions_arr);
}

static PyMethodDef predict_onestep_states_Methods[] = {
    {"run",  run, METH_VARARGS,
     "Generates predictions for BKT model"},
    {NULL, NULL, 0, NULL}        /* Sentinel */
};

static struct PyModuleDef predict_onestep_states_module = {
   PyModuleDef_HEAD_INIT,
   "predict_onestep_states",   /* name of module */
   NULL, /* module documentation, may be NULL */
   -1,       /* size of per-interpreter state of the module,
                or -1 if the module keeps state in global variables. */
   predict_onestep_states_Methods
};

PyMODINIT_FUNC PyInit_predict_onestep_states() {
    return PyModule_Create(&predict_onestep_states_module);
}


/**********************************************************
Copyright 2014 by Luca Zini and Francesca Odone,
Department of Informatics, Bioengineering, Robotics, and Systems
Engineering University of Genova.

This is a free software: you can redistribute it and/or modify it
under the terms of the CC-BY Public License, Version 4.0, 25
November 2013. This program is distributed in the hope that it will
be useful, but WITHOUT ANY WARRANTY, including but not limited to
the warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the CC-BY Public License copy provided with this software for more
details.

@author Luca Zini (luca.zini@gmail.com)

***********************************************************/
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <boost/lexical_cast.hpp>
#include <boost/program_options.hpp>
#include <boost/thread/mutex.hpp>
#include "roc.h"
#include "extern/Liblinear/linear.h"
#include "cmd-utils.h"
#include "parfor.h"
#include "dataset_management.h"
#include "errors_evaluation.h"

struct parameter buildPar(int algo, double eps, double c){
    struct parameter param;
    param.solver_type = algo;
    param.C = c;
    param.eps = eps;
    param.p = 0.1;
    param.nr_weight = 0;
    param.weight_label = 0;
    param.weight = 0;
    return param;
}

void free_problem(problem& prob){
    delete[] prob.y;
    for(unsigned i = 0; i < prob.l; i++){
        delete[] prob.x[i];
    }
    delete[] prob.x;
}

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

template <class ERR_EVAL>
class ParLibLinear : public ERR_EVAL{
public:
    void operator()(int cIndex){
        struct parameter par = buildPar(algorithm, epsilon, cs.at(cIndex));
        double* ris = 0;
        if(validation){
            ris = new double[val.l];
        }else if(kcv > 1){
            ris = new double[prob.l];
        }
        std::pair<double, double> prec(0,0);
        if(validation){
            model* mod = train(&prob, &par);
            if(saveAll){
                std::stringstream modelPath;
                modelPath << baseModelPath <<  "_"<< cs.at(cIndex);
                modelPath << ".model";
                save_model(modelPath.str().c_str(),mod);
            }
            for(unsigned j = 0; j < val.l; j++){
                predict_values(mod, val.x[j], &(ris[j]));
            }
            prec = ERR_EVAL::accuracy(ris, val.y, val.l);
            {
                boost::mutex::scoped_lock l(m_mutex);
                if(rocBasePath != "" && saveAll){
                    std::stringstream rocName;
                    rocName << rocBasePath <<  "_"<< cs.at(cIndex);
                    rocName << ".roc";
                    std::ofstream outroc(rocName.str().c_str());
                    saveRoc(outroc, val.y, ris, val.l);
                    outroc.close();
                }else if(rocBasePath != "" && prec.first > bestPrecision.first){
                    std::ofstream outroc(rocBasePath.c_str());
                    saveRoc(outroc, val.y, ris, val.l);
                    outroc.close();
                }
                if(prec > bestPrecision){
                    if(bestModel != 0){
                        free_and_destroy_model(&bestModel);
                    }
                    bestPrecision = prec;
                    bestIndex = cIndex;
                    bestModel = mod;
                }else{
                    free_and_destroy_model(&mod);
                }
                dstream << "Liblinear classifier C = " << par.C<< "\n"  << "Accuracy: " << prec.first << "\n";
            }

        }else if(kcv > 1){
            cross_validation_fuzzy(&prob, &par, kcv, ris);
            prec = ERR_EVAL::accuracy(ris, prob.y,  prob.l);
            {
                boost::mutex::scoped_lock l(m_mutex);
                if(rocBasePath != "" && saveAll){
                    std::stringstream rocName;
                    rocName << rocBasePath <<  "_"<< cs.at(cIndex);
                    rocName << ".roc";
                    std::ofstream outroc(rocName.str().c_str());
                    saveRoc(outroc, prob.y, ris, prob.l);
                    outroc.close();
                }else  if(rocBasePath != ""&& prec > bestPrecision){
                    std::ofstream outroc(rocBasePath.c_str());
                    saveRoc(outroc, prob.y, ris, prob.l);
                    outroc.close();
                }
                if(prec > bestPrecision){
                    bestPrecision = prec;
                    bestIndex = cIndex;
                }
                dstream << "Liblinear classifier C = " << par.C<< "\n" << "Accuracy: " << prec.first << "\n \n";
            }
        }else if(saveAll || cs.size() == 1){
            model* mod = train(&prob, &par);
            {
                boost::mutex::scoped_lock l(m_mutex);
                std::stringstream modelPath;
                if(saveAll){
                    modelPath << baseModelPath <<  "_"<< cs.at(cIndex);
                    modelPath << ".model";
                }else{
                    modelPath << baseModelPath;
                }
                save_model(modelPath.str().c_str(),mod);
                dstream << "Liblinear classifier C = " << par.C  << "\n";
                if(cs.size() > 1){
                    free_and_destroy_model(&mod);
                }else{
                    bestModel = mod;
                }
            }
        }else{
            throw std::runtime_error("Undefined");
        }
        delete[] ris;
    }

    double getTH() const {
        return bestPrecision.second;
    }

    double getPrecision() const {
        return bestPrecision.first;
    }

    unsigned getBestIndex() const {
        return bestIndex;
    }

    model* getBestModel() const {
        return bestModel;
    }

    ParLibLinear( std::vector<double> cs, struct problem prob, struct problem val, bool validation, unsigned kcv, int algorithm, double epsilon, std::string rocBasePath, std::string modelBasePath, bool saveAll):cs(cs), prob(prob), val(val),validation(validation), kcv(kcv),algorithm(algorithm), epsilon(epsilon), rocBasePath(rocBasePath), saveAll(saveAll), baseModelPath(modelBasePath){
        bestPrecision = std::pair<double, double>(-1, -1);
        bestIndex = -1;
        bestModel = 0;
    }

private:
    model* bestModel;
    std::string rocBasePath;
    std::pair<double, double> bestPrecision;
    int bestIndex;
    std::vector<double> cs;
    struct problem prob;
    struct problem val;
    bool validation;
    int kcv;
    double epsilon;
    int algorithm;
    boost::mutex m_mutex;
    bool saveAll;
    std::string baseModelPath;
};

int main(int argc, char** argv){
    double bias = 1;
    double requiredTP = 0.995;
    int ERR_TYPE = 1;
    bool singlePrecision = false;
    bool binary = false;
    int kcv = 1, algorithm = 2;
    double* ris = 0;
    std::string rocBasePath = "", debugFile, trainFile, testFile, cRange, modelFile;
    struct problem val;
    double epsilon = 0.01;
    bool saveAll = false;
    std::vector<double> cs;
    po::options_description desc("Allowed options");
    desc.add_options()
            ("help", "produce help message")
            ("save-all", "save all ROCs. If omitted saves only the roc selected with kcv or validation")
            ("binary", "use binary dataset instead of libsvm format")
            ("roc", po::value<std::string>(&rocBasePath), "roc base path and name")
            ("log", po::value<std::string>(&debugFile),"log file")
            ("train-set", po::value<std::string>(&trainFile),"train data file")
            ("validation-set", po::value<std::string>(&testFile), "validation data file used for model selection")
            ("c", po::value<std::string>(&cRange), "C parameter of the SVM. It can be a scalar or a range in the form first:N:end. In this second case a geometric serie of N elements will be used to sample in the range first-end")
            ("epsilon", po::value<double>(&epsilon), " set tolerance of termination criterion\n -s 0 and 2\n |f'(w)|_2 <= eps*min(pos,neg)/l*|f'(w0)|_2,\n where f is the primal function and pos/neg are # of\n positive/negative data (default 0.01)\n -s 1, 3, 4 and 7\n Dual maximal violation <= eps; similar to libsvm (default 0.1)\n -s 5 and 6\n |f'(w)|_1 <= eps*min(pos,neg)/l*|f'(w0)|_1,\n	where f is the primal function (default 0.01)\n")
            ("algorithm", po::value<int>(&algorithm), "set type of solver (default 1)\n	 0 -- L2-regularized logistic regression (primal)\n 1 -- L2-regularized L2-loss support vector classification (dual)\n 2 -- L2-regularized L2-loss support vector classification (primal)\n 3 -- L2-regularized L1-loss support vector classification (dual)\n 4 -- multi-class support vector classification by Crammer and Singer\n 5 -- L1-regularized L2-loss support vector classification\n 6 -- L1-regularized logistic regression\n 7 -- L2-regularized logistic regression (dual)\n")
            ("model", po::value<std::string>(&modelFile), "output model file")
            ("folds", po::value<int>(&kcv), "kcv folds")
            ("err-type", po::value<int>(&ERR_TYPE), " Function used to evaluate the error: \n 1 -- error counting \n 2 -- equal error rate \n 3 -- fixed true positive rate ")
            ("required-tp", po::value<double>(&requiredTP), " required TP if is selected fixedTPerror ")
            ("single-precision", "the algorithms use single precision floating point instead of double")
            ("mt", "it use multithread algorithm")
            ;
    po::variables_map vm;
    po::store(po::parse_command_line(argc, argv, desc), vm);
    po::notify(vm);

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

    check(fileExists(trainFile), "Cannot open training set file", desc);
    if(vm.count("validation-set")){
        check(fileExists(testFile), "Cannot open validation set file", desc);
    }

    if (vm.count("save-all")) {
        saveAll = true;
    }

    check(vm.count("c"), "no C specified", desc);
    if(vm.count("roc")){
        check(vm.count("validation-set") || vm.count("folds"), "KCV or validation set is needed to compute a ROC", desc);
    }

    singlePrecision = vm.count("single-precision");
    binary = vm.count("binary");

    {
        ThreadManager* tmp = ThreadManager::getInstance();
        if (vm.count("mt")) {
            tmp->setThreadNumber();
        }else{
            tmp->setThreadNumber(1);
        }
    }

    if(vm.count("log")){
        dstream.setStream(new std::ofstream(debugFile.c_str()));
        for(unsigned i = 0; i < argc; i++){
            dstream << argv[i] << " ";
        }
        dstream << "\n";
    }

    dstream << "C range: ";
    cs = getRange(cRange);
    if(cs.size() > 1){
        check(vm.count("save-all") || vm.count("validation-set") || vm.count("folds"), "Multiple parameter require --save-all or --fold or --validation-set", desc);
    }

    struct problem prob;
    if(binary){
        if(singlePrecision){
            readProblemBinaryLibLinear<float>(prob, trainFile, bias);
        }else{
            readProblemBinaryLibLinear<double>(prob, trainFile, bias);
        }
    }else{
        readProblemASCIILibLinear(prob, trainFile, bias);
    }

    if(vm.count("validation-set")){
        if(binary){
            if(singlePrecision){
                readProblemBinaryLibLinear<float>(val, testFile, bias);
            }else{
                readProblemBinaryLibLinear<double>(val, testFile, bias);
            }
        }else{
            readProblemASCIILibLinear(val, testFile, bias);
        }
    }
    if(kcv > 1){
        ris = new double[prob.l];
    }
    double bestPrecision = -1;
    int bestIndex = -1;
    model* bestModel = 0;
    double offset = 0;

    if(ERR_TYPE == 1){
        ParLibLinear<StandardClassificationAccuracy<double> > MT(cs, prob, val,vm.count("validation-set"),  kcv, algorithm, epsilon, rocBasePath, modelFile, saveAll);
        par_for_each(0, cs.size(), MT);
        bestPrecision = MT.getPrecision();
        bestIndex = MT.getBestIndex();
        bestModel = MT.getBestModel();
    }else if(ERR_TYPE == 2){
        ParLibLinear<EqualErrorRateAccuracy<double> > MT(cs, prob, val,vm.count("validation-set"),  kcv, algorithm, epsilon, rocBasePath, modelFile, saveAll);
        par_for_each(0, cs.size(), MT);
        bestPrecision = MT.getPrecision();
        bestIndex = MT.getBestIndex();
        bestModel = MT.getBestModel();
    }else if(ERR_TYPE == 3){
        check(vm.count("required-tp") , "required true positive rate not set", desc);
        ParLibLinear<FixedTPAccuracy<double> > MT(cs, prob, val,vm.count("validation-set"),  kcv, algorithm, epsilon, rocBasePath, modelFile, saveAll);
        MT.setRequiredTP(requiredTP);
        par_for_each(0, cs.size(), MT);
        bestPrecision = MT.getPrecision();
        bestIndex = MT.getBestIndex();
        bestModel = MT.getBestModel();
        offset = MT.getTH();
    }else{
        throw std::runtime_error("Undefined");
    }

    if(bestIndex >= 0){
        dstream << "Precision: " << bestPrecision << "\n" << "C: " << cs.at(bestIndex) << "\n";
        if(vm.count("validation-set") || kcv > 1){
            cout << "Precision: " << bestPrecision << std::endl << "C: " << cs.at(bestIndex) << std::endl;
        }
    }
    if(bestIndex >= 0 && modelFile != ""){
        model* mod = bestModel;
        if(mod == 0){
            assert(kcv > 0);
            std::cout << "Recomputing model with the selected parameters..." << std::endl;
            struct parameter par = buildPar(algorithm, epsilon, cs.at(bestIndex));
            const char* err = check_parameter(&prob, &par);
            if(err != 0){
                check(false, err, desc);
            }
            mod = train(&prob, &par);
        }
        std::cout << "Saving final model..." << std::endl;
        save_model(modelFile.c_str(), mod);
        free_and_destroy_model(&mod);
    }
    free_problem(prob);
    if(vm.count("validation-set")){
        free_problem(val);
    }
    std::cout << "Done" << std::endl;
    return 0;
}


#ifndef BOOST_SAFE_NUMERICS_INTERVAL_HPP
#define BOOST_SAFE_NUMERICS_INTERVAL_HPP

//  Copyright (c) 2012 Robert Ramey
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#include <limits>
#include <cassert>
#include <type_traits>
#include <initializer_list>
#include <algorithm> // minmax, min, max

#include <boost/logic/tribool.hpp>

#include "utility.hpp" // log

#include "concept/integer.hpp"

// from stack overflow
// http://stackoverflow.com/questions/23815138/implementing-variadic-min-max-functions

namespace boost {
namespace safe_numerics {

template<typename R>
struct interval {
    const R l;
    const R u;

    template<typename T>
    constexpr interval(const T & lower, const T & upper) :
        l(lower),
        u(upper)
    {
        // assert(static_cast<bool>(l <= u));
    }
    template<typename T>
    constexpr interval(const std::pair<T, T> & p) :
        l(p.first),
        u(p.second)
    {}
    template<class T>
    constexpr interval(const interval<T> & rhs) :
        l(rhs.l),
        u(rhs.u)
    {}
    constexpr interval() :
        l(std::numeric_limits<R>::min()),
        u(std::numeric_limits<R>::max())
    {}
    // return true if this interval contains the given point
    constexpr tribool includes(const R & t) const {
        return l <= t && t <= u;
    }
    // if this interval contains every point found in some other inteval t
    //  return true
    // otherwise
    //  return false or indeterminate
    constexpr tribool includes(const interval<R> & t) const {
        return u >= t.u && l <= t.l;
    }

    // return true if this interval contains the given point
    constexpr tribool excludes(const R & t) const {
        return t < l || t > u;
    }
    // if this interval excludes every point found in some other inteval t
    //  return true
    // otherwise
    //  return false or indeterminate
    constexpr tribool excludes(const interval<R> & t) const {
        return t.u < l || u < t.l;
    }

};

template<class R>
constexpr inline interval<R> make_interval(){
    return interval<R>();
}
template<class R>
constexpr  inline interval<R> make_interval(const R &){
    return interval<R>();
}

// account for the fact that for floats and doubles
// the most negative value is called "lowest" rather
// than min
template<>
constexpr inline interval<float>::interval() :
    l(std::numeric_limits<float>::lowest()),
    u(std::numeric_limits<float>::max())
{}
template<>
constexpr inline interval<double>::interval() :
    l(std::numeric_limits<double>::lowest()),
    u(std::numeric_limits<double>::max())
{}

template<typename T>
constexpr inline interval<T> operator+(const interval<T> & t, const interval<T> & u){
    // adapted from https://en.wikipedia.org/wiki/Interval_arithmetic
    return {t.l + u.l, t.u + u.u};
}

template<typename T>
constexpr inline interval<T> operator-(const interval<T> & t, const interval<T> & u){
    // adapted from https://en.wikipedia.org/wiki/Interval_arithmetic
    return {t.l - u.u, t.u - u.l};
}

template<typename T>
constexpr inline interval<T> operator*(const interval<T> & t, const interval<T> & u){
    // adapted from https://en.wikipedia.org/wiki/Interval_arithmetic
    return utility::minmax<T>(
        std::initializer_list<T> {
            t.l * u.l,
            t.l * u.u,
            t.u * u.l,
            t.u * u.u
        }
    );
}

// interval division
// note: presumes 0 is not included in the range of the denominator
template<typename T>
constexpr inline interval<T> operator/(const interval<T> & t, const interval<T> & u){
    assert(static_cast<bool>(u.excludes(T(0))));
    return utility::minmax<T>(
        std::initializer_list<T> {
            t.l / u.l,
            t.l / u.u,
            t.u / u.l,
            t.u / u.u
        }
    );
}

// modulus of two intervals.  This will give a new range of for the modulus.
// note: presumes 0 is not included in the range of the denominator
template<typename T>
constexpr inline interval<T> operator%(const interval<T> & t, const interval<T> & u){
    assert(static_cast<bool>(u.excludes(T(0))));
    return utility::minmax<T>(
        std::initializer_list<T> {
            t.l % u.l,
            t.l % u.u,
            t.u % u.l,
            t.u % u.u
        }
    );
}

template<typename T>
constexpr inline interval<T> operator<<(const interval<T> & t, const interval<T> & u){
    static_assert(
        boost::safe_numerics::Integer<T>::value,
        "left shift only defined for integral type"
    );
    //return interval<T>{t.l << u.l, t.u << u.u};
    return utility::minmax<T>(
        std::initializer_list<T> {
            t.l << u.l,
            t.l << u.u,
            t.u << u.l,
            t.u << u.u
        }
    );
}

template<typename T>
constexpr inline interval<T> operator>>(const interval<T> & t, const interval<T> & u){
    static_assert(
        boost::safe_numerics::Integer<T>::value,
        "right shift only defined for integral type"
    );
    //return interval<T>{t.l >> u.u, t.u >> u.l};
    return utility::minmax<T>(
        std::initializer_list<T> {
            t.l >> u.l,
            t.l >> u.u,
            t.u >> u.l,
            t.u >> u.u
        }
    );
}

// union of two intervals
template<typename T>
constexpr interval<T> operator|(const interval<T> & t, const interval<T> & u){
    const T & rl = std::min(t.l, u.l);
    const T & ru = std::max(t.u, u.u);
    return interval<T>(rl, ru);
}

// intersection of two intervals
template<typename T>
constexpr inline interval<T> operator&(const interval<T> & t, const interval<T> & u){
    const T & rl = std::max(t.l, u.l);
    const T & ru = std::min(t.u, u.u);
    return interval<T>(rl, ru);
}

// determine whether two intervals intersect
template<typename T>
constexpr inline boost::logic::tribool intersect(const interval<T> & t, const interval<T> & u){
    return t.u >= u.l || t.l <= u.u;
}

template<typename T>
constexpr inline boost::logic::tribool operator<(
    const interval<T> & t,
    const interval<T> & u
){
    return
        // if every element in t is less than every element in u
        t.u < u.l ? boost::logic::tribool(true):
        // if every element in t is greater than every element in u
        t.l > u.u ? boost::logic::tribool(false):
        // otherwise some element(s) in t are greater than some element in u
        boost::logic::indeterminate
    ;
}

template<typename T>
constexpr inline boost::logic::tribool operator>(
    const interval<T> & t,
    const interval<T> & u
){
    return
        // if every element in t is greater than every element in u
        t.l > u.u ? boost::logic::tribool(true) :
        // if every element in t is less than every element in u
        t.u < u.l ? boost::logic::tribool(false) :
        // otherwise some element(s) in t are greater than some element in u
        boost::logic::indeterminate
    ;
}

template<typename T>
constexpr inline bool operator==(
    const interval<T> & t,
    const interval<T> & u
){
    // intervals have the same limits
    return t.l == u.l && t.u == u.u;
}

template<typename T>
constexpr inline bool operator!=(
    const interval<T> & t,
    const interval<T> & u
){
    return ! (t == u);
}

template<typename T>
constexpr inline boost::logic::tribool operator<=(
    const interval<T> & t,
    const interval<T> & u
){
    return ! (t > u);
}

template<typename T>
constexpr inline boost::logic::tribool operator>=(
    const interval<T> & t,
    const interval<T> & u
){
    return ! (t < u);
}

} // safe_numerics
} // boost

#include <iosfwd>

namespace std {

template<typename CharT, typename Traits, typename T>
inline std::basic_ostream<CharT, Traits> &
operator<<(
    std::basic_ostream<CharT, Traits> & os,
    const boost::safe_numerics::interval<T> & i
){
    return os << '[' << i.l << ',' << i.u << ']';
}
template<typename CharT, typename Traits>
inline std::basic_ostream<CharT, Traits> &
operator<<(
    std::basic_ostream<CharT, Traits> & os,
    const boost::safe_numerics::interval<unsigned char> & i
){
    os << "[" << (unsigned)i.l << "," << (unsigned)i.u << "]";
    return os;
}

template<typename CharT, typename Traits>
inline std::basic_ostream<CharT, Traits> &
operator<<(
    std::basic_ostream<CharT, Traits> & os,
    const boost::safe_numerics::interval<signed char> & i
){
    os << "[" << (int)i.l << "," << (int)i.u << "]";
    return os;
}

} // std

#endif // BOOST_SAFE_NUMERICS_INTERVAL_HPP


#ifndef LINEAR_SOLVERS_HPP
#define LINEAR_SOLVERS_HPP

#include "compressed_sensing.hpp"
#include <boost/shared_ptr.hpp>

namespace Pecos {

void normalise_columns( RealMatrix &A, RealVector &result );

class LinearSolver
{
protected:

  RealVector residualTols_;

  int maxIters_;

  int verbosity_;

  bool normaliseInputs_;

  Real solverTol_;

  Real conjugateGradientTol_;

  int numPrimaryEqs_;

public:

  LinearSolver() :
    maxIters_(std::numeric_limits<int>::max()), verbosity_(0),
    normaliseInputs_(false), solverTol_(1.e-6), conjugateGradientTol_(-1.),
    numPrimaryEqs_( -1 )
  { residualTols_.size( 1 ); };

  ~LinearSolver()
  {
    clear();
  };

  void clear()
  {
    maxIters_ = std::numeric_limits<int>::max();
    verbosity_ = 0; solverTol_ = 1.e-6; conjugateGradientTol_ = -1.;
    numPrimaryEqs_ = -1; residualTols_.size( 1 );
  }

  /**
   * \brief Find a regularized solution to AX = B
   */
  virtual void solve( RealMatrix &A, RealMatrix &B, RealMatrix &result_0,
		      RealMatrix &result_1 )
  {
    std::string msg = "solve() Has not been implemented for ";
    msg += "this class.";
    throw( std::runtime_error( msg ) );
  };

  virtual void solve_using_points( RealMatrix &build_points, RealMatrix &B,
				   RealMatrix &result_0,
				   RealMatrix &result_1 )
  {
    std::string msg = "solve_using_points() Has not been implemented for ";
    msg += "this class.";
    throw( std::runtime_error( msg ) );
  };

  void set_verbosity( int verbosity )
  {
    verbosity_ = verbosity;
  };

  void set_residual_tolerance( Real tolerance )
  {
    RealVector residual_tols( 1, false );
    residual_tols[0] = tolerance;
    set_residual_tolerances( residual_tols );
  };

  void set_residual_tolerances( const RealVector &residual_tols )
  {
    residualTols_.sizeUninitialized( residual_tols.length() );
    residualTols_.assign( residual_tols );
  };

  void set_solver_tolerance( Real tolerance )
  {
    solverTol_  = tolerance;
  };

  void set_conjugate_gradient_tolerance( Real tolerance )
  {
    conjugateGradientTol_  = tolerance;
  };

  void set_num_primary_equations( int num_primary_eqs )
  {
    numPrimaryEqs_  = num_primary_eqs;
  };

  Real get_residual_tolerance()
  {
    return residualTols_[0];
  };

  void get_residual_tolerances( RealVector &result_0 )
  {
    result_0.sizeUninitialized( residualTols_.length() );
    result_0.assign( residualTols_ );
  };

  void set_max_iters( int max_iters )
  {
    maxIters_ = max_iters;
  };

  void set_normalise_inputs( bool normalise )
  {
    normaliseInputs_ = normalise;
  };

  void copy( const LinearSolver &source )
  {
    set_residual_tolerances( source.residualTols_ );
    set_max_iters( source.maxIters_ );
    set_verbosity( source.verbosity_ );
  };

  void normalise_columns( RealMatrix &A, RealVector &result )
  {
    int M = A.numRows(), N = A.numCols();
    result.sizeUninitialized( N );
    for ( int i = 0; i < N; i++ )
      {
	RealVector col( Teuchos::View, A[i], M );
	result[i] = col.normFrobenius();
	col.scale(1./result[i]);
      }
  };

  void adjust_coefficients( const RealVector &normalisation_factors,
			    RealMatrix &coefficients )
  {
    int num_coeff = coefficients.numRows(), num_qoi = coefficients.numCols();
    for ( int i = 0; i < num_qoi; i++ )
      {
	for ( int j = 0; j < num_coeff; j++ )
	  coefficients(j,i) /= normalisation_factors[j];
      }
  };

  /**
   * For adaptive methods such as orthogonal least interpolation
   * it is sometimes necessary to reconstruct the Matrix A of Ax=b
   * for instance when performing cross validation
   */
  virtual void build_matrix( const RealMatrix &build_points,
			     RealMatrix &result_0 )
  {
    std::string msg = "linear_solver::build_matrix() Not implemented.";
    throw( std::runtime_error( msg ) );
  }
};

class BPSolver : public LinearSolver
{
protected:

public:

  BPSolver(){};

  ~BPSolver(){};

  /**
   * \brief Find the solution min ||x||_0 such that |AX = B||_2 == 0
   */
  void solve( RealMatrix &A, RealMatrix &B, RealMatrix &result_0,
	      RealMatrix &result_1 )
  {
    if ( B.numCols() != 1 )
      throw( std::runtime_error(" BPSolver::solve() B must be a vector") );

    RealVector b( Teuchos::View, B[0], B.numRows() );
    RealMatrix A_copy( A );

    RealVector column_norms;
    if ( normaliseInputs_ )
      normalise_columns( A_copy, column_norms );

    BP_primal_dual_interior_point_method( A_copy, b,
					  result_0,
					  solverTol_,
					  conjugateGradientTol_,
					  verbosity_ );

    if ( normaliseInputs_ )
      adjust_coefficients( column_norms, result_0 );

    result_1.shapeUninitialized( 2, 1 );
    result_1(0,0) = 0.;
    int num_non_zeros = 0;
    for ( int i = 0; i < result_0.numRows(); i++ )
      if ( std::abs( result_0(i,0) ) > std::numeric_limits<double>::epsilon() )
	num_non_zeros++;
    result_1(1,0) = num_non_zeros;
  };
};

class BPDNSolver : public LinearSolver
{
public:

  BPDNSolver(){};

  ~BPDNSolver(){};

  /**
   * \brief Find the solution min ||x||_0 such that |AX = B||_2 < eps
   */
  void solve( RealMatrix &A, RealMatrix &B, RealMatrix &result_0,
	      RealMatrix &result_1 )
  {
    if ( residualTols_.length() <= 0 )
      throw( std::runtime_error(" BPDNSolver::solve() set residual tols") );

    if ( B.numCols() != 1 )
      throw( std::runtime_error(" BPDNSolver::solve() B must be a vector") );

    RealVector b( Teuchos::View, B[0], B.numRows() );
    RealMatrix A_copy( A );

    RealVector column_norms;
    if ( normaliseInputs_ )
      normalise_columns( A_copy, column_norms );

    result_0.shapeUninitialized( A.numCols(), residualTols_.length() );
    result_1.shapeUninitialized( 2, residualTols_.length() );
    for ( int j = 0; j < residualTols_.length(); j++ )
      {
	RealMatrix x;
	BPDN_log_barrier_interior_point_method( A_copy, b,
						x,
						residualTols_[j],
						solverTol_,
						conjugateGradientTol_,
						verbosity_ );

	if ( normaliseInputs_ )
	  adjust_coefficients( column_norms, x );

	result_1(0,j) = residualTols_[j];
	int num_non_zeros = 0;
	for ( int i = 0; i < result_0.numRows(); i++ )
	  {
	    if ( std::abs( x(i,0) ) > std::numeric_limits<double>::epsilon() )
	      num_non_zeros++;
	    result_0(i,j) = x(i,0);
	  }
	result_1(1,j) = num_non_zeros;
      }
  };
};

class OMPSolver : public LinearSolver
{
protected:
  IntVector ordering_; // enforce a set of columns to be chosen first

public:

  OMPSolver(){};

  ~OMPSolver(){};

  /**
   * \brief Find the solution min ||x||_0 such that |AX = B||_2 < eps
   */
  void solve( RealMatrix &A, RealMatrix &B, RealMatrix &result_0,
	      RealMatrix &result_1 )
  {
    if ( B.numCols() != 1 )
      throw( std::runtime_error(" OMPSolver::solve() B must be a vector") );

    RealVector b( Teuchos::View, B[0], B.numRows() );
    RealMatrix A_copy( A );

    RealVector column_norms;
    if ( normaliseInputs_ )
      normalise_columns( A_copy, column_norms );

    orthogonal_matching_pursuit( A_copy, b, result_0, result_1,
				 residualTols_[0], maxIters_, verbosity_,
				 ordering_ );

    if ( normaliseInputs_ )
      adjust_coefficients( column_norms, result_0 );
  };

  void set_ordering( IntVector &ordering )
  {
    ordering_.resize( ordering.length() );
    ordering_.assign( ordering );
  };
};

class LARSSolver : public LinearSolver
{
private:

  int solver_;

  Real delta_;

public:
  LARSSolver() : solver_( LEAST_ANGLE_REGRESSION ), delta_( 0.0 ){};

  ~LARSSolver(){clear();};

  void clear()
  {
    LinearSolver::clear();
    solver_ = LEAST_ANGLE_REGRESSION; delta_ = 0.0;
  };

  void set_sub_solver( int solver_id )
  {
    if ( ( solver_id != LASSO_REGRESSION ) &&
	 ( solver_id != LEAST_ANGLE_REGRESSION ) )
      {
	std::stringstream msg;
	msg << "set_sub_solver() solver id must be either: " << LASSO_REGRESSION
	    << " or " << LEAST_ANGLE_REGRESSION << "\n";
	throw( std::runtime_error( msg.str() ) );
      }
    solver_ = solver_id;
  };

  void set_delta( Real delta )
  {
    delta_ = delta;
  }

  /**
   * \brief Find the solution min ||x||_0 such that |AX = B||_2 < eps
   */
  void solve( RealMatrix &A, RealMatrix &B, RealMatrix &result_0,
	      RealMatrix &result_1 )
  {
    if ( B.numCols() != 1 )
      throw( std::runtime_error(" LARSSolver::solve() B must be a vector") );

    RealVector b( Teuchos::View, B[0], B.numRows() );
    RealMatrix A_copy( A );

    RealVector column_norms;
    if ( normaliseInputs_ )
      normalise_columns( A_copy, column_norms );

    least_angle_regression( A_copy, b, result_0, result_1,
			    residualTols_[0], solver_, delta_,
			    maxIters_, verbosity_ );

    if ( normaliseInputs_ )
      adjust_coefficients( column_norms, result_0 );
  };
};

class COSAMPSolver : public LinearSolver
{
private:
  int sparsity_;

public:
  COSAMPSolver() : sparsity_( 0 ) {};

  ~COSAMPSolver(){clear();};

  void clear()
  {
    LinearSolver::clear();
    sparsity_ = 0;
  };

  void set_sparsity( int sparsity )
  {
    sparsity_ = sparsity;
  }

  /**
   * \brief Find the solution min ||x||_0 such that |AX = B||_2 < eps
   */
  void solve( RealMatrix &A, RealMatrix &B, RealMatrix &result_0,
	      RealMatrix &result_1 )
  {
    if ( B.numCols() != 1 )
      throw( std::runtime_error(" COSAMPSolver::solve() B must be a vector") );

    RealVector b( Teuchos::View, B[0], B.numRows() );
    RealMatrix A_copy( A );

    RealVector column_norms;
    if ( normaliseInputs_ )
      normalise_columns( A_copy, column_norms );

    cosamp( A_copy, b, result_0, result_1, sparsity_,
	    maxIters_, verbosity_ );

    if ( normaliseInputs_ )
      adjust_coefficients( column_norms, result_0 );
  };
};

class LSQSolver : public LinearSolver
{
public:
  LSQSolver(){};

  ~LSQSolver(){};

  /**
   * \brief Find the solution min ||x||_0 such that |AX = B||_2 < eps
   */
  void solve( RealMatrix &A, RealMatrix &B, RealMatrix &result_0,
	      RealMatrix &result_1 )
  {
    if ( B.numCols() != 1 )
      throw( std::runtime_error("LSQSolver::solve() B must be a vector") );

   if ( A.numRows() < A.numCols() )
     std::cout << "LSQSolver::solve() Warning A is under-determined. " <<
       "M = " << A.numRows() << " N = " << A.numCols() <<
       ". Returning minimum norm solution\n";

    RealVector b( Teuchos::View, B[0], B.numRows() );
    RealMatrix A_copy( A );

    RealVector column_norms;
    if ( normaliseInputs_ )
      normalise_columns( A_copy, column_norms );

    RealVector singular_values;
    int rank(0);
    svd_solve( A_copy, b, result_0, singular_values, rank,
	       solverTol_ );

    result_1.shapeUninitialized( 2, 1 );
    RealVector residual( b );
    residual.multiply( Teuchos::NO_TRANS, Teuchos::NO_TRANS,
		       -1.0, A_copy, result_0, 1.0 );
    result_1(0,0) = residual.normFrobenius();
    int num_non_zeros = 0;
    for ( int i = 0; i < result_0.numRows(); i++ )
      {
	if ( std::abs( result_0(i,0) ) > std::numeric_limits<double>::epsilon() )
	  num_non_zeros++;
      }
    result_1(1,0) = num_non_zeros;

    if ( normaliseInputs_ )
      adjust_coefficients( column_norms, result_0 );
  };
};

class EqualityConstrainedLSQSolver : public LinearSolver
{

public:
  EqualityConstrainedLSQSolver(){};

  ~EqualityConstrainedLSQSolver(){};

  /**
   * \brief Find the solution min ||x||_0 such that |AX = B||_2 < eps
   */
  void solve( RealMatrix &A, RealMatrix &B, RealMatrix &result_0,
	      RealMatrix &result_1 )
  {
    if ( B.numCols() != 1 )
      throw( std::runtime_error(" EqualityConstrainedLSQSolver::solve() B must be a vector") );

    if ( numPrimaryEqs_ <= 0 )
      throw( std::runtime_error(" EqualityConstrainedLSQSolver::solve() set num primary equations") );

    if ( numPrimaryEqs_ > A.numCols() )
      throw( std::runtime_error(" EqualityConstrainedLSQSolver::solve() num primary equations is larger than the number of columns in A") );

    if ( A.numRows() < A.numCols() )
      throw( std::runtime_error(" EqualityConstrainedLSQSolver::solve() A is underdetermined") );

    RealMatrix A_copy( A );

    RealVector column_norms;
    if ( normaliseInputs_ )
      normalise_columns( A_copy, column_norms );

    RealMatrix C_eq( Teuchos::View, A_copy, numPrimaryEqs_,
		     A_copy.numCols(), 0, 0 );
    RealMatrix A_eq( Teuchos::View, A_copy,
		     A_copy.numRows() - numPrimaryEqs_,
		     A_copy.numCols(), numPrimaryEqs_, 0 );
    RealVector d_eq( Teuchos::View, B.values(), numPrimaryEqs_ );
    RealVector b_eq( Teuchos::View,
		     B.values() + numPrimaryEqs_,
		     B.numRows() - numPrimaryEqs_ );
    equality_constrained_least_squares_solve( A_eq, b_eq, C_eq, d_eq,
					      result_0 );

    result_1.shapeUninitialized( 2, 1 );
    RealVector residual( b_eq );
    residual.multiply( Teuchos::NO_TRANS, Teuchos::NO_TRANS,
		       -1.0, A_eq, result_0, 1.0 );
    result_1(0,0) = residual.normFrobenius();
    int num_non_zeros = 0;
    for ( int i = 0; i < result_0.numRows(); i++ )
      {
	if ( std::abs( result_0(i,0) ) > std::numeric_limits<double>::epsilon() )
	  num_non_zeros++;
      }
    result_1(1,0) = num_non_zeros;

    if ( normaliseInputs_ )
      adjust_coefficients( column_norms, result_0 );
  };
};

typedef boost::shared_ptr<LinearSolver> LinearSolver_ptr;

/**
 * \brief Specify a set of options for using the CompressedSensingTool
 */
class CompressedSensingOptions
{
public:
  //solverType solver; //!< Specify which regression solver to use. See solverType
  short solver; //!< Specify which regression solver to use: see pecos_global_defs
  Real solverTolerance; //!< Specify the internal tolerance of the solver
  Real epsilon;         //!< Specify the residual tolerance of the solver
  Real delta;           //!< Specify the regularization parameter value
  int maxNumIterations; //!< Specify the maximum number of solver iterations
  bool standardizeInputs;  //!< Specify if the inputs need to be standardized
  bool storeHistory;       //<! Specify if the solution history should be stored
  Real conjugateGradientsTolerance; //<! Specify wether to use conjugate gradients internally to solve newton step in BP and BPDN.  If < 0 cholesky factorization will be used.
  int verbosity;           //!< The verbosity level. 0: off, 1: warnings on,  2: all print statements on.
  int numFunctionSamples; //!< The number of function samples used to construct A and B. Used when A contains gradient information. If zero then numFunctionSamples = A.numRows()

public:

  CompressedSensingOptions() :
    solver( DEFAULT_LEAST_SQ_REGRESSION ), solverTolerance( -1. ),
    epsilon( 0.0 ), delta( 0.0 ),
    maxNumIterations( std::numeric_limits<int>::max() ),
    standardizeInputs( false ), storeHistory( false ),
    conjugateGradientsTolerance( -1 ), verbosity( 0 ), numFunctionSamples( 0 )
  {};

  ~CompressedSensingOptions(){};

  void print()
  {
    std::cout << "Solver: " << solver << "\n";
    std::cout << "Solver Tolerance: " << solverTolerance << "\n";
    std::cout << "Epsilon: " << epsilon << "\n";
    std::cout << "Delta: " << delta << "\n";
    std::cout << "MaxNumIterations: " << maxNumIterations << "\n";
    std::cout << "StandardizeInputs: " << standardizeInputs << "\n";
    std::cout << "StoreHistory: " << storeHistory << "\n";
    std::cout << "Verbosity: " << verbosity << "\n";
  };

  CompressedSensingOptions& operator=(const CompressedSensingOptions& source )
  {
    if(this == &source)
      return (*this);
    solver = source.solver;
    solverTolerance = source.solverTolerance;
    epsilon = source.epsilon;
    delta = source.delta;
    maxNumIterations = source.maxNumIterations;
    standardizeInputs = source.standardizeInputs;
    storeHistory = source.storeHistory;
    conjugateGradientsTolerance = source.conjugateGradientsTolerance;
    verbosity = source.verbosity;
    numFunctionSamples = source.numFunctionSamples;
    return (*this);
  }

  CompressedSensingOptions( const CompressedSensingOptions& source )
  {
    solver = source.solver;
    solverTolerance = source.solverTolerance;
    epsilon = source.epsilon;
    delta = source.delta;
    maxNumIterations = source.maxNumIterations;
    standardizeInputs = source.standardizeInputs;
    storeHistory = source.storeHistory;
    conjugateGradientsTolerance = source.conjugateGradientsTolerance;
    verbosity = source.verbosity;
    numFunctionSamples = source.numFunctionSamples;
  }
};

typedef std::vector< std::vector<CompressedSensingOptions> > CompressedSensingOptionsList;

/**
 * \class CompressedSensingTool
 * \brief Tool that implements a number of popular compressed sensing algorithms
 */
class CompressedSensingTool
{
protected:
  LinearSolver_ptr linearSolver_;

public:

  /// Default constructor
  CompressedSensingTool()
  {
    std::cout.precision( std::numeric_limits<Real>::digits10 );
    std::cout.setf( std::ios::scientific );
  };

  // Deconstructor
  ~CompressedSensingTool(){};

  //! @name Interface tools.
  //@{

  /**
   * \brief Wrapper to call any of the compressed sensing methods
   *
   * \param A ( M x N ) matrix of the linear system AX=B
   *
   * \param B ( M x num_rhs ) matrix of the linear system AX=B
   *
   * \param solutions (output) vector containing multiple solutions
   * to AX=B. Each entry in solutions is a ( N x num_rhs ) matrix
   * opts.solver=LS will return only one solution whilst methods such
   * as OMP, LARS, and LASSO will return a history of solutions
   *
   * \param opts specifies the method options
   *
   * \param opts_list specifies the method options that can be used to
   * reproduce all the solutions found.
   */
  void solve( RealMatrix &A,
	      RealMatrix &B,
	      RealMatrixArray &solutions,
	      CompressedSensingOptions &opts,
	      CompressedSensingOptionsList &opts_list );

  void set_linear_solver( CompressedSensingOptions &opts );

  LinearSolver_ptr get_linear_solver();
};

} // namespace Pecos

#endif //LINEAR_SOLVERS_HPP


//
// Copyright (c) 2016 - 2017 Mesh Consultants Inc.
// 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 "Geomlib_TriMeshSubdivide.h"

#include <iostream>
#include <vector>

#include <Urho3D/Core/Variant.h>

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

#pragma warning(push, 0)
#include <igl/edges.h>
#include <igl/edge_flaps.h>
#include <igl/unique_edge_map.h>
#pragma warning(pop)

#include "ConversionUtilities.h"
#include "TriMesh.h"

#pragma warning(disable : 4244)

// collection of helper functions quarantined in anonymous namespace
namespace {

	// reviewed: CHECK
	int look_up_edge_index(const Eigen::MatrixXi& E, int s, int t)
	{
		int edge_index = -1;
		for (unsigned e = 0; e < E.rows(); ++e) {
			if ((E(e, 0) == s && E(e, 1) == t) || (E(e, 0) == t && E(e, 1) == s)) {
				edge_index = e;
				break;
			}
		}
		return edge_index;
	}

	void face_edge_map(
		const Eigen::MatrixXi& F,
		const Eigen::MatrixXi& E,
		Eigen::MatrixXi& FE)
	{
		FE.setZero(F.rows(), 3);

		Eigen::VectorXi EMAP;
		Eigen::MatrixXi NE, EF, EI;
		igl::edge_flaps(F, NE, EMAP, EF, EI);

		for (int f = 0; f < F.rows(); ++f) {
			for (int i = 0; i < 3; ++i) {
				int j = (i + 1) % 3;
				int k = (i + 2) % 3;
				int r, s, t;
				r = F(f, i);
				s = F(f, j);
				t = F(f, k);
				int e = look_up_edge_index(E, s, t);
				FE(f, i) = e;
			}
		}
		return;
	}

	// If a row of F has entries
	//   a, b, c
	// Then the corresponding row for FM has entries
	//   i, j, k
	// where
	//   i is the index (into edge-subdivided vertex matrix) of the midpoint of the edge joining (b,c)
	//   j is the index (into edge-subdivided vertex matrix) of the midpoint of the edge joining (c,a)
	//   k is the index (into edge-subdivided vertex matrix) of the midpoint of the edge joining (a,b)
	void face_edge_midpoint_map(
		int numVertices,
		const Eigen::MatrixXi& F,
		const Eigen::MatrixXi& E,
		Eigen::MatrixXi& FM)
	{
		FM.setZero(F.rows(), 3);

		for (int f = 0; f < F.rows(); ++f) {
			for (int i = 0; i < 3; ++i) {
				int j = (i + 1) % 3;
				int k = (i + 2) % 3;
				int r, s, t;
				r = F(f, i);
				s = F(f, j);
				t = F(f, k);
				int e = look_up_edge_index(E, s, t);
				FM(f, i) = e + numVertices;
			}
		}
		return;
	}

	void subdivide_mesh(
		const Eigen::MatrixXd& OV,
		const Eigen::MatrixXi& OF,
		Eigen::MatrixXd& NV,
		Eigen::MatrixXi& NF)
	{
		Eigen::MatrixXi E;
		Eigen::MatrixXi uE;
		Eigen::VectorXi EMAP;
		std::vector<std::vector<int> > uE2E;
		igl::unique_edge_map(OF, E, uE, EMAP, uE2E);

		// add midpoints to edge-subdivided vertex matrix NV
		NV.setZero(OV.rows() + uE.rows(), 3);
		NV.block(0, 0, OV.rows(), 3) = OV;
		for (int e = 0; e < uE.rows(); ++e) {
			int v, w;
			v = uE(e, 0);
			w = uE(e, 1);
			NV.row(OV.rows() + e) = 0.5 * (OV.row(v) + OV.row(w));
		}

		// compute subdivided face data into NF
		NF.setZero(4 * OF.rows(), 3);
		for (int f = 0; f < OF.rows(); ++f) {
			int v0, v1, v2;
			v0 = OV.rows() + EMAP(f);
			v1 = OV.rows() + EMAP(OF.rows() + f);
			v2 = OV.rows() + EMAP(2 * OF.rows() + f);
			//
			NF.row(4 * f) = Eigen::RowVector3i(v0, v1, v2);
			NF.row(4 * f + 1) = Eigen::RowVector3i(OF(f, 0), v2, v1);
			NF.row(4 * f + 2) = Eigen::RowVector3i(v2, OF(f, 1), v0);
			NF.row(4 * f + 3) = Eigen::RowVector3i(v1, v0, OF(f, 2));
		}
		return;
	}

	void subdivide_mesh(
		const Eigen::MatrixXd& OV,
		const Eigen::MatrixXi& OF,
		const Eigen::VectorXi& KEEP,
		Eigen::MatrixXd& NV,
		Eigen::MatrixXi& NF,
		Eigen::VectorXi& NKEEP)
	{
		Eigen::MatrixXi E;
		Eigen::MatrixXi uE;
		Eigen::VectorXi EMAP;
		std::vector<std::vector<int> > uE2E;
		igl::unique_edge_map(OF, E, uE, EMAP, uE2E);

		NKEEP.setZero(4 * KEEP.rows());

		// add midpoints to edge-subdivided vertex matrix NV
		NV.setZero(OV.rows() + uE.rows(), 3);
		NV.block(0, 0, OV.rows(), 3) = OV;
		for (int e = 0; e < uE.rows(); ++e) {
			int v, w;
			v = uE(e, 0);
			w = uE(e, 1);
			NV.row(OV.rows() + e) = 0.5 * (OV.row(v) + OV.row(w));
		}

		// compute subdivided face data into NF
		NF.setZero(4 * OF.rows(), 3);
		for (int f = 0; f < OF.rows(); ++f) {
			int v0, v1, v2;
			v0 = OV.rows() + EMAP(f);
			v1 = OV.rows() + EMAP(OF.rows() + f);
			v2 = OV.rows() + EMAP(2 * OF.rows() + f);
			//
			NF.row(4 * f) = Eigen::RowVector3i(v0, v1, v2);
			NF.row(4 * f + 1) = Eigen::RowVector3i(OF(f, 0), v2, v1);
			NF.row(4 * f + 2) = Eigen::RowVector3i(v2, OF(f, 1), v0);
			NF.row(4 * f + 3) = Eigen::RowVector3i(v1, v0, OF(f, 2));
			int val = KEEP(f);
			NKEEP(4 * f) = val;
			NKEEP(4 * f + 1) = val;
			NKEEP(4 * f + 2) = val;
			NKEEP(4 * f + 3) = val;
		}
		return;
	}

	void double_subdivide_mesh(
		const Eigen::MatrixXd& OV,
		const Eigen::MatrixXi& OF,
		Eigen::MatrixXd& NV,
		Eigen::MatrixXi& NF)
	{
		Eigen::MatrixXd V;
		Eigen::MatrixXi F;
		subdivide_mesh(OV, OF, V, F);
		subdivide_mesh(V, F, NV, NF);

		return;
	}

	void subdivide_mesh(
		const Eigen::MatrixXd& OV,
		const Eigen::MatrixXi& OF,
		const Eigen::VectorXi& KEEP,
		Eigen::MatrixXd& NV,
		Eigen::MatrixXi& NF,
		Eigen::VectorXi& NKEEP,
		int steps)
	{
		Eigen::MatrixXd V = OV;
		Eigen::MatrixXi F = OF;
		Eigen::VectorXi nKEEP = KEEP;
		for (int i = 0; i < steps; ++i) {
			subdivide_mesh(V, F, nKEEP, NV, NF, NKEEP);
			V.setZero(NV.rows(), 3);
			F.setZero(NF.rows(), 3);
			nKEEP.setZero(NKEEP.rows());
			V = NV;
			F = NF;
			nKEEP = NKEEP;
		}
		return;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////

	// Inputs:
	//     V: #V by 3 list of vertices
	//     F: #F by 3 list of faces into V
	//     KEEP: #F by 1 list taking values -1 (in shadow), 0 (on shadow-light boundary), 1 (in sunlight)
	// Outputs:
	//     NV: #NV by 3 list of vertices in the subdivided mesh
	//     NF: #NF by 3 list of faces (into NV) of the subdivided mesh
	//     NKEEP: #NF by 1 list taking values -1, 0, 1
	void subdivide_step(
		const Eigen::MatrixXd& V,
		const Eigen::MatrixXi& F,
		const Eigen::VectorXi& KEEP,
		Eigen::MatrixXd& NV,
		Eigen::MatrixXi& NF,
		Eigen::VectorXi& NKEEP)
	{
		Eigen::VectorXi nKEEP = KEEP;

		// compute adjacency data
		Eigen::MatrixXi E;
		Eigen::MatrixXi uE;
		Eigen::VectorXi EMAP;
		std::vector<std::vector<int> > uE2E;
		igl::unique_edge_map(F, E, uE, EMAP, uE2E);

		// set up the new vertex matrix
		// make it as large as it could possibly need to be, i.e., in case every face gets subdivided
		NV.setZero(V.rows() + uE.rows(), 3);
		NV.block(0, 0, V.rows(), 3) = V;
		// set up the new face matrix
		// make it as large as it could possibly need to be, i.e., in case every face gets subdivided
		NF.resize(4 * F.rows(), 3);
		NF = Eigen::MatrixXi::Constant(4 * F.rows(), 3, -1);
		NF.block(0, 0, F.rows(), 3) = F;
		NKEEP.setZero(NF.rows());

		// FIRST PASS of FACE SUBDIVISION

		// Rows of SPLIT correspond to rows of uE (unordered edges).
		// If row e of SPLIT has value -1 then row e (index into uE) has not yet been split.
		// If row e of SPLIT has non-negative value i then edge e has been split,
		// in the sense that its midpoint has been added to the updated vertex matrix
		// and its index there is i.
		Eigen::VectorXi SPLIT = Eigen::VectorXi::Constant(uE.rows(), -1);

		int facesDivided = 0;
		int edgesSplit = 0;
		std::vector<int> undivided_faces;
		for (int f = 0; f < F.rows(); ++f) {
			// indices into V (and therefore into NV) of vertices of face f
			int i, j, k;
			i = F(f, 0);
			j = F(f, 1);
			k = F(f, 2);
			// indices into uE of edges of face f
			int e0, e1, e2;
			e0 = EMAP(f);
			e1 = EMAP(F.rows() + f);
			e2 = EMAP(2 * F.rows() + f);
			// indices into NV of midpoints of edges e0, e1, e2
			int m0, m1, m2;

			if (nKEEP(f) == 0) {
				// split edges with indices e0, e1, e2 if they have not already been split

				// split e0 if necessary
				int v = SPLIT(e0);
				m0 = v;
				if (v == -1) {
					v = V.rows() + edgesSplit;
					SPLIT(e0) = v;
					m0 = v;
					// do the split
					NV.row(v) = 0.5 * (V.row(uE(e0, 0)) + V.row(uE(e0, 1)));
					edgesSplit++;
				}
				// split e1 if necessary
				v = SPLIT(e1);
				m1 = v;
				if (v == -1) {
					v = V.rows() + edgesSplit;
					SPLIT(e1) = v;
					m1 = v;
					// do the split
					NV.row(v) = 0.5 * (V.row(uE(e1, 0)) + V.row(uE(e1, 1)));
					edgesSplit++;
				}
				// split e2 if necessary
				v = SPLIT(e2);
				m2 = v;
				if (v == -1) {
					v = V.rows() + edgesSplit;
					SPLIT(e2) = v;
					m2 = v;
					// do the split
					NV.row(v) = 0.5 * (V.row(uE(e2, 0)) + V.row(uE(e2, 1)));
					edgesSplit++;
				}

				// Now all the edges are split (whether we did it just now or it was split at a previous face).
				// face f has vertices with indices i,j,k into NV
				// midpoints of edges have indices m0,m1,m2 into NV
				// and the four faces should go into NF with indices
				//   f (inner face overwrites big original face)
				//   f0, f1, f2
				int f0, f1, f2;
				f0 = F.rows() + 3 * facesDivided;
				f1 = f0 + 1;
				f2 = f0 + 2;
				facesDivided++;
				NF.row(f) = Eigen::RowVector3i(m0, m1, m2);
				NF.row(f0) = Eigen::RowVector3i(i, m2, m1);
				NF.row(f1) = Eigen::RowVector3i(m2, j, m0);
				NF.row(f2) = Eigen::RowVector3i(m1, m0, k);
			}
			else {
				undivided_faces.push_back(f);
			}
		}

		// clean up new vertex matrix NV
		NV.conservativeResize(V.rows() + edgesSplit, 3);

		// SECOND PASS of FACE SUBDIVISION, i.e., clean up

		for (int i = 0; i < undivided_faces.size(); ++i) {
			int f = undivided_faces[i];
			int e0, e1, e2;
			e0 = EMAP(f);
			e1 = EMAP(f + F.rows());
			e2 = EMAP(f + 2 * F.rows());
			int m0, m1, m2;
			m0 = SPLIT(e0);
			m1 = SPLIT(e1);
			m2 = SPLIT(e2);
			int v0, v1, v2;
			v0 = F(f, 0);
			v1 = F(f, 1);
			v2 = F(f, 2);
			int f0, f1, f2;
			f0 = F.rows() + 3 * facesDivided;
			f1 = f0 + 1;
			f2 = f0 + 2;
			facesDivided++;

			int l = nKEEP(f);
			NKEEP(f) = l;
			NKEEP(f0) = l;
			NKEEP(f1) = l;
			NKEEP(f2) = l;

			if (m0 == m1 && m1 == m2) {
				// face has had NO edges split
			}
			else if (m0 == m1) {
				// face has had only edge e2 split
				NF.row(f) = Eigen::RowVector3i(v0, m2, v2);
				NF.row(f0) = Eigen::RowVector3i(m2, v1, v2);
			}
			else if (m1 == m2) { // -1, -1, m0
								 // face has had only edge e0 split
				NF.row(f) = Eigen::RowVector3i(v1, m0, v0);
				NF.row(f0) = Eigen::RowVector3i(m0, v2, v0);
			}
			else if (m2 == m0) { // -1, -1, m1
								 // face has had only edge e1 split
				NF.row(f) = Eigen::RowVector3i(v2, m1, v1);
				NF.row(f0) = Eigen::RowVector3i(m1, v0, v1);
			}
			else {
				if (m0 == -1) {
					// face has had edges e1, e2 split
					NF.row(f) = Eigen::RowVector3i(v1, v2, m2);
					NF.row(f0) = Eigen::RowVector3i(m2, v2, m1);
					NF.row(f1) = Eigen::RowVector3i(m2, m1, v0);
				}
				else if (m1 == -1) {
					// face has had edges e0, e2 split
					NF.row(f) = Eigen::RowVector3i(v2, v0, m0);
					NF.row(f0) = Eigen::RowVector3i(m0, v0, m2);
					NF.row(f1) = Eigen::RowVector3i(m0, m2, v1);
				}
				else if (m2 == -1) {
					// face has had edges e0, e1 split
					NF.row(f) = Eigen::RowVector3i(v0, v1, m1);
					NF.row(f0) = Eigen::RowVector3i(m1, v1, m0);
					NF.row(f1) = Eigen::RowVector3i(m1, m0, v2);
				}
				else {
					// all three edges e0, e1, e2 have been split
					// face f has vertices with indices v0, v1, v2 into NV
					// midpoints of edges have indices m0, m1, m2 into NV
					// and the four faces should go into NF with indices
					//   f (inner face overwrites big original face)
					//   f0, f1, f2
					NF.row(f) = Eigen::RowVector3i(m0, m1, m2);
					NF.row(f0) = Eigen::RowVector3i(v0, m2, m1);
					NF.row(f1) = Eigen::RowVector3i(m2, v1, m0);
					NF.row(f2) = Eigen::RowVector3i(m1, m0, v2);
				}
			}

		}

		// clean up new face matrix NF and new kept faces vector NKEEP
		std::vector<int> faces;
		std::vector<int> keep;
		for (int f = 0; f < NF.rows(); ++f) {
			if (NF(f, 0) != -1) {
				faces.push_back(NF(f, 0));
				faces.push_back(NF(f, 1));
				faces.push_back(NF(f, 2));
				keep.push_back(NKEEP(f));
			}
		}
		int numFaces = faces.size() / 3;
		NF.resize(numFaces, 3);
		NKEEP.resize(numFaces);
		for (int f = 0; f < NF.rows(); ++f) {
			NF(f, 0) = faces[3 * f + 0];
			NF(f, 1) = faces[3 * f + 1];
			NF(f, 2) = faces[3 * f + 2];
			NKEEP(f) = keep[f];
		}

		return;
	}

	void smart_subdivide_mesh(
		const Eigen::MatrixXd& OV,
		const Eigen::MatrixXi& OF,
		Eigen::MatrixXd& NV,
		Eigen::MatrixXi& NF,
		Eigen::VectorXi& NKEEP,
		int steps)
	{

		Eigen::MatrixXd V;
		Eigen::MatrixXi F;
		Eigen::VectorXi nKEEP;

		Eigen::VectorXi KEEP = Eigen::VectorXi::Constant(OF.rows(), 0);
		subdivide_step(OV, OF, KEEP, NV, NF, NKEEP);

		if (steps > 1) {
			V.resize(NV.rows(), 3);
			V = NV;
			F.resize(NF.rows(), 3);
			F = NF;
			nKEEP.resize(NKEEP.rows());
			nKEEP = NKEEP;
		}

		for (int i = 1; i < steps; ++i) {
			subdivide_step(V, F, nKEEP, NV, NF, NKEEP);
			// update intermediate matrices
			V.resize(NV.rows(), 3);
			V = NV;
			F.resize(NF.rows(), 3);
			F = NF;
			nKEEP.resize(NKEEP.rows());
			nKEEP = NKEEP;
		}
		return;
	}

	void subdivide_mesh(
		const Eigen::MatrixXd& OV,
		const Eigen::MatrixXi& OF,
		Eigen::MatrixXd& NV,
		Eigen::MatrixXi& NF,
		int steps)
	{
		Eigen::MatrixXd V = OV;
		Eigen::MatrixXi F = OF;
		for (int i = 0; i < steps; ++i) {
			subdivide_mesh(V, F, NV, NF);
			V.setZero(NV.rows(), 3);
			F.setZero(NF.rows(), 3);
			V = NV;
			F = NF;
		}
		return;
	}
}

bool Geomlib::TriMeshSubdivide(
	const Urho3D::Variant& meshIn,
	int steps,
	Urho3D::Variant& meshOut
)
{
	if (!TriMesh_Verify(meshIn)) {
		meshOut = Urho3D::Variant();
		return false;
	}

	Eigen::MatrixXf V;
	Eigen::MatrixXi F;
	IglMeshToMatrices(meshIn, V, F);

	Eigen::MatrixXd Vd = IglFloatToDouble(V);
	Eigen::MatrixXd NVd;
	Eigen::MatrixXi NF;

	subdivide_mesh(Vd, F, NVd, NF, steps);

	Eigen::MatrixXf NV = IglDoubleToFloat(NVd);

	meshOut = TriMesh_Make(NV, NF);
	return true;
}


//=======================================================================
// Copyright (c) 2014
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================
/**
 * @file frequent_directions_example.cpp
 * @brief
 * @author Tomasz Strozak
 * @version 1.0
 * @date 2014-11-31
 */

//! [Frequent Directions Example]
#include "paal/sketch/frequent_directions.hpp"
#include "paal/utils/irange.hpp"
#include "paal/utils/print_collection.hpp"

#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/matrix_proxy.hpp>

#include <iostream>
#include <vector>

using coordinate_t = double;
using matrix_t = boost::numeric::ublas::matrix<coordinate_t>;

int main() {
    std::size_t const rows_count = 5;
    std::size_t const columns_count = 3;
    std::size_t const sketch_size = 4;

    auto fd_sketch = paal::make_frequent_directions<coordinate_t>(sketch_size, columns_count);

    matrix_t data(rows_count, columns_count);
    for (auto i : paal::irange(rows_count)) {
        for (auto j : paal::irange(columns_count)) {
            data(i, j) = i + j;
        }
    }
    fd_sketch.update(std::move(data));

    std::vector<std::vector<coordinate_t>> rows = {
        {2, 1, 0},
        {3, 2, 1},
        {4, 3, 2},
        {5, 4, 3},
        {6, 5, 4}};
    fd_sketch.update_range(std::move(rows));

    auto row = {7.0, 6.0, 5.0};
    fd_sketch.update_row(std::move(row));

    fd_sketch.compress();

    auto actual_size = fd_sketch.get_sketch().second;
    std::cout << "Actual sketch size: " << actual_size << std::endl;
    auto sketch = fd_sketch.get_sketch().first;
    boost::numeric::ublas::matrix_range<matrix_t> sketch_range (sketch,
         boost::numeric::ublas::range(0, actual_size),
         boost::numeric::ublas::range(0, columns_count));
    std::cout << "Sketch data:" << std::endl;
    paal::print_matrix(std::cout, sketch_range, " ");
    std::cout << std::endl;

    return 0;
}

//! [Frequent Directions Example]


#include <Eigen/Core>
#include <Eigen/Dense>
#include <iostream>
#include <Eigen/Geometry>

#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <string>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/visualization/point_cloud_color_handlers.h>

#include <pcl/filters/passthrough.h>
#include <pcl/segmentation/sac_segmentation.h>
#include <pcl/filters/project_inliers.h>
#include <pcl/surface/concave_hull.h>

std::string CLOUDIN = "/home/mobrob/cpp/tutorials_flat/pointclouds/table_scene_mug_stereo_textured.pcd";

int
main (int argc, char** argv)
{
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>),
																			cloud_projected(new pcl::PointCloud<pcl::PointXYZ>),
																			cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);
	pcl::PCDReader reader;
	reader.read (CLOUDIN, *cloud);

	// Build a filter to remove spurious NaNs
	pcl::PassThrough<pcl::PointXYZ> pass;
	pass.setInputCloud (cloud);
	pass.setFilterFieldName ("z");
	pass.setFilterLimits (0, 1.1);
	pass.filter (*cloud_filtered);
	std::cerr << "PointCloud after filtering has: " << cloud_filtered->points.size () << " data points." << std::endl;

	pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients);
	pcl::PointIndices::Ptr inliers (new pcl::PointIndices);
	// Create the segmentation object
	pcl::SACSegmentation<pcl::PointXYZ> seg;
	// Optional
	seg.setOptimizeCoefficients (true);
	// Mandatory
	seg.setModelType (pcl::SACMODEL_PLANE);
	seg.setMethodType (pcl::SAC_RANSAC);
	seg.setDistanceThreshold (0.01);

	seg.setInputCloud (cloud_filtered);
	seg.segment (*inliers, *coefficients);

	// Project the model inliers
	pcl::ProjectInliers<pcl::PointXYZ> proj;
	proj.setModelType (pcl::SACMODEL_PLANE);
	proj.setInputCloud (cloud_filtered);
	proj.setIndices (inliers);
	proj.setModelCoefficients (coefficients);
	proj.filter (*cloud_projected);

	// Create a Convex Hull representation of the projected inliers
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_hull (new pcl::PointCloud<pcl::PointXYZ>);
	pcl::ConvexHull<pcl::PointXYZ> chull;
	chull.setInputCloud (cloud_projected);
	chull.reconstruct (*cloud_hull);

	std::cerr << "Convex hull has: " << cloud_hull->points.size () << " data points." << std::endl;

	pcl::visualization::PCLVisualizer viser("cloud_cluster");
	pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> single_color1(cloud_hull, 150, 150, 0);
	pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> single_color2(cloud_projected, 0, 150, 200);
	pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> single_color3(cloud_filtered, 150, 0, 200);
	viser.addPointCloud (cloud_hull, single_color1, "cloud_hull");
	viser.addPointCloud (cloud_projected, single_color2, "cloud_projected");
	viser.addPointCloud (cloud_filtered, single_color3, "cloud_filtered");
	viser.addCoordinateSystem (1.0);
	viser.spin ();
	1;

	return (0);
}


/**
 * @file BoostGeometryRegister.hpp
 * @author bwu
 * @brief Adaption of the generic geometry models to boost geometry concept
 * @version 0.1
 * @date 2022-02-22
 */
#ifndef GENERIC_GEOMETRY_BOOSTGEOMETRYREGISTER_HPP
#define GENERIC_GEOMETRY_BOOSTGEOMETRYREGISTER_HPP
#include <boost/geometry/core/coordinate_dimension.hpp>
#include <boost/geometry/geometries/geometries.hpp>
#include <boost/geometry/core/coordinate_type.hpp>
#include <boost/geometry/core/mutable_range.hpp>
#include <boost/geometry/core/point_order.hpp>
#include <boost/range/mutable_iterator.hpp>
#include <boost/geometry/core/closure.hpp>
#include <boost/geometry/core/access.hpp>
#include <boost/range/const_iterator.hpp>
#include <boost/geometry/core/tags.hpp>
#include <boost/geometry/geometry.hpp>
#include <boost/geometry/core/cs.hpp>
#include "Geometries.hpp"

namespace generic {
namespace geometry{
    template <typename num_type>
    class PolygonWithHoles2DRange;
}
}

namespace boost {
namespace geometry {
namespace traits {

using namespace generic::geometry;
//point
template <typename num_type>
struct tag<Point2D<num_type> >
{ using type = point_tag; };

template <typename num_type>
struct dimension<Point2D<num_type> > : boost::mpl::int_<2> {};

template <typename num_type>
struct coordinate_type<Point2D<num_type> >
{ using type = num_type; };

template <typename num_type>
struct coordinate_system<Point2D<num_type> >
{ using type = cs::cartesian; };

template <typename num_type>
struct access<Point2D<num_type>, 0>
{
    static inline num_type get(const Point2D<num_type> & p) { return p[0]; }
    static inline void set(Point2D<num_type> & p, const num_type & n) { p[0] = n; }
};

template <typename num_type>
struct access<Point2D<num_type>, 1>
{
    static inline num_type get(const Point2D<num_type> & p) { return p[1]; }
    static inline void set(Point2D<num_type> & p, const num_type & n) { p[1] = n; }
};

//segment
template <typename num_type>
struct tag<Segment2D<num_type> >
{ using type = segment_tag; };

template <typename num_type>
struct point_type<Segment2D<num_type> >
{ using type = Point2D<num_type>; };

template <typename num_type, size_t dimension>
struct indexed_access<Segment2D<num_type>, 0, dimension>
{
    static inline num_type get(const Segment2D<num_type> & s) { return geometry::get<dimension>(s[0]); }
    static inline void set(Segment2D<num_type> & s, const num_type & n) { geometry::set<dimension>(s[0], n); }
};

template <typename num_type, size_t dimension>
struct indexed_access<Segment2D<num_type>, 1, dimension>
{
    static inline num_type get(const Segment2D<num_type> & s) { return geometry::get<dimension>(s[1]); }
    static inline void set(Segment2D<num_type> & s, const num_type & n) { geometry::set<dimension>(s[1], n); }
};

//box
template <typename num_type>
struct tag<Box2D<num_type> >
{ using type = box_tag; };

template <typename num_type>
struct point_type<Box2D<num_type> >
{ using type = Point2D<num_type>; };

template <typename num_type, size_t dimension>
struct indexed_access<Box2D<num_type>, 0, dimension>
{
    static inline num_type get(const Box2D<num_type> & b) { return geometry::get<dimension>(b[0]); }
    static inline void set(Box2D<num_type> & b, const num_type & n) { geometry::set<dimension>(b[0], n); }
};

template <typename num_type, size_t dimension>
struct indexed_access<Box2D<num_type>, 1, dimension>
{
    static inline num_type get(const Box2D<num_type> & b) { return geometry::get<dimension>(b[1]); }
    static inline void set(Box2D<num_type> & b, const num_type & n) { geometry::set<dimension>(b[1], n); }
};

//triangle
template <typename num_type>
struct tag<Triangle2D<num_type> >
{ using type = ring_tag; };

template <typename num_type>
struct closure<Triangle2D<num_type> >
{ static const closure_selector value = open; };

//linestring
template <typename num_type>
struct tag<Polyline2D<num_type> >
{ using type = linestring_tag; };

//polygon
template <typename num_type>
struct tag<Polygon2D<num_type> >
{ using type = ring_tag; };

template <typename num_type>
struct closure<Polygon2D<num_type> >
{ static const closure_selector value = open; };

template <typename num_type>
struct clear<Polygon2D<num_type> >
{
    static inline void apply(Polygon2D<num_type> & polygon)
    {
        polygon.Clear();
    }
};

template <typename num_type>
struct push_back<Polygon2D<num_type> >
{
    static inline void apply(Polygon2D<num_type> & polygon, const Point2D<num_type> & point)
    {
        polygon.Insert(polygon.End(), point);
    }
};

template <typename num_type>
struct resize<Polygon2D<num_type> >
{
    using point_type = typename Polygon2D<num_type>::point_t;
    static inline void apply(Polygon2D<num_type> & polygon, size_t size)
    {
        polygon.Resize(size);
    }
};

//polygon with holes
template <typename num_type>
struct tag<PolygonWithHoles2D<num_type> >
{ using type = polygon_tag; };

template <typename num_type>
struct ring_const_type<PolygonWithHoles2D<num_type> >
{ using type = const typename PolygonWithHoles2D<num_type>::outline_type; };

template <typename num_type>
struct ring_mutable_type<PolygonWithHoles2D<num_type> >
{ using type = typename PolygonWithHoles2D<num_type>::outline_type; };

template <typename num_type>
struct interior_const_type<PolygonWithHoles2D<num_type> >
{ using type = const PolygonWithHoles2DRange<num_type>; };

template <typename num_type>
struct interior_mutable_type<PolygonWithHoles2D<num_type> >
{ using type = PolygonWithHoles2DRange<num_type>; };

template <typename num_type>
struct exterior_ring<PolygonWithHoles2D<num_type> >
{
    using outline_type = typename PolygonWithHoles2D<num_type>::outline_type;
    using const_outline_type = typename PolygonWithHoles2D<num_type>::outline_type const;
    static inline outline_type & get(PolygonWithHoles2D<num_type> & pwh)
    {
        return pwh.outline;
    }

    static inline const_outline_type & get(const PolygonWithHoles2D<num_type> & pwh)
    {
        return pwh.outline;
    }
};

template <typename num_type>
struct interior_rings<PolygonWithHoles2D<num_type> >
{
    static inline PolygonWithHoles2DRange<num_type> get(PolygonWithHoles2D<num_type> & pwh)
    {
        return PolygonWithHoles2DRange<num_type>(pwh.holes.begin(), pwh.holes.end());
    }

    static inline const PolygonWithHoles2DRange<num_type> get(const PolygonWithHoles2D<num_type> & pwh)
    {
        return PolygonWithHoles2DRange<num_type>(pwh.holes.begin(), pwh.holes.end());
    }
};

}//namespace traits
}//namespace geometry
}//namespace boost

namespace boost {

using namespace generic::geometry;
//triangle
template <typename num_type>
struct range_mutable_iterator<Triangle2D<num_type> >
{
    using type = typename Triangle2D<num_type>::iterator;
};

template <typename num_type>
struct range_const_iterator<Triangle2D<num_type> >
{
    using type = typename Triangle2D<num_type>::const_iterator;
};

template<typename num_type>
struct range_size<Triangle2D<num_type> >
{
    using type = std::size_t;
};

//polygon
template <typename num_type>
struct range_mutable_iterator<Polygon2D<num_type> >
{
    using type = typename Polygon2D<num_type>::point_iterator;
};

template <typename num_type>
struct range_const_iterator<Polygon2D<num_type> >
{
    using type = typename Polygon2D<num_type>::const_point_iterator;
};

template<typename num_type>
struct range_size<Polygon2D<num_type> >
{
    using type = std::size_t;
};

//polygon with hole
template <typename num_type>
struct range_mutable_iterator<PolygonWithHoles2DRange<num_type> >
{
    using type = typename PolygonWithHoles2DRange<num_type>::hole_iterator;
};

template <typename num_type>
struct range_const_iterator<PolygonWithHoles2DRange<num_type> >
{
    using type = typename PolygonWithHoles2DRange<num_type>::const_hole_iterator;
};

template<typename num_type>
struct range_size<PolygonWithHoles2DRange<num_type> >
{
    using type = std::size_t;
};

}//namesapce boost

namespace generic {
namespace geometry{
//triangle
template<typename num_type>
inline typename Triangle2D<num_type>::iterator
range_begin(Triangle2D<num_type> & triangle)
{
    return triangle.Begin();
}

template<typename num_type>
inline typename Triangle2D<num_type>::const_iterator
range_begin(const Triangle2D<num_type> & triangle)
{
    return triangle.ConstBegin();
}

template<typename num_type>
inline typename Triangle2D<num_type>::iterator
range_end(Triangle2D<num_type> & triangle)
{
    return triangle.End();
}

template<typename num_type>
inline typename Triangle2D<num_type>::const_iterator
range_end(const Triangle2D<num_type> & triangle)
{
    return triangle.ConstEnd();
}

template<typename num_type>
inline std::size_t range_calculate_size(const Triangle2D<num_type> & triangle)
{
    return 3;
}

//polygon
template<typename num_type>
inline typename Polygon2D<num_type>::point_iterator
range_begin(Polygon2D<num_type> & polygon)
{
    return polygon.Begin();
}

template<typename num_type>
inline typename Polygon2D<num_type>::const_point_iterator
range_begin(const Polygon2D<num_type> & polygon)
{
    return polygon.ConstBegin();
}

template<typename num_type>
inline typename Polygon2D<num_type>::point_iterator
range_end(Polygon2D<num_type> & polygon)
{
    return polygon.End();
}

template<typename num_type>
inline typename Polygon2D<num_type>::const_point_iterator
range_end(const Polygon2D<num_type> & polygon)
{
    return polygon.ConstEnd();
}

template<typename num_type>
inline std::size_t range_calculate_size(const Polygon2D<num_type> & polygon)
{
    return polygon.Size();
}

//polygon with holes
template <typename num_type>
class PolygonWithHoles2DRange
{
public:
    using hole_iterator = typename PolygonWithHoles2D<num_type>::hole_iterator;
    using const_hole_iterator = typename PolygonWithHoles2D<num_type>::const_hole_iterator;
    PolygonWithHoles2DRange() = delete;
    PolygonWithHoles2DRange(hole_iterator begin, hole_iterator end)
        : m_begin(begin), m_end(end), m_bConst(false) {}

    PolygonWithHoles2DRange(const_hole_iterator begin, const_hole_iterator end)
        : m_constBegin(begin), m_constEnd(end), m_bConst(true) {}

    hole_iterator Begin() { assert(!m_bConst); return m_begin; }
    hole_iterator End() { assert(!m_bConst); return m_end; }
    const_hole_iterator ConstBegin() const { assert(m_bConst); return m_constBegin; }
    const_hole_iterator ConstEnd() const { assert(m_bConst); return m_constEnd; }

    std::size_t Size() const;

private:
    hole_iterator m_begin;
    hole_iterator m_end;
    const_hole_iterator m_constBegin;
    const_hole_iterator m_constEnd;
    bool m_bConst = false;
};

template <typename num_type>
inline std::size_t PolygonWithHoles2DRange<num_type>::Size() const
{
    if(m_bConst) return std::distance(m_constBegin, m_constEnd);
    else return std::distance(m_begin, m_end);
}

template <typename num_type>
inline typename PolygonWithHoles2DRange<num_type>::hole_iterator
range_begin(PolygonWithHoles2DRange<num_type> & range) { return range.Begin(); }

template <typename num_type>
inline typename PolygonWithHoles2DRange<num_type>::const_hole_iterator
range_begin(const PolygonWithHoles2DRange<num_type> & range) { return range.ConstBegin(); }

template <typename num_type>
inline typename PolygonWithHoles2DRange<num_type>::hole_iterator
range_end(PolygonWithHoles2DRange<num_type> & range) { return range.End(); }

template <typename num_type>
inline typename PolygonWithHoles2DRange<num_type>::const_hole_iterator
range_end(const PolygonWithHoles2DRange<num_type> & range) { return range.ConstEnd(); }

template<typename num_type>
inline std::size_t range_calculate_size(const PolygonWithHoles2DRange<num_type> & range)
{ return range.Size(); }

}//namespace geometry
}//namespace generic

//adaptor
namespace generic  {
namespace geometry {

namespace bg = boost::geometry;

template <typename num_type>
using boost_point2d_t = bg::model::point<num_type, 2, bg::cs::cartesian>;

template <typename num_type, bool clockwise = false, bool closed = false,
          template <typename, typename> class container = std::vector,
          template <typename> class allocator = std::allocator>
using boost_ring_t = bg::model::ring<boost_point2d_t<num_type>, clockwise, closed, container, allocator>;

template <typename num_type, bool clockwise = false, bool closed = false,
          template <typename, typename> class point_list = std::vector,
          template <typename, typename> class ring_list = std::vector,
          template <typename> class point_alloc = std::allocator,
          template <typename> class ring_alloc = std::allocator>
using boost_polygon_t = bg::model::polygon<boost_point2d_t<num_type>, clockwise, closed, point_list, ring_list, point_alloc, ring_alloc>;

template <typename num_type>
inline Point2D<num_type> toPoint2D(const boost_point2d_t<num_type> & point)
{
    return Point2D<num_type>(bg::get<0>(point), bg::get<1>(point));
}

template <typename num_type, bool clockwise = false, bool closed = false,
          template <typename, typename> class container = std::vector,
          template <typename> class allocator = std::allocator>
inline Polygon2D<num_type> toPolygon2D(const boost_ring_t<num_type, clockwise, closed, container, allocator> & ring)
{
    Polygon2D<num_type> polygon;
    for(const auto & point : ring)
        polygon << toPoint2D(point);
    if(polygon.Size() && (polygon.Back() == polygon.Front()))
        polygon.PopBack();
    return polygon;
}

template <typename num_type, bool clockwise = false, bool closed = false,
          template <typename, typename> class point_list = std::vector,
          template <typename, typename> class ring_list = std::vector,
          template <typename> class point_alloc = std::allocator,
          template <typename> class ring_alloc = std::allocator>
inline PolygonWithHoles2D<num_type> toPolygonWithHoles2D(const boost_polygon_t<num_type, clockwise, closed, point_list, ring_list, point_alloc, ring_alloc> & polygon)
{
    PolygonWithHoles2D<num_type> pwh;
    pwh.outline = toPolygon2D(polygon.outer());
    for(const auto & hole : polygon.inners())
        pwh.holes.emplace_back(toPolygon2D(hole));
    return pwh;
}
}//namespace geometry
}//namespace generic

#endif//GENERIC_GEOMETRY_BOOSTGEOMETRYREGISTER_HPP


#include "all_your_base.h"
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>

using namespace std;

BOOST_AUTO_TEST_CASE(test1) {
    vector<unsigned int> in_digits{0};
    vector<unsigned int> out_digits = all_your_base::convert(10, in_digits, 2);
    vector<unsigned int> expected{};
    BOOST_REQUIRE_EQUAL_COLLECTIONS(expected.begin(), expected.end(),
                                    out_digits.begin(), out_digits.end());
}

BOOST_AUTO_TEST_CASE(single_bit_one_to_decimal) {
    vector<unsigned int> in_digits{1};
    vector<unsigned int> expected{1};
    vector<unsigned int> out_digits = all_your_base::convert(2, in_digits, 10);

    BOOST_REQUIRE_EQUAL_COLLECTIONS(expected.begin(), expected.end(),
                                    out_digits.begin(), out_digits.end());
}

BOOST_AUTO_TEST_CASE(binary_to_single_decimal) {
    vector<unsigned int> in_digits{1, 0, 1};
    vector<unsigned int> expected{5};
    vector<unsigned int> out_digits = all_your_base::convert(2, in_digits, 10);

    BOOST_REQUIRE_EQUAL_COLLECTIONS(expected.begin(), expected.end(),
                                    out_digits.begin(), out_digits.end());
}

BOOST_AUTO_TEST_CASE(single_decimal_to_binary) {
    vector<unsigned int> in_digits{5};
    vector<unsigned int> expected{1, 0, 1};
    vector<unsigned int> out_digits = all_your_base::convert(10, in_digits, 2);

    BOOST_REQUIRE_EQUAL_COLLECTIONS(expected.begin(), expected.end(),
                                    out_digits.begin(), out_digits.end());
}

BOOST_AUTO_TEST_CASE(binary_to_multiple_decimal) {
    vector<unsigned int> in_digits{1, 0, 1, 0, 1, 0};
    vector<unsigned int> expected{4, 2};
    vector<unsigned int> out_digits = all_your_base::convert(2, in_digits, 10);

    BOOST_REQUIRE_EQUAL_COLLECTIONS(expected.begin(), expected.end(),
                                    out_digits.begin(), out_digits.end());
}

BOOST_AUTO_TEST_CASE(decimal_to_binary) {
    vector<unsigned int> in_digits{4, 2};
    vector<unsigned int> expected{1, 0, 1, 0, 1, 0};
    vector<unsigned int> out_digits = all_your_base::convert(10, in_digits, 2);

    BOOST_REQUIRE_EQUAL_COLLECTIONS(expected.begin(), expected.end(),
                                    out_digits.begin(), out_digits.end());
}

BOOST_AUTO_TEST_CASE(trinary_to_hexadecimal) {
    vector<unsigned int> in_digits{1, 1, 2, 0};
    vector<unsigned int> expected{2, 10};
    vector<unsigned int> out_digits = all_your_base::convert(3, in_digits, 16);

    BOOST_REQUIRE_EQUAL_COLLECTIONS(expected.begin(), expected.end(),
                                    out_digits.begin(), out_digits.end());
}

BOOST_AUTO_TEST_CASE(hexadecimal_to_trinary) {
    vector<unsigned int> in_digits{2, 10};
    vector<unsigned int> expected{1, 1, 2, 0};
    vector<unsigned int> out_digits = all_your_base::convert(16, in_digits, 3);

    BOOST_REQUIRE_EQUAL_COLLECTIONS(expected.begin(), expected.end(),
                                    out_digits.begin(), out_digits.end());
}

BOOST_AUTO_TEST_CASE(fifteen_bit_integer) {
    vector<unsigned int> in_digits{3, 46, 60};
    vector<unsigned int> expected{6, 10, 45};
    vector<unsigned int> out_digits = all_your_base::convert(97, in_digits, 73);

    BOOST_REQUIRE_EQUAL_COLLECTIONS(expected.begin(), expected.end(),
                                    out_digits.begin(), out_digits.end());
}

BOOST_AUTO_TEST_CASE(empty_list) {
    vector<unsigned int> in_digits{};
    vector<unsigned int> expected{};
    vector<unsigned int> out_digits = all_your_base::convert(2, in_digits, 10);

    BOOST_REQUIRE_EQUAL_COLLECTIONS(expected.begin(), expected.end(),
                                    out_digits.begin(), out_digits.end());
}

BOOST_AUTO_TEST_CASE(single_zero) {
    vector<unsigned int> in_digits{0};
    vector<unsigned int> expected{};
    vector<unsigned int> out_digits = all_your_base::convert(10, in_digits, 2);

    BOOST_REQUIRE_EQUAL_COLLECTIONS(expected.begin(), expected.end(),
                                    out_digits.begin(), out_digits.end());
}

BOOST_AUTO_TEST_CASE(multiple_zeros) {
    vector<unsigned int> in_digits{0, 0, 0};
    vector<unsigned int> expected{};
    vector<unsigned int> out_digits = all_your_base::convert(10, in_digits, 2);

    BOOST_REQUIRE_EQUAL_COLLECTIONS(expected.begin(), expected.end(),
                                    out_digits.begin(), out_digits.end());
}

BOOST_AUTO_TEST_CASE(leading_zeros) {
    vector<unsigned int> in_digits{0, 6, 0};
    vector<unsigned int> expected{};
    vector<unsigned int> out_digits = all_your_base::convert(7, in_digits, 10);

    BOOST_REQUIRE_EQUAL_COLLECTIONS(expected.begin(), expected.end(),
                                    out_digits.begin(), out_digits.end());
}

BOOST_AUTO_TEST_CASE(first_base_is_one) {
    vector<unsigned int> in_digits{};
    vector<unsigned int> expected{};
    vector<unsigned int> out_digits = all_your_base::convert(1, in_digits, 10);

    BOOST_REQUIRE_EQUAL_COLLECTIONS(expected.begin(), expected.end(),
                                    out_digits.begin(), out_digits.end());
}

BOOST_AUTO_TEST_CASE(first_base_is_zero) {
    vector<unsigned int> in_digits{};
    vector<unsigned int> expected{};
    vector<unsigned int> out_digits = all_your_base::convert(0, in_digits, 10);

    BOOST_REQUIRE_EQUAL_COLLECTIONS(expected.begin(), expected.end(),
                                    out_digits.begin(), out_digits.end());
}

BOOST_AUTO_TEST_CASE(invalid_positive_digit) {
    vector<unsigned int> in_digits{1, 2, 1, 0, 1, 0};
    vector<unsigned int> expected{};
    vector<unsigned int> out_digits = all_your_base::convert(2, in_digits, 10);

    BOOST_REQUIRE_EQUAL_COLLECTIONS(expected.begin(), expected.end(),
                                    out_digits.begin(), out_digits.end());
}

BOOST_AUTO_TEST_CASE(second_base_is_one) {
    vector<unsigned int> in_digits{1, 0, 1, 0, 1, 0};
    vector<unsigned int> expected{};
    vector<unsigned int> out_digits = all_your_base::convert(2, in_digits, 1);

    BOOST_REQUIRE_EQUAL_COLLECTIONS(expected.begin(), expected.end(),
                                    out_digits.begin(), out_digits.end());
}

BOOST_AUTO_TEST_CASE(second_base_is_zero) {
    vector<unsigned int> in_digits{7};
    vector<unsigned int> expected{};
    vector<unsigned int> out_digits = all_your_base::convert(10, in_digits, 0);

    BOOST_REQUIRE_EQUAL_COLLECTIONS(expected.begin(), expected.end(),
                                    out_digits.begin(), out_digits.end());
}