#include <algorithm>
#include <cfloat>
#include <cmath>
#include <iostream>
#include <list>
#include <map>
#include <string>
#include <thread>
#include <vector>
using namespace std;

#include "log.hpp"
#include "phoenixword.hpp"

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/graphviz.hpp>

typedef boost::adjacency_list<boost::listS, boost::vecS, boost::directedS,
    boost::no_property, boost::property<boost::edge_weight_t, float> > Graph;
typedef std::pair<int, int>                             Edge;
typedef boost::graph_traits<Graph>::vertex_descriptor   Vertex;

typedef std::tuple<string,double,double,string,string,string,int> LabelVertex;

struct sample_graph_writer {
  void operator()(std::ostream& out, int i) const {
    auto wordPair = labelVertex_.at(i);
    out << " [ label = <";
    out << "
";
    out << "
";
    out << "" << std::get<0>(wordPair) << "";
    out << "
";
    out << "
";
    out << "" << 1.0/std::get<1>(wordPair);
    out << " " << std::get<3>(wordPair);
    out << "";
    out << "
";
    out << "
";
    out << "" << std::get<1>(wordPair);
    out << " " << std::get<4>(wordPair);
    out  << "";
    out << "
";
    out << "
";
    out << "" << std::get<2>(wordPair);
    out << " " << std::get<5>(wordPair);
    out << "";
    out << "
";
    out << "
";
/*
    out << "" << std::get<0>(wordPair) << "";
    out << "
";
    out << "" << " " ;
    out << 1.0/(std::get<1>(wordPair)) << "
";
    out << std::get<1>(wordPair) << "
" ;
    out << std::get<2>(wordPair) << "";
*/
    out << ">]";
  }
  sample_graph_writer(vector<LabelVertex> &labelVertex):labelVertex_(labelVertex) {
  }
  vector<LabelVertex> &labelVertex_;
};

void PhoenixWord::calcPrediction(const multimap<int, WordElement> &confuse) {
  Graph g;
  multimap<int, std::tuple<string, Vertex,double,double>> vertexWator;
  vector<LabelVertex> labelVertex;

  auto vrtxStart = boost::add_vertex(g);
  auto vrtxPrStart = std::make_tuple("S", vrtxStart,1.0,1.0);
  vertexWator.insert(std::make_pair(-1, vrtxPrStart));
  labelVertex.push_back(std::make_tuple("S",1.0,1.0,"","","",-1));

  int posLast = 0;
  for (auto elem : confuse) {
    auto word = std::get<0>(elem.second);
    auto position = std::get<1>(elem.second);
    auto weight = std::get<3>(elem.second);
    auto weightO = std::get<4>(elem.second);
    auto vrtx = boost::add_vertex(g);
    auto vrtxPr = std::make_tuple(word, vrtx,weight,weightO);
    vertexWator.insert(std::make_pair(position, vrtxPr));
    labelVertex.push_back(std::make_tuple(word,weight,weightO,"","","",position));
    posLast = position + word.size();
  }
  auto vrtxEnd = add_vertex(g);
  auto vrtxPrvrtxEnd = std::make_tuple("E", vrtxEnd,1.0,1.0);
  vertexWator.insert(std::make_pair(posLast,vrtxPrvrtxEnd));
  labelVertex.push_back(std::make_tuple("E",1.0,1.0,"","","",-1));

  // add dummy start.
  {
    auto startPos = confuse.begin()->first;
    auto rangeSelf = vertexWator.equal_range(startPos);
    for (auto itSelf = rangeSelf.first; itSelf != rangeSelf.second; itSelf++) {
      auto wordSelf = std::get<0>(itSelf->second);
      auto vrtxSelf = std::get<1>(itSelf->second);
      auto weightR = std::get<2>(itSelf->second);
      auto weightRO = std::get<3>(itSelf->second);
      auto ed = boost::add_edge(vrtxStart, vrtxSelf,g);
      auto weightOld = boost::get(boost::edge_weight_t(), g, ed.first);
      boost::put(boost::edge_weight_t(), g, ed.first, 1.0/weightR);
      auto weightNew = boost::get(boost::edge_weight_t(), g, ed.first);
      TRACE_VAR(weightR,weightRO,weightOld,weightNew);
    }
  }

  for (auto elem : wordHintSeq_) {
    auto word = std::get<0>(elem.second);
    auto position = std::get<1>(elem.second);
    auto range = std::get<2>(elem.second);
    auto next = position + range;
    auto rangeSelf = vertexWator.equal_range(position);
    for (auto itSelf = rangeSelf.first; itSelf != rangeSelf.second; itSelf++) {
      auto wordSelf = std::get<0>(itSelf->second);
      auto vrtxSelf = std::get<1>(itSelf->second);
      auto weightR = std::get<2>(itSelf->second);
      auto weightRO = std::get<3>(itSelf->second);
      if (word == wordSelf) {
        auto rangeNext = vertexWator.equal_range(next);
        for (auto itNext = rangeNext.first; itNext != rangeNext.second;
             itNext++) {
          auto vrtxNext = std::get<1>(itNext->second);
          auto ed = boost::add_edge(vrtxSelf, vrtxNext,g);
          auto weightOld = boost::get(boost::edge_weight_t(), g, ed.first);
          boost::put(boost::edge_weight_t(), g, ed.first, 1.0/weightR);
          auto weightNew = boost::get(boost::edge_weight_t(), g, ed.first);
          TRACE_VAR(weightR,weightRO,weightOld,weightNew);
        }
        break;
      }
    }
    // add dummy end.
    if(posLast == position + range) {
      auto rangeSelf = vertexWator.equal_range(position);
      for (auto itSelf = rangeSelf.first; itSelf != rangeSelf.second; itSelf++) {
        auto wordSelf = std::get<0>(itSelf->second);
        auto vrtxSelf = std::get<1>(itSelf->second);
        if (word == wordSelf) {
          auto ed = boost::add_edge(vrtxSelf,vrtxEnd,g);
          auto weight = boost::get(boost::edge_weight_t(), g, ed.first);
          boost::put(boost::edge_weight_t(), g, ed.first, 0.0);
          auto weight2 = boost::get(boost::edge_weight_t(), g, ed.first);
          TRACE_VAR(weight,weight2);
          break;
        }
      }
    }
  }

  sample_graph_writer gw(labelVertex);

  std::stringstream ss;
  boost::write_graphviz(ss, g, gw);
  auto dotStr = ss.str();
  boost::algorithm::replace_all(
      dotStr, "digraph G {",
      "digraph G { \n rankdir=LR;\n graph [charset=\"UTF-8\"];\n");
  TRACE_VAR(dotStr);
  // dot -v -T svg 1.dot -o 1.svg

  TRACE_VAR(boost::num_vertices(g),vrtxStart,vrtxEnd);

  std::vector<Vertex> parents(boost::num_vertices(g));
  std::vector<float> distance(boost::num_vertices(g));

  auto predmap = boost::predecessor_map(boost::make_iterator_property_map(parents.begin(), boost::get(boost::vertex_index, g))).
                          distance_map(boost::make_iterator_property_map(distance.begin(), boost::get(boost::vertex_index, g)));

  boost::dijkstra_shortest_paths(g,vrtxStart,predmap);

  #if 0
  boost::graph_traits < Graph >::vertex_iterator vi, vend;
  for (boost::tie(vi, vend) = vertices(g); vi != vend; ++vi) {
    auto labelSelf = std::get<0>(labelVertex.at(*vi));
    auto labelParent = std::get<0>(labelVertex.at(parents[*vi]));
    std::cout << "distance(" << labelSelf << ") = " << distance[*vi] << ", ";
    std::cout << "parent(" << labelSelf << ") = " << labelParent << std::endl;
  }
  #endif

  TRACE_VAR(distance.at(vrtxEnd));

  if (parents[vrtxEnd] == vrtxEnd) {
    std::cout << "no path" << std::endl;
    return ;
  }
  std::vector<int> path;
  for(auto v = vrtxEnd; v != vrtxStart; v = parents[v]) {
    if(v != vrtxEnd) {
      path.push_back(v);
    }
  }
  for(auto it = path.rbegin();it != path.rend();it++) {
      auto wordSelected = std::get<0>(labelVertex.at(*it));
      auto positionSelected = std::get<6>(labelVertex.at(*it));
      TRACE_VAR(*it,wordSelected);
      for (auto elem : confuse) {
        auto word = std::get<0>(elem.second);
        auto position = std::get<1>(elem.second);
        if(word ==wordSelected && position == positionSelected) {
            wordSeqTopSelected_.insert(elem);
            break;
        }
      }
  }
}

