#include <Eigen/Core>
#include <filesystem/resolver.h>
#include <fstream>
#include <nori/integrator.h>
#include <nori/ray.h>
#include <nori/scene.h>
#include <random>
#include <sh/default_image.h>
#include <sh/spherical_harmonics.h>
#include <stb_image.h>

NORI_NAMESPACE_BEGIN

namespace ProjEnv
{
    std::vector<std::unique_ptr<float[]>>
    LoadCubemapImages(const std::string &cubemapDir, int &width, int &height,
                      int &channel)
    {
        std::vector<std::string> cubemapNames{"negx.jpg", "posx.jpg", "posy.jpg",
                                              "negy.jpg", "posz.jpg", "negz.jpg"};
        std::vector<std::unique_ptr<float[]>> images(6);
        for (int i = 0; i < 6; i++)
        {
            std::string filename = cubemapDir + "/" + cubemapNames[i];
            int w, h, c;
            float *image = stbi_loadf(filename.c_str(), &w, &h, &c, 3);
            if (!image)
            {
                std::cout << "Failed to load image: " << filename << std::endl;
                exit(-1);
            }
            if (i == 0)
            {
                width = w;
                height = h;
                channel = c;
            }
            else if (w != width || h != height || c != channel)
            {
                std::cout << "Dismatch resolution for 6 images in cubemap" << std::endl;
                exit(-1);
            }
            images[i] = std::unique_ptr<float[]>(image);
            int index = (0 * 128 + 0) * channel;
            // std::cout << images[i][index + 0] << "\t" << images[i][index + 1] << "\t"
            //           << images[i][index + 2] << std::endl;
        }
        return images;
    }

    const Eigen::Vector3f cubemapFaceDirections[6][3] = {
        {{0, 0, 1}, {0, -1, 0}, {-1, 0, 0}},  // negx
        {{0, 0, 1}, {0, -1, 0}, {1, 0, 0}},   // posx
        {{1, 0, 0}, {0, 0, -1}, {0, -1, 0}},  // negy
        {{1, 0, 0}, {0, 0, 1}, {0, 1, 0}},    // posy
        {{-1, 0, 0}, {0, -1, 0}, {0, 0, -1}}, // negz
        {{1, 0, 0}, {0, -1, 0}, {0, 0, 1}},   // posz
    };

    float CalcPreArea(const float &x, const float &y)
    {
        return std::atan2(x * y, std::sqrt(x * x + y * y + 1.0));
    }

    float CalcArea(const float &u_, const float &v_, const int &width,
                   const int &height)
    {
        // transform from [0..res - 1] to [- (1 - 1 / res) .. (1 - 1 / res)]
        // ( 0.5 is for texel center addressing)
        float u = (2.0 * (u_ + 0.5) / width) - 1.0;
        float v = (2.0 * (v_ + 0.5) / height) - 1.0;

        // shift from a demi texel, mean 1.0 / size  with u and v in [-1..1]
        float invResolutionW = 1.0 / width;
        float invResolutionH = 1.0 / height;

        // u and v are the -1..1 texture coordinate on the current face.
        // get projected area for this texel
        float x0 = u - invResolutionW;
        float y0 = v - invResolutionH;
        float x1 = u + invResolutionW;
        float y1 = v + invResolutionH;
        float angle = CalcPreArea(x0, y0) - CalcPreArea(x0, y1) -
                      CalcPreArea(x1, y0) + CalcPreArea(x1, y1);

        return angle;
    }

    // template <typename T> T ProjectSH() {}

    template <size_t SHOrder>
    std::vector<Eigen::Array3f> PrecomputeCubemapSH(const std::vector<std::unique_ptr<float[]>> &images,
                                                    const int &width, const int &height,
                                                    const int &channel)
    {
        /// cubemapDirs，保存了cubemap 6张贴图上每个像素对应的单位向量
        std::vector<Eigen::Vector3f> cubemapDirs;
        cubemapDirs.reserve(6 * width * height);

        /// 写入cubemapDirs
        for (int i = 0; i < 6; i++)
        {
            Eigen::Vector3f faceDirX = cubemapFaceDirections[i][0];
            Eigen::Vector3f faceDirY = cubemapFaceDirections[i][1];
            Eigen::Vector3f faceDirZ = cubemapFaceDirections[i][2];
            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    float u = 2 * ((x + 0.5) / width) - 1;
                    float v = 2 * ((y + 0.5) / height) - 1;
                    // Eigen::Vector3f dir = (faceDirX * u + faceDirY * v + faceDirZ).normalized();
                    // cubemapDirs.push_back(dir);
                    cubemapDirs.emplace_back((faceDirX * u + faceDirY * v + faceDirZ).normalized());
                }
            }
        }

        constexpr int SHNum = (SHOrder + 1) * (SHOrder + 1);

        //        std::vector<Eigen::Array3f> SHCoeffiecents(SHNum);
        //        for (int i = 0; i < SHNum; i++)
        //            SHCoeffiecents[i] = Eigen::Array3f(0);
        std::vector<Eigen::Array3f> SHCoeffiecents(SHNum, Eigen::Array3f(0));
        /// fill up SHCoeffiecents

        /// unused?
        // float sumWeight = 0;
        for (int i = 0; i < 6; i++)
        {
            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    // TODO: here you need to compute light sh of each face of cubemap of each pixel
                    // TODO: 此处你需要计算每个像素下cubemap某个面的球谐系数

                    /// Note: requiring double in EvalSH()
                    const Eigen::Vector3d dir = cubemapDirs[i * width * height + y * width + x].cast<double>().normalized();
                    int index = (y * width + x) * channel;
                    /// L_env, environment light
                    Eigen::Array3f Le(images[i][index + 0], images[i][index + 1], images[i][index + 2]);

                    /// finish code here
                    for (int level = 0; level <= SHOrder; ++level)
                    {
                        for (int m = -level; m <= level; ++m)
                        {
                            SHCoeffiecents[sh::GetIndex(level, m)] += Le * sh::EvalSH(level, m, dir) * CalcArea(x, y, width, height);
                        }
                    }
                }
            }
        }
        return SHCoeffiecents;
    }
} // namespace ProjEnv

class PRTIntegrator : public Integrator
{
public: // static
    static constexpr int SHOrder = 2;

    static constexpr int SHCoeffLength = (SHOrder + 1) * (SHOrder + 1);

public: // class definition
    enum class Type
    {
        Unshadowed = 0,
        Shadowed = 1,
        Interreflection = 2
    };

public: // constructor
    PRTIntegrator(const PropertyList &props)
    {
        /* No parameters this time */
        m_SampleCount = props.getInteger("PRTSampleCount", 100);
        m_CubemapPath = props.getString("cubemap");
        auto type = props.getString("type", "unshadowed");
        if (type == "unshadowed")
        {
            m_Type = Type::Unshadowed;
        }
        else if (type == "shadowed")
        {
            m_Type = Type::Shadowed;
        }
        else if (type == "interreflection")
        {
            m_Type = Type::Interreflection;
            m_Bounce = props.getInteger("bounce", 1);
        }
        else
        {
            throw NoriException("Unsupported type: %s.", type);
        }
    }

public: // virtual func
    void preprocess(const Scene *scene) override
    {
        // Here only compute one mesh
        const auto mesh = scene->getMeshes()[0];
        // Projection environment
        /// get cubePath (file path)
        auto cubePath = getFileResolver()->resolve(m_CubemapPath);

        int width, height, channel;
        std::vector<std::unique_ptr<float[]>> images = ProjEnv::LoadCubemapImages(cubePath.str(), width, height, channel);

        /// write
        auto lightPath = cubePath / "light.txt";
        auto transPath = cubePath / "transport.txt";

        std::ofstream lightFout(lightPath.str());
        std::ofstream fout(transPath.str());

        /// get pre-computed SH coeffs
        auto envCoeffs = ProjEnv::PrecomputeCubemapSH<SHOrder>(images, width, height, channel);

        m_LightCoeffs.resize(3, SHCoeffLength);
        for (int i = 0; i < envCoeffs.size(); i++)
        {
            lightFout << (envCoeffs)[i].x() << " " << (envCoeffs)[i].y() << " " << (envCoeffs)[i].z() << std::endl;
            m_LightCoeffs.col(i) = (envCoeffs)[i];
        }
        std::cout << "Computed light sh coeffs from: " << cubePath.str() << " to: " << lightPath.str() << std::endl;

        // Projection transport
        /// (SHOrder+1)^2 rows, VertexCount cols
        /// SHCoeffLength = (SHOrder+1)^2
        m_TransportSHCoeffs.resize(SHCoeffLength, mesh->getVertexCount());
        /// write in file
        fout << mesh->getVertexCount() << std::endl;

        for (int i = 0; i < mesh->getVertexCount(); i++)
        {
            const Point3f &v = mesh->getVertexPositions().col(i);
            const Normal3f &n = mesh->getVertexNormals().col(i);

            /// input: w_i, output: L(w_i) * max(0, cos(\theta)).
            auto shFunc = [&](double phi, double theta) -> double
            {
                Eigen::Array3d d = sh::ToVector(phi, theta);

                /// sample vector
                const auto wi = Vector3f(d.x(), d.y(), d.z());

                double H = wi.dot(n);

                if (H < 0.0)
                    return 0.0;
                if (m_Type == Type::Unshadowed)
                {
                    return H;
                    // TODO: here you need to calculate unshadowed transport term of a given direction
                    // TODO: 此处你需要计算给定方向下的unshadowed传输项球谐函数值
                    // return 0;
                }
                else
                {
                    // test if there should be shadow
                    return (scene->rayIntersect(Ray3f(v, wi))) ? 0.0 : H;

                    // TODO: here you need to calculate shadowed transport term of a given direction
                    // TODO: 此处你需要计算给定方向下的shadowed传输项球谐函数值
                    // return 0;
                }
            };

            /// lambda is passed to sh::ProjectFunction
            /// sh::ProjectFunction returns std::vector<double>
            auto shCoeff = sh::ProjectFunction(SHOrder, shFunc, m_SampleCount);
            for (int j = 0; j < shCoeff->size(); j++)
            {
                m_TransportSHCoeffs.col(i).coeffRef(j) = (*shCoeff)[j];
            }
        }
        if (m_Type == Type::Interreflection)
        {
            Eigen::MatrixXf interreflectionCoefs;
            interreflectionCoefs.resize(SHCoeffLength, mesh->getVertexCount());

            for (int i = 0; i < mesh->getVertexCount(); ++i)
            {
                bool vis = false;
                for (int j = 0; j < SHCoeffLength; ++j)
                {
                    if (m_TransportSHCoeffs.col(i).coeffRef(j) != 0)
                    {
                        vis = true;
                        break;
                    }
                }
                if (vis)
                    continue;
                const Point3f v = mesh->getVertexPositions().col(i);
                const Normal3f n = mesh->getVertexNormals().col(i);
                auto shFunc = [&](double phi, double theta) -> std::vector<double>
                {
                    Eigen::Array3d d = sh::ToVector(phi, theta);

                    /// sample vector
                    const auto wi = Vector3f(d.x(), d.y(), d.z());

                    double H = wi.dot(n);
                    std::vector<double> ans(SHCoeffLength, 0.0);
                    if (H < 0.0)
                        return ans;
                    Intersection it;
                    bool intersect = scene->rayIntersect(Ray3f(v, wi), it);
                    if (!intersect)
                        return ans;
                    auto a = m_TransportSHCoeffs.col(it.tri_index.x());
                    auto b = m_TransportSHCoeffs.col(it.tri_index.y());
                    auto c = m_TransportSHCoeffs.col(it.tri_index.z());
                    auto x = it.bary.x();
                    auto y = it.bary.y();
                    auto z = it.bary.z();
                    auto result = (a * x + b * y + c * z) * H;
                    for (int i = 0; i < ans.size(); ++i)
                    {
                        ans[i] = result[i];
                    }
                    return ans;
                };

                const int sample_side = static_cast<int>(floor(sqrt(m_SampleCount)));
                // auto shCoeff = sh::ProjectFunction(SHOrder, shFunc, m_SampleCount);

                std::random_device rd;
                std::mt19937 gen(rd());
                std::uniform_real_distribution<> rng(0.0, 1.0);
                for (int t = 0; t < sample_side; t++)
                {
                    for (int p = 0; p < sample_side; p++)
                    {
                        double alpha = (t + rng(gen)) / sample_side;
                        double beta = (p + rng(gen)) / sample_side;
                        // See http://www.bogotobogo.com/Algorithms/uniform_distribution_sphere.php
                        double phi = 2.0 * M_PI * beta;
                        double theta = acos(2.0 * alpha - 1.0);

                        // evaluate the analytic function for the current spherical coords
                        auto func_value = shFunc(phi, theta);

                        // evaluate the SH basis functions up to band O, scale them by the
                        // function's value and accumulate them over all generated samples
                        for (int j = 0; j < SHCoeffLength; ++j)
                        {
                            interreflectionCoefs.col(i).coeffRef(j) += func_value[j];
                        }
                    }
                }

                // scale by the probability of a particular sample, which is
                // 4pi/sample_side^2. 4pi for the surface area of a unit sphere, and
                // 1/sample_side^2 for the number of samples drawn uniformly.
                interreflectionCoefs.col(i) *= 4.0 * M_PI / (sample_side * sample_side);
            }
            // TODO: leave for bonus

            for (int i = 0; i < mesh->getVertexCount(); ++i)
            {
                m_TransportSHCoeffs.col(i) += interreflectionCoefs.col(i);
            }
        }

        // Save in face format
        for (int f = 0; f < mesh->getTriangleCount(); f++)
        {
            const MatrixXu &F = mesh->getIndices();
            uint32_t idx0 = F(0, f), idx1 = F(1, f), idx2 = F(2, f);
            for (int j = 0; j < SHCoeffLength; j++)
            {
                fout << m_TransportSHCoeffs.col(idx0).coeff(j) << " ";
            }
            fout << std::endl;
            for (int j = 0; j < SHCoeffLength; j++)
            {
                fout << m_TransportSHCoeffs.col(idx1).coeff(j) << " ";
            }
            fout << std::endl;
            for (int j = 0; j < SHCoeffLength; j++)
            {
                fout << m_TransportSHCoeffs.col(idx2).coeff(j) << " ";
            }
            fout << std::endl;
        }
        std::cout << "Computed SH coeffs"
                  << " to: " << transPath.str() << std::endl;
    }

public: // member func
    Color3f Li(const Scene *scene, Sampler *sampler, const Ray3f &ray) const
    {
        Intersection its;
        if (!scene->rayIntersect(ray, its))
            return Color3f(0.0f);

        const Eigen::Matrix<Vector3f::Scalar, SHCoeffLength, 1> sh0 = m_TransportSHCoeffs.col(its.tri_index.x()),
                                                                sh1 = m_TransportSHCoeffs.col(its.tri_index.y()),
                                                                sh2 = m_TransportSHCoeffs.col(its.tri_index.z());
        const Eigen::Matrix<Vector3f::Scalar, SHCoeffLength, 1> rL = m_LightCoeffs.row(0), gL = m_LightCoeffs.row(1), bL = m_LightCoeffs.row(2);

        Color3f c0 = Color3f(rL.dot(sh0), gL.dot(sh0), bL.dot(sh0)),
                c1 = Color3f(rL.dot(sh1), gL.dot(sh1), bL.dot(sh1)),
                c2 = Color3f(rL.dot(sh2), gL.dot(sh2), bL.dot(sh2));

        const Vector3f &bary = its.bary;
        Color3f c = bary.x() * c0 + bary.y() * c1 + bary.z() * c2;
        // TODO: you need to delete the following four line codes after finishing your calculation to SH,
        //       we use it to visualize the normals of model for debug.
        // TODO: 在完成了球谐系数计算后，你需要删除下列四行，这四行代码的作用是用来可视化模型法线
        // if (c.isZero()) {
        //     auto n_ = its.shFrame.n.cwiseAbs();
        //     return Color3f(n_.x(), n_.y(), n_.z());
        // }
        return c;
    }

    std::string toString() const
    {
        return "PRTIntegrator[]";
    }

private:
    Type m_Type;
    int m_Bounce = 1;
    int m_SampleCount = 100;
    std::string m_CubemapPath;
    Eigen::MatrixXf m_TransportSHCoeffs;
    Eigen::MatrixXf m_LightCoeffs;
};

NORI_REGISTER_CLASS(PRTIntegrator, "prt");
NORI_NAMESPACE_END


#include <iostream>
#include <cstdlib>
#include <ctime>

#include <Eigen/Dense>

#include "problem.hpp"

using namespace integrator_chains;

int main()
{
    Eigen::Vector2i numdim_output_bounds( 2, 2 );
    Eigen::Vector2i num_integrators_bounds( 2, 2 );

    Eigen::Vector4d Y_box;
    Y_box << -1, 10, -2, 10;

    Eigen::Vector4d U_box;
    U_box << -1, 1, -1, 1;

    Eigen::Vector2d period_bounds( 0.05, 0.1 );

    Eigen::Vector2i number_goals_bounds( 1, 2 );
    Eigen::Vector2i number_obstacles_bounds( 0, 1 );

    std::srand( std::time( 0 ) );
    Problem *prob = Problem::random( numdim_output_bounds,
                                     num_integrators_bounds,
                                     Y_box, U_box,
                                     number_goals_bounds,
                                     number_obstacles_bounds,
                                     period_bounds );
    std::cout << *prob << std::endl;

    prob->to_formula( std::cerr );
    std::cerr << std::endl;

    delete prob;
    return 0;
}


// Copyright (c) 2019 CNES
//
// All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#pragma once
#include "pyinterp/detail/geometry/box.hpp"
#include "pyinterp/detail/geometry/point.hpp"
#include <algorithm>
#include <boost/geometry.hpp>
#include <optional>

namespace pyinterp {
namespace detail {
namespace geometry {

/// Index points in the Cartesian space at N dimensions.
///
/// @tparam Coordinate The class of storage for a point's coordinates.
/// @tparam Type The type of data stored in the tree.
/// @tparam N Number of dimensions in the Cartesian space handled.
template <typename Coordinate, typename Type, size_t N>
class RTree {
 public:
  /// Type of distances between two points.
  using distance_t = typename boost::geometry::default_distance_result<
      geometry::PointND<Coordinate, N>, geometry::PointND<Coordinate, N>>::type;

  /// Type of query results.
  using result_t = std::pair<distance_t, Type>;

  /// Value handled by this object
  using value_t = std::pair<geometry::PointND<Coordinate, N>, Type>;

  /// Spatial index used
  using rtree_t =
      boost::geometry::index::rtree<value_t, boost::geometry::index::rstar<16>>;

  /// Default constructor
  RTree() : tree_(new rtree_t{}) {}

  /// Default destructor
  virtual ~RTree() = default;

  /// Default copy constructor
  RTree(const RTree &) = default;

  /// Default copy assignment operator
  RTree &operator=(const RTree &) = default;

  /// Move constructor
  RTree(RTree &&) noexcept = default;

  /// Move assignment operator
  RTree &operator=(RTree &&) noexcept = default;

  /// Returns the box able to contain all values stored in the container.
  ///
  /// @returns The box able to contain all values stored in the container or an
  /// invalid box if there are no values in the container.
  virtual std::optional<geometry::BoxND<Coordinate, N>> bounds() const {
    if (empty()) {
      return {};
    }
    return tree_->bounds();
  }

  /// Returns the number of points of this mesh
  ///
  /// @return the number of points
  inline size_t size() const { return tree_->size(); }

  /// Query if the container is empty.
  ///
  /// @return true if the container is empty.
  inline bool empty() const { return tree_->empty(); }

  /// Removes all values stored in the container.
  inline void clear() { tree_->clear(); }

  /// The tree is created using packing algorithm (The old data is erased before
  /// construction.)
  ///
  /// @param points
  void packing(const std::vector<value_t> &points) { *tree_ = rtree_t(points); }

  /// Insert new data into the search tree
  ///
  /// @param point
  void insert(const value_t &value) { tree_->insert(value); }

  /// Search for the K nearest neighbors of a given point.
  ///
  /// @param point Point of interest
  /// @param k The number of nearest neighbors to search.
  /// @return the k nearest neighbors
  std::vector<result_t> query(const geometry::PointND<Coordinate, N> &point,
                              const uint32_t k) const {
    auto result = std::vector<result_t>();
    std::for_each(
        tree_->qbegin(boost::geometry::index::nearest(point, k)), tree_->qend(),
        [&point, &result](const auto &item) {
          result.emplace_back(std::make_pair(
              boost::geometry::distance(point, item.first), item.second));
        });
    return result;
  }

  /// Search for the nearest neighbors of a given point within a radius r.
  ///
  /// @param point Point of interest
  /// @param radius distance within which neighbors are returned
  /// @return the k nearest neighbors
  std::vector<result_t> query_ball(
      const geometry::PointND<Coordinate, N> &point,
      const double radius) const {
    auto result = std::vector<result_t>();
    std::for_each(
        tree_->qbegin(boost::geometry::index::satisfies([&](const auto &item) {
          return boost::geometry::distance(item.first, point) <= radius;
        })),
        tree_->qend(), [&point, &result](const auto &item) {
          result.emplace_back(std::make_pair(
              boost::geometry::distance(point, item.first), item.second));
        });
    return result;
  }

  /// Search for the nearest K neighbors around a given point.
  ///
  /// @param point Point of interest
  /// @param k The number of nearest neighbors to search.
  /// @return the k nearest neighbors if the point is within by its
  /// neighbors.
  std::vector<result_t> query_within(
      const geometry::PointND<Coordinate, N> &point, const uint32_t k) const {
    auto result = std::vector<result_t>();
    auto points =
        boost::geometry::model::multi_point<geometry::PointND<Coordinate, N>>();
    points.reserve(k);

    std::for_each(
        tree_->qbegin(boost::geometry::index::nearest(point, k)), tree_->qend(),
        [&points, &point, &result](const auto &item) {
          points.emplace_back(item.first);
          result.emplace_back(std::make_pair(
              boost::geometry::distance(point, item.first), item.second));
        });

    // Are found points located around the requested point?
    if (!boost::geometry::covered_by(
            point,
            boost::geometry::return_envelope<
                boost::geometry::model::box<geometry::PointND<Coordinate, N>>>(
                points))) {
      return {};
    }
    return result;
  }

 protected:
  /// Geographic index used to store data and their searches.
  std::shared_ptr<rtree_t> tree_;
};

}  // namespace geometry
}  // namespace detail
}  // namespace pyinterp


// Copyright (c) Dietmar Wolz.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory.

// Eigen based implementation of differential evolution using on the DE/best/1 strategy.
// Uses two deviations from the standard DE algorithm:
// a) temporal locality introduced in
// https://www.researchgate.net/publication/309179699_Differential_evolution_for_protein_folding_optimization_based_on_a_three-dimensional_AB_off-lattice_model
// b) reinitialization of individuals based on their age.
// requires https://github.com/imneme/pcg-cpp
// To be used to further optimize a given solution. Initial population is created using a normal distribition
// with mean=init and sdev=sigma (normalized over the bounds, defined separately for each variable).

#include <Eigen/Core>
#include <iostream>
#include <float.h>
#include <ctime>
#include <random>
#include "pcg_random.hpp"
#include "call_java.hpp"

using namespace std;

typedef Eigen::Matrix<double, Eigen::Dynamic, 1> vec;
typedef Eigen::Matrix<int, Eigen::Dynamic, 1> ivec;
typedef Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> mat;

typedef double (*callback_type)(int, double[]);

namespace l_differential_evolution {

static uniform_real_distribution<> distr_01 = std::uniform_real_distribution<>(
        0, 1);
static normal_distribution<> gauss_01 = std::normal_distribution<>(0, 1);

static vec zeros(int n) {
    return Eigen::MatrixXd::Zero(n, 1);
}

static vec constant(int n, double val) {
    return vec::Constant(n, val);
}

static Eigen::MatrixXd uniform(int dx, int dy, pcg64 &rs) {
    return Eigen::MatrixXd::NullaryExpr(dx, dy, [&]() {
        return distr_01(rs);
    });
}

static Eigen::MatrixXd uniformVec(int dim, pcg64 &rs) {
    return Eigen::MatrixXd::NullaryExpr(dim, 1, [&]() {
        return distr_01(rs);
    });
}

static double normreal(double mean, double sdev, pcg64 &rs) {
    return gauss_01(rs) * sdev + mean;
}

static vec normalVec(const vec &mean, const vec &sdev, int dim, pcg64 &rs) {
    vec nv = Eigen::MatrixXd::NullaryExpr(dim, 1, [&]() {
        return gauss_01(rs);
    });
    return (nv.array() * sdev.array()).matrix() + mean;
}

static int index_min(vec &v) {
    double minv = DBL_MAX;
    int mi = -1;
    for (int i = 0; i < v.size(); i++) {
        if (v[i] < minv) {
            mi = i;
            minv = v[i];
        }
    }
    return mi;
}

struct IndexVal {
    int index;
    double val;
};

// wrapper around the fittness function, scales according to boundaries

class Fitness {

public:

    Fitness(CallJava *func_, int dim_, const vec &lower_limit,
            const vec &upper_limit, const vec &guess_, const vec &sigma_,
            pcg64 &rs_) {
        func = func_;
        dim = dim_;
        lower = lower_limit;
        upper = upper_limit;
        // initial guess for the arguments of the fitness function
        guess = guess_;
        xmean = vec(guess);
        rs = rs_;
        evaluationCounter = 0;
        if (lower.size() > 0) // bounds defined
            scale = (upper - lower);
        else
            scale = constant(dim, 1.0);
        invScale = scale.cwiseInverse();
        maxSigma = 0.25 * scale;
        // individual sigma values - initial search volume. inputSigma determines
        // the initial coordinate wise standard deviations for the search.
        if (sigma_.size() == 1)
            sigma0 =
                    0.5
                            * (scale.array()
                                    * (vec::Constant(dim, sigma_[0])).array()).matrix();
        else
            sigma0 = 0.5 * (scale.array() * sigma_.array()).matrix();
        sigma = vec(sigma0);
    }

    void updateSigma(const vec &X) {
        vec delta = (xmean - X).cwiseAbs() * 0.5;
        sigma = delta.cwiseMin(maxSigma);
        xmean = X;
    }

    vec normX() {
        return distr_01(rs) < 0.5 ?
                getClosestFeasible(normalVec(xmean, sigma0, dim, rs)) :
                getClosestFeasible(normalVec(xmean, sigma, dim, rs));
    }

    double normXi(int i) {
        double nx;
        if (distr_01(rs) < 0.5) {
            do {
                nx = normreal(xmean[i], sigma0[i], rs);
            } while (!feasible(i, nx));
        } else {
            do {
                nx = normreal(xmean[i], sigma[i], rs);
            } while (!feasible(i, nx));
        }
        return nx;
    }

    bool feasible(int i, double x) {
        return lower.size() == 0 || (x >= lower[i] && x <= upper[i]);
    }

    vec sample() {
        if (lower.size() > 0) {
            vec rv = uniformVec(dim, rs);
            return (rv.array() * scale.array()).matrix() + lower;
        } else
            return normX();
    }

    double sample_i(int i) {
        if (lower.size() > 0)
            return lower[i] + scale[i] * distr_01(rs);
        else
            return normXi(i);
    }

    vec getClosestFeasible(const vec &X) const {
        if (lower.size() > 0) {
            return X.cwiseMin(upper).cwiseMax(lower);
        }
        return X;
    }

    double eval(const vec &X) {
        int n = X.size();
        double parg[n];
        for (int i = 0; i < n; i++)
            parg[i] = X(i);
        double res = func->evalJava1(n, parg);
        evaluationCounter++;
        return res;
    }

    int getEvaluations() {
        return evaluationCounter;
    }

    vec guess;

private:
    CallJava *func;
    int dim;
    vec lower;
    vec upper;
    vec xmean;
    vec sigma0;
    vec sigma;
    vec maxSigma;
    pcg64 rs;
    long evaluationCounter;
    vec scale;
    vec invScale;
};

class LDeOptimizer {

public:

    LDeOptimizer(long runid_, Fitness *fitfun_, int dim_, pcg64 *rs_,
            int popsize_, int maxEvaluations_, double keep_,
            double stopfitness_, double F_, double CR_) {
        // runid used to identify a specific run
        runid = runid_;
        // fitness function to minimize
        fitfun = fitfun_;
        // Number of objective variables/problem dimension
        dim = dim_;
        // Population size
        popsize = popsize_ > 0 ? popsize_ : 15 * dim;
        // maximal number of evaluations allowed.
        maxEvaluations = maxEvaluations_ > 0 ? maxEvaluations_ : 50000;
        // keep best young after each iteration.
        keep = keep_ > 0 ? keep_ : 30;
        // Limit for fitness value.
        stopfitness = stopfitness_;
        F0 = F_ > 0 ? F_ : 0.5;
        CR0 = CR_ > 0 ? CR_ : 0.9;
        // Number of iterations already performed.
        iterations = 0;
        bestY = DBL_MAX;
        // stop criteria
        stop = 0;
        rs = rs_;
        init();
    }

    ~LDeOptimizer() {
        delete rs;
    }

    double rnd01() {
        return distr_01(*rs);
    }

    double rnd02() {
        double rnd = distr_01(*rs);
        return rnd * rnd;
    }

    int rndInt(int max) {
        return (int) (max * distr_01(*rs));
    }

    void doOptimize() {

        // -------------------- Generation Loop --------------------------------
        for (iterations = 1; fitfun->getEvaluations() < maxEvaluations;
                iterations++) {

            double CR = iterations % 2 == 0 ? 0.5 * CR0 : CR0;
            double F = iterations % 2 == 0 ? 0.5 * F0 : F0;

            for (int p = 0; p < popsize; p++) {
                vec xp = popX.col(p);
                vec xb = popX.col(bestI);

                int r1, r2;
                do {
                    r1 = rndInt(popsize);
                } while (r1 == p || r1 == bestI);
                do {
                    r2 = rndInt(popsize);
                } while (r2 == p || r2 == bestI || r2 == r1);
                vec x1 = popX.col(r1);
                vec x2 = popX.col(r2);
                int r = rndInt(dim);
                vec x = vec(xp);
                for (int j = 0; j < dim; j++) {
                    if (j == r || rnd01() < CR) {
                        x[j] = xb[j] + F * (x1[j] - x2[j]);
                        if (!fitfun->feasible(j, x[j]))
                            x[j] = fitfun->normXi(j);
                    }
                }
                double y = fitfun->eval(x);
                if (isfinite(y) && y < popY[p]) {
                    // temporal locality
                    vec x2 = fitfun->getClosestFeasible(xb + ((x - xp) * 0.5));
                    double y2 = fitfun->eval(x2);
                    if (isfinite(y2) && y2 < y) {
                        y = y2;
                        x = x2;
                    }
                    popX.col(p) = x;
                    popY(p) = y;
                    popIter[p] = iterations;
                    if (y < popY[bestI]) {
                        bestI = p;
                        if (y < bestY) {
                            fitfun->updateSigma(x);
                            bestY = y;
                            bestX = x;
                            if (isfinite(stopfitness) && bestY < stopfitness) {
                                stop = 1;
                                return;
                            }
                        }
                    }
                } else {
                    // reinitialize individual
                    if (keep * rnd01() < iterations - popIter[p]) {
                        popX.col(p) = fitfun->normX();
                        popY[p] = fitfun->eval(popX.col(p)); // compute fitness
                    }
                }
            }
        }
    }

    void init() {
        popX = mat(dim, popsize);
        popY = vec(popsize);
        for (int p = 0; p < popsize; p++) {
            popX.col(p) = fitfun->guess;
            popY[p] = DBL_MAX; // compute fitness
        }
        bestI = 0;
        bestX = popX.col(bestI);
        popIter = zeros(popsize);
    }

    vec getBestX() {
        return bestX;
    }

    double getBestValue() {
        return bestY;
    }

    double getIterations() {
        return iterations;
    }

    double getStop() {
        return stop;
    }

private:
    long runid;
    Fitness *fitfun;
    int popsize; // population size
    int dim;
    int maxEvaluations;
    double keep;
    double stopfitness;
    int iterations;
    double bestY;
    vec bestX;
    int bestI;
    int stop;
    double F0;
    double CR0;
    pcg64 *rs;
    mat popX;
    vec popY;
    vec popIter;
};

// see https://cvstuff.wordpress.com/2014/11/27/wraping-c-code-with-python-ctypes-memory-and-pointers/

}

using namespace l_differential_evolution;

/*
 * Class:     fcmaes_core_Jni
 * Method:    optimizeLDE
 * Signature: (Lfcmaes/core/Fitness;[D[D[D[DIDIDDDJI)I
 */
JNIEXPORT jint JNICALL Java_fcmaes_core_Jni_optimizeLDE(JNIEnv *env, jclass cls,
        jobject func, jdoubleArray jlower, jdoubleArray jupper,
        jdoubleArray jinit, jdoubleArray jsigma, jint maxEvals,
        jdouble stopfitness, jint popsize, jdouble keep, jdouble F, jdouble CR,
        jlong seed, jint runid) {

    double *init = env->GetDoubleArrayElements(jinit, JNI_FALSE);
    double *lower = env->GetDoubleArrayElements(jlower, JNI_FALSE);
    double *upper = env->GetDoubleArrayElements(jupper, JNI_FALSE);
    double *sigma = env->GetDoubleArrayElements(jsigma, JNI_FALSE);
    int dim = env->GetArrayLength(jinit);

    vec guess(dim), lower_limit(dim), upper_limit(dim), inputSigma(dim);
    bool useLimit = false;
    for (int i = 0; i < dim; i++) {
        guess[i] = init[i];
        inputSigma[i] = sigma[i];
        lower_limit[i] = lower[i];
        upper_limit[i] = upper[i];
        useLimit |= (lower[i] != 0);
        useLimit |= (upper[i] != 0);
    }
    if (useLimit == false) {
        lower_limit.resize(0);
        upper_limit.resize(0);
    }
    pcg64 *rs = new pcg64(seed);
    CallJava callJava(func, env);
    Fitness fitfun(&callJava, dim, lower_limit, upper_limit, guess, inputSigma,
            *rs);
    LDeOptimizer opt(runid, &fitfun, dim, rs, popsize, maxEvals, keep,
            stopfitness, F, CR);
    try {
        opt.doOptimize();
        vec bestX = opt.getBestX();
        double bestY = opt.getBestValue();

        for (int i = 0; i < dim; i++)
            init[i] = bestX[i];

        env->SetDoubleArrayRegion(jinit, 0, dim, (jdouble*) init);
        env->ReleaseDoubleArrayElements(jinit, init, 0);
        env->ReleaseDoubleArrayElements(jupper, upper, 0);
        env->ReleaseDoubleArrayElements(jlower, lower, 0);
        env->ReleaseDoubleArrayElements(jsigma, sigma, 0);
        return fitfun.getEvaluations();

    } catch (std::exception &e) {
        cout << e.what() << endl;
        return fitfun.getEvaluations();
    }
    return 0;
}


//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2012   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
/*!
 * \file
**/
#ifndef BOOST_SIMD_ARITHMETIC_FUNCTIONS_FMA_HPP_INCLUDED
#define BOOST_SIMD_ARITHMETIC_FUNCTIONS_FMA_HPP_INCLUDED
#include <boost/simd/include/functor.hpp>
#include <boost/dispatch/include/functor.hpp>

/*!
 * \ingroup boost_simd_arithmetic
 * \defgroup boost_simd_arithmetic_fma fma
 *
 * \par Description
 * computes \c a0*a1+a2, but the computation with only one rounding operation
 * is only done on architectures possessing this hard wired capability
 *
 * \par Header file
 *
 * \code
 * #include <nt2/include/functions/fma.hpp>
 * \endcode
 *
 * \par Aliases
 * \arg madd
 * \arg fam
 * \arg amul
 *
 * \synopsis
 *
 * \code
 * namespace boost::simd
 * {
 *   template <class A0>
 *     meta::call<tag::fma_(A0,A0,A0)>::type
 *     fma(const A0 & a0,const A0 & a1,const A0 & a2);
 * }
 * \endcode
 *
 * \param a0 the first parameter of fma
 * \param a1 the second parameter of fma
 * \param a2 the third parameter of fma
 *
 * \return a value of the common type of the parameters
 *
 * \par Notes
 * In SIMD mode, this function acts elementwise on the inputs vectors elements
 * \par
 *
**/

namespace boost { namespace simd { namespace tag
  {
    /*!
     * \brief Define the tag fma_ of functor fma
     *        in namespace boost::simd::tag for toolbox boost.simd.arithmetic
    **/
    struct fma_ : ext::elementwise_<fma_> { typedef ext::elementwise_<fma_> parent; };
  }
  BOOST_DISPATCH_FUNCTION_IMPLEMENTATION(tag::fma_, fma, 3)
  BOOST_DISPATCH_FUNCTION_IMPLEMENTATION(tag::fma_, madd, 3)