#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/lexical_cast.hpp>
#include <fstream>

string PhoenixWord::createGraph(const string &text,const string &sentence) {
  Graph g;
  multimap<int, std::tuple<string, Vertex,double,double>> vertexWator;
  vector<LabelVertex> labelVertex;

  auto vrtxStart = boost::add_vertex(g);
  auto vrtxPrStart = std::make_tuple("S", vrtxStart,1.0,1.0);
  vertexWator.insert(std::make_pair(-1, vrtxPrStart));
  labelVertex.push_back(std::make_tuple("S",1.0,1.0,"","","",-1));

  int posLast = 0;
  for (auto elem : wordAdjustedSeq_) {
    auto word = std::get<0>(elem.second);
    auto position = std::get<1>(elem.second);
    auto weight = std::get<3>(elem.second);
    auto weightO = std::get<4>(elem.second);
    auto vrtx = boost::add_vertex(g);
    auto vrtxPr = std::make_tuple(word, vrtx,weight,weightO);
    vertexWator.insert(std::make_pair(position, vrtxPr));
    labelVertex.push_back(std::make_tuple(word,weight,weightO,"","","",position));
    posLast = position + word.size();
  }
  auto vrtxEnd = add_vertex(g);
  auto vrtxPrvrtxEnd = std::make_tuple("E", vrtxEnd,1.0,1.0);
  vertexWator.insert(std::make_pair(posLast,vrtxPrvrtxEnd));
  labelVertex.push_back(std::make_tuple("E",1.0,1.0,"","","",-1));

  auto vrtxTitle = add_vertex(g);
  auto title = u8"input=【" + text + u8"】";
  auto vrtxPrvrtxTitle = std::make_tuple(title, vrtxTitle,0.0,0.0);
  vertexWator.insert(std::make_pair(posLast + 10,vrtxPrvrtxTitle));
  labelVertex.push_back(std::make_tuple(title,1.0,1.0,"distance : 1.0 / adjusted duplicate rate"
                                        ," adjusted duplicate rate:  duplicate rate* power(CONST, word length)"
                                        ,"duplicate rate : this word duplicate couter / max duplicate couter",-1));

  auto vrtxSentence = add_vertex(g);
  auto titleSentence = u8"output=【" + sentence + u8"】";
  auto vrtxPrvrtxvrtxSentence = std::make_tuple(titleSentence, vrtxSentence,0.0,0.0);
  vertexWator.insert(std::make_pair(posLast + 11,vrtxPrvrtxvrtxSentence));
  labelVertex.push_back(std::make_tuple(titleSentence,1.0,1.0,"---"
                                        ,"---"
                                        ,"---",-1));

  auto ed = boost::add_edge(vrtxTitle,vrtxSentence,g);

  // add dummy start.
  {
    auto startPos = wordAdjustedSeq_.begin()->first;
    auto rangeSelf = vertexWator.equal_range(startPos);
    for (auto itSelf = rangeSelf.first; itSelf != rangeSelf.second; itSelf++) {
      auto wordSelf = std::get<0>(itSelf->second);
      auto vrtxSelf = std::get<1>(itSelf->second);
      auto weightR = std::get<2>(itSelf->second);
      auto weightRO = std::get<3>(itSelf->second);
      auto ed = boost::add_edge(vrtxStart, vrtxSelf,g);
      auto weightOld = boost::get(boost::edge_weight_t(), g, ed.first);
      boost::put(boost::edge_weight_t(), g, ed.first, 1.0/weightR);
      auto weightNew = boost::get(boost::edge_weight_t(), g, ed.first);
      TRACE_VAR(weightR,weightRO,weightOld,weightNew);
    }
  }

  for (auto elem : wordAdjustedSeq_) {
    auto word = std::get<0>(elem.second);
    auto position = std::get<1>(elem.second);
    auto range = std::get<2>(elem.second);
    auto next = position + range;
    auto rangeSelf = vertexWator.equal_range(position);
    for (auto itSelf = rangeSelf.first; itSelf != rangeSelf.second; itSelf++) {
      auto wordSelf = std::get<0>(itSelf->second);
      auto vrtxSelf = std::get<1>(itSelf->second);
      auto weightR = std::get<2>(itSelf->second);
      auto weightRO = std::get<3>(itSelf->second);
      if (word == wordSelf) {
        auto rangeNext = vertexWator.equal_range(next);
        for (auto itNext = rangeNext.first; itNext != rangeNext.second;
             itNext++) {
          auto vrtxNext = std::get<1>(itNext->second);
          auto ed = boost::add_edge(vrtxSelf, vrtxNext,g);
          auto weightOld = boost::get(boost::edge_weight_t(), g, ed.first);
          boost::put(boost::edge_weight_t(), g, ed.first, 1.0/weightR);
          auto weightNew = boost::get(boost::edge_weight_t(), g, ed.first);
          TRACE_VAR(weightR,weightRO,weightOld,weightNew);
        }
        break;
      }
    }
    // add dummy end.
    if(posLast == position + range) {
      auto rangeSelf = vertexWator.equal_range(position);
      for (auto itSelf = rangeSelf.first; itSelf != rangeSelf.second; itSelf++) {
        auto wordSelf = std::get<0>(itSelf->second);
        auto vrtxSelf = std::get<1>(itSelf->second);
        if (word == wordSelf) {
          auto ed = boost::add_edge(vrtxSelf,vrtxEnd,g);
          auto weight = boost::get(boost::edge_weight_t(), g, ed.first);
          boost::put(boost::edge_weight_t(), g, ed.first, 0.0);
          auto weight2 = boost::get(boost::edge_weight_t(), g, ed.first);
          TRACE_VAR(weight,weight2);
          break;
        }
      }
    }
  }

  sample_graph_writer gw(labelVertex);

  std::stringstream ss;
  boost::write_graphviz(ss, g, gw);
  auto dotStr = ss.str();
  boost::algorithm::replace_all(
      dotStr, "digraph G {",
      "digraph G { \n rankdir=LR;\n graph [charset=\"UTF-8\"];\n");
  TRACE_VAR(dotStr);

  int prePost = -2;
  string oneSame;
  string allSame;
  int elemPost = 1;
  for (auto elem : wordAdjustedSeq_) {
      auto position = std::get<1>(elem.second);
      if(position == prePost) {
          oneSame += ";";
          oneSame += to_string(elemPost);
      } else {
          if(oneSame.empty()==false) {
              oneSame += "}\n";
              allSame += oneSame;
          }
          oneSame = "{rank = same;";
          oneSame += to_string(elemPost);
      }
      prePost = position;
      elemPost++;
  }
  TRACE_VAR(allSame);
  boost::algorithm::replace_all(
      dotStr, "}",
      allSame +"}");
  TRACE_VAR(dotStr);

  /*{rank = same; A; X;}*/

  auto id = boost::uuids::random_generator()();
  auto fileName = boost::lexical_cast<std::string>(id);
  ::system("mkdir -p /tmp/wator/wai/graph/");
  string pathDot = "/tmp/wator/wai/graph/" + fileName +".dot";
  std::ofstream outfile(pathDot,std::ofstream::binary);
  if(outfile.good()) {
    outfile << dotStr;
    outfile.close();
  }
  ::system("mkdir -p /watorvapor/autogen/wator/wai/graph/");
  {
      string dotCmd = "dot -v -T png ";
      dotCmd += pathDot;
      dotCmd += " -o ";
      dotCmd += "/watorvapor/autogen/wator/wai/graph/" + fileName +".png";
      ::system(dotCmd.c_str());
      // dot -v -T png 1.dot -o 1.png
  }
  {
      string dotCmd = "dot -v -T svg ";
      dotCmd += pathDot;
      dotCmd += " -o ";
      dotCmd += "/watorvapor/autogen/wator/wai/graph/" + fileName +".svg";
      ::system(dotCmd.c_str());
      // dot -v -T svg 1.dot -o 1.svg
  }

    string url("/autogen/wai/graph/");
    url += fileName +"";
    return url;
}