  BOOST_DISPATCH_FUNCTION_INTERFACE(tag::fma_, fam, 3)
  {
    typename boost::dispatch::make_functor<tag::fma_, A0>::type callee;
    return callee(a1, a2, a0);
  }

  BOOST_DISPATCH_FUNCTION_INTERFACE(tag::fma_, amul, 3)
  {
    typename boost::dispatch::make_functor<tag::fma_, A0>::type callee;
    return callee(a1, a2, a0);
  }

} }

#endif


#include <Eigen/Core>
#include <Eigen/Eigenvalues>
#include <iostream>
#include <numeric>
#include <unordered_map>
#include <vector>

#include <misc3d/common/normal_estimation.h>
#include <misc3d/logging.h>
#include <misc3d/utils.h>

namespace misc3d {
namespace common {

void VectorToPointer(const std::vector<Eigen::Vector3d> &data, double *ptr) {
    const size_t num = data.size();
#pragma omp parallel for
    for (int i = 0; i < num; i++) {
        ptr[3 * i] = data[i](0);
        ptr[3 * i + 1] = data[i](1);
        ptr[3 * i + 2] = data[i](2);
    }
}

void PointerToVector(const double *ptr, int num,
                     std::vector<Eigen::Vector3d> &data) {
    data.resize(num);
#pragma omp parallel for
    for (int i = 0; i < num; i++) {
        Eigen::Vector3d d(ptr[i * 3], ptr[i * 3 + 1], ptr[i * 3 + 2]);
        data[i] = d;
    }
}

template <typename T0, typename T1>
void SumDense(const T0 *data, const bool *mask, const unsigned int w,
              const unsigned int h, const unsigned int k, T1 *dst) {
    const size_t range_rows[2] = {k, h - k}, range_cols[2] = {k + 1, w - k},
                 double_k = 2 * k;
#pragma omp parallel for
    for (int r = (int)range_rows[0]; r < (int)range_rows[1]; r++) {
        size_t idx_r = (size_t)r * w;
        T1 *ptr = dst + idx_r + k;
        *ptr = 0;
        for (size_t r0 = (size_t)r - k, idx_r0; r0 <= (size_t)r + k; r0++) {
            idx_r0 = r0 * w;
            for (size_t c0 = 0; c0 <= double_k; c0++) {
                *ptr += data[idx_r0 + c0];
            }
        }
        ptr++;
        for (size_t c = range_cols[0]; c < range_cols[1]; c++, ptr++) {
            *ptr = *(ptr - 1);
            for (size_t r0 = (size_t)r - k, c0 = c - k - 1, c1 = c + k;
                 r0 <= (size_t)r + k; r0++) {
                *ptr += *(data + r0 * w + c1) - *(data + r0 * w + c0);
            }
        }
    }
}

void CalcNormalsFromPointMap(const double *xyzs, const unsigned int w,
                             const unsigned int h, const unsigned int k,
                             double *normals, const double view_point[3]) {
    size_t expand_w = w + 2 * k, expand_h = h + 2 * k,
           expand_wh = expand_w * expand_h;
    const size_t range_rows[2] = {k, expand_h - k},
                 range_cols[2] = {k, expand_w - k};
    bool *mask = (bool *)calloc(expand_wh, sizeof(bool));
    double *buffer0 = (double *)calloc(expand_wh * 9, sizeof(double));
    double *x = buffer0;
    double *y = x + expand_wh;
    double *z = y + expand_wh;
    double *xx = z + expand_wh;
    double *xy = xx + expand_wh;
    double *xz = xy + expand_wh;
    double *yy = xz + expand_wh;
    double *yz = yy + expand_wh;
    double *zz = yz + expand_wh;

#pragma omp parallel for
    for (int r = (int)range_rows[0]; r < (int)range_rows[1]; r++) {
        size_t idx_r = (size_t)r * expand_w, idx;
        const double *xyz = xyzs + ((size_t)r - k) * w * 3;
        for (size_t c = range_cols[0]; c < range_cols[1]; c++, xyz += 3) {
            idx = idx_r + c;
            if (xyz[2] == xyz[2]) {
                mask[idx] = true;
                x[idx] = xyz[0];
                y[idx] = xyz[1];
                z[idx] = xyz[2];
                xx[idx] = xyz[0] * xyz[0];
                xy[idx] = xyz[0] * xyz[1];
                xz[idx] = xyz[0] * xyz[2];
                yy[idx] = xyz[1] * xyz[1];
                yz[idx] = xyz[1] * xyz[2];
                zz[idx] = xyz[2] * xyz[2];
            }
        }
    }

    size_t *neighbor_nums = new size_t[expand_wh];
    double *buffer1 = (double *)malloc(expand_wh * 9 * sizeof(double));
    double *sum_x = buffer1;
    double *sum_y = sum_x + expand_wh;
    double *sum_z = sum_y + expand_wh;
    double *sum_xx = sum_z + expand_wh;
    double *sum_xy = sum_xx + expand_wh;
    double *sum_xz = sum_xy + expand_wh;
    double *sum_yy = sum_xz + expand_wh;
    double *sum_yz = sum_yy + expand_wh;
    double *sum_zz = sum_yz + expand_wh;

    SumDense(x, mask, (unsigned int)expand_w, (unsigned int)expand_h, k, sum_x);
    SumDense(y, mask, (unsigned int)expand_w, (unsigned int)expand_h, k, sum_y);
    SumDense(z, mask, (unsigned int)expand_w, (unsigned int)expand_h, k, sum_z);
    SumDense(xx, mask, (unsigned int)expand_w, (unsigned int)expand_h, k,
             sum_xx);
    SumDense(xy, mask, (unsigned int)expand_w, (unsigned int)expand_h, k,
             sum_xy);
    SumDense(xz, mask, (unsigned int)expand_w, (unsigned int)expand_h, k,
             sum_xz);
    SumDense(yy, mask, (unsigned int)expand_w, (unsigned int)expand_h, k,
             sum_yy);
    SumDense(yz, mask, (unsigned int)expand_w, (unsigned int)expand_h, k,
             sum_yz);
    SumDense(zz, mask, (unsigned int)expand_w, (unsigned int)expand_h, k,
             sum_zz);
    SumDense(mask, mask, (unsigned int)expand_w, (unsigned int)expand_h, k,
             neighbor_nums);

#pragma omp parallel for
    for (int r = (int)range_rows[0]; r < (int)range_rows[1]; r++) {
        size_t idx_r = (size_t)r * expand_w, idx;
        for (size_t c = range_cols[0]; c < range_cols[1]; c++) {
            idx = idx_r + c;
            if (!mask[idx])
                continue;
            double scale = 1. / neighbor_nums[idx];
            double hat_x = sum_x[idx] * scale, hat_y = sum_y[idx] * scale,
                   hat_z = sum_z[idx] * scale;
            double covariance[3][3] = {sum_xx[idx] * scale - hat_x * hat_x,
                                       sum_xy[idx] * scale - hat_x * hat_y,
                                       sum_xz[idx] * scale - hat_x * hat_z,
                                       0,
                                       sum_yy[idx] * scale - hat_y * hat_y,
                                       sum_yz[idx] * scale - hat_y * hat_z,
                                       0,
                                       0,
                                       sum_zz[idx] * scale - hat_z * hat_z};
            covariance[1][0] = covariance[0][1];
            covariance[2][0] = covariance[0][2];
            covariance[2][1] = covariance[1][2];

            Eigen::Matrix<double, 3, 3, Eigen::RowMajor | Eigen::DontAlign>
                covariance_matrix(covariance[0]);
            Eigen::SelfAdjointEigenSolver<
                Eigen::Matrix<double, 3, 3, Eigen::RowMajor | Eigen::DontAlign>>
                solver;
            solver.compute(covariance_matrix, Eigen::ComputeEigenvectors);
            Eigen::Vector3d v0 = solver.eigenvectors().col(0);
            double *temp_n = v0.data();
            if ((view_point[0] - x[idx]) * temp_n[0] +
                    (view_point[1] - y[idx]) * temp_n[1] +
                    (view_point[2] - z[idx]) * temp_n[2] <
                0) {
                temp_n[0] *= -1;
                temp_n[1] *= -1;
                temp_n[2] *= -1;
            }
            memcpy(normals + (((size_t)r - k) * w + c - k) * 3, v0.data(),
                   sizeof(double) * 3);
        }
    }

    delete[] neighbor_nums;
    free(buffer0);
    free(buffer1);
    free(mask);
}

void EstimateNormalsFromMap(const PointCloudPtr &pc,
                            const std::tuple<int, int> shape, int k,
                            const std::array<double, 3> &view_point) {
    const size_t num = pc->points_.size();
    const int w = std::get<0>(shape);
    const int h = std::get<1>(shape);

    if (num != w * h) {
        misc3d::LogError(
            "The point cloud size is not equal to given point map size.");
        return;
    }

    double *normals_ptr = new double[num * 3];
    double *points_ptr = new double[num * 3];
    VectorToPointer(pc->points_, points_ptr);
    CalcNormalsFromPointMap(points_ptr, w, h, k, normals_ptr,
                            view_point.data());

    // assign normals
    std::vector<Eigen::Vector3d> normals;
    PointerToVector(normals_ptr, num, normals);
    pc->normals_ = normals;

    delete[] normals_ptr;
    delete[] points_ptr;
}

}  // namespace common
}  // namespace misc3d


#include "config.h"
#include "Scene_points_with_normal_item.h"
#include "Scene_polygon_soup_item.h"
#include "Scene_surface_mesh_item.h"
#include <CGAL/Three/Scene_group_item.h>

#include <CGAL/Three/Polyhedron_demo_plugin_helper.h>
#include <CGAL/Three/Polyhedron_demo_plugin_interface.h>
#include <CGAL/Three/Scene_group_item.h>

#include <CGAL/Orthogonal_k_neighbor_search.h>
#include <CGAL/Fuzzy_sphere.h>
#include <CGAL/Search_traits_3.h>
#include <CGAL/Search_traits_adapter.h>

#include <CGAL/linear_least_squares_fitting_3.h>

#include <CGAL/Random.h>
#include <CGAL/Real_timer.h>

#include <CGAL/Shape_detection.h>
#include <CGAL/Regularization.h>
#include <CGAL/Delaunay_triangulation_2.h>
#include <CGAL/Alpha_shape_2.h>
#include <CGAL/Alpha_shape_face_base_2.h>
#include <CGAL/Alpha_shape_vertex_base_2.h>

#include <CGAL/structure_point_set.h>

#include <QObject>
#include <QAction>
#include <QMainWindow>
#include <QApplication>
#include <QtPlugin>
#include <QMessageBox>

#include <boost/iterator/function_output_iterator.hpp>

#include "run_with_qprogressdialog.h"

#include "ui_Point_set_shape_detection_plugin.h"

template <typename Shape_detection>
struct Detect_shapes_functor
  : public Functor_with_signal_callback
{
  Shape_detection& shape_detection;
  typename Shape_detection::Parameters& op;

  Detect_shapes_functor (Shape_detection& shape_detection,
                         typename Shape_detection::Parameters& op)
    : shape_detection (shape_detection), op (op)
  { }

  void operator()()
  {
    shape_detection.detect(op, *(this->callback()));
  }
};

struct build_from_pair
{
  Point_set& m_pts;

  build_from_pair (Point_set& pts) : m_pts (pts) { }

  void operator() (const std::pair<Point_set::Point, Point_set::Vector>& pair)
  {
    m_pts.insert (pair.first, pair.second);
  }

};

class Point_set_demo_point_set_shape_detection_dialog : public QDialog, public Ui::PointSetShapeDetectionDialog
{
  Q_OBJECT
public:
  Point_set_demo_point_set_shape_detection_dialog(QWidget * /*parent*/ = nullptr)
  {
    setupUi(this);
    m_normal_tolerance_field->setMaximum(1.0);
    m_probability_field->setRange(0.00001, 1.0);
    m_epsilon_field->setMinimum(0.000001);
    m_normal_tolerance_field->setMinimum(0.01);
    m_cluster_epsilon_field->setMinimum(0.000001);
  }