//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2014   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 NT2_LINALG_FUNCTIONS_LAPACK_SNE_HPP_INCLUDED
#define NT2_LINALG_FUNCTIONS_LAPACK_SNE_HPP_INCLUDED

#include <nt2/linalg/functions/sne.hpp>
#include <nt2/include/functions/width.hpp>
#include <nt2/include/functions/of_size.hpp>
#include <nt2/include/functions/qr.hpp>
#include <nt2/include/functions/mtimes.hpp>
#include <nt2/include/functions/linsolve.hpp>
#include <nt2/linalg/details/utility/f77_wrapper.hpp>
#include <nt2/include/functions/trsm.hpp>
#include <nt2/include/functions/qr.hpp>
#include <nt2/include/functions/mtimes.hpp>
#include <nt2/include/functions/transpose.hpp>
#include <nt2/include/functions/triu.hpp>
#include <nt2/include/functions/colon.hpp>
#include <boost/dispatch/meta/terminal_of.hpp>
#include <nt2/core/container/table/table.hpp>

namespace nt2{ namespace ext
{
  BOOST_DISPATCH_IMPLEMENT  ( sne_, tag::cpu_
                            , (A0)(A1)
                            , ((ast_<A0, nt2::container::domain>))
                              ((ast_<A1, nt2::container::domain>))
                            )
  {
    typedef typename A0::value_type ctype_t;
    typedef typename nt2::container::table<ctype_t> t_t;
    typedef nt2::memory::container<tag::table_, ctype_t, nt2::settings(nt2::_2D)> desired_semantic;
    typedef  t_t                                      result_type;

     BOOST_FORCEINLINE result_type operator()(A0 const& A, A1 const& B) const
    {
      NT2_AS_TERMINAL_IN(desired_semantic,a,A);
      NT2_AS_TERMINAL_IN(desired_semantic,b,B);

      nt2_la_int na = nt2::width(a);
      nt2::container::table<ctype_t,nt2::upper_triangular_> r = nt2::qr(a);
      r= nt2::triu( r( _(1,na), _ ) );

      t_t x = nt2::mtimes(nt2::trans(a),b);

      x = nt2::linsolve(nt2::trans(r),x);
      x = nt2::linsolve(r,x);

      return x;
    }
  };

} }

#endif


#include <CGAL/Three/Polyhedron_demo_plugin_helper.h>
#include <CGAL/Three/Polyhedron_demo_plugin_interface.h>
#include "ui_Mean_curvature_flow_skeleton_plugin.h"
#include "Scene_polyhedron_item.h"
#include "Scene_points_with_normal_item.h"
#include "Scene_polylines_item.h"
#include "Scene.h"

#include "Polyhedron_type.h"

#include <QApplication>
#include <QMainWindow>
#include <QInputDialog>
#include <QTime>
#include <QMessageBox>

#include <Eigen/Sparse>

#include <boost/property_map/property_map.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>

#include <CGAL/Simple_cartesian.h>
#include <CGAL/Eigen_solver_traits.h>
#include <CGAL/extract_mean_curvature_flow_skeleton.h>
#include <CGAL/boost/graph/graph_traits_Polyhedron_3.h>
#include <CGAL/iterator.h>
#include <CGAL/Polygon_mesh_processing/connected_components.h>
#include <CGAL/boost/graph/split_graph_into_polylines.h>
#include <CGAL/mesh_segmentation.h>
#include <CGAL/Polyhedron_copy_3.h>
#include <queue>

typedef boost::graph_traits<Polyhedron>::vertex_descriptor          vertex_descriptor;
typedef boost::graph_traits<Polyhedron>::vertex_iterator            vertex_iterator;
typedef boost::graph_traits<Polyhedron>::halfedge_descriptor        halfedge_descriptor;
typedef Polyhedron::Facet_iterator                                  Facet_iterator;

typedef CGAL::Mean_curvature_flow_skeletonization<Polyhedron>      Mean_curvature_skeleton;
typedef Mean_curvature_skeleton::Skeleton Skeleton;

typedef Polyhedron::Traits         Kernel;
typedef Kernel::Point_3            Point;

struct Polyline_visitor
{
  typedef std::vector<Point> Polyline;
  typedef std::vector<std::size_t> Polyline_of_ids;

  std::list<Polyline>& polylines;
  Skeleton& skeleton;

  Polyline_visitor(std::list<Polyline>& lines, Skeleton& skeleton)
    : polylines(lines),
      skeleton(skeleton)
  {}

  void start_new_polyline()
  {
    Polyline V;
    polylines.push_back(V);
  }

  void add_node(boost::graph_traits<Skeleton>::vertex_descriptor vd)
  {
    Polyline& polyline = polylines.back();
    polyline.push_back(skeleton[vd].point);
  }

  void end_polyline(){}
};

template<class ValueType>
struct Facet_with_id_pmap
    : public boost::put_get_helper<ValueType&,
             Facet_with_id_pmap<ValueType> >
{
    typedef Polyhedron::Face_handle key_type;
    typedef ValueType value_type;
    typedef value_type& reference;
    typedef boost::lvalue_property_map_tag category;

    Facet_with_id_pmap(
      std::vector<ValueType>& internal_vector
    ) : internal_vector(internal_vector) { }

    reference operator[](key_type key) const
    { return internal_vector[key->id()]; }
private:
    std::vector<ValueType>& internal_vector;
};
using namespace CGAL::Three;
class Polyhedron_demo_mean_curvature_flow_skeleton_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* actionMCFSkeleton;
  QAction* actionConvert_to_medial_skeleton;