  bool region_growing() const { return m_region_growing->isChecked(); }
  double cluster_epsilon() const { return m_cluster_epsilon_field->value(); }
  double epsilon() const { return m_epsilon_field->value(); }
  unsigned int min_points() const { return m_min_pts_field->value(); }
  double normal_tolerance() const { return m_normal_tolerance_field->value(); }
  double search_probability() const { return m_probability_field->value(); }
  double gridCellSize() const { return 1.0; }
  bool detect_plane() const { return planeCB->isChecked(); }
  bool detect_sphere() const { return sphereCB->isChecked(); }
  bool detect_cylinder() const { return cylinderCB->isChecked(); }
  bool detect_torus() const { return torusCB->isChecked(); }
  bool detect_cone() const { return coneCB->isChecked(); }
  bool add_property() const { return m_add_property->isChecked(); }
  bool generate_colored_point_set() const { return m_one_colored_point_set->isChecked(); }
  bool generate_subset() const { return m_point_subsets->isChecked(); }
  bool generate_alpha() const { return m_alpha_shapes->isChecked(); }
  bool regularize() const { return m_regularize->isChecked(); }
  bool generate_structured() const { return m_generate_structured->isChecked(); }
};

typedef CGAL::Exact_predicates_inexact_constructions_kernel Epic_kernel;
typedef Epic_kernel::Point_3 Point;
using namespace CGAL::Three;
class Polyhedron_demo_point_set_shape_detection_plugin :
  public QObject,
  public Polyhedron_demo_plugin_helper
{
  Q_OBJECT
    Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface)
    Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0")

  QAction* actionDetect;
  QAction* actionEstimateParameters;
  QAction* actionDetectShapesSM;

  typedef Point_set_3<Kernel>::Point_map PointPMap;
  typedef Point_set_3<Kernel>::Vector_map NormalPMap;

  typedef CGAL::Shape_detection::Efficient_RANSAC_traits<Epic_kernel, Point_set, PointPMap, NormalPMap> Traits;

public:
  void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface, Messages_interface*) {
    scene = scene_interface;
    mw = mainWindow;
    actionDetect = new QAction(tr("Point Set Shape Detection"), mainWindow);
    actionDetect->setObjectName("actionDetect");
    actionEstimateParameters = new QAction(tr("Point Set Shape Detection (parameter estimation)"), mainWindow);
    actionEstimateParameters->setObjectName("actionEstimateParameters");
    actionDetectShapesSM = new QAction(tr("Surface Mesh Shape Detection"), mainWindow);
    actionDetectShapesSM->setObjectName("actionDetectShapesSM");
    autoConnectActions();
  }

  bool applicable(QAction* action) const {

    Scene_points_with_normal_item* item =
      qobject_cast<Scene_points_with_normal_item*>(scene->item(scene->mainSelectionIndex()));
    Scene_surface_mesh_item* sm_item =
      qobject_cast<Scene_surface_mesh_item*>(scene->item(scene->mainSelectionIndex()));

    if (action->objectName() == "actionDetectShapesSM") {
      if (sm_item)
        return true;
      else return false;
    }
    if (item && item->has_normals()) return true;
    return false;
  }

  QList<QAction*> actions() const {
    return QList<QAction*>() << actionDetect << actionEstimateParameters << actionDetectShapesSM;
  }

  public Q_SLOTS:
    void on_actionDetect_triggered();
    void on_actionEstimateParameters_triggered();
    void on_actionDetectShapesSM_triggered();

private:

  typedef Kernel::Plane_3 Plane_3;
  typedef Kernel::Point_3 Point_3;
  typedef Kernel::Vector_3 Vector_3;

  // RANSAC can handle all types of shapes
  template <typename Traits, typename Shape>
  void add_shape (CGAL::Shape_detection::Efficient_RANSAC<Traits>& ransac,
                  const Shape&)
  {
    ransac.template add_shape_factory<Shape>();
  }

  void detect_shapes_with_region_growing_sm (
    Scene_surface_mesh_item* sm_item,
    Point_set_demo_point_set_shape_detection_dialog& dialog) {

    using Face_range = typename SMesh::Face_range;

    using Neighbor_query =
    CGAL::Shape_detection::Polygon_mesh::One_ring_neighbor_query<SMesh>;
    using Region_type =
    CGAL::Shape_detection::Polygon_mesh::Least_squares_plane_fit_region<Kernel, SMesh>;

    using Vertex_to_point_map = typename Region_type::Vertex_to_point_map;
    using Region_growing = CGAL::Shape_detection::Region_growing<Face_range, Neighbor_query, Region_type>;

    CGAL::Random rand(static_cast<unsigned int>(time(nullptr)));
    const SMesh& mesh = *(sm_item->polyhedron());
    scene->setSelectedItem(-1);
    const Face_range face_range = faces(mesh);

    // Set parameters.
    const double max_distance_to_plane =
    dialog.epsilon();
    const double max_accepted_angle =
    dialog.normal_tolerance();
    const std::size_t min_region_size =
    dialog.min_points();

    // Region growing.
    Neighbor_query neighbor_query(mesh);
    const Vertex_to_point_map vertex_to_point_map(get(CGAL::vertex_point, mesh));
    Region_type region_type(
      mesh,
      max_distance_to_plane, max_accepted_angle, min_region_size,
      vertex_to_point_map);

    Region_growing region_growing(
      face_range, neighbor_query, region_type);

    std::vector< std::vector<std::size_t> > regions;
    region_growing.detect(std::back_inserter(regions));

    std::cerr << "* " << regions.size() <<
    " regions have been found"
    << std::endl;

    // Output result as a new colored item.
    Scene_surface_mesh_item *colored_item = new Scene_surface_mesh_item;
    colored_item->setName(QString("%1 (region growing)").arg(sm_item->name()));
    SMesh& fg = *(colored_item->polyhedron());

    fg = mesh;
    const Face_range fr = faces(fg);

    colored_item->setItemIsMulticolor(true);
    colored_item->computeItemColorVectorAutomatically(false);
    auto& color_vector = colored_item->color_vector();
    color_vector.clear();

    for (std::size_t i = 0; i < regions.size(); ++i) {
      for (const std::size_t idx : regions[i]) {
        const auto fit = fr.begin() + idx;
        fg.property_map<face_descriptor, int>("f:patch_id").first[*fit] =
        static_cast<int>(i);
      }
      CGAL::Random rnd(static_cast<unsigned int>(i));
      color_vector.push_back(QColor(
        64 + rnd.get_int(0, 192),
        64 + rnd.get_int(0, 192),
        64 + rnd.get_int(0, 192)));
    }
    if(color_vector.empty())
    {
      for(const auto& f : faces(fg))
      {
        fg.property_map<face_descriptor, int>("f:patch_id").first[f] =
            static_cast<int>(0);
      }
      CGAL::Random rnd(static_cast<unsigned int>(0));
      color_vector.push_back(QColor(
        64 + rnd.get_int(0, 192),
        64 + rnd.get_int(0, 192),
        64 + rnd.get_int(0, 192)));
    }
    colored_item->invalidateOpenGLBuffers();
    scene->addItem(colored_item);
  }

  void detect_shapes_with_region_growing (
    Scene_points_with_normal_item* item,
    Point_set_demo_point_set_shape_detection_dialog& dialog) {

    using Point_map = typename Point_set::Point_map;
    using Normal_map = typename Point_set::Vector_map;

    using Neighbor_query =
    CGAL::Shape_detection::Point_set::Sphere_neighbor_query<Kernel, Point_set, Point_map>;
    using Region_type =
    CGAL::Shape_detection::Point_set::Least_squares_plane_fit_region<Kernel, Point_set, Point_map, Normal_map>;
    using Region_growing =
    CGAL::Shape_detection::Region_growing<Point_set, Neighbor_query, Region_type>;

    // Set parameters.
    const double search_sphere_radius =
    dialog.cluster_epsilon();
    const double max_distance_to_plane =
    dialog.epsilon();
    const double max_accepted_angle =
    dialog.normal_tolerance();
    const std::size_t min_region_size =
    dialog.min_points();

    // Get a point set.
    CGAL::Random rand(static_cast<unsigned int>(time(nullptr)));
    Point_set* points = item->point_set();

    scene->setSelectedItem(-1);
    Scene_points_with_normal_item *colored_item =
    new Scene_points_with_normal_item;

    colored_item->setName(QString("%1 (region growing)").arg(item->name()));
    if (dialog.generate_colored_point_set()) {

      colored_item->point_set()->template add_property_map<unsigned char>("r", 128);
      colored_item->point_set()->template add_property_map<unsigned char>("g", 128);
      colored_item->point_set()->template add_property_map<unsigned char>("b", 128);
      colored_item->point_set()->check_colors();
      scene->addItem(colored_item);
    }
    std::string& comments = item->comments();

    Point_set::Property_map<int> shape_id;
    if (dialog.add_property()) {
      bool added = false;
      boost::tie(shape_id, added) = points->template add_property_map<int> ("shape", -1);
      if (!added) {
        for (auto it = points->begin(); it != points->end(); ++ it)
          shape_id[*it] = -1;
      }

      // Remove previously detected shapes from comments.
      std::string new_comment;

      std::istringstream stream(comments);
      std::string line;
      while (getline(stream, line)) {
        std::string tag;
        std::stringstream iss(line);

        if (iss >> tag && tag == "shape")
          continue;
        new_comment += line + "\n";
      }
      comments = new_comment;
      comments += "shape -1 no assigned shape\n";
    }
    QApplication::setOverrideCursor(Qt::BusyCursor);

    // Region growing set up.
    Neighbor_query neighbor_query(
      *points,
      search_sphere_radius,
      points->point_map());

    Region_type region_type(
      *points,
      max_distance_to_plane, max_accepted_angle, min_region_size,
      points->point_map(), points->normal_map());

    Region_growing region_growing(
      *points, neighbor_query, region_type);

    std::vector<Scene_group_item *> groups;
    groups.resize(1);
    if (dialog.detect_plane()){
      groups[0] = new Scene_group_item("Planes");
      groups[0]->setRenderingMode(Points);
    }

    // The actual shape detection.
    CGAL::Real_timer t;
    t.start();
    std::vector< std::vector<std::size_t> > regions;
    region_growing.detect(std::back_inserter(regions));
    t.stop();

    std::cout << regions.size() <<
      " shapes found in " << t.time() << " second(s)" << std::endl;

    std::vector<Plane_3> planes;
    CGAL::Shape_detection::internal::create_planes_from_points(
      *points, points->point_map(), regions, planes);

    if (dialog.regularize()) {

      std::cerr << "Regularization of planes... " << std::endl;
      CGAL::regularize_planes(
        *points,
        points->point_map(),
        planes,
        CGAL::Identity_property_map<Plane_3>(),
        CGAL::Shape_detection::RG::Point_to_shape_index_map(*points, regions),
        true, true, true, true,
        max_accepted_angle,
        max_distance_to_plane);

      std::cerr << "done" << std::endl;
    }
    std::map<Point_3, QColor> color_map;

    int index = 0;
    for (const auto& plane : planes) {

      if (dialog.add_property()) {
        std::ostringstream oss;
        oss << "shape " << index;
        oss << " plane " << plane << std::endl;
        comments += oss.str();
      }
      Scene_points_with_normal_item *point_item =
      new Scene_points_with_normal_item;

      for (const std::size_t idx : regions[index]) {
        point_item->point_set()->insert(points->point(*(points->begin() + idx)));
        if (dialog.add_property())
          shape_id[*(points->begin() + idx)] = index;
      }

      unsigned char r, g, b;
      r = static_cast<unsigned char>(64 + rand.get_int(0, 192));
      g = static_cast<unsigned char>(64 + rand.get_int(0, 192));
      b = static_cast<unsigned char>(64 + rand.get_int(0, 192));
      point_item->setRgbColor(r, g, b);

      std::size_t nb_colored_pts = 0;
      if (dialog.generate_colored_point_set()) {
        for(std::size_t idx : regions[index]) {
          auto it = colored_item->point_set()->insert(
            points->point(*(points->begin() + idx)));
          ++nb_colored_pts;
          colored_item->point_set()->set_color(*it, r, g, b);
        }
        colored_item->invalidateOpenGLBuffers();
      }

      // Providing a useful name consisting of the order of detection,
      // name of type and number of inliers.
      std::stringstream ss;
      ss << item->name().toStdString() << "_plane_";

      Vector_3 plane_normal = plane.orthogonal_vector();
      const double normal_length = CGAL::sqrt(plane_normal.squared_length());
      CGAL_precondition(normal_length > 0.0);
      plane_normal /= normal_length;

      Kernel::Point_3 ref = CGAL::ORIGIN + plane_normal;
      if (color_map.find(ref) == color_map.end()) {

        ref = CGAL::ORIGIN + (-1.0) * plane_normal;
        if (color_map.find(ref) == color_map.end())
          color_map[ref] = point_item->color();
        else
          point_item->setColor(color_map[ref]);

      } else point_item->setColor(color_map[ref]);

      if (dialog.generate_colored_point_set()) {
        for (std::size_t i = 0; i < nb_colored_pts; ++i) {
          colored_item->point_set()->set_color(
            *(colored_item->point_set()->end() - 1 - i),
            color_map[ref].red(),
            color_map[ref].green(),
            color_map[ref].blue());
        }
      }
      ss << "(" << ref << ")_";

      if (dialog.generate_alpha()) {
        // If plane, build alpha shape
        Scene_surface_mesh_item* sm_item = nullptr;
        sm_item = new Scene_surface_mesh_item;

        using Plane = CGAL::Shape_detection::RG::Plane<Kernel>;
        boost::shared_ptr<Plane> rg_plane(new Plane(*points, points->point_map(), regions[index], plane));
        build_alpha_shape(
          *(point_item->point_set()), rg_plane,
          sm_item, search_sphere_radius);

        if (sm_item){
          sm_item->setColor(point_item->color ());
          sm_item->setName(QString("%1%2_alpha_shape").arg(QString::fromStdString(ss.str()))
          .arg(QString::number(regions[index].size())));
          sm_item->setRenderingMode(Flat);
          sm_item->invalidateOpenGLBuffers();
          scene->addItem(sm_item);
          if (scene->item_id(groups[0]) == -1)
            scene->addItem(groups[0]);
          scene->changeGroup(sm_item, groups[0]);
        }
      }
      ss << regions[index].size();

      point_item->setName(QString::fromStdString(ss.str()));
      point_item->setRenderingMode(item->renderingMode());

      if (dialog.generate_subset()){
        point_item->invalidateOpenGLBuffers();
        scene->addItem(point_item);
        point_item->point_set()->add_normal_map();

        // Set normals for point_item to the plane's normal.
        for (auto it = point_item->point_set()->begin();
        it != point_item->point_set()->end(); ++it)
          point_item->point_set()->normal(*it) = plane_normal;

        if (scene->item_id(groups[0]) == -1)
          scene->addItem(groups[0]);
        point_item->invalidateOpenGLBuffers();
        scene->changeGroup(point_item, groups[0]);
      }
      else delete point_item;
      ++index;
    }

    Q_FOREACH(Scene_group_item* group, groups)
      if(group && group->getChildren().empty())
        delete group;

    if (dialog.generate_structured()) {
      std::cerr << "Structuring point set... ";

      Scene_points_with_normal_item *pts_full = new Scene_points_with_normal_item;
      pts_full->point_set()->add_normal_map();

      CGAL::structure_point_set(
        *points,
        planes,
        boost::make_function_output_iterator(build_from_pair((*(pts_full->point_set())))),
        search_sphere_radius,
        points->parameters().
        plane_map(CGAL::Identity_property_map<Plane_3>()).
        plane_index_map(CGAL::Shape_detection::RG::Point_to_shape_index_map(*points, regions)));

      if (pts_full->point_set()->empty())
        delete pts_full;
      else {
        pts_full->point_set()->unselect_all();
        pts_full->setName(tr("%1 (structured)").arg(item->name()));
        pts_full->setRenderingMode(PointsPlusNormals);
        pts_full->setColor(Qt::blue);
        pts_full->invalidateOpenGLBuffers();
        scene->addItem(pts_full);
      }
      std::cerr << "done" << std::endl;
    }

    // Updates scene.
    scene->itemChanged(index);
    QApplication::restoreOverrideCursor();
    item->setVisible(false);
  }

  void detect_shapes_with_ransac (Scene_points_with_normal_item* item,
                      Point_set_demo_point_set_shape_detection_dialog& dialog)
  {
    typedef Point_set::Point_map PointPMap;
    typedef Point_set::Vector_map NormalPMap;

    typedef CGAL::Shape_detection::Efficient_RANSAC_traits<Epic_kernel, Point_set, PointPMap, NormalPMap> Traits;
    typedef CGAL::Shape_detection::Efficient_RANSAC<Traits> Ransac;

    Ransac::Parameters op;
    op.probability = dialog.search_probability();       // probability to miss the largest primitive on each iteration.
    op.min_points = dialog.min_points();          // Only extract shapes with a minimum number of points.
    op.epsilon = dialog.epsilon();          // maximum euclidean distance between point and shape.
    op.cluster_epsilon = dialog.cluster_epsilon();    // maximum euclidean distance between points to be clustered.
    op.normal_threshold = std::cos(CGAL_PI * dialog.normal_tolerance() / 180.);   // normal_threshold < dot(surface_normal, point_normal);

    CGAL::Random rand(static_cast<unsigned int>(time(nullptr)));
    // Gets point set
    Point_set* points = item->point_set();

    Scene_points_with_normal_item::Bbox bb = item->bbox();

    double diam = CGAL::sqrt((bb.xmax()-bb.xmin())*(bb.xmax()-bb.xmin()) + (bb.ymax()-bb.ymin())*(bb.ymax()-bb.ymin()) + (bb.zmax()-bb.zmin())*(bb.zmax()-bb.zmin()));

    scene->setSelectedItem(-1);
    Scene_points_with_normal_item *colored_item
      = new Scene_points_with_normal_item;
    colored_item->setName (QString("%1 (ransac)").arg(item->name()));
    if (dialog.generate_colored_point_set())
    {
      colored_item->point_set()->template add_property_map<unsigned char>("r", 128);
      colored_item->point_set()->template add_property_map<unsigned char>("g", 128);
      colored_item->point_set()->template add_property_map<unsigned char>("b", 128);
      colored_item->point_set()->check_colors();
      scene->addItem(colored_item);
    }

    std::string& comments = item->comments();

    Point_set::Property_map<int> shape_id;
    if (dialog.add_property())
    {
      bool added = false;
      boost::tie (shape_id, added) = points->template add_property_map<int> ("shape", -1);
      if (!added)
      {
        for (Point_set::iterator it = points->begin(); it != points->end(); ++ it)
          shape_id[*it] = -1;
      }

      // Remove previously detected shapes from comments
      std::string new_comment;

      std::istringstream stream (comments);
      std::string line;
      while (getline(stream, line))
      {
        std::stringstream iss (line);
        std::string tag;
        if (iss >> tag && tag == "shape")
          continue;
        new_comment += line + "\n";
      }
      comments = new_comment;
      comments += "shape -1 no assigned shape\n";
    }

    QApplication::setOverrideCursor(Qt::BusyCursor);

    Ransac ransac;
    ransac.set_input(*points, points->point_map(), points->normal_map());

    std::vector<Scene_group_item *> groups;
    groups.resize(5);
    // Shapes to be searched for are registered by using the template Shape_factory
    if(dialog.detect_plane()){
      groups[0] = new Scene_group_item("Planes");
      groups[0]->setRenderingMode(Points);
      add_shape<Traits> (ransac, CGAL::Shape_detection::Plane<Traits>());
    }
    if(dialog.detect_cylinder()){
      groups[1] = new Scene_group_item("Cylinders");
      groups[1]->setRenderingMode(Points);
      add_shape<Traits> (ransac, CGAL::Shape_detection::Cylinder<Traits>());
    }
    if(dialog.detect_torus()){
      groups[2] = new Scene_group_item("Torus");
      groups[2]->setRenderingMode(Points);
      add_shape<Traits> (ransac, CGAL::Shape_detection::Torus<Traits>());
    }
    if(dialog.detect_cone()){
      groups[3] = new Scene_group_item("Cones");
      groups[3]->setRenderingMode(Points);
      add_shape<Traits> (ransac, CGAL::Shape_detection::Cone<Traits>());
    }
    if(dialog.detect_sphere()){
      groups[4] = new Scene_group_item("Spheres");
      groups[4]->setRenderingMode(Points);
      add_shape<Traits> (ransac, CGAL::Shape_detection::Sphere<Traits>());
    }

    // The actual shape detection.
    CGAL::Real_timer t;
    t.start();
    Detect_shapes_functor<Ransac> functor (ransac, op);
    run_with_qprogressdialog<CGAL::Sequential_tag> (functor, "Detecting shapes...", mw);
    t.stop();

    std::cout << ransac.shapes().size() << " shapes found in "
              << t.time() << " second(s)" << std::endl;

    if (dialog.regularize ())
      {
        std::cerr << "Regularization of planes... " << std::endl;
        typename Ransac::Plane_range planes = ransac.planes();
        CGAL::regularize_planes (*points,
                                 points->point_map(),
                                 planes,
                                 CGAL::Shape_detection::Plane_map<Traits>(),
                                 CGAL::Shape_detection::Point_to_shape_index_map<Traits>(*points, planes),
                                 true, true, true, true,
                                 op.normal_threshold, op.epsilon);

        std::cerr << "done" << std::endl;
      }

    std::map<Kernel::Point_3, QColor> color_map;

    int index = 0;
    for(boost::shared_ptr<typename Ransac::Shape> shape : ransac.shapes())
    {
      CGAL::Shape_detection::Cylinder<Traits> *cyl;
      cyl = dynamic_cast<CGAL::Shape_detection::Cylinder<Traits> *>(shape.get());
      if (cyl != nullptr){
        if(cyl->radius() > diam){
          continue;
        }
      }

      if (dialog.add_property())
      {
        std::ostringstream oss;
        oss << "shape " << index;
        if (CGAL::Shape_detection::Plane<Traits>* s
            = dynamic_cast<CGAL::Shape_detection::Plane<Traits> *>(shape.get()))
          oss << " plane " << Kernel::Plane_3(*s) << std::endl;
        else if (CGAL::Shape_detection::Cylinder<Traits>* s
            = dynamic_cast<CGAL::Shape_detection::Cylinder<Traits> *>(shape.get()))
          oss << " cylinder axis = [" << s->axis() << "] radius = " << s->radius() << std::endl;
        else if (CGAL::Shape_detection::Cone<Traits>* s
            = dynamic_cast<CGAL::Shape_detection::Cone<Traits> *>(shape.get()))
          oss << " cone apex = [" << s->apex() << "] axis = [" << s->axis()
              << "] angle = " << s->angle() << std::endl;
        else if (CGAL::Shape_detection::Torus<Traits>* s
            = dynamic_cast<CGAL::Shape_detection::Torus<Traits> *>(shape.get()))
          oss << " torus center = [" << s->center() << "] axis = [" << s->axis()
              << "] R = " << s->major_radius() << " r = " << s->minor_radius() << std::endl;
        else if (CGAL::Shape_detection::Sphere<Traits>* s
            = dynamic_cast<CGAL::Shape_detection::Sphere<Traits> *>(shape.get()))
          oss << " sphere center = [" << s->center() << "] radius = " << s->radius() << std::endl;

        comments += oss.str();
      }

      Scene_points_with_normal_item *point_item = new Scene_points_with_normal_item;

      for(std::size_t i : shape->indices_of_assigned_points())
      {
        point_item->point_set()->insert(points->point(*(points->begin()+i)));
        if (dialog.add_property())
          shape_id[*(points->begin()+i)] = index;
      }

      unsigned char r, g, b;

      r = static_cast<unsigned char>(64 + rand.get_int(0, 192));
      g = static_cast<unsigned char>(64 + rand.get_int(0, 192));
      b = static_cast<unsigned char>(64 + rand.get_int(0, 192));

      point_item->setRgbColor(r, g, b);

      std::size_t nb_colored_pts = 0;
      if (dialog.generate_colored_point_set())
      {
        for(std::size_t i : shape->indices_of_assigned_points())
        {
          Point_set::iterator it = colored_item->point_set()->insert(points->point(*(points->begin()+i)));
          ++ nb_colored_pts;
          colored_item->point_set()->set_color(*it, r, g, b);
        }
        colored_item->invalidateOpenGLBuffers();
      }

      // Providing a useful name consisting of the order of detection, name of type and number of inliers
      std::stringstream ss;
      if (dynamic_cast<CGAL::Shape_detection::Cylinder<Traits> *>(shape.get())){
        CGAL::Shape_detection::Cylinder<Traits> * cyl
          = dynamic_cast<CGAL::Shape_detection::Cylinder<Traits> *>(shape.get());
        ss << item->name().toStdString() << "_cylinder_" << cyl->radius() << "_";
      }
      else if (dynamic_cast<CGAL::Shape_detection::Plane<Traits> *>(shape.get()))
        {
          ss << item->name().toStdString() << "_plane_";

          boost::shared_ptr<CGAL::Shape_detection::Plane<Traits> > pshape
            = boost::dynamic_pointer_cast<CGAL::Shape_detection::Plane<Traits> > (shape);

          Kernel::Point_3 ref = CGAL::ORIGIN + pshape->plane_normal ();

          if (color_map.find (ref) == color_map.end ())
            {
              ref = CGAL::ORIGIN + (-1.) * pshape->plane_normal ();
              if (color_map.find (ref) == color_map.end ())
                color_map[ref] = point_item->color ();
              else
                point_item->setColor (color_map[ref]);
            }
          else
            point_item->setColor (color_map[ref]);

          if (dialog.generate_colored_point_set())
          {
            for (std::size_t i = 0; i < nb_colored_pts; ++ i)
            {
              colored_item->point_set()->set_color(*(colored_item->point_set()->end() - 1 - i), color_map[ref].red(),
                                                   color_map[ref].green(),
                                                   color_map[ref].blue());
            }
          }

          ss << "(" << ref << ")_";

          if (dialog.generate_alpha ())
            {
              // If plane, build alpha shape
              Scene_surface_mesh_item* sm_item = nullptr;
                sm_item = new Scene_surface_mesh_item;

              build_alpha_shape (*(point_item->point_set()), pshape,
                                 sm_item, dialog.cluster_epsilon());

              if(sm_item){
                sm_item->setColor(point_item->color ());
                sm_item->setName(QString("%1%2_alpha_shape").arg(QString::fromStdString(ss.str()))
                                   .arg (QString::number (shape->indices_of_assigned_points().size())));
                sm_item->setRenderingMode (Flat);
                sm_item->invalidateOpenGLBuffers();
                scene->addItem(sm_item);
                if(scene->item_id(groups[0]) == -1)
                  scene->addItem(groups[0]);
                scene->changeGroup(sm_item, groups[0]);
              }
            }
        }
      else if (dynamic_cast<CGAL::Shape_detection::Cone<Traits> *>(shape.get()))
        ss << item->name().toStdString() << "_cone_";
      else if (dynamic_cast<CGAL::Shape_detection::Torus<Traits> *>(shape.get()))
        ss << item->name().toStdString() << "_torus_";
      else if (dynamic_cast<CGAL::Shape_detection::Sphere<Traits> *>(shape.get()))
        ss << item->name().toStdString() << "_sphere_";
      ss << shape->indices_of_assigned_points().size();

      //names[i] = ss.str(
      point_item->setName(QString::fromStdString(ss.str()));
      point_item->setRenderingMode(item->renderingMode());

      if (dialog.generate_subset()){
        point_item->invalidateOpenGLBuffers();
        scene->addItem(point_item);
        if (dynamic_cast<CGAL::Shape_detection::Cylinder<Traits> *>(shape.get()))
        {
          if(scene->item_id(groups[1]) == -1)
             scene->addItem(groups[1]);
          scene->changeGroup(point_item, groups[1]);
        }
        else if (dynamic_cast<CGAL::Shape_detection::Plane<Traits> *>(shape.get()))
        {
          point_item->point_set()->add_normal_map();
          CGAL::Shape_detection::Plane<Traits> * plane = dynamic_cast<CGAL::Shape_detection::Plane<Traits> *>(shape.get());
          //set normals for point_item to the plane's normal
          for(Point_set::iterator it = point_item->point_set()->begin(); it != point_item->point_set()->end(); ++it)
            point_item->point_set()->normal(*it) = plane->plane_normal();

          if(scene->item_id(groups[0]) == -1)
            scene->addItem(groups[0]);

          point_item->invalidateOpenGLBuffers();
          scene->changeGroup(point_item, groups[0]);
        }
        else if (dynamic_cast<CGAL::Shape_detection::Cone<Traits> *>(shape.get()))
        {
          if(scene->item_id(groups[3]) == -1)
             scene->addItem(groups[3]);
          scene->changeGroup(point_item, groups[3]);
        }
        else if (dynamic_cast<CGAL::Shape_detection::Torus<Traits> *>(shape.get()))
        {
          if(scene->item_id(groups[2]) == -1)
             scene->addItem(groups[2]);
          scene->changeGroup(point_item, groups[2]);
        }
        else if (dynamic_cast<CGAL::Shape_detection::Sphere<Traits> *>(shape.get()))
        {
          if(scene->item_id(groups[4]) == -1)
             scene->addItem(groups[4]);
          scene->changeGroup(point_item, groups[4]);
        }
      }
      else
        delete point_item;

      ++index;
    }
    Q_FOREACH(Scene_group_item* group, groups)
      if(group && group->getChildren().empty())
        delete group;

    if (dialog.generate_structured ())
      {
        std::cerr << "Structuring point set... ";

        Scene_points_with_normal_item *pts_full = new Scene_points_with_normal_item;
        pts_full->point_set()->add_normal_map();

        typename Ransac::Plane_range planes = ransac.planes();
        CGAL::structure_point_set (*points,
                                   planes,
                                   boost::make_function_output_iterator (build_from_pair ((*(pts_full->point_set())))),
                                   op.cluster_epsilon,
                                   points->parameters().
                                   plane_map(CGAL::Shape_detection::Plane_map<Traits>()).
                                   plane_index_map(CGAL::Shape_detection::Point_to_shape_index_map<Traits>(*points, planes)));

        if (pts_full->point_set ()->empty ())
          delete pts_full;
        else
          {
            pts_full->point_set ()->unselect_all();
            pts_full->setName(tr("%1 (structured)").arg(item->name()));
            pts_full->setRenderingMode(PointsPlusNormals);
            pts_full->setColor(Qt::blue);
            pts_full->invalidateOpenGLBuffers();
            scene->addItem (pts_full);
          }
        std::cerr << "done" << std::endl;
      }

    // Updates scene
    scene->itemChanged(index);

    QApplication::restoreOverrideCursor();

    item->setVisible(false);
  }

  Kernel::Point_2 to_2d (const Point_3& centroid,
                         const Vector_3& base1,
                         const Vector_3& base2,
                         const Point_3& query)
  {
    Vector_3 v (centroid, query);
    return Kernel::Point_2 (v * base1, v * base2);
  }

  Point_3 to_3d (const Point_3& centroid,
                 const Vector_3& base1,
                 const Vector_3& base2,
                 const Kernel::Point_2& query)
  {
    return centroid + query.x() * base1 + query.y() * base2;
  }

    template<typename Plane>
    void build_alpha_shape (Point_set& points, boost::shared_ptr<Plane> plane,
                          Scene_surface_mesh_item* sm_item, double epsilon);

}; // end Polyhedron_demo_point_set_shape_detection_plugin