public:
  // used by Polyhedron_demo_plugin_helper
  QStringList actionsNames() const {
    return QStringList() << "actionMCFSkeleton" << "actionConvert_to_medial_skeleton";
  }

  void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface) {
    mcs = NULL;
    dockWidget = NULL;
    ui = NULL;

    actionMCFSkeleton = new QAction(tr("Mean Curvature Skeleton (Advanced)"), mainWindow);
    actionMCFSkeleton->setProperty("subMenuName", "Triangulated Surface Mesh Skeletonization");
    actionMCFSkeleton->setObjectName("actionMCFSkeleton");

    actionConvert_to_medial_skeleton = new QAction(tr("Extract Medial Skeleton"), mainWindow);
    actionConvert_to_medial_skeleton->setProperty("subMenuName", "Triangulated Surface Mesh Skeletonization");
    actionConvert_to_medial_skeleton->setObjectName("actionConvert_to_medial_skeleton");

    Polyhedron_demo_plugin_helper::init(mainWindow, scene_interface);

    dockWidget = new QDockWidget(mw);
    dockWidget->setVisible(false);
    ui = new Ui::Mean_curvature_flow_skeleton_plugin();
    ui->setupUi(dockWidget);
    dockWidget->setFeatures(QDockWidget::DockWidgetMovable
                          | QDockWidget::DockWidgetFloatable
                          | QDockWidget::DockWidgetClosable);
    dockWidget->setWindowTitle("Mean Curvature Flow Skeleton");
    add_dock_widget(dockWidget);

    connect(ui->pushButton_contract, SIGNAL(clicked()),
            this, SLOT(on_actionContract()));
    connect(ui->pushButton_collapse, SIGNAL(clicked()),
            this, SLOT(on_actionCollapse()));
    connect(ui->pushButton_split, SIGNAL(clicked()),
            this, SLOT(on_actionSplit()));
    connect(ui->pushButton_degeneracy, SIGNAL(clicked()),
            this, SLOT(on_actionDegeneracy()));
    connect(ui->pushButton_run, SIGNAL(clicked()),
            this, SLOT(on_actionRun()));
    connect(ui->pushButton_skeletonize, SIGNAL(clicked()),
            this, SLOT(on_actionSkeletonize()));
    connect(ui->pushButton_converge, SIGNAL(clicked()),
            this, SLOT(on_actionConverge()));
    connect(dynamic_cast<Scene*>(scene), SIGNAL(updated_bbox()),
            this, SLOT(on_actionUpdateBBox()));
    connect(ui->pushButton_segment, SIGNAL(clicked()),
            this, SLOT(on_actionSegment()));

    QObject* scene_object = dynamic_cast<QObject*>(scene);
    connect(scene_object, SIGNAL(itemAboutToBeDestroyed(CGAL::Three::Scene_item*)),
            this, SLOT(on_actionItemAboutToBeDestroyed(CGAL::Three::Scene_item*)));
  }

  virtual void closure()
  {
    dockWidget->hide();
  }

  QList<QAction*> actions() const {
    return QList<QAction*>() << actionMCFSkeleton << actionConvert_to_medial_skeleton;
  }

  bool applicable(QAction*) const {
    return qobject_cast<Scene_polyhedron_item*>(scene->item(scene->mainSelectionIndex()));
  }

  void init_ui(double diag) {
    ui->omega_H->setValue(0.1);
    ui->omega_H->setSingleStep(0.1);
    ui->omega_H->setDecimals(3);
    ui->omega_P->setValue(0.2);
    ui->omega_P->setSingleStep(0.1);
    ui->omega_P->setDecimals(3);
    ui->min_edge_length->setDecimals(7);
    ui->min_edge_length->setValue(0.002 * diag);
    ui->min_edge_length->setSingleStep(0.0000001);
    ui->delta_area->setDecimals(7);
    ui->delta_area->setValue(1e-4);
    ui->delta_area->setSingleStep(1e-5);
    ui->is_medially_centered->setChecked(false);

    ui->label_omega_H->setToolTip(QString("omega_H controls the velocity of movement and approximation quality"));
    ui->label_omega_P->setToolTip(QString("omega_P controls the smoothness of the medial approximation"));
    ui->pushButton_contract->setToolTip(QString("contract mesh based on mean curvature flow"));
    ui->pushButton_collapse->setToolTip(QString("collapse short edges"));
    ui->pushButton_split->setToolTip(QString("split obtuse triangles"));
    ui->pushButton_degeneracy->setToolTip(QString("fix degenerate points"));
    ui->pushButton_skeletonize->setToolTip(QString("Turn mesh to a skeleton curve"));
    ui->pushButton_run->setToolTip(QString("run one iteration of contract, collapse, split, detect degeneracy"));
    ui->pushButton_converge->setToolTip(QString("iteratively contract the mesh until convergence"));
  }

  bool check_item_index(int index) {
    if (index < 0)
    {
      QMessageBox msgBox;
      msgBox.setText("Please select an item first");
      msgBox.exec();
      return false;
    }
    return true;
  }

  /// \todo move this function into an include
  bool is_mesh_valid(Polyhedron *pMesh) {
    if (!pMesh->is_closed())
    {
      QMessageBox msgBox;
      msgBox.setText("The mesh is not closed.");
      msgBox.exec();
      return false;
    }
    if (!pMesh->is_pure_triangle())
    {
      QMessageBox msgBox;
      msgBox.setText("The mesh is not a pure triangle mesh.");
      msgBox.exec();
      return false;
    }

    // the algorithm is only applicable on a mesh
    // that has only one connected component
    std::size_t num_component;
    CGAL::Counting_output_iterator output_it(&num_component);
    CGAL::internal::corefinement::extract_connected_components(*pMesh, output_it);
    ++output_it;
    if (num_component != 1)
    {
      QMessageBox msgBox;
      QString str = QString("The mesh is not a single closed mesh.\n It has %1 components.").arg(num_component);
      msgBox.setText(str);
      msgBox.exec();
      return false;
    }
    return true;
  }

  /// \todo remove duplicated code
  // check if the Mean_curvature_skeleton exists
  // or has the same polyheron item
  // check if the mesh is a watertight triangle mesh
  bool check_mesh(Scene_polyhedron_item* item) {
    double omega_H = ui->omega_H->value();
    double omega_P = ui->omega_P->value();
    double min_edge_length = ui->min_edge_length->value();
    double delta_area = ui->delta_area->value();
    bool is_medially_centered = ui->is_medially_centered->isChecked();

    Polyhedron *pMesh = item->polyhedron();

    if (mcs == NULL)
    {
      if (!is_mesh_valid(pMesh))
      {
        return false;
      }

      mcs = new Mean_curvature_skeleton(*pMesh);
      meso_skeleton = new Polyhedron(*pMesh);
      input_triangle_mesh = pMesh;
      //set algorithm parameters
      mcs->set_quality_speed_tradeoff(omega_H);
      mcs->set_medially_centered_speed_tradeoff(omega_P);
      mcs->set_min_edge_length(min_edge_length);
      mcs->set_is_medially_centered(is_medially_centered);
      mcs->set_area_variation_factor(delta_area);

      Scene_polyhedron_item* contracted_item = new Scene_polyhedron_item( meso_skeleton );
      contracted_item->setName(QString("contracted mesh of %1").arg(item->name()));

      InputMeshItemIndex = scene->mainSelectionIndex();

      contractedItemIndex = scene->addItem(contracted_item);

      item->setVisible(false);

      fixedPointsItemIndex = -1;
      nonFixedPointsItemIndex = -1;
      poleLinesItemIndex = -1;
    }
    else
    {
      if (input_triangle_mesh != pMesh)
      {
        if (!is_mesh_valid(pMesh))
        {
          return false;
        }

        delete mcs;

        mcs = new Mean_curvature_skeleton(*pMesh);
        meso_skeleton = new Polyhedron(*pMesh);
        input_triangle_mesh = pMesh;
        //set algorithm parameters
        mcs->set_quality_speed_tradeoff(omega_H);
        mcs->set_medially_centered_speed_tradeoff(omega_P);
        mcs->set_min_edge_length(min_edge_length);
        mcs->set_is_medially_centered(is_medially_centered);
        mcs->set_area_variation_factor(delta_area);

        Scene_polyhedron_item* contracted_item = new Scene_polyhedron_item(meso_skeleton);
        contracted_item->setName(QString("contracted mesh of %1").arg(item->name()));

        InputMeshItemIndex = scene->mainSelectionIndex();

        contractedItemIndex = scene->addItem(contracted_item);

        item->setVisible(false);

        fixedPointsItemIndex = -1;
        nonFixedPointsItemIndex = -1;
        poleLinesItemIndex = -1;
      }
      else
      {
        mcs->set_quality_speed_tradeoff(omega_H);
        mcs->set_medially_centered_speed_tradeoff(omega_P);
        mcs->set_min_edge_length(min_edge_length);
        mcs->set_area_variation_factor(delta_area);
        mcs->set_is_medially_centered(is_medially_centered);
      }
    }
    return true;
  }

  void update_meso_skeleton()
  {
    CGAL::Polyhedron_copy_3<Mean_curvature_skeleton::Meso_skeleton, Polyhedron::HalfedgeDS> modifier(mcs->meso_skeleton());
    meso_skeleton->delegate(modifier);
    scene->item(contractedItemIndex)->invalidateOpenGLBuffers();
    scene->itemChanged(contractedItemIndex);
  }

  void update_parameters(Mean_curvature_skeleton* mcs)
  {
    double omega_H = ui->omega_H->value();
    double omega_P = ui->omega_P->value();
    double min_edge_length = ui->min_edge_length->value();
    double delta_area = ui->delta_area->value();
    bool is_medially_centered = ui->is_medially_centered->isChecked();

    mcs->set_quality_speed_tradeoff(omega_H);
    mcs->set_medially_centered_speed_tradeoff(omega_P);
    mcs->set_min_edge_length(min_edge_length);
    mcs->set_area_variation_factor(delta_area);
    mcs->set_is_medially_centered(is_medially_centered);
  }