void Polyhedron_demo_point_set_shape_detection_plugin::on_actionDetectShapesSM_triggered() {

  const CGAL::Three::Scene_interface::Item_id index = scene->mainSelectionIndex();

  Scene_surface_mesh_item* sm_item =
  qobject_cast<Scene_surface_mesh_item*>(scene->item(index));

  if(sm_item) {

    // Get a surface mesh.
    SMesh* mesh = sm_item->polyhedron();
    if(mesh == nullptr) return;

    Point_set_demo_point_set_shape_detection_dialog dialog;

    dialog.ransac->setEnabled(false);
    dialog.m_regularize->setEnabled(false);
    dialog.m_generate_structured->setEnabled(false);
    dialog.label_4->setEnabled(false);
    dialog.m_cluster_epsilon_field->setEnabled(false);
    dialog.groupBox_3->setEnabled(false);
    //todo: check default values
    dialog.m_epsilon_field->setValue(0.01*sm_item->diagonalBbox());
    std::size_t nb_faces = mesh->number_of_faces();
    dialog.m_min_pts_field->setValue((std::max)(static_cast<int>(0.01*nb_faces), 1));
    if(!dialog.exec()) return;

    if(dialog.min_points() > static_cast<unsigned int>(nb_faces))
      dialog.m_min_pts_field->setValue(static_cast<unsigned int>(nb_faces));
    QApplication::setOverrideCursor(Qt::WaitCursor);
    if (dialog.region_growing()) {
      detect_shapes_with_region_growing_sm(sm_item, dialog);
    }

    dialog.ransac->setEnabled(true);
    dialog.m_regularize->setEnabled(true);
    dialog.m_generate_structured->setEnabled(true);
    dialog.label_4->setEnabled(true);
    dialog.m_cluster_epsilon_field->setEnabled(true);
    dialog.groupBox_3->setEnabled(true);

    // Update scene.
    scene->itemChanged(index);
    QApplication::restoreOverrideCursor();
    sm_item->setVisible(false);
  }
}

void Polyhedron_demo_point_set_shape_detection_plugin::on_actionDetect_triggered() {

  const CGAL::Three::Scene_interface::Item_id index = scene->mainSelectionIndex();

  Scene_points_with_normal_item* item =
    qobject_cast<Scene_points_with_normal_item*>(scene->item(index));

  if(item)
    {
      // Gets point set
      Point_set* points = item->point_set();

      if(points == nullptr)
        return;

      //Epic_kernel::FT diag = sqrt(((points->bounding_box().max)() - (points->bounding_box().min)()).squared_length());

      // Gets options
      Point_set_demo_point_set_shape_detection_dialog dialog;
      if(!dialog.exec())
        return;
      if(dialog.min_points() > static_cast<unsigned int>(points->size()))
        dialog.m_min_pts_field->setValue(static_cast<unsigned int>(points->size()));

      QApplication::setOverrideCursor(Qt::WaitCursor);
      if (dialog.region_growing())
      {
        detect_shapes_with_region_growing(item, dialog);
      }
      else
      {
        detect_shapes_with_ransac(item, dialog);
      }

      // Updates scene
      scene->itemChanged(index);

      QApplication::restoreOverrideCursor();

      item->setVisible(false);
    }
}

template<typename Plane>
void Polyhedron_demo_point_set_shape_detection_plugin::build_alpha_shape
(Point_set& points,  boost::shared_ptr<Plane> plane, Scene_surface_mesh_item* sm_item, double epsilon)
{
  typedef Kernel::Point_2  Point_2;
  typedef CGAL::Alpha_shape_vertex_base_2<Kernel> Vb;
  typedef CGAL::Alpha_shape_face_base_2<Kernel>  Fb;
  typedef CGAL::Triangulation_data_structure_2<Vb,Fb> Tds;
  typedef CGAL::Delaunay_triangulation_2<Kernel,Tds> Triangulation_2;
  typedef CGAL::Alpha_shape_2<Triangulation_2>  Alpha_shape_2;

  std::vector<Point_2> projections;
  projections.reserve (points.size ());

  for (Point_set::const_iterator it = points.begin(); it != points.end(); ++ it)
    projections.push_back (plane->to_2d (points.point(*it)));

  Alpha_shape_2 ashape (projections.begin (), projections.end (), epsilon);

  std::map<Alpha_shape_2::Vertex_handle, std::size_t> map_v2i;

  Scene_polygon_soup_item *soup_item = new Scene_polygon_soup_item;

  soup_item->init_polygon_soup(points.size(), ashape.number_of_faces ());
  std::size_t current_index = 0;

  for (Alpha_shape_2::Finite_faces_iterator it = ashape.finite_faces_begin ();
       it != ashape.finite_faces_end (); ++ it)
    {
      if (ashape.classify (it) != Alpha_shape_2::INTERIOR)
        continue;

      for (int i = 0; i < 3; ++ i)
        {
          if (map_v2i.find (it->vertex (i)) == map_v2i.end ())
            {
              map_v2i.insert (std::make_pair (it->vertex (i), current_index ++));
              Point p = plane->to_3d (it->vertex (i)->point ());
              soup_item->new_vertex (p.x (), p.y (), p.z ());
            }
        }
      soup_item->new_triangle (map_v2i[it->vertex (0)],
                               map_v2i[it->vertex (1)],
                               map_v2i[it->vertex (2)]);
    }

  soup_item->orient();
  if(sm_item){
    soup_item->exportAsSurfaceMesh (sm_item->polyhedron());
  }

  if (soup_item->isEmpty ())
    {
      std::cerr << "POLYGON SOUP EMPTY" << std::endl;
      for (std::size_t i = 0; i < projections.size (); ++ i)
        std::cerr << projections[i] << std::endl;

    }

  delete soup_item;
}