public Q_SLOTS:
  void on_actionMCFSkeleton_triggered();
  void on_actionConvert_to_medial_skeleton_triggered();
  void on_actionContract();
  void on_actionCollapse();
  void on_actionSplit();
  void on_actionDegeneracy();
  void on_actionRun();
  void on_actionSkeletonize();
  void on_actionConverge();
  void on_actionUpdateBBox();
  void on_actionSegment();
  void on_actionItemAboutToBeDestroyed(CGAL::Three::Scene_item*);

private:
  Mean_curvature_skeleton* mcs;
  Polyhedron* meso_skeleton; // a copy of the meso_skeleton that is displayed
  Polyhedron* input_triangle_mesh;
  QDockWidget* dockWidget;
  Ui::Mean_curvature_flow_skeleton_plugin* ui;

  int fixedPointsItemIndex;
  int nonFixedPointsItemIndex;
  int poleLinesItemIndex;
  int contractedItemIndex;
  int InputMeshItemIndex;

  Skeleton skeleton_curve;
}; // end Polyhedron_demo_mean_curvature_flow_skeleton_plugin

void Polyhedron_demo_mean_curvature_flow_skeleton_plugin::on_actionMCFSkeleton_triggered()
{
  dockWidget->show();
  dockWidget->raise();

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

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

  if(item)
  {
    Polyhedron* pMesh = item->polyhedron();

    if(!pMesh) return;

    double diag = scene->len_diagonal();
    init_ui(diag);

    fixedPointsItemIndex = -1;
    nonFixedPointsItemIndex = -1;
    poleLinesItemIndex = -1;
    contractedItemIndex = -1;
    InputMeshItemIndex = -1;
  }
}

void Polyhedron_demo_mean_curvature_flow_skeleton_plugin::on_actionUpdateBBox()
{
  double diag = scene->len_diagonal();
  ui->min_edge_length->setValue(0.002 * diag);
}

void Polyhedron_demo_mean_curvature_flow_skeleton_plugin::on_actionSegment()
{
  if (num_vertices(skeleton_curve)==0 ) on_actionSkeletonize();
  if (num_vertices(skeleton_curve)==0 ) return;

  QTime time;
  time.start();
  QApplication::setOverrideCursor(Qt::WaitCursor);

    // init the polyhedron simplex indices
  CGAL::set_halfedgeds_items_id(*input_triangle_mesh);

  //for each input vertex compute its distance to the skeleton
  std::vector<double> distances(num_vertices(*input_triangle_mesh));
  BOOST_FOREACH(boost::graph_traits<Skeleton>::vertex_descriptor v, vertices(skeleton_curve) )
  {
    const Point& skel_pt = skeleton_curve[v].point;
    BOOST_FOREACH(vertex_descriptor mesh_v, skeleton_curve[v].vertices)
    {
      const Point& mesh_pt = mesh_v->point();
      distances[mesh_v->id()] = std::sqrt(CGAL::squared_distance(skel_pt, mesh_pt));
    }
  }

  // create a property-map for sdf values
  std::vector<double> sdf_values( num_faces(*input_triangle_mesh) );
  Facet_with_id_pmap<double> sdf_property_map(sdf_values);

  // compute sdf values with skeleton
  BOOST_FOREACH(Polyhedron::Face_handle f, faces(*input_triangle_mesh))
  {
    double dist = 0;
    BOOST_FOREACH(Polyhedron::Halfedge_handle hd, halfedges_around_face(halfedge(f, *input_triangle_mesh), *input_triangle_mesh))
      dist+=distances[target(hd, *input_triangle_mesh)->id()];
    sdf_property_map[f] = dist / 3.;
  }

  // post-process the sdf values
  CGAL::sdf_values_postprocessing(*input_triangle_mesh, sdf_property_map);

  // create a property-map for segment-ids (it is an adaptor for this case)
  std::vector<std::size_t> segment_ids( num_faces(*input_triangle_mesh) );
  Facet_with_id_pmap<std::size_t> segment_property_map(segment_ids);

  // segment the mesh using default parameters
  std::cout << "Number of segments: "
            << CGAL::segmentation_from_sdf_values(*input_triangle_mesh, sdf_property_map, segment_property_map) <<"\n";

  Polyhedron* segmented_polyhedron = new Polyhedron(*input_triangle_mesh);

  int i=0;
  BOOST_FOREACH(Polyhedron::Face_handle fd, faces(*segmented_polyhedron))
  {
    fd->set_patch_id( static_cast<int>(segment_ids[i++] ));
  }

  scene->item(InputMeshItemIndex)->setVisible(false);
  Scene_polyhedron_item* item_segmentation = new Scene_polyhedron_item(segmented_polyhedron);
  item_segmentation->setItemIsMulticolor(true);
  scene->addItem(item_segmentation);
  item_segmentation->setName(QString("segmentation"));

  QApplication::restoreOverrideCursor();
}

void Polyhedron_demo_mean_curvature_flow_skeleton_plugin::on_actionConvert_to_medial_skeleton_triggered()
{
  const CGAL::Three::Scene_interface::Item_id index = scene->mainSelectionIndex();

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

  if(item)
  {
    Polyhedron* pMesh = item->polyhedron();

    if ( !is_mesh_valid(pMesh) ) return;

    QTime time;
    time.start();
    QApplication::setOverrideCursor(Qt::WaitCursor);

    Skeleton skeleton;
    CGAL::extract_mean_curvature_flow_skeleton(*pMesh, skeleton);

    std::cout << "ok (" << time.elapsed() << " ms, " << ")" << std::endl;

    //create the polylines representing the skeleton
    Scene_polylines_item* skeleton_item = new Scene_polylines_item();
    skeleton_item->setColor(QColor(175, 0, 255));

    Polyline_visitor polyline_visitor(skeleton_item->polylines, skeleton);
    CGAL::split_graph_into_polylines( skeleton,
                                      polyline_visitor,
                                      CGAL::internal::IsTerminalDefault() );

    skeleton_item->setName(QString("Medial skeleton curve of %1").arg(item->name()));
    scene->addItem(skeleton_item);
    skeleton_item->invalidateOpenGLBuffers();

    item->setPointsMode();

    QApplication::restoreOverrideCursor();
  }
}

void Polyhedron_demo_mean_curvature_flow_skeleton_plugin::on_actionContract()
{
  const CGAL::Three::Scene_interface::Item_id index = scene->mainSelectionIndex();
  if (!check_item_index(index))
  {
    return;
  }

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

  if (!check_mesh(item))
  {
    return;
  }

  QTime time;
  time.start();
  std::cout << "Contract...\n";
  QApplication::setOverrideCursor(Qt::WaitCursor);

  update_parameters(mcs);
  mcs->contract_geometry();

  std::cout << "ok (" << time.elapsed() << " ms, " << ")" << std::endl;

  update_meso_skeleton();
  QApplication::restoreOverrideCursor();
}

void Polyhedron_demo_mean_curvature_flow_skeleton_plugin::on_actionCollapse()
{
  const CGAL::Three::Scene_interface::Item_id index = scene->mainSelectionIndex();
  if (!check_item_index(index))
  {
    return;
  }

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

  if (!check_mesh(item))
  {
    return;
  }

  QTime time;
  time.start();
  std::cout << "Collapse...\n";
  QApplication::setOverrideCursor(Qt::WaitCursor);

  update_parameters(mcs);
  std::size_t num_collapses = mcs->collapse_edges();
  std::cout << "collapsed " << num_collapses << " edges.\n";

  std::cout << "ok (" << time.elapsed() << " ms, " << ")" << std::endl;

  update_meso_skeleton();
  QApplication::restoreOverrideCursor();
}

void Polyhedron_demo_mean_curvature_flow_skeleton_plugin::on_actionSplit()
{
  const CGAL::Three::Scene_interface::Item_id index = scene->mainSelectionIndex();
  if (!check_item_index(index))
  {
    return;
  }

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

  if (!check_mesh(item))
  {
    return;
  }

  QTime time;
  time.start();
  std::cout << "Split...\n";
  QApplication::setOverrideCursor(Qt::WaitCursor);

  update_parameters(mcs);
  std::size_t num_split = mcs->split_faces();
  std::cout << "split " << num_split << " triangles.\n";

  std::cout << "ok (" << time.elapsed() << " ms, " << ")" << std::endl;

  update_meso_skeleton();
  QApplication::restoreOverrideCursor();
}

void Polyhedron_demo_mean_curvature_flow_skeleton_plugin::on_actionDegeneracy()
{
  const CGAL::Three::Scene_interface::Item_id index = scene->mainSelectionIndex();
  if (!check_item_index(index))
  {
    return;
  }

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

  if (!check_mesh(item))
  {
    return;
  }

  QTime time;
  time.start();
  std::cout << "Detect degeneracy...\n";
  QApplication::setOverrideCursor(Qt::WaitCursor);

  update_parameters(mcs);
  mcs->detect_degeneracies();

  std::cout << "ok (" << time.elapsed() << " ms, " << ")" << std::endl;

  Scene_points_with_normal_item* fixedPointsItem = new Scene_points_with_normal_item;
  fixedPointsItem->setName(QString("fixed points of %1").arg(item->name()));

  std::vector<Point> fixedPoints;
  mcs->fixed_points(fixedPoints);

  Point_set *ps = fixedPointsItem->point_set();
  for (size_t i = 0; i < fixedPoints.size(); ++i)
  {
    UI_point_3<Kernel> point(fixedPoints[i].x(), fixedPoints[i].y(), fixedPoints[i].z());
    ps->push_back(point);
  }
  ps->select_all ();

  if (fixedPointsItemIndex == -1)
  {
    fixedPointsItemIndex = scene->addItem(fixedPointsItem);
  }
  else
  {
    Scene_item* temp = scene->replaceItem(fixedPointsItemIndex, fixedPointsItem, false);
    delete temp;
  }
  // update scene
  update_meso_skeleton();
  scene->item(fixedPointsItemIndex)->invalidateOpenGLBuffers();
  scene->itemChanged(fixedPointsItemIndex);
  scene->setSelectedItem(index);
  QApplication::restoreOverrideCursor();
}

void Polyhedron_demo_mean_curvature_flow_skeleton_plugin::on_actionRun()
{
  const CGAL::Three::Scene_interface::Item_id index = scene->mainSelectionIndex();
  if (!check_item_index(index))
  {
    return;
  }

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

  if (!check_mesh(item))
  {
    return;
  }

  QTime time;
  time.start();
  QApplication::setOverrideCursor(Qt::WaitCursor);

  std::cout << "Run one iteration...\n";

  update_parameters(mcs);
  mcs->contract();

  std::cout << "ok (" << time.elapsed() << " ms, " << ")" << std::endl;

  CGAL::Three::Scene_interface::Item_id contracted_item_index = scene->mainSelectionIndex();
  Scene_polyhedron_item* contracted_item =
    qobject_cast<Scene_polyhedron_item*>(scene->item(contracted_item_index));

  // update scene
  Scene_points_with_normal_item* fixedPointsItem = new Scene_points_with_normal_item;
  fixedPointsItem->setName(QString("fixed points of %1").arg(contracted_item->name()));

  std::vector<Point> fixedPoints;
  mcs->fixed_points(fixedPoints);

  Point_set *ps = fixedPointsItem->point_set();
  for (size_t i = 0; i < fixedPoints.size(); ++i)
  {
    UI_point_3<Kernel> point(fixedPoints[i].x(), fixedPoints[i].y(), fixedPoints[i].z());
    ps->push_back(point);
  }
  ps->select_all();

  if (fixedPointsItemIndex == -1)
  {
    fixedPointsItemIndex = scene->addItem(fixedPointsItem);
  }
  else
  {
    Scene_item* temp = scene->replaceItem(fixedPointsItemIndex, fixedPointsItem, false);
    delete temp;
  }

//#define DRAW_NON_FIXED_POINTS
#ifdef DRAW_NON_FIXED_POINTS
  // draw non-fixed points
  Scene_points_with_normal_item* nonFixedPointsItem = new Scene_points_with_normal_item;
  nonFixedPointsItem->setName("non-fixed points");
  nonFixedPointsItem->setColor(QColor(0, 255, 0));
  std::vector<Point> nonFixedPoints;
  mcs->non_fixed_points(nonFixedPoints);
  ps = nonFixedPointsItem->point_set();
  for (size_t i = 0; i < nonFixedPoints.size(); ++i)
  {
    UI_point_3<Kernel> point(nonFixedPoints[i].x(), nonFixedPoints[i].y(), nonFixedPoints[i].z());
    ps->push_back(point);
  }
  if (nonFixedPointsItemIndex == -1)
  {
    nonFixedPointsItemIndex = scene->addItem(nonFixedPointsItem);
  }
  else
  {
    scene->replaceItem(nonFixedPointsItemIndex, nonFixedPointsItem, false);
  }
  scene->itemChanged(nonFixedPointsItemIndex);
#endif

//#define DRAW_POLE_LINE
#ifdef DRAW_POLE_LINE
  // draw lines connecting surface points and their correspondent poles
  Scene_polylines_item* poleLinesItem = new Scene_polylines_item();

  Polyhedron* pMesh = item->polyhedron();
  std::vector<Point> pole_points;
  mcs->poles(pole_points);
  vertex_iterator vb, ve;
  int id = 0;
  for (boost::tie(vb, ve) = vertices(*pMesh); vb != ve; ++vb)
  {
    std::vector<Point> line;
    line.clear();

    vertex_descriptor v = *vb;
    Point s = v->point();
    Point t = pole_points[id++];

    line.push_back(s);
    line.push_back(t);
    poleLinesItem->polylines.push_back(line);
  }

  if (poleLinesItemIndex == -1)
  {
    poleLinesItemIndex = scene->addItem(poleLinesItem);
  }
  else
  {
    scene->replaceItem(poleLinesItemIndex, poleLinesItem, false);
  }
#endif

  update_meso_skeleton();
  scene->setSelectedItem(index);
  QApplication::restoreOverrideCursor();
}