void Polyhedron_demo_point_set_shape_detection_plugin::on_actionEstimateParameters_triggered() {

  CGAL::Random rand(static_cast<unsigned int>(time(nullptr)));
  const CGAL::Three::Scene_interface::Item_id index = scene->mainSelectionIndex();

  Scene_points_with_normal_item* item =
    qobject_cast<Scene_points_with_normal_item*>(scene->item(index));

  if(item)
    {
      // Gets point set
      Point_set* points = item->point_set();

      if(points == nullptr)
        return;

      if (points->nb_selected_points() == 0)
        {
          QMessageBox::information(nullptr,
                                   tr("Warning"),
                                   tr("Selection is empty.\nTo estimate parameters, please select a planar section."));
          return;
        }

      QApplication::setOverrideCursor(Qt::WaitCursor);

      typedef CGAL::Search_traits_3<Kernel> SearchTraits_3;
      typedef CGAL::Search_traits_adapter <Point_set::Index,
                                           Point_set::Point_map, SearchTraits_3> Search_traits;
      typedef CGAL::Orthogonal_k_neighbor_search<Search_traits> Neighbor_search;
      typedef Neighbor_search::Tree Tree;
      typedef Neighbor_search::Distance Distance;

      // build kdtree
      Tree tree(points->first_selected(),
                points->end(),
                Tree::Splitter(),
                Search_traits (points->point_map())
                );
      Distance tr_dist(points->point_map());

      Plane_3 plane;
      CGAL::linear_least_squares_fitting_3(boost::make_transform_iterator
                                           (points->first_selected(),
                                            CGAL::Property_map_to_unary_function<Point_set::Point_map>
                                            (points->point_map())),
                                           boost::make_transform_iterator
                                           (points->end(),
                                            CGAL::Property_map_to_unary_function<Point_set::Point_map>
                                            (points->point_map())),
                                           plane,
                                           CGAL::Dimension_tag<0>());

      std::vector<double> epsilon, dispersion, cluster_epsilon;

      Vector_3 norm = plane.orthogonal_vector();
      norm = norm / std::sqrt (norm * norm);
      for (Point_set::iterator it = points->first_selected(); it != points->end(); ++ it)
        {
          double dist = CGAL::squared_distance (plane, points->point(*it));
          epsilon.push_back(dist);

          double disp = std::fabs (norm * points->normal(*it));
          dispersion.push_back (disp);

          Neighbor_search search(tree, points->point(*it), 2, 0, true, tr_dist);
          Neighbor_search::iterator nit = search.begin();
          ++ nit;
          double eps = nit->second;
          cluster_epsilon.push_back(eps);
        }

      std::sort (epsilon.begin(), epsilon.end());
      std::sort (dispersion.begin(), dispersion.end());
      std::sort (cluster_epsilon.begin(), cluster_epsilon.end());

      QApplication::restoreOverrideCursor();

      QMessageBox::information(nullptr,
                               tr("Estimated Parameters"),
                               tr("Epsilon = [%1 ; %2 ; %3 ; %4 ; %5]\nNormal Tolerance = [%6 ; %7 ; %8 ; %9 ; %10]\nMinimum Number of Points = %11\nConnectivity Epsilon = [%12 ; %13 ; %14 ; %15 ; %16]")
                               .arg(std::sqrt(epsilon.front()))
                               .arg(std::sqrt(epsilon[epsilon.size() / 10]))
                               .arg(std::sqrt(epsilon[epsilon.size() / 2]))
                               .arg(std::sqrt(epsilon[9 * epsilon.size() / 10]))
                               .arg(std::sqrt(epsilon.back()))
                               .arg(dispersion.back())
                               .arg(dispersion[9 * dispersion.size() / 10])
                               .arg(dispersion[dispersion.size() / 2])
                               .arg(dispersion[dispersion.size() / 10])
                               .arg(dispersion.front())
                               .arg(points->nb_selected_points())
                               .arg(std::sqrt(cluster_epsilon.front()))
                               .arg(std::sqrt(cluster_epsilon[cluster_epsilon.size() / 10]))
                               .arg(std::sqrt(cluster_epsilon[cluster_epsilon.size() / 2]))
                               .arg(std::sqrt(cluster_epsilon[9 * cluster_epsilon.size() / 10]))
                               .arg(std::sqrt(cluster_epsilon.back())));
    }
}

#include <QtPlugin>

#include "Point_set_shape_detection_plugin.moc"


#include <Eigen/Dense>

void readFromVector(){
     using Eigen::MatrixXd;

     Eigen::Vector3d

int main(){
    readFromVector();
    return 0;
}


//==============================================================================
//         Copyright 2003 - 2011   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef BOOST_SIMD_CONSTANT_CONSTANTS_NBDIGITS_HPP_INCLUDED
#define BOOST_SIMD_CONSTANT_CONSTANTS_NBDIGITS_HPP_INCLUDED

#include <boost/simd/include/functor.hpp>
#include <boost/simd/meta/int_c.hpp>
#include <boost/simd/constant/hierarchy.hpp>

namespace boost { namespace simd
{
  namespace tag
  {
   /*!
     @brief Nbdigits generic tag

     Represents the Nbdigits constant in generic contexts.

     @par Models:
        Hierarchy
   **/
    struct Nbdigits : ext::pure_constant_<Nbdigits>
    {
      typedef double default_type;
      typedef ext::pure_constant_<Nbdigits> parent;

      /// INTERNAL ONLY
      template<class Target, class Dummy=void>
      struct  apply : meta::int_c <typename Target::type,0> {};
      template<class... Args>
      static BOOST_FORCEINLINE BOOST_AUTO_DECLTYPE dispatch(Args&&... args)
      BOOST_AUTO_DECLTYPE_BODY( dispatching_Nbdigits( ext::adl_helper(), static_cast<Args&&>(args)... ) )
    };

    /// INTERNAL ONLY
    template<class T, class Dummy>
    struct  Nbdigits::apply<boost::dispatch::meta::single_<T>,Dummy>
          : meta::int_c<boost::simd::int32_t,24> {};

    /// INTERNAL ONLY
    template<class T, class Dummy>
    struct  Nbdigits::apply<boost::dispatch::meta::double_<T>,Dummy>
          : meta::int_c<boost::simd::int64_t,53> {};
  }
  namespace ext
  {
   template<class Site, class... Ts>
   BOOST_FORCEINLINE generic_dispatcher<tag::Nbdigits, Site> dispatching_Nbdigits(adl_helper, boost::dispatch::meta::unknown_<Site>, boost::dispatch::meta::unknown_<Ts>...)
   {
     return generic_dispatcher<tag::Nbdigits, Site>();
   }
   template<class... Args>
   struct impl_Nbdigits;
  }
  /*!
    Generates the number of mantissa bits of a floating point number,
    and the total number of bits for integral types.

    @par Semantic:

    @code
    T r = Nbdigits<T>();
    @endcode

    is similar to:

    @code
    if T is integral
      r = sizeof(T)*8
    else if T is double
      r =  53;
    else if T is float
      r =  24;
    @endcode

  **/
  BOOST_SIMD_CONSTANT_IMPLEMENTATION(boost::simd::tag::Nbdigits, Nbdigits)
} }

#include <boost/simd/constant/common.hpp>

#endif


/**
 * @file test_tools.hpp
 * @author Ryan Curtin
 *
 * This file includes some useful macros for tests.
 *
 * mlpack is free software; you may redistribute it and/or modify it under the
 * terms of the 3-clause BSD license.  You should have received a copy of the
 * 3-clause BSD license along with mlpack.  If not, see
 * http://www.opensource.org/licenses/BSD-3-Clause for more information.
 */
#ifndef MLPACK_TESTS_TEST_TOOLS_HPP
#define MLPACK_TESTS_TEST_TOOLS_HPP

#include <mlpack/core.hpp>
#include <boost/version.hpp>

// Require the approximation L to be within a relative error of E respect to the
// actual value R.
#define REQUIRE_RELATIVE_ERR(L, R, E) \
    BOOST_REQUIRE_LE(std::abs((R) - (L)), (E) * std::abs(R))

// Check the values of two matrices.
inline void CheckMatrices(const arma::mat& a,
                          const arma::mat& b,
                          double tolerance = 1e-5)
{
  BOOST_REQUIRE_EQUAL(a.n_rows, b.n_rows);
  BOOST_REQUIRE_EQUAL(a.n_cols, b.n_cols);

  for (size_t i = 0; i < a.n_elem; ++i)
  {
    if (std::abs(a[i]) < tolerance / 2)
      BOOST_REQUIRE_SMALL(b[i], tolerance / 2);
    else
      BOOST_REQUIRE_CLOSE(a[i], b[i], tolerance);
  }
}

// Check the values of two unsigned matrices.
inline void CheckMatrices(const arma::Mat<size_t>& a,
                          const arma::Mat<size_t>& b)
{
  BOOST_REQUIRE_EQUAL(a.n_rows, b.n_rows);
  BOOST_REQUIRE_EQUAL(a.n_cols, b.n_cols);

  for (size_t i = 0; i < a.n_elem; ++i)
    BOOST_REQUIRE_EQUAL(a[i], b[i]);
}

// Check the values of two cubes.
inline void CheckMatrices(const arma::cube& a,
                          const arma::cube& b,
                          double tolerance = 1e-5)
{
  BOOST_REQUIRE_EQUAL(a.n_rows, b.n_rows);
  BOOST_REQUIRE_EQUAL(a.n_cols, b.n_cols);
  BOOST_REQUIRE_EQUAL(a.n_slices, b.n_slices);

  for (size_t i = 0; i < a.n_elem; ++i)
  {
    if (std::abs(a[i]) < tolerance / 2)
      BOOST_REQUIRE_SMALL(b[i], tolerance / 2);
    else
      BOOST_REQUIRE_CLOSE(a[i], b[i], tolerance);
  }
}

// Check if two matrices are different.
inline void CheckMatricesNotEqual(const arma::mat& a,
                                  const arma::mat& b,
                                  double tolerance = 1e-5)
{
  bool areDifferent = false;

  // Only check the elements if the dimensions are equal.
  if (a.n_rows == b.n_rows && a.n_cols == b.n_cols)
  {
    for (size_t i = 0; i < a.n_elem; ++i)
    {
      if (std::abs(a[i]) < tolerance / 2 &&
          b[i] > tolerance / 2)
      {
        areDifferent = true;
        break;
      }
      else if (std::abs(a[i] - b[i]) > tolerance)
      {
        areDifferent = true;
        break;
      }
    }
  }
  else
    areDifferent = true;

  if (!areDifferent)
    BOOST_ERROR("The matrices are equal.");
}

// Check if two unsigned matrices are different.
inline void CheckMatricesNotEqual(const arma::Mat<size_t>& a,
                                  const arma::Mat<size_t>& b)
{
  bool areDifferent = false;

  // Only check the elements if the dimensions are equal.
  if (a.n_rows == b.n_rows && a.n_cols == b.n_cols)
  {
    for (size_t i = 0; i < a.n_elem; ++i)
    {
      if (a[i] != b[i])
      {
        areDifferent = true;
        break;
      }
    }
  }
  else
    areDifferent = true;

  if (!areDifferent)
    BOOST_ERROR("The matrices are equal.");
}

// Check if two cubes are different.
inline void CheckMatricesNotEqual(const arma::cube& a,
                                  const arma::cube& b,
                                  double tolerance = 1e-5)
{
  bool areDifferent = false;

  // Only check the elements if the dimensions are equal.
  if (a.n_rows == b.n_rows && a.n_cols == b.n_cols &&
      a.n_slices == b.n_slices)
  {
    for (size_t i = 0; i < a.n_elem; ++i)
    {
      if (std::abs(a[i]) < tolerance / 2 &&
          b[i] > tolerance / 2)
      {
        areDifferent = true;
        break;
      }
      else if (std::abs(a[i] - b[i]) > tolerance)
      {
        areDifferent = true;
        break;
      }
    }
  }
  else
    areDifferent = true;

  if (!areDifferent)
    BOOST_ERROR("The matrices are equal.");
}

// Filter typeinfo string to generate unique filenames for serialization tests.
inline std::string FilterFileName(const std::string& inputString)
{
  // Take the last valid 32 characters for the filename.
  std::string fileName;
  for (auto it = inputString.rbegin(); it != inputString.rend() &&
      fileName.size() != 32; ++it)
  {
    if (std::isalnum(*it))
      fileName.push_back(*it);
  }

  return fileName;
}

#endif