void Polyhedron_demo_mean_curvature_flow_skeleton_plugin::on_actionSkeletonize()
{
  const CGAL::Three::Scene_interface::Item_id index = scene->mainSelectionIndex();
  if (!check_item_index(index))
  {
    return;
  }

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

  if (!check_mesh(item))
  {
    return;
  }

  QTime time;
  time.start();
  QApplication::setOverrideCursor(Qt::WaitCursor);

  update_parameters(mcs);

  mcs->convert_to_skeleton(skeleton_curve);

  std::cout << "ok (" << time.elapsed() << " ms, " << ")" << std::endl;

  //create the polylines representing the skeleton
  Scene_polylines_item* skeleton = new Scene_polylines_item();
  skeleton->setColor(QColor(175, 0, 255));

  Polyline_visitor polyline_visitor(skeleton->polylines, skeleton_curve);
  CGAL::split_graph_into_polylines( skeleton_curve,
                                    polyline_visitor,
                                    CGAL::internal::IsTerminalDefault() );

  skeleton->setName(QString("skeleton curve of %1").arg(item->name()));
  scene->addItem(skeleton);
  skeleton->invalidateOpenGLBuffers();

  // set the fixed points and contracted mesh as invisible
  if (fixedPointsItemIndex >= 0)
  {
    scene->item(fixedPointsItemIndex)->setVisible(false);
    scene->itemChanged(fixedPointsItemIndex);
  }
  scene->item(contractedItemIndex)->setVisible(false);
  scene->itemChanged(contractedItemIndex);
  // display the original mesh in transparent mode
  item->setVisible(false);
  if (InputMeshItemIndex >= 0)
  {
    scene->item(InputMeshItemIndex)->setVisible(true);
    scene->item(InputMeshItemIndex)->setPointsMode();
    scene->itemChanged(InputMeshItemIndex);
  }

  // update scene
  QApplication::restoreOverrideCursor();
}

void Polyhedron_demo_mean_curvature_flow_skeleton_plugin::on_actionConverge()
{
  const CGAL::Three::Scene_interface::Item_id index = scene->mainSelectionIndex();
  if (!check_item_index(index))
  {
    return;
  }

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

  if (!check_mesh(item))
  {
    return;
  }

  QTime time;
  time.start();
  QApplication::setOverrideCursor(Qt::WaitCursor);

  mcs->contract_until_convergence();

  std::cout << "ok (" << time.elapsed() << " ms, " << ")" << std::endl;

  // update scene
  Scene_points_with_normal_item* fixedPointsItem = new Scene_points_with_normal_item;
  fixedPointsItem->setName(QString("fixed points of %1").arg(item->name()));

  std::vector<Point> fixedPoints;
  mcs->fixed_points(fixedPoints);

  Point_set *ps = fixedPointsItem->point_set();
  for (size_t i = 0; i < fixedPoints.size(); ++i)
  {
    UI_point_3<Kernel> point(fixedPoints[i].x(), fixedPoints[i].y(), fixedPoints[i].z());
    ps->push_back(point);
  }
  ps->select_all();

  if (fixedPointsItemIndex == -1)
  {
    fixedPointsItemIndex = scene->addItem(fixedPointsItem);
  }
  else
  {
    Scene_item* temp = scene->replaceItem(fixedPointsItemIndex, fixedPointsItem, false);
    delete temp;
  }

  scene->item(fixedPointsItemIndex)->invalidateOpenGLBuffers();
  scene->itemChanged(fixedPointsItemIndex);
  update_meso_skeleton();
  scene->setSelectedItem(index);

  QApplication::restoreOverrideCursor();
}

void Polyhedron_demo_mean_curvature_flow_skeleton_plugin::on_actionItemAboutToBeDestroyed(CGAL::Three::Scene_item* /* item */)
{
  if (mcs != NULL)
  {
    delete mcs;
    mcs = NULL;
  }
}

#include "Mean_curvature_flow_skeleton_plugin.moc"


/* rmse_test.cc
   Jeremy Barnes, 9 November 2009
   Copyright (c) 2009 Jeremy Barnes.  All rights reserved.

   Test for the RMSE functions.
*/

#define BOOST_TEST_MAIN
#define BOOST_TEST_DYN_LINK

#include <boost/test/unit_test.hpp>
#include <iostream>
#include "jml/stats/rmse.h"
#include <boost/assign/list_of.hpp>

using namespace ML;
using namespace std;

using boost::unit_test::test_suite;

BOOST_AUTO_TEST_CASE( test )
{
    distribution<float> targets
        = boost::assign::list_of<float>(1.0)(0.0);
    distribution<float> outputs
        = boost::assign::list_of<float>(2.0)(1.0);

    BOOST_CHECK_EQUAL(calc_rmse(outputs, outputs), 0.0);

    BOOST_CHECK_EQUAL(calc_rmse(outputs, targets), 1.0);

    distribution<float> weights(2, 0.1);
    BOOST_CHECK_EQUAL(calc_rmse(outputs, targets, weights), 1.0);

    weights.push_back(1.0);
    BOOST_CHECK_THROW(calc_rmse(outputs, targets, weights), Exception);
}


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

#include "micro_graph_optimizer.h"

#include <math.h>
#include <map>
#include <Eigen/Sparse>

namespace mgo
{
  namespace
  {
    struct VariableInfo
    {
    public:
      inline VariableInfo& set_dim(int val) { m_dim = val; return *this; }
      inline VariableInfo& set_idx(int val) { m_idx = val; return *this; }
      inline int dim()const { return m_dim; }
      inline int idx()const { return m_idx; }

    private:
      int m_dim;
      int m_idx; // The index of this variable in the H matrix.
    };

    struct ScratchPad
    {
      int total_variables_dim = 0;
      int total_factors_dim = 0;
      std::map<Variable*, VariableInfo> variable_lookup_table;
      inline const VariableInfo& variable_lookup(Variable* var) { MGO_ASSERT(variable_lookup_table.count(var) != 0); return variable_lookup_table[var]; }
      typedef Eigen::Triplet<double> T;
      std::vector<T> tripletList;
      Eigen::SparseMatrix<double> H;
      Eigen::VectorXd b;
    };

    // ===================================================================
    void compute_graph_info(const FactorGraph& graph, ScratchPad* pad)
    {
      const std::vector<Variable*>& variables = graph.get_variables();
      int total_variables_dim = 0;
      std::map<Variable*, VariableInfo> variable_lookup_table;
      for (int i = 0, var_idx = 0, count = variables.size(); i < count; ++i)
      {
        Variable* var = variables[i];
        if (!var->fixed)
        {
          const int v_dim = var->dim();
          total_variables_dim += v_dim;
          variable_lookup_table[var] = VariableInfo().set_dim(v_dim).set_idx(var_idx);
          var_idx += v_dim;
        }
      }
      pad->total_variables_dim = total_variables_dim;
      pad->variable_lookup_table.swap(variable_lookup_table);

      const std::vector<Factor*>& factors = graph.get_factors();
      int total_factors_dim = 0;
      for (int i = 0, count = factors.size(); i < count; ++i)
      {
        const int f_dim = factors[i]->dim();
        total_factors_dim += f_dim;
      }
      pad->total_factors_dim = total_factors_dim;

      pad->H = Eigen::SparseMatrix<double>(total_variables_dim, total_variables_dim);
      pad->b = Eigen::VectorXd::Zero(total_variables_dim);
    }

    // ===================================================================
    double compute_error_norm_squared(const FactorGraph& graph)
    {
      const std::vector<Factor*>& factors = graph.get_factors();
      double error = 0.0;
      for (size_t i = 0, count = factors.size(); i < count; ++i)
      {
        error += factors[i]->error().squaredNorm();
      }
      return error;
    }

    // ===================================================================
    void linearize_single_factor(Factor* factor, ScratchPad* pad)
    {
      // Our goal in this function is to add the contribution of a factor
      // to the linearized system (H, b).

      // First we need to calculate the jacobian of the factor wrt each of
      // its variables then stack them horizontally in Js then compute Jt*J matrix.
      const int n_rows = factor->dim();
      const int num_variables = factor->num_variables();
      std::vector<int> vars_cols(num_variables, -1);
      std::vector<int> vars_dim(num_variables, -1);
      int n_cols = 0;
      for (int i = 0; i < num_variables; ++i)
      {
        Variable* var = factor->variable_at(i);
        if (!var->fixed)
        {
          const int var_dim = pad->variable_lookup(var).dim();
          vars_cols[i] = n_cols;
          vars_dim[i] = var_dim;
          n_cols += var_dim;
        }
      }

      if (n_cols == 0)
      {
        MGO_LOG("All variables connected to this factor are fixed.");
        return;
      }

      Eigen::MatrixXd Js(n_rows, n_cols);
      for (int i = 0, start_col = 0; i < num_variables; ++i)
      {
        if (!factor->variable_at(i)->fixed)
        {
          Eigen::MatrixXd J = factor->jacobian(i);
          Js.block(0, start_col, J.rows(), J.cols()) = J;
          start_col += J.cols();
        }
      }

      Eigen::MatrixXd JtJ(Js.cols(), Js.cols());
      JtJ.noalias() = Js.transpose() * Js;

      // Now we need to add the contribution to H. Note that we are only filling the lower triangular part.
      for (int i = 0; i < num_variables; ++i)
      {
        if (factor->variable_at(i)->fixed)
        {
          continue;
        }
        const int H_col = pad->variable_lookup(factor->variable_at(i)).idx();
        const int JtJ_col = vars_cols[i];
        for (int j = i; j < num_variables; ++j)
        {
          if (factor->variable_at(j)->fixed)
          {
            continue;
          }
          const int H_row = pad->variable_lookup(factor->variable_at(j)).idx();
          const int JtJ_row = vars_cols[j];
          for (int JtJ_i = JtJ_col, H_i = H_col; JtJ_i < (JtJ_col + vars_dim[i]); ++JtJ_i, ++H_i)
          {
            for (int JtJ_j = JtJ_row, H_j = H_row; JtJ_j < (JtJ_row + vars_dim[j]); ++JtJ_j, ++H_j)
            {
              pad->tripletList.push_back(ScratchPad::T(H_j, H_i, JtJ(JtJ_j, JtJ_i)));
            }
          }
        }
      }

      // Handle the vector b.
      const Eigen::VectorXd Jtb = Js.transpose() * factor->error();
      for (int i = 0; i < num_variables; ++i)
      {
        Variable* var = factor->variable_at(i);
        if (!var->fixed)
        {
          const int var_idx = pad->variable_lookup(var).idx();
          const int var_dim = vars_dim[i];
          pad->b.segment(var_idx, var_dim) -= Jtb.segment(vars_cols[i], var_dim);
        }
      }
    }

    // ===================================================================
    bool iterate(FactorGraph* graph, ScratchPad* pad)
    {
      const std::vector<Factor*>& factors = graph->get_factors();
      pad->b.setZero();
      pad->tripletList.clear();
      for (size_t i = 0, count = factors.size(); i < count; ++i)
      {
        linearize_single_factor(factors[i], pad);
      }
      MGO_ASSERT(!pad->tripletList.empty());
      pad->H.setFromTriplets(pad->tripletList.begin(), pad->tripletList.end());

      Eigen::SimplicialLDLT<Eigen::SparseMatrix<double>> chol(pad->H);
      Eigen::VectorXd dx = chol.solve(pad->b);
      if (chol.info() == Eigen::Success)
      {
        std::vector<Variable*>& variables = graph->get_variables();
        for (int i = 0, d = 0, count = variables.size(); i < count; ++i)
        {
          Variable* var = variables[i];
          if (!var->fixed)
          {
            const int vd = pad->variable_lookup_table[var].dim();
            var->plus(dx.segment(d, vd));
            d += vd;
          }
        }
        return true;
      }
      else
      {
        MGO_LOG("Linear solver failed.");
        return false;
      }
    }

    // ===================================================================
    bool continue_iterating_check(int iter_num, double current_error, double new_error, const OptimizationParameters& params, bool* converged)
    {
      if (!std::isfinite(new_error))
      {
        return false;
      }

      if (iter_num == params.max_iterations)
      {
        MGO_LOG("Max iterations reached.");
        return false;
      }

      const double error_decrease = current_error - new_error;
      if ((error_decrease <= params.absolute_error_th) ||
        ((error_decrease / current_error) <= params.relative_error_th))
      {
        MGO_LOG("Converged.");
        *converged = true;
        return false;
      }

      return true;
    }

    // ===================================================================
  } // anonymous namespace

  bool optimize_gn(FactorGraph* graph, const OptimizationParameters& params)
  {
    MGO_LOG("Started optimization with %i factors and %i variables.", (int)graph->get_factors().size(), (int)graph->get_variables().size());
    ScratchPad pad;
    compute_graph_info(*graph, &pad);
    double current_error = 0.5 * compute_error_norm_squared(*graph);
    MGO_LOG("Initial error: %f", current_error);
    double new_error = current_error;
    int iter_num = 0;
    bool converged = false;
    bool continue_iterating = true;
    while (continue_iterating)
    {
      current_error = new_error;
      if (!iterate(graph, &pad))
      {
        return false;
      }
      ++iter_num;
      new_error = 0.5 * compute_error_norm_squared(*graph);
      MGO_LOG("New error after iteration %i: %f", iter_num, new_error);
      continue_iterating = continue_iterating_check(iter_num, current_error, new_error, params, &converged);
    }
    return converged;
  }

  // ===================================================================
  Eigen::MatrixXd Factor::jacobian(int idx)const
  {
    MGO_ASSERT(m_num_variables > 0 && idx < m_num_variables);
    return compute_numerical_jacobian(m_variables[idx]);
  }

  // ===================================================================
  Eigen::MatrixXd Factor::compute_numerical_jacobian(Variable* v)const
  {
    constexpr double h = 1e-5;
    const int N = v->dim();
    const int M = this->dim();
    Eigen::MatrixXd J = Eigen::MatrixXd::Zero(M, N);
    Eigen::VectorXd dx = Eigen::VectorXd::Zero(N);
    Eigen::VectorXd dy0 = this->error();
    constexpr double k = 1.0 / (2.0 * h);
    for (int i = 0; i < N; ++i)
    {
      dx(i) = h;
      v->plus(dx); // right
      const Eigen::VectorXd dy1 = this->subtract_error(this->error(), dy0);
      dx(i) = -2.0 * h;
      v->plus(dx); // left
      const Eigen::VectorXd dy2 = this->subtract_error(this->error(), dy0);
      dx(i) = h;
      v->plus(dx); // return to original state.
      dx(i) = 0.0;
      J.col(i) << (dy1 - dy2) * k;
    }
    return J;
  }

  // ===================================================================
  FactorGraph::FactorGraph() = default;

  FactorGraph::~FactorGraph()
  {
    for (Factor* f : m_factors)
    {
      delete f;
    }
    for (Variable* v : m_variables)
    {
      delete v;
    }
  }
}