// Copyright (C) 2013  Davis E. King (davis@dlib.net)
// License: Boost Software License   See LICENSE.txt for the full license.

#include <sstream>
#include "tester.h"
#include <dlib/svm_threaded.h>
#include <dlib/rand.h>

namespace
{
    using namespace test;
    using namespace dlib;
    using namespace std;

    logger dlog("test.sequence_segmenter");

// ----------------------------------------------------------------------------------------

    dlib::rand rnd;

    template <bool use_BIO_model_, bool use_high_order_features_, bool allow_negative_weights_>
    class unigram_extractor
    {
    public:

        const static bool use_BIO_model = use_BIO_model_;
        const static bool use_high_order_features = use_high_order_features_;
        const static bool allow_negative_weights = allow_negative_weights_;

        typedef std::vector<unsigned long> sequence_type;

        std::map<unsigned long, matrix<double,0,1> > feats;

        unigram_extractor()
        {
            matrix<double,0,1> v1, v2, v3;
            v1 = randm(num_features(), 1, rnd);
            v2 = randm(num_features(), 1, rnd);
            v3 = randm(num_features(), 1, rnd);
            v1(0) = 1;
            v2(1) = 1;
            v3(2) = 1;
            v1(3) = -1;
            v2(4) = -1;
            v3(5) = -1;
            for (unsigned long i = 0; i < num_features(); ++i)
            {
                if ( i < 3)
                    feats[i] = v1;
                else if (i < 6)
                    feats[i] = v2;
                else
                    feats[i] = v3;
            }
        }

        unsigned long num_features() const { return 10; }
        unsigned long window_size() const { return 3; }

        template <typename feature_setter>
        void get_features (
            feature_setter& set_feature,
            const sequence_type& x,
            unsigned long position
        ) const
        {
            const matrix<double,0,1>& m = feats.find(x[position])->second;
            for (unsigned long i = 0; i < num_features(); ++i)
            {
                set_feature(i, m(i));
            }
        }

    };

    template <bool use_BIO_model_, bool use_high_order_features_, bool neg>
    void serialize(const unigram_extractor<use_BIO_model_,use_high_order_features_,neg>& item , std::ostream& out )
    {
        serialize(item.feats, out);
    }

    template <bool use_BIO_model_, bool use_high_order_features_, bool neg>
    void deserialize(unigram_extractor<use_BIO_model_,use_high_order_features_,neg>& item, std::istream& in)
    {
        deserialize(item.feats, in);
    }

// ----------------------------------------------------------------------------------------

    void make_dataset (
        std::vector<std::vector<unsigned long> >& samples,
        std::vector<std::vector<unsigned long> >& labels,
        unsigned long dataset_size
    )
    {
        samples.clear();
        labels.clear();

        samples.resize(dataset_size);
        labels.resize(dataset_size);

        unigram_extractor<true,true,true> fe;
        dlib::rand rnd;

        for (unsigned long iter = 0; iter < dataset_size; ++iter)
        {

            samples[iter].resize(10);
            labels[iter].resize(10);

            for (unsigned long i = 0; i < samples[iter].size(); ++i)
            {
                samples[iter][i] = rnd.get_random_32bit_number()%fe.num_features();
                if (samples[iter][i] < 3)
                {
                    labels[iter][i] = impl_ss::BEGIN;
                }
                else if (samples[iter][i] < 6)
                {
                    labels[iter][i] = impl_ss::INSIDE;
                }
                else
                {
                    labels[iter][i] = impl_ss::OUTSIDE;
                }

                if (i != 0)
                {
                    // do rejection sampling to avoid impossible labels
                    if (labels[iter][i] == impl_ss::INSIDE &&
                        labels[iter][i-1] == impl_ss::OUTSIDE)
                    {
                        --i;
                    }
                }
            }
        }
    }

// ----------------------------------------------------------------------------------------

    void make_dataset2 (
        std::vector<std::vector<unsigned long> >& samples,
        std::vector<std::vector<std::pair<unsigned long, unsigned long> > >& segments,
        unsigned long dataset_size
    )
    {
        segments.clear();
        std::vector<std::vector<unsigned long> > labels;
        make_dataset(samples, labels, dataset_size);
        segments.resize(samples.size());

        // Convert from BIO tagging to the explicit segments representation.
        for (unsigned long k = 0; k < labels.size(); ++k)
        {
            for (unsigned long i = 0; i < labels[k].size(); ++i)
            {
                if (labels[k][i] == impl_ss::BEGIN)
                {
                    const unsigned long begin = i;
                    ++i;
                    while (i < labels[k].size() && labels[k][i] == impl_ss::INSIDE)
                        ++i;

                    segments[k].push_back(std::make_pair(begin, i));
                    --i;
                }
            }
        }
    }

// ----------------------------------------------------------------------------------------

    template <bool use_BIO_model, bool use_high_order_features, bool allow_negative_weights>
    void do_test()
    {
        dlog << LINFO << "use_BIO_model: "<< use_BIO_model;
        dlog << LINFO << "use_high_order_features: "<< use_high_order_features;
        dlog << LINFO << "allow_negative_weights: "<< allow_negative_weights;

        std::vector<std::vector<unsigned long> > samples;
        std::vector<std::vector<std::pair<unsigned long,unsigned long> > > segments;
        make_dataset2( samples, segments, 100);

        print_spinner();
        typedef unigram_extractor<use_BIO_model,use_high_order_features,allow_negative_weights> fe_type;

        fe_type fe_temp;
        fe_type fe_temp2;
        structural_sequence_segmentation_trainer<fe_type> trainer(fe_temp2);
        trainer.set_c(5);
        trainer.set_num_threads(1);

        sequence_segmenter<fe_type> labeler = trainer.train(samples, segments);

        print_spinner();

        const std::vector<std::pair<unsigned long, unsigned long> > predicted_labels = labeler(samples[1]);
        const std::vector<std::pair<unsigned long, unsigned long> > true_labels = segments[1];
        /*
        for (unsigned long i = 0; i < predicted_labels.size(); ++i)
            cout << "["<<predicted_labels[i].first<<","<<predicted_labels[i].second<<") ";
        cout << endl;
        for (unsigned long i = 0; i < true_labels.size(); ++i)
            cout << "["<<true_labels[i].first<<","<<true_labels[i].second<<") ";
        cout << endl;
        */

        DLIB_TEST(predicted_labels.size() > 0);
        DLIB_TEST(predicted_labels.size() == true_labels.size());
        for (unsigned long i = 0; i < predicted_labels.size(); ++i)
        {
            DLIB_TEST(predicted_labels[i].first == true_labels[i].first);
            DLIB_TEST(predicted_labels[i].second == true_labels[i].second);
        }

        matrix<double> res;

        res = cross_validate_sequence_segmenter(trainer, samples, segments, 3);
        dlog << LINFO << "cv res:   "<< res;
        DLIB_TEST(min(res) > 0.98);
        make_dataset2( samples, segments, 100);
        res = test_sequence_segmenter(labeler, samples, segments);
        dlog << LINFO << "test res: "<< res;
        DLIB_TEST(min(res) > 0.98);

        print_spinner();

        ostringstream sout;
        serialize(labeler, sout);
        istringstream sin(sout.str());
        sequence_segmenter<fe_type> labeler2;
        deserialize(labeler2, sin);

        res = test_sequence_segmenter(labeler2, samples, segments);
        dlog << LINFO << "test res2: "<< res;
        DLIB_TEST(min(res) > 0.98);

        long N;
        if (use_BIO_model)
            N = 3*3+3;
        else
            N = 5*5+5;
        const double min_normal_weight = min(colm(labeler2.get_weights(), 0, labeler2.get_weights().size()-N));
        const double min_trans_weight = min(labeler2.get_weights());
        dlog << LINFO << "min_normal_weight: " << min_normal_weight;
        dlog << LINFO << "min_trans_weight:  " << min_trans_weight;
        if (allow_negative_weights)
        {
            DLIB_TEST(min_normal_weight < 0);
            DLIB_TEST(min_trans_weight < 0);
        }
        else
        {
            DLIB_TEST(min_normal_weight == 0);
            DLIB_TEST(min_trans_weight < 0);
        }
    }

// ----------------------------------------------------------------------------------------

    class unit_test_sequence_segmenter : public tester
    {
    public:
        unit_test_sequence_segmenter (
        ) :
            tester ("test_sequence_segmenter",
                "Runs tests on the sequence segmenting code.")
        {}

        void perform_test (
        )
        {
            do_test<true,true,false>();
            do_test<true,false,false>();
            do_test<false,true,false>();
            do_test<false,false,false>();
            do_test<true,true,true>();
            do_test<true,false,true>();
            do_test<false,true,true>();
            do_test<false,false,true>();
        }
    } a;

}


#pragma once

#include <boost/optional.hpp>

#include <elle/compiler.hh>
#include <elle/date/date.h>
#include <elle/printf.hh> // Actually needed by our <chrono>.
// #include <elle/serialization/Serializer.hh>
#include <elle/time.hh>

namespace elle
{
  using namespace std::literals;

  /// Whether T is a std::duration.
  template <typename T>
  struct is_duration : std::false_type {};

  template <typename... Args>
  struct is_duration<std::chrono::duration<Args...>> : std::true_type {};

  /// A clock.
  using Clock = std::chrono::system_clock;
  /// A reference date.
  using Time = Clock::time_point;
  /// A duration related to our clock.
  using Duration = Clock::duration;

  /// An optional Duration.
  ///
  /// This is basically just `using DurationOpt = boost::optional<Duration>`.
  /// However then we don't have enough implicit conversions, and
  /// signatures such as `auto(DurationOpt d = 30s) -> void` fail
  /// to compile, we need `auto(DurationOpt d = DurationOpt{30s}) -> void`.
  /// This `struct` is here only to add a convenience ctor.
  struct DurationOpt
    : boost::optional<Duration>
  {
    using Super = boost::optional<Duration>;

    // We used to simply have `using Super::Super;`, but old compilers
    // (Apple Clang++ on top of libstdc++) fails to discard some
    // `boost::disable_if_c` code in the base constructors.

    template <typename Rep, typename Ratio>
    DurationOpt(std::chrono::duration<Rep, Ratio> d) noexcept
      : Super{d}
    {}

    DurationOpt(boost::none_t) noexcept
      : Super{}
    {}

    // Needed although there is already a default ctor in Super. Why?
    // Cannot be constexpr with Boost 1.60.
    DurationOpt() noexcept
      : Super{}
    {}
  };

  /// The number of seconds in `d`.
  inline
  auto
  num_seconds(Duration d)
  {
    return std::chrono::duration_cast<std::chrono::seconds>(d).count();
  }

  /// The number of microseconds in `d`.
  inline
  auto
  num_milliseconds(Duration d)
  {
    return std::chrono::duration_cast<std::chrono::milliseconds>(d).count();
  }

  /// The number of microseconds in `d`.
  inline
  auto
  num_microseconds(Duration d)
  {
    return std::chrono::duration_cast<std::chrono::microseconds>(d).count();
  }

  ELLE_API
  std::ostream&
  operator << (std::ostream& s, const DurationOpt& opt);
}

namespace std
{
  template <typename Clock, typename Duration>
  std::ostream&
  operator << (std::ostream& o,
               std::chrono::time_point<Clock, Duration> const& t)
  {
    return date::to_stream(o, "%F %T", t);
  }
}


#include <iostream>
#include <vector>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/undirected_graph.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <time.h>
#include <stdlib.h>
#include "OrientedGraph.h"
#include "GraphManager.h"
#include "CustomGenerator.h"

int main()
{
    BaseGraph* g = GenerateGraphFromCin();
    delete g;
    return 0;
}


// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2015 Alec Jacobson <alecjacobson@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#include "outer_hull_legacy.h"
#include "extract_cells.h"
#include "remesh_self_intersections.h"
#include "assign.h"
#include "../../remove_unreferenced.h"

#include <CGAL/AABB_tree.h>
#include <CGAL/AABB_traits.h>
#include <CGAL/AABB_triangle_primitive.h>
#include <CGAL/intersections.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>

#include "points_inside_component.h"
#include "order_facets_around_edges.h"
#include "outer_facet.h"
#include "../../sortrows.h"
#include "../../facet_components.h"
#include "../../winding_number.h"
#include "../../triangle_triangle_adjacency.h"
#include "../../unique_edge_map.h"
#include "../../barycenter.h"
#include "../../per_face_normals.h"
#include "../../sort_angles.h"
#include <Eigen/Geometry>
#include <vector>
#include <map>
#include <queue>
#include <iostream>
#include <type_traits>
#include <CGAL/number_utils.h>
//#define IGL_OUTER_HULL_DEBUG

template <
  typename DerivedV,
  typename DerivedF,
  typename DerivedG,
  typename DerivedJ,
  typename Derivedflip>
IGL_INLINE void igl::copyleft::cgal::outer_hull_legacy(
  const Eigen::PlainObjectBase<DerivedV> & V,
  const Eigen::PlainObjectBase<DerivedF> & F,
  Eigen::PlainObjectBase<DerivedG> & G,
  Eigen::PlainObjectBase<DerivedJ> & J,
  Eigen::PlainObjectBase<Derivedflip> & flip)
{
#ifdef IGL_OUTER_HULL_DEBUG
  std::cerr << "Extracting outer hull" << std::endl;
#endif
  using namespace Eigen;
  using namespace std;
  typedef typename DerivedF::Index Index;
  Matrix<Index,DerivedF::RowsAtCompileTime,1> C;
  typedef Matrix<typename DerivedV::Scalar,Dynamic,DerivedV::ColsAtCompileTime> MatrixXV;
  //typedef Matrix<typename DerivedF::Scalar,Dynamic,DerivedF::ColsAtCompileTime> MatrixXF;
  typedef Matrix<typename DerivedG::Scalar,Dynamic,DerivedG::ColsAtCompileTime> MatrixXG;
  typedef Matrix<typename DerivedJ::Scalar,Dynamic,DerivedJ::ColsAtCompileTime> MatrixXJ;
  const Index m = F.rows();

  // UNUSED:
  //const auto & duplicate_simplex = [&F](const int f, const int g)->bool
  //{
  //  return
  //    (F(f,0) == F(g,0) && F(f,1) == F(g,1) && F(f,2) == F(g,2)) ||
  //    (F(f,1) == F(g,0) && F(f,2) == F(g,1) && F(f,0) == F(g,2)) ||
  //    (F(f,2) == F(g,0) && F(f,0) == F(g,1) && F(f,1) == F(g,2)) ||
  //    (F(f,0) == F(g,2) && F(f,1) == F(g,1) && F(f,2) == F(g,0)) ||
  //    (F(f,1) == F(g,2) && F(f,2) == F(g,1) && F(f,0) == F(g,0)) ||
  //    (F(f,2) == F(g,2) && F(f,0) == F(g,1) && F(f,1) == F(g,0));
  //};

#ifdef IGL_OUTER_HULL_DEBUG
  cout<<"outer hull..."<<endl;
#endif

#ifdef IGL_OUTER_HULL_DEBUG
  cout<<"edge map..."<<endl;
#endif
  typedef Matrix<typename DerivedF::Scalar,Dynamic,2> MatrixX2I;
  typedef Matrix<typename DerivedF::Index,Dynamic,1> VectorXI;
  //typedef Matrix<typename DerivedV::Scalar, 3, 1> Vector3F;
  MatrixX2I E,uE;
  VectorXI EMAP;
  vector<vector<typename DerivedF::Index> > uE2E;
  unique_edge_map(F,E,uE,EMAP,uE2E);
#ifdef IGL_OUTER_HULL_DEBUG
  for (size_t ui=0; ui<uE.rows(); ui++) {
      std::cout << ui << ": " << uE2E[ui].size() << " -- (";
      for (size_t i=0; i<uE2E[ui].size(); i++) {
          std::cout << uE2E[ui][i] << ", ";
      }
      std::cout << ")" << std::endl;
  }
#endif

  std::vector<std::vector<typename DerivedF::Index> > uE2oE;
  std::vector<std::vector<bool> > uE2C;
  order_facets_around_edges(V, F, uE, uE2E, uE2oE, uE2C);
  uE2E = uE2oE;
  VectorXI diIM(3*m);
  for (auto ue : uE2E) {
      for (size_t i=0; i<ue.size(); i++) {
          auto fe = ue[i];
          diIM[fe] = i;
      }
  }

  vector<vector<vector<Index > > > TT,_1;
  triangle_triangle_adjacency(E,EMAP,uE2E,false,TT,_1);
  VectorXI counts;
#ifdef IGL_OUTER_HULL_DEBUG
  cout<<"facet components..."<<endl;
#endif
  facet_components(TT,C,counts);
  assert(C.maxCoeff()+1 == counts.rows());
  const size_t ncc = counts.rows();
  G.resize(0,F.cols());
  J.resize(0,1);
  flip.setConstant(m,1,false);

#ifdef IGL_OUTER_HULL_DEBUG
  cout<<"reindex..."<<endl;
#endif
  // H contains list of faces on outer hull;
  vector<bool> FH(m,false);
  vector<bool> EH(3*m,false);
  vector<MatrixXG> vG(ncc);
  vector<MatrixXJ> vJ(ncc);
  vector<MatrixXJ> vIM(ncc);
  //size_t face_count = 0;
  for(size_t id = 0;id<ncc;id++)
  {
    vIM[id].resize(counts[id],1);
  }
  // current index into each IM
  vector<size_t> g(ncc,0);
  // place order of each face in its respective component
  for(Index f = 0;f<m;f++)
  {
    vIM[C(f)](g[C(f)]++) = f;
  }

#ifdef IGL_OUTER_HULL_DEBUG
  cout<<"barycenters..."<<endl;
#endif
  // assumes that "resolve" has handled any coplanar cases correctly and nearly
  // coplanar cases can be sorted based on barycenter.
  MatrixXV BC;
  barycenter(V,F,BC);

#ifdef IGL_OUTER_HULL_DEBUG
  cout<<"loop over CCs (="<<ncc<<")..."<<endl;
#endif
  for(Index id = 0;id<(Index)ncc;id++)
  {
    auto & IM = vIM[id];
    // starting face that's guaranteed to be on the outer hull and in this
    // component
    int f;
    bool f_flip;
#ifdef IGL_OUTER_HULL_DEBUG
  cout<<"outer facet..."<<endl;
#endif
  igl::copyleft::cgal::outer_facet(V,F,IM,f,f_flip);
#ifdef IGL_OUTER_HULL_DEBUG
  cout<<"outer facet: "<<f<<endl;
  //cout << V.row(F(f, 0)) << std::endl;
  //cout << V.row(F(f, 1)) << std::endl;
  //cout << V.row(F(f, 2)) << std::endl;
#endif
    int FHcount = 1;
    FH[f] = true;
    // Q contains list of face edges to continue traversing upong
    queue<int> Q;
    Q.push(f+0*m);
    Q.push(f+1*m);
    Q.push(f+2*m);
    flip(f) = f_flip;
    //std::cout << "face " << face_count++ << ": " << f << std::endl;
    //std::cout << "f " << F.row(f).array()+1 << std::endl;
    //cout<<"flip("<<f<<") = "<<(flip(f)?"true":"false")<<endl;
#ifdef IGL_OUTER_HULL_DEBUG
  cout<<"BFS..."<<endl;
#endif
    while(!Q.empty())
    {
      // face-edge
      const int e = Q.front();
      Q.pop();
      // face
      const int f = e%m;
      // corner
      const int c = e/m;
#ifdef IGL_OUTER_HULL_DEBUG
      std::cout << "edge: " << e << ", ue: " << EMAP(e) << std::endl;
      std::cout << "face: " << f << std::endl;
      std::cout << "corner: " << c << std::endl;
      std::cout << "consistent: " << uE2C[EMAP(e)][diIM[e]] << std::endl;
#endif
      // Should never see edge again...
      if(EH[e] == true)
      {
        continue;
      }
      EH[e] = true;
      // source of edge according to f
      const int fs = flip(f)?F(f,(c+2)%3):F(f,(c+1)%3);
      // destination of edge according to f
      const int fd = flip(f)?F(f,(c+1)%3):F(f,(c+2)%3);
      // edge valence
      const size_t val = uE2E[EMAP(e)].size();
#ifdef IGL_OUTER_HULL_DEBUG
      //std::cout << "vd: " << V.row(fd) << std::endl;
      //std::cout << "vs: " << V.row(fs) << std::endl;
      //std::cout << "edge: " << V.row(fd) - V.row(fs) << std::endl;
      for (size_t i=0; i<val; i++) {
          if (i == diIM(e)) {
              std::cout << "* ";
          } else {
              std::cout << "  ";
          }
          std::cout << i << ": "
              << " (e: " << uE2E[EMAP(e)][i] << ", f: "
              << uE2E[EMAP(e)][i] % m * (uE2C[EMAP(e)][i] ? 1:-1) << ")" << std::endl;
      }
#endif

      // is edge consistent with edge of face used for sorting
      const int e_cons = (uE2C[EMAP(e)][diIM(e)] ? 1: -1);
      int nfei = -1;
      // Loop once around trying to find suitable next face
      for(size_t step = 1; step<val+2;step++)
      {
        const int nfei_new = (diIM(e) + 2*val + e_cons*step*(flip(f)?-1:1))%val;
        const int nf = uE2E[EMAP(e)][nfei_new] % m;
        {
#ifdef IGL_OUTER_HULL_DEBUG
        //cout<<"Next facet: "<<(f+1)<<" --> "<<(nf+1)<<", |"<<
        //  di[EMAP(e)][diIM(e)]<<" - "<<di[EMAP(e)][nfei_new]<<"| = "<<
        //    abs(di[EMAP(e)][diIM(e)] - di[EMAP(e)][nfei_new])
        //    <<endl;
#endif

          // Only use this face if not already seen
          if(!FH[nf])
          {
            nfei = nfei_new;
          //} else {
          //    std::cout << "skipping face " << nfei_new << " because it is seen before"
          //        << std::endl;
          }
          break;
        //} else {
        //    std::cout << di[EMAP(e)][diIM(e)].transpose() << std::endl;
        //    std::cout << di[EMAP(e)][diIM(nfei_new)].transpose() << std::endl;
        //    std::cout << "skipping face " << nfei_new << " with identical dihedral angle"
        //        << std::endl;
        }
//#ifdef IGL_OUTER_HULL_DEBUG
//        cout<<"Skipping co-planar facet: "<<(f+1)<<" --> "<<(nf+1)<<endl;
//#endif
      }

      int max_ne = -1;
      if(nfei >= 0)
      {
        max_ne = uE2E[EMAP(e)][nfei];
      }

      if(max_ne>=0)
      {
        // face of neighbor
        const int nf = max_ne%m;
#ifdef IGL_OUTER_HULL_DEBUG
        if(!FH[nf])
        {
          // first time seeing face
          cout<<(f+1)<<" --> "<<(nf+1)<<endl;
        }
#endif
        FH[nf] = true;
        //std::cout << "face " << face_count++ << ": " << nf << std::endl;
        //std::cout << "f " << F.row(nf).array()+1 << std::endl;
        FHcount++;
        // corner of neighbor
        const int nc = max_ne/m;
        const int nd = F(nf,(nc+2)%3);
        const bool cons = (flip(f)?fd:fs) == nd;
        flip(nf) = (cons ? flip(f) : !flip(f));
        //cout<<"flip("<<nf<<") = "<<(flip(nf)?"true":"false")<<endl;
        const int ne1 = nf+((nc+1)%3)*m;
        const int ne2 = nf+((nc+2)%3)*m;
        if(!EH[ne1])
        {
          Q.push(ne1);
        }
        if(!EH[ne2])
        {
          Q.push(ne2);
        }
      }
    }

    {
      vG[id].resize(FHcount,3);
      vJ[id].resize(FHcount,1);
      //nG += FHcount;
      size_t h = 0;
      assert(counts(id) == IM.rows());
      for(int i = 0;i<counts(id);i++)
      {
        const size_t f = IM(i);
        //if(f_flip)
        //{
        //  flip(f) = !flip(f);
        //}
        if(FH[f])
        {
          vG[id].row(h) = (flip(f)?F.row(f).reverse().eval():F.row(f));
          vJ[id](h,0) = f;
          h++;
        }
      }
      assert((int)h == FHcount);
    }
  }

  // Is A inside B? Assuming A and B are consistently oriented but closed and
  // non-intersecting.
  const auto & has_overlapping_bbox = [](
    const Eigen::PlainObjectBase<DerivedV> & V,
    const MatrixXG & A,
    const MatrixXG & B)->bool
  {
    const auto & bounding_box = [](
      const Eigen::PlainObjectBase<DerivedV> & V,
      const MatrixXG & F)->
        DerivedV
    {
      DerivedV BB(2,3);
      BB<<
         1e26,1e26,1e26,
        -1e26,-1e26,-1e26;
      const size_t m = F.rows();
      for(size_t f = 0;f<m;f++)
      {
        for(size_t c = 0;c<3;c++)
        {
          const auto & vfc = V.row(F(f,c)).eval();
          BB(0,0) = std::min(BB(0,0), vfc(0,0));
          BB(0,1) = std::min(BB(0,1), vfc(0,1));
          BB(0,2) = std::min(BB(0,2), vfc(0,2));
          BB(1,0) = std::max(BB(1,0), vfc(0,0));
          BB(1,1) = std::max(BB(1,1), vfc(0,1));
          BB(1,2) = std::max(BB(1,2), vfc(0,2));
        }
      }
      return BB;
    };
    // A lot of the time we're dealing with unrelated, distant components: cull
    // them.
    DerivedV ABB = bounding_box(V,A);
    DerivedV BBB = bounding_box(V,B);
    if( (BBB.row(0)-ABB.row(1)).maxCoeff()>0  ||
        (ABB.row(0)-BBB.row(1)).maxCoeff()>0 )
    {
      // bounding boxes do not overlap
      return false;
    } else {
      return true;
    }
  };

  // Reject components which are completely inside other components
  vector<bool> keep(ncc,true);
  size_t nG = 0;
  // This is O( ncc * ncc * m)
  for(size_t id = 0;id<ncc;id++)
  {
    if (!keep[id]) continue;
    std::vector<size_t> unresolved;
    for(size_t oid = 0;oid<ncc;oid++)
    {
      if(id == oid || !keep[oid])
      {
        continue;
      }
      if (has_overlapping_bbox(V, vG[id], vG[oid])) {
          unresolved.push_back(oid);
      }
    }
    const size_t num_unresolved_components = unresolved.size();
    DerivedV query_points(num_unresolved_components, 3);
    for (size_t i=0; i<num_unresolved_components; i++) {
        const size_t oid = unresolved[i];
        DerivedF f = vG[oid].row(0);
        query_points(i,0) = (V(f(0,0), 0) + V(f(0,1), 0) + V(f(0,2), 0))/3.0;
        query_points(i,1) = (V(f(0,0), 1) + V(f(0,1), 1) + V(f(0,2), 1))/3.0;
        query_points(i,2) = (V(f(0,0), 2) + V(f(0,1), 2) + V(f(0,2), 2))/3.0;
    }
    Eigen::VectorXi inside;
    igl::copyleft::cgal::points_inside_component(V, vG[id], query_points, inside);
    assert((size_t)inside.size() == num_unresolved_components);
    for (size_t i=0; i<num_unresolved_components; i++) {
        if (inside(i, 0)) {
            const size_t oid = unresolved[i];
            keep[oid] = false;
        }
    }
  }
  for (size_t id = 0; id<ncc; id++) {
      if (keep[id]) {
          nG += vJ[id].rows();
      }
  }

  // collect G and J across components
  G.resize(nG,3);
  J.resize(nG,1);
  {
    size_t off = 0;
    for(Index id = 0;id<(Index)ncc;id++)
    {
      if(keep[id])
      {
        assert(vG[id].rows() == vJ[id].rows());
        G.block(off,0,vG[id].rows(),vG[id].cols()) = vG[id];
        J.block(off,0,vJ[id].rows(),vJ[id].cols()) = vJ[id];
        off += vG[id].rows();
      }
    }
  }
}

#ifdef IGL_STATIC_LIBRARY
// Explicit template instantiation
// generated by autoexplicit.sh
template void igl::copyleft::cgal::outer_hull_legacy<Eigen::Matrix<CGAL::Epeck::FT, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1> >(Eigen::PlainObjectBase<Eigen::Matrix<CGAL::Epeck::FT, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&);
template void igl::copyleft::cgal::outer_hull_legacy< Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1> >(Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > &, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > &, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > &);
template void igl::copyleft::cgal::outer_hull_legacy<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1> >(Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&);
#ifdef WIN32
#endif
#endif


// Copyright Abel Sinkovics (abel@sinkovics.hu) 2013.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#include <mpllibs/metamonad/letrec_c.hpp>
#include <mpllibs/metamonad/let.hpp>
#include <mpllibs/metamonad/lambda_c.hpp>
#include <mpllibs/metamonad/syntax.hpp>
#include <mpllibs/metamonad/eval_syntax.hpp>
#include <mpllibs/metamonad/lazy.hpp>
#include <mpllibs/metamonad/if_.hpp>
#include <mpllibs/metamonad/apply.hpp>

#include <boost/test/unit_test.hpp>

#include <boost/mpl/minus.hpp>
#include <boost/mpl/assert.hpp>

#include <boost/type_traits.hpp>

#include "common.hpp"

BOOST_AUTO_TEST_CASE(test_letrec_c)
{
  using boost::mpl::equal_to;
  using boost::mpl::minus;

  using mpllibs::metamonad::letrec_c;
  using mpllibs::metamonad::if_;
  using mpllibs::metamonad::let;
  using mpllibs::metamonad::lambda_c;
  using mpllibs::metamonad::lazy;
  using mpllibs::metamonad::eval_syntax;
  using mpllibs::metamonad::syntax;
  using mpllibs::metamonad::apply;

  using boost::is_same;

  // name is replaced with a nullary metafunction evaluating to the
  // substituted expression
  // test_letrec_name
  BOOST_MPL_ASSERT((equal_to<int13, eval_syntax<letrec_c<x,int13, x> >::type>));

  // test_letrec_not_name
  BOOST_MPL_ASSERT((
    equal_to<int11, eval_syntax<letrec_c<x, int13, int11> >::type>
  ));

  // test_template
  BOOST_MPL_ASSERT((
    equal_to<
      int26,
      eval_syntax<letrec_c<x, int13, lazy_double_value<x> > >::type
    >
  ));

  // test_nested_letrec
  BOOST_MPL_ASSERT((
    equal_to<
      int24,
      eval_syntax<
        letrec_c<x, int13, eval_syntax<letrec_c<y, int11, lazy_plus<x, y> > > >
      >::type
    >
  ));

  // test_shadowing
  BOOST_MPL_ASSERT((
    equal_to<
      int37,
      eval_syntax<
        letrec_c<
          x, int13,
          double_lazy_plus<
            x,
            eval_syntax<letrec_c<x, int11, lazy_plus<x, int13> > >
          >
        >
      >::type
    >
  ));

  // test_recursion
  BOOST_MPL_ASSERT((
    equal_to<
      int24,
      eval_syntax<
        letrec_c<
          x,
          lambda_c<
            y,
            if_<
              lazy_equal_to<y, int0>,
              int1,
              lazy_times<apply<x, minus<y, int1> >, y>
            >
          >,
          apply<x, int4>
        >
      >::type
    >
  ));

  // test_letrec_lazy
  BOOST_MPL_ASSERT((
    equal_to<
      int11,
      eval_syntax<letrec_c<x, int2, lazy<minus<int13, x> > > >::type
    >
  ));

  // test_letrec_c_in_let
  BOOST_MPL_ASSERT((
    is_same<
      syntax<letrec_c<x, int13, x> >,
      let<x, syntax<int13>, syntax<letrec_c<x, x, x> > >::type
    >
  ));
}


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

  This source file is part of the Avogadro project.

  Copyright 2010 Eric C. Brown

  This source code is released under the New BSD License, (the "License").

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

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

#include "qtaimcriticalpointlocator.h"
#include "qtaimwavefunction.h"
#include "qtaimodeintegrator.h"
#include "qtaimlsodaintegrator.h"
#include "qtaimmathutilities.h"

#include <Eigen/Core>

#include <QList>

#include <QtConcurrentMap>

#include <QTemporaryFile>
#include <QFile>
#include <QDataStream>
#include <QDir>

#include <QVariant>

#include <QProgressDialog>
#include <QFutureWatcher>
#include <QFuture>

using namespace std;
using namespace Eigen;

#define HUGE_REAL_NUMBER 1.e20
#define SMALL_GRADIENT_NORM 1.e-4

namespace Avogadro {
namespace QtPlugins {

  QList<QVariant> QTAIMLocateNuclearCriticalPoint( QList<QVariant> input  )
  {
    const QString fileName=input.at(0).toString();
    const qint64 nucleus=input.at(1).toInt();
    const QVector3D x0y0z0(
        input.at(2).toReal(),
        input.at(3).toReal(),
        input.at(4).toReal()
        );

    QTAIMWavefunction wfn;
    wfn.loadFromBinaryFile(fileName);

    QTAIMWavefunctionEvaluator eval(wfn);

    QVector3D result;

    if( wfn.nuclearCharge(nucleus) < 4 )
    {
      //      QTAIMODEIntegrator ode(eval,QTAIMODEIntegrator::CMBPMinusThreeGradientInElectronDensity);
      QTAIMLSODAIntegrator ode(eval,QTAIMLSODAIntegrator::CMBPMinusThreeGradientInElectronDensity);
      result=ode.integrate(x0y0z0);
    }
    else
    {
      result=x0y0z0;
    }

    bool correctSignature;
    Matrix<qreal,3,1> xyz; xyz << result.x(), result.y(), result.z();

    if(
        QTAIMMathUtilities::signatureOfASymmetricThreeByThreeMatrix(
            eval.hessianOfElectronDensity(xyz)
            ) == -3
        )
    {
      correctSignature=true;
    }
    else
    {
      correctSignature=false;
    }

    QList<QVariant> value;

    if( correctSignature )
    {
      value.append(correctSignature);
      value.append(result.x());
      value.append(result.y());
      value.append(result.z());
    }
    else
    {
      value.append(false);
    }

    return value;

  }

  QList<QVariant> QTAIMLocateBondCriticalPoint( QList<QVariant> input  )
  {

    QList<QVariant> value;
    value.clear();

    const QString wfnFileName=input.at(0).toString();
    const QString nuclearCriticalPointsFileName=input.at(1).toString();
    const qint64 nucleusA=input.at(2).toInt();
    const qint64 nucleusB=input.at(3).toInt();
    const QVector3D x0y0z0(
        input.at(4).toReal(),
        input.at(5).toReal(),
        input.at(6).toReal()
        );

    QTAIMWavefunction wfn;
    wfn.loadFromBinaryFile(wfnFileName);

    QList<QVector3D> nuclearCriticalPoints;
    QFile nuclearCriticalPointsFile(nuclearCriticalPointsFileName);
    nuclearCriticalPointsFile.open(QIODevice::ReadOnly);
    QDataStream nuclearCriticalPointsFileIn(&nuclearCriticalPointsFile);
    nuclearCriticalPointsFileIn >> nuclearCriticalPoints ;
    nuclearCriticalPointsFile.close();

    QList<QPair<QVector3D,qreal> > betaSpheres;
    for( qint64 i=0 ; i < nuclearCriticalPoints.length() ; ++i )
    {
      QPair<QVector3D,qreal> thisBetaSphere;
      thisBetaSphere.first=nuclearCriticalPoints.at(i);
      thisBetaSphere.second=0.1;
      betaSpheres.append(thisBetaSphere);
    }

    QTAIMWavefunctionEvaluator eval(wfn);

    QList<QVector3D> ncpList;

    QVector3D result;
    //    QTAIMODEIntegrator ode(eval,QTAIMODEIntegrator::CMBPMinusOneGradientInElectronDensity);
    QTAIMLSODAIntegrator ode(eval,QTAIMLSODAIntegrator::CMBPMinusOneGradientInElectronDensity);
    result=ode.integrate(x0y0z0);
    Matrix<qreal,3,1> xyz; xyz << result.x(), result.y(), result.z();

    if(
        !( QTAIMMathUtilities::signatureOfASymmetricThreeByThreeMatrix(
            eval.hessianOfElectronDensity(xyz)
            ) == -1 )
        || (eval.gradientOfElectronDensity(xyz)).norm() > SMALL_GRADIENT_NORM
        )
    {
      value.append(false);
      value.append(result.x());
      value.append(result.y());
      value.append(result.z());
      return value;
    }

    Matrix<qreal,3,3> eigenvectorsOfHessian;
    eigenvectorsOfHessian=QTAIMMathUtilities::eigenvectorsOfASymmetricThreeByThreeMatrix(
        eval.hessianOfElectronDensity(xyz)
        );
    Matrix<qreal,3,1> highestEigenvectorOfHessian;
    highestEigenvectorOfHessian <<
        eigenvectorsOfHessian(0,2),
        eigenvectorsOfHessian(1,2),
        eigenvectorsOfHessian(2,2);

    const qreal smallStep=0.01;

    QVector3D forwardStartingPoint( result.x() + smallStep*highestEigenvectorOfHessian(0),
                                    result.y() + smallStep*highestEigenvectorOfHessian(1),
                                    result.z() + smallStep*highestEigenvectorOfHessian(2) );

    QVector3D backwardStartingPoint( result.x() - smallStep*highestEigenvectorOfHessian(0),
                                     result.y() - smallStep*highestEigenvectorOfHessian(1),
                                     result.z() - smallStep*highestEigenvectorOfHessian(2) );

    //    QTAIMODEIntegrator forwardODE(eval,QTAIMODEIntegrator::SteepestAscentPathInElectronDensity);
    QTAIMLSODAIntegrator forwardODE(eval,QTAIMLSODAIntegrator::SteepestAscentPathInElectronDensity);
    forwardODE.setBetaSpheres( betaSpheres );
    QVector3D forwardEndpoint=forwardODE.integrate(forwardStartingPoint);
    QList<QVector3D> forwardPath=forwardODE.path();

    //    QTAIMODEIntegrator backwardODE(eval,QTAIMODEIntegrator::SteepestAscentPathInElectronDensity);
    QTAIMLSODAIntegrator backwardODE(eval,QTAIMLSODAIntegrator::SteepestAscentPathInElectronDensity);
    backwardODE.setBetaSpheres( betaSpheres );
    QVector3D backwardEndpoint=backwardODE.integrate(backwardStartingPoint);
    QList<QVector3D> backwardPath=backwardODE.path();

    qreal smallestDistance=HUGE_REAL_NUMBER;
    qint64 smallestDistanceIndex=0;

    for( qint64 n=0 ; n < wfn.numberOfNuclei()  ; ++n )
    {
      Matrix<qreal,3,1> a(forwardEndpoint.x(),forwardEndpoint.y(),forwardEndpoint.z());
      Matrix<qreal,3,1> b(wfn.xNuclearCoordinate(n), wfn.yNuclearCoordinate(n), wfn.zNuclearCoordinate(n));

      qreal distance=QTAIMMathUtilities::distance(a,b);

      if( distance < smallestDistance )
      {
        smallestDistance = distance;
        smallestDistanceIndex=n;
      }
    }
    qint64 forwardNucleusIndex=smallestDistanceIndex;

    smallestDistance=HUGE_REAL_NUMBER;
    smallestDistanceIndex=0;

    for( qint64 n=0 ; n < wfn.numberOfNuclei()  ; ++n )
    {
      Matrix<qreal,3,1> a(backwardEndpoint.x(),backwardEndpoint.y(),backwardEndpoint.z());
      Matrix<qreal,3,1> b(wfn.xNuclearCoordinate(n), wfn.yNuclearCoordinate(n), wfn.zNuclearCoordinate(n));

      qreal distance=QTAIMMathUtilities::distance(a,b);

      if( distance < smallestDistance )
      {
        smallestDistance = distance;
        smallestDistanceIndex=n;
      }
    }
    qint64 backwardNucleusIndex=smallestDistanceIndex;

    bool bondPathConnectsPair;
    if( (forwardNucleusIndex == nucleusA && backwardNucleusIndex == nucleusB) ||
        (forwardNucleusIndex == nucleusB && backwardNucleusIndex == nucleusA) )
    {
      bondPathConnectsPair=true;
    }
    else
    {
      bondPathConnectsPair=false;
    }

    if( bondPathConnectsPair )
    {
      value.append(true);
      value.append(nucleusA);
      value.append(nucleusB);
      value.append(result.x());
      value.append(result.y());
      value.append(result.z());
      Matrix<qreal,3,1> xyz_ ; xyz_ << result.x(),result.y(),result.z();
      value.append( eval.laplacianOfElectronDensity(xyz_) );
      value.append( QTAIMMathUtilities::ellipticityOfASymmetricThreeByThreeMatrix(
          eval.hessianOfElectronDensity(xyz_)
          )
                    );
      value.append( 1 + forwardPath.length() + 1 + backwardPath.length() + 1);
      value.append( forwardEndpoint.x() );
      for(qint64 i=forwardPath.length() - 1 ; i >= 0 ; --i)
      {
        value.append( forwardPath.at(i).x() );
      }
      value.append(result.x());
      for(qint64 i=0; i < backwardPath.length() ; ++i)
      {
        value.append( backwardPath.at(i).x() );
      }
      value.append( backwardEndpoint.x() );
      value.append( forwardEndpoint.y() );
      for(qint64 i=forwardPath.length() - 1 ; i >= 0 ; --i)
      {
        value.append( forwardPath.at(i).y() );
      }
      value.append(result.y());
      for(qint64 i=0; i < backwardPath.length() ; ++i)
      {
        value.append( backwardPath.at(i).y() );
      }
      value.append( backwardEndpoint.y() );
      value.append( forwardEndpoint.z() );
      for(qint64 i=forwardPath.length() - 1 ; i >= 0 ; --i)
      {
        value.append( forwardPath.at(i).z() );
      }
      value.append(result.z());
      for(qint64 i=0; i < backwardPath.length() ; ++i)
      {
        value.append( backwardPath.at(i).z() );
      }
      value.append( backwardEndpoint.z() );

    }
    else
    {
      value.append(false);
      // for debugging
      value.append(result.x());
      value.append(result.y());
      value.append(result.z());
    }

    return value;
  }

  QList<QVariant> QTAIMLocateElectronDensitySink( QList<QVariant> input  )
  {
    qint64 counter=0;
    const QString fileName=input.at(counter).toString(); counter++;
    //    const qint64 nucleus=input.at(counter).toInt(); counter++
    qreal x0=input.at(counter).toReal(); counter++;
    qreal y0=input.at(counter).toReal(); counter++;
    qreal z0=input.at(counter).toReal(); counter++;

    const QVector3D x0y0z0(x0,y0,z0);

    QTAIMWavefunction wfn;
    wfn.loadFromBinaryFile(fileName);

    QTAIMWavefunctionEvaluator eval(wfn);

    bool correctSignature;
    QVector3D result;

    Matrix<qreal,3,1> xyz; xyz << x0, y0, z0;
    if( eval.electronDensity( xyz ) < 1.e-1 )
    {
      correctSignature=false;
    }
    else
    {
      //      QTAIMODEIntegrator ode(eval,QTAIMODEIntegrator::CMBPMinusThreeGradientInElectronDensityLaplacian);
      QTAIMLSODAIntegrator ode(eval,QTAIMLSODAIntegrator::CMBPMinusThreeGradientInElectronDensityLaplacian);
      result=ode.integrate(x0y0z0);

      Matrix<qreal,3,1> xyz_; xyz_ << result.x(), result.y(), result.z();

      if( eval.electronDensity(xyz_) > 1.e-1 &&
          eval.gradientOfElectronDensityLaplacian(xyz_).norm() < 1.e-3 )
      {
        if(
            QTAIMMathUtilities::signatureOfASymmetricThreeByThreeMatrix(
                eval.hessianOfElectronDensityLaplacian(xyz_)
                ) == -3
            )
        {
          correctSignature=true;
        }
        else
        {
          correctSignature=false;
        }
      }
      else
      {
        correctSignature=false;
      }
    }

    QList<QVariant> value;
    if( correctSignature )
    {
      value.append(correctSignature);
      value.append(result.x());
      value.append(result.y());
      value.append(result.z());
    }
    else
    {
      value.append(false);
    }

    return value;

  }

  QList<QVariant> QTAIMLocateElectronDensitySource( QList<QVariant> input  )
  {
    qint64 counter=0;
    const QString fileName=input.at(counter).toString(); counter++;
    //    const qint64 nucleus=input.at(counter).toInt(); counter++
    qreal x0=input.at(counter).toReal(); counter++;
    qreal y0=input.at(counter).toReal(); counter++;
    qreal z0=input.at(counter).toReal(); counter++;

    const QVector3D x0y0z0(x0,y0,z0);

    QTAIMWavefunction wfn;
    wfn.loadFromBinaryFile(fileName);

    QTAIMWavefunctionEvaluator eval(wfn);

    bool correctSignature;
    QVector3D result;

    Matrix<qreal,3,1> xyz; xyz << x0, y0, z0;
    if( eval.electronDensity( xyz ) < 1.e-1 )
    {
      correctSignature=false;
    }
    else
    {
      //      QTAIMODEIntegrator ode(eval,QTAIMODEIntegrator::CMBPPlusThreeGradientInElectronDensityLaplacian);
      QTAIMLSODAIntegrator ode(eval,QTAIMLSODAIntegrator::CMBPPlusThreeGradientInElectronDensityLaplacian);
      result=ode.integrate(x0y0z0);

      Matrix<qreal,3,1> xyz_; xyz_ << result.x(), result.y(), result.z();

      if( eval.electronDensity(xyz_) > 1.e-1 &&
          eval.gradientOfElectronDensityLaplacian(xyz_).norm() < 1.e-3 )
      {
        if(
            QTAIMMathUtilities::signatureOfASymmetricThreeByThreeMatrix(
                eval.hessianOfElectronDensityLaplacian(xyz_)
                ) == 3
            )
        {
          correctSignature=true;
        }
        else
        {
          correctSignature=false;
        }
      }
      else
      {
        correctSignature=false;
      }
    }

    QList<QVariant> value;
    if( correctSignature )
    {
      value.append(correctSignature);
      value.append(result.x());
      value.append(result.y());
      value.append(result.z());
    }
    else
    {
      value.append(false);
    }

    return value;

  }

  QTAIMCriticalPointLocator::QTAIMCriticalPointLocator( QTAIMWavefunction &wfn)
  {
    m_wfn=&wfn;

    m_nuclearCriticalPoints.empty();
    m_bondCriticalPoints.empty();
    m_ringCriticalPoints.empty();
    m_cageCriticalPoints.empty();

    m_laplacianAtBondCriticalPoints.empty();
    m_ellipticityAtBondCriticalPoints.empty();

    m_bondPaths.empty();
    m_bondedAtoms.empty();

    m_electronDensitySources.empty();
    m_electronDensitySinks.empty();

  }

  void QTAIMCriticalPointLocator::locateNuclearCriticalPoints()
  {

    QString tempFileName=QTAIMCriticalPointLocator::temporaryFileName();

    QList<QList<QVariant> > inputList;

    const qint64 numberOfNuclei = m_wfn->numberOfNuclei();

    for( qint64 n=0 ; n < numberOfNuclei ; ++n)
    {
      QList<QVariant> input;
      input.append( tempFileName );
      input.append( n );
      input.append( m_wfn->xNuclearCoordinate(n) );
      input.append( m_wfn->yNuclearCoordinate(n) );
      input.append( m_wfn->zNuclearCoordinate(n) );

      inputList.append(input);
    }

    m_wfn->saveToBinaryFile(tempFileName);

    QProgressDialog dialog;
    dialog.setWindowTitle("QTAIM");
    dialog.setLabelText(QString("Nuclear Critical Points Search"));

    QFutureWatcher<void> futureWatcher;
    QObject::connect(&futureWatcher, SIGNAL(finished()), &dialog, SLOT(reset()));
    QObject::connect(&dialog, SIGNAL(canceled()), &futureWatcher, SLOT(cancel()));
    QObject::connect(&futureWatcher, SIGNAL(progressRangeChanged(int,int)), &dialog, SLOT(setRange(int,int)));
    QObject::connect(&futureWatcher, SIGNAL(progressValueChanged(int)), &dialog, SLOT(setValue(int)));

    QFuture<QList<QVariant> > future=QtConcurrent::mapped(inputList, QTAIMLocateNuclearCriticalPoint);
    futureWatcher.setFuture(future);
    dialog.exec();
    futureWatcher.waitForFinished();

    QList<QList<QVariant> > results;
    if( futureWatcher.future().isCanceled() )
    {
      results.clear();
    }
    else
    {
      results=future.results();
    }

    QFile file;
    file.remove(tempFileName);

    for( qint64 n=0 ; n < results.length() ; ++n )
    {

      bool correctSignature = results.at(n).at(0).toBool();

      if (correctSignature)
      {

        QVector3D result(
          results.at(n).at(1).toReal(),
          results.at(n).at(2).toReal(),
          results.at(n).at(3).toReal()
        );

        m_nuclearCriticalPoints.append( result );
      }

    }

  }

  void QTAIMCriticalPointLocator::locateBondCriticalPoints()
  {

    if( m_nuclearCriticalPoints.length() < 1 )
    {
      return;
    }

    const qint64 numberOfNuclei = m_wfn->numberOfNuclei();

    if( numberOfNuclei < 2)
    {
      return;
    }

    QString tempFileName=QTAIMCriticalPointLocator::temporaryFileName();

    QString nuclearCriticalPointsFileName=QTAIMCriticalPointLocator::temporaryFileName();
    QFile nuclearCriticalPointsFile(nuclearCriticalPointsFileName);
    nuclearCriticalPointsFile.open(QIODevice::WriteOnly);
    QDataStream nuclearCriticalPointsOut(&nuclearCriticalPointsFile);
    nuclearCriticalPointsOut << m_nuclearCriticalPoints;
    nuclearCriticalPointsFile.close();

    QList<QList<QVariant> > inputList;

    for( qint64 M=0 ; M < numberOfNuclei - 1 ; ++M )
    {
      for( qint64 N=M+1 ; N < numberOfNuclei ; ++N )
      {

        const qreal distanceCutoff = 8.0 ;

        Matrix<qreal,3,1> a;
        Matrix<qreal,3,1> b;

        a << m_wfn->xNuclearCoordinate(M), m_wfn->yNuclearCoordinate(M), m_wfn->zNuclearCoordinate(M) ;
        b << m_wfn->xNuclearCoordinate(N), m_wfn->yNuclearCoordinate(N), m_wfn->zNuclearCoordinate(N) ;

        if( QTAIMMathUtilities::distance(a,b) < distanceCutoff )
        {
          QVector3D x0y0z0( ( m_wfn->xNuclearCoordinate(M) + m_wfn->xNuclearCoordinate(N) ) / 2.0 ,
                            ( m_wfn->yNuclearCoordinate(M) + m_wfn->yNuclearCoordinate(N) ) / 2.0,
                            ( m_wfn->zNuclearCoordinate(M) + m_wfn->zNuclearCoordinate(N) ) / 2.0 );

          QList<QVariant> input;
          input.append( tempFileName );
          input.append( nuclearCriticalPointsFileName );
          input.append( M );
          input.append( N );
          input.append( x0y0z0.x() );
          input.append( x0y0z0.y() );
          input.append( x0y0z0.z() );

          inputList.append(input);
        }
      } // end N
    } // end M

    m_wfn->saveToBinaryFile(tempFileName);

    QProgressDialog dialog;
    dialog.setWindowTitle("QTAIM");
    dialog.setLabelText(QString("Bond Critical Points Search"));

    QFutureWatcher<void> futureWatcher;
    QObject::connect(&futureWatcher, SIGNAL(finished()), &dialog, SLOT(reset()));
    QObject::connect(&dialog, SIGNAL(canceled()), &futureWatcher, SLOT(cancel()));
    QObject::connect(&futureWatcher, SIGNAL(progressRangeChanged(int,int)), &dialog, SLOT(setRange(int,int)));
    QObject::connect(&futureWatcher, SIGNAL(progressValueChanged(int)), &dialog, SLOT(setValue(int)));

    QFuture<QList<QVariant> > future=QtConcurrent::mapped(inputList, QTAIMLocateBondCriticalPoint);;
    futureWatcher.setFuture(future);
    dialog.exec();
    futureWatcher.waitForFinished();

    QList<QList<QVariant> > results;
    if( futureWatcher.future().isCanceled() )
    {
      results.clear();
    }
    else
    {
      results=future.results();
    }

    QFile file;
    file.remove(tempFileName);
    file.remove(nuclearCriticalPointsFileName);

    for( qint64 i=0 ; i < results.length() ; ++i )
    {
      QList<QVariant> thisCriticalPoint=results.at(i);

      bool success=thisCriticalPoint.at(0).toBool();

      if(success)
      {
        QPair<qint64,qint64> bondedAtoms_;
        bondedAtoms_.first=thisCriticalPoint.at(1).toInt();
        bondedAtoms_.second=thisCriticalPoint.at(2).toInt();
        m_bondedAtoms.append( bondedAtoms_ );

        QVector3D coordinates(thisCriticalPoint.at(3).toReal(),
                              thisCriticalPoint.at(4).toReal(),
                              thisCriticalPoint.at(5).toReal());

        m_bondCriticalPoints.append( coordinates );

        m_laplacianAtBondCriticalPoints.append(thisCriticalPoint.at(6).toReal());
        m_ellipticityAtBondCriticalPoints.append(thisCriticalPoint.at(7).toReal());
        qint64 pathLength=thisCriticalPoint.at(8).toInt();

        QList<QVector3D> bondPath;
        for( qint64 j=0 ; j < pathLength ; ++j )
        {
          QVector3D pathPoint(thisCriticalPoint.at(9 + j                ).toReal(),
                              thisCriticalPoint.at(9 + j +   pathLength ).toReal(),
                              thisCriticalPoint.at(9 + j + 2*pathLength ).toReal());

          bondPath.append(pathPoint);
        }

        m_bondPaths.append(bondPath);
      }

    }

  }

  void QTAIMCriticalPointLocator::locateElectronDensitySources()
  {

    QString tempFileName=QTAIMCriticalPointLocator::temporaryFileName();

    QList<QList<QVariant> > inputList;

    qreal xmin,ymin,zmin;
    qreal xmax,ymax,zmax;
    qreal xstep,ystep,zstep;

    // TODO: if only we were using Eigen data structures...
    QList<qreal> xNuclearCoordinates;
    QList<qreal> yNuclearCoordinates;
    QList<qreal> zNuclearCoordinates;

    for( qint64 i=0; i < m_wfn->numberOfNuclei() ; ++i )
    {
      xNuclearCoordinates.append( m_wfn->xNuclearCoordinate(i) );
      yNuclearCoordinates.append( m_wfn->yNuclearCoordinate(i) );
      zNuclearCoordinates.append( m_wfn->zNuclearCoordinate(i) );
    }

    xmin=xNuclearCoordinates.first();
    xmax=xNuclearCoordinates.first();
    for( qint64 i=1 ; i < m_wfn->numberOfNuclei() ; ++i)
    {
      if( xNuclearCoordinates.at(i) < xmin )
      {
        xmin=xNuclearCoordinates.at(i);
      }
      if( xNuclearCoordinates.at(i) > xmax )
      {
        xmax=xNuclearCoordinates.at(i);
      }
    }

    ymin=yNuclearCoordinates.first();
    ymax=yNuclearCoordinates.first();
    for( qint64 i=1 ; i < yNuclearCoordinates.count() ; ++i)
    {
      if( yNuclearCoordinates.at(i) < ymin )
      {
        ymin=yNuclearCoordinates.at(i);
      }
      if( yNuclearCoordinates.at(i) > ymax )
      {
        ymax=yNuclearCoordinates.at(i);
      }
    }

    zmin=zNuclearCoordinates.first();
    zmax=zNuclearCoordinates.first();
    for( qint64 i=1 ; i < zNuclearCoordinates.count() ; ++i)
    {
      if( zNuclearCoordinates.at(i) < zmin )
      {
        zmin=zNuclearCoordinates.at(i);
      }
      if( zNuclearCoordinates.at(i) > zmax )
      {
        zmax=zNuclearCoordinates.at(i);
      }
    }

    xmin= -2.0 + xmin;
    ymin= -2.0 + ymin;
    zmin= -2.0 + zmin;

    xmax = 2.0 + xmax;
    ymax = 2.0 + ymax;
    zmax = 2.0 + zmax;

    xstep=ystep=zstep= 0.5;

    for( qreal x=xmin ; x < xmax+xstep ; x=x+xstep)
    {
      for( qreal y=ymin ; y < ymax+ystep ; y=y+ystep)
      {
        for( qreal z=zmin ; z < zmax+zstep ; z=z+zstep)
        {
          QList<QVariant> input;
          input.append( tempFileName );
//          input.append( n );
          input.append( x );
          input.append( y );
          input.append( z );

          inputList.append(input);
        }
      }
    }

    m_wfn->saveToBinaryFile(tempFileName);

    QProgressDialog dialog;
    dialog.setWindowTitle("QTAIM");
    dialog.setLabelText(QString("Electron Density Sources Search"));

    QFutureWatcher<void> futureWatcher;
    QObject::connect(&futureWatcher, SIGNAL(finished()), &dialog, SLOT(reset()));
    QObject::connect(&dialog, SIGNAL(canceled()), &futureWatcher, SLOT(cancel()));
    QObject::connect(&futureWatcher, SIGNAL(progressRangeChanged(int,int)), &dialog, SLOT(setRange(int,int)));
    QObject::connect(&futureWatcher, SIGNAL(progressValueChanged(int)), &dialog, SLOT(setValue(int)));

    QFuture<QList<QVariant> > future=QtConcurrent::mapped(inputList, QTAIMLocateElectronDensitySource );
    futureWatcher.setFuture(future);
    dialog.exec();
    futureWatcher.waitForFinished();

    QList<QList<QVariant> > results;
    if( futureWatcher.future().isCanceled() )
    {
      results.clear();
    }
    else
    {
      results=future.results();
    }

    QFile file;
    file.remove(tempFileName);

    for( qint64 n=0 ; n < results.length() ; ++n )
    {

      qint64 counter=0;
      bool correctSignature = results.at(n).at(counter).toBool(); counter++;

      if( correctSignature )
      {
        qreal x=results.at(n).at(counter).toReal(); counter++;
        qreal y=results.at(n).at(counter).toReal(); counter++;
        qreal z=results.at(n).at(counter).toReal(); counter++;

        if( (xmin < x && x < xmax) &&
            (ymin < y && y < ymax) &&
            (zmin < z && z < zmax) )
        {
          QVector3D result(x,y,z);

          qreal smallestDistance=HUGE_REAL_NUMBER;

          for(qint64 i=0 ; i < m_electronDensitySources.length() ; ++i )
          {

            Matrix<qreal,3,1> a(x,y,z);
            Matrix<qreal,3,1> b(m_electronDensitySources.at(i).x(),
                                m_electronDensitySources.at(i).y(),
                                m_electronDensitySources.at(i).z());

            qreal distance=QTAIMMathUtilities::distance(a,b);

            if( distance < smallestDistance )
            {
              smallestDistance=distance;
            }

          }

          if( smallestDistance > 1.e-2 )
          {
            m_electronDensitySources.append( result );
          }
        }
      }
    }
//    qDebug() << "SOURCES" << m_electronDensitySources;
  }

  void QTAIMCriticalPointLocator::locateElectronDensitySinks()
  {

    QString tempFileName=QTAIMCriticalPointLocator::temporaryFileName();

    QList<QList<QVariant> > inputList;

    qreal xmin,ymin,zmin;
    qreal xmax,ymax,zmax;
    qreal xstep,ystep,zstep;

    // TODO: if only we were using Eigen data structures...
    QList<qreal> xNuclearCoordinates;
    QList<qreal> yNuclearCoordinates;
    QList<qreal> zNuclearCoordinates;

    for( qint64 i=0; i < m_wfn->numberOfNuclei() ; ++i )
    {
      xNuclearCoordinates.append( m_wfn->xNuclearCoordinate(i) );
      yNuclearCoordinates.append( m_wfn->yNuclearCoordinate(i) );
      zNuclearCoordinates.append( m_wfn->zNuclearCoordinate(i) );
    }

    xmin=xNuclearCoordinates.first();
    xmax=xNuclearCoordinates.first();
    for( qint64 i=1 ; i < m_wfn->numberOfNuclei() ; ++i)
    {
      if( xNuclearCoordinates.at(i) < xmin )
      {
        xmin=xNuclearCoordinates.at(i);
      }
      if( xNuclearCoordinates.at(i) > xmax )
      {
        xmax=xNuclearCoordinates.at(i);
      }
    }

    ymin=yNuclearCoordinates.first();
    ymax=yNuclearCoordinates.first();
    for( qint64 i=1 ; i < yNuclearCoordinates.count() ; ++i)
    {
      if( yNuclearCoordinates.at(i) < ymin )
      {
        ymin=yNuclearCoordinates.at(i);
      }
      if( yNuclearCoordinates.at(i) > ymax )
      {
        ymax=yNuclearCoordinates.at(i);
      }
    }

    zmin=zNuclearCoordinates.first();
    zmax=zNuclearCoordinates.first();
    for( qint64 i=1 ; i < zNuclearCoordinates.count() ; ++i)
    {
      if( zNuclearCoordinates.at(i) < zmin )
      {
        zmin=zNuclearCoordinates.at(i);
      }
      if( zNuclearCoordinates.at(i) > zmax )
      {
        zmax=zNuclearCoordinates.at(i);
      }
    }

    xmin= -2.0 + xmin;
    ymin= -2.0 + ymin;
    zmin= -2.0 + zmin;

    xmax = 2.0 + xmax;
    ymax = 2.0 + ymax;
    zmax = 2.0 + zmax;

    xstep=ystep=zstep= 0.5;

    for( qreal x=xmin ; x < xmax+xstep ; x=x+xstep)
    {
      for( qreal y=ymin ; y < ymax+ystep ; y=y+ystep)
      {
        for( qreal z=zmin ; z < zmax+zstep ; z=z+zstep)
        {
          QList<QVariant> input;
          input.append( tempFileName );
//          input.append( n );
          input.append( x );
          input.append( y );
          input.append( z );

          inputList.append(input);
        }
      }
    }

    m_wfn->saveToBinaryFile(tempFileName);

    QProgressDialog dialog;
    dialog.setWindowTitle("QTAIM");
    dialog.setLabelText(QString("Electron Density Sinks Search"));

    QFutureWatcher<void> futureWatcher;
    QObject::connect(&futureWatcher, SIGNAL(finished()), &dialog, SLOT(reset()));
    QObject::connect(&dialog, SIGNAL(canceled()), &futureWatcher, SLOT(cancel()));
    QObject::connect(&futureWatcher, SIGNAL(progressRangeChanged(int,int)), &dialog, SLOT(setRange(int,int)));
    QObject::connect(&futureWatcher, SIGNAL(progressValueChanged(int)), &dialog, SLOT(setValue(int)));

    QFuture<QList<QVariant> > future=QtConcurrent::mapped(inputList, QTAIMLocateElectronDensitySink );
    futureWatcher.setFuture(future);
    dialog.exec();
    futureWatcher.waitForFinished();

    QList<QList<QVariant> > results;
    if( futureWatcher.future().isCanceled() )
    {
      results.clear();
    }
    else
    {
      results=future.results();
    }

    QFile file;
    file.remove(tempFileName);

    for( qint64 n=0 ; n < results.length() ; ++n )
    {

      qint64 counter=0;
      bool correctSignature = results.at(n).at(counter).toBool(); counter++;

      if( correctSignature )
      {
        qreal x=results.at(n).at(counter).toReal(); counter++;
        qreal y=results.at(n).at(counter).toReal(); counter++;
        qreal z=results.at(n).at(counter).toReal(); counter++;

        if( (xmin < x && x < xmax) &&
            (ymin < y && y < ymax) &&
            (zmin < z && z < zmax) )
        {
          QVector3D result(x,y,z);

          qreal smallestDistance=HUGE_REAL_NUMBER;

          for(qint64 i=0 ; i < m_electronDensitySinks.length() ; ++i )
          {

            Matrix<qreal,3,1> a(x,y,z);
            Matrix<qreal,3,1> b(m_electronDensitySinks.at(i).x(),
                                m_electronDensitySinks.at(i).y(),
                                m_electronDensitySinks.at(i).z());

            qreal distance=QTAIMMathUtilities::distance(a,b);

            if( distance < smallestDistance )
            {
              smallestDistance=distance;
            }

          }

          if( smallestDistance > 1.e-2 )
          {
            m_electronDensitySinks.append( result );
          }
        }
      }
    }
//    qDebug() << "SINKS" << m_electronDensitySinks;
  }

  QString QTAIMCriticalPointLocator::temporaryFileName()
  {
    QTemporaryFile temporaryFile;
    temporaryFile.open();
    QString tempFileName=temporaryFile.fileName();
    temporaryFile.close();
    temporaryFile.remove();

    // wait for temporary file to be deleted
    QDir dir;
    do
    {
      // Nothing
    } while ( dir.exists(tempFileName) );

    return tempFileName;
  }

} // namespace QtPlugins
} // namespace Avogadro


/*
@copyright Louis Dionne 2014
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
 */

#include <boost/hana/identity.hpp>

#include <boost/hana/comparable/laws.hpp>
#include <boost/hana/detail/assert.hpp>
#include <boost/hana/functional.hpp>
#include <boost/hana/list/instance.hpp>
#include <boost/hana/logical/logical.hpp>

#include "unique_comparable.hpp"
using namespace boost::hana;

auto implies = infix([](auto p, auto q) {
    return or_(not_(p), q);
});

auto iff = infix([](auto p, auto q) {
    return and_(p ^implies^ q, q ^implies^ p);
});

auto check = [](auto x, auto y) {
    return equal(x, y) ^iff^ equal(identity(x), identity(y));
};

auto x = unique_comparable([]{});
auto y = unique_comparable([]{});
auto z = unique_comparable([]{});

BOOST_HANA_CONSTANT_ASSERT(
    all_of(ap(list(check), list(x,y,z), list(x,y,z)))
);

BOOST_HANA_CONSTANT_ASSERT(Comparable::laws::check(
    list(identity(x), identity(y), identity(z))
));

int main() { (void)check; }


#pragma once

#include <Eigen/Dense>
#include <boost/assert.hpp>
#include "collision_tensor_galerkin.hpp"

namespace boltzmann {

class CollisionTensorGalerkinSparse : public CollisionTensorGalerkin
{
 public:
  CollisionTensorGalerkinSparse(const basis_t& basis, int bufsize)
      : CollisionTensorGalerkin(basis)
      , buffer_(basis.size(), bufsize)
  {
    /* empty */
  }

  template <typename DERIVED1, typename DERIVED2>
  void apply(Eigen::DenseBase<DERIVED1>& out, const Eigen::DenseBase<DERIVED2>& in);

 public:
  mutable Eigen::MatrixXd buffer_;

  EIGEN_MAKE_ALIGNED_OPERATOR_NEW
};

template <typename DERIVED1, typename DERIVED2>
void
CollisionTensorGalerkinSparse::apply(Eigen::DenseBase<DERIVED1>& out,
                                     const Eigen::DenseBase<DERIVED2>& in)
{
  for (unsigned int i = 0; i < slices_.size(); ++i) {
    buffer_ = slices_[i] * in.derived().matrix();
    for (int k = 0; k < in.cols(); ++k) {
      out(i, k) = buffer_.col(k).dot(in.col(k).matrix());
    }
  }
}

}  // namespace boltzmann


/*
Copyright (c) 2013, Kai Klindworth
All rights reserved.

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

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

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

#include "taskanalysis.h"

#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>

#include "disparity_toolkit/stereotask.h"
#include "genericfunctions.h"
#include <boost/math/common_factor.hpp>

task_analysis::task_analysis() : error_hist_left(maxdiff, 0), error_hist_right(maxdiff, 0)
{
}

disparity_map create_diff_disparity(const disparity_map& disparity, const disparity_map& ground_truth)
{
	cv::Mat_<short> scaledDisp, scaledGround;
	int common_sampling = boost::math::lcm(disparity.sampling, ground_truth.sampling);
	if(disparity.sampling != ground_truth.sampling)
	{
		scaledDisp = disparity * (common_sampling / disparity.sampling);
		scaledGround = ground_truth * (common_sampling / ground_truth.sampling);
	}
	else
	{
		scaledDisp = disparity;
		scaledGround = ground_truth;
	}

	cv::Mat error_mat_temp;
	cv::absdiff(scaledDisp, scaledGround, error_mat_temp);

	return disparity_map(error_mat_temp, common_sampling);
}

void task_analysis::create_internal(const single_stereo_task& task, const disparity_map& disparity, cv::Mat_<unsigned char>& error_mat, std::vector<int>& hist, unsigned int ignore_border)
{
	if(task.groundTruth.data)
	{
		disparity_map error_mat_temp = create_diff_disparity(disparity, task.groundTruth);
		error_mat_temp.convertTo(error_mat, CV_8UC1);

		if(task.occ.data)
			foreign_threshold(error_mat, task.occ, (unsigned char)128, true);
		foreign_null(error_mat, task.groundTruth); //doesn't work with signed gt

		const int diff_bound = hist.size() - 1;
		for(unsigned int y = ignore_border; y < error_mat.rows - ignore_border; ++y)
		{
			for(unsigned int x = ignore_border; x < error_mat.cols - ignore_border; ++x)
			{
				unsigned char idx = std::min(diff_bound, error_mat(y,x)/error_mat_temp.sampling);
				if(task.occ.data && task.groundTruth(y,x) != 0)
				{
					if(task.occ(y,x) > 128)
						++(hist[idx]);
				}
				else if(task.groundTruth(y,x) != 0)
					++(hist[idx]);
			}
		}
	}
	else
		std::clog << "no ground truth data" << std::endl;
}

task_analysis::task_analysis(const stereo_task& task, const disparity_map& disparity_left, const disparity_map& disparity_right, int ignore_border) : error_hist_left(maxdiff, 0), error_hist_right(maxdiff, 0)
{
	create_internal(task.forward, disparity_left, this->diff_mat_left, this->error_hist_left, ignore_border);
	create_internal(task.backward, disparity_right, this->diff_mat_right, this->error_hist_right, ignore_border);
}

/*void TaskAnalysis::write(cv::FileNode& node) const
{
	node << "error_hist" << "[:";
	for(int count : error_hist_left)
		node << count;
	node << "]";

	node << "error_hist_right" << "[:";
	for(int count : error_hist_right)
		node << count;
	node << "]";
}*/

cv::FileStorage& operator<<(cv::FileStorage& stream, const task_analysis& analysis)
{
	stream << "error_hist" << "[:";
	for(int count : analysis.error_hist_left)
		stream << count;
	stream << "]";

	stream << "error_hist_right" << "[:";
	for(int count : analysis.error_hist_right)
		stream << count;
	stream << "]";

	return stream;
}


/* boost random/detail/signed_unsigned_tools.hpp header file
 *
 * Copyright Jens Maurer 2006
 * Distributed under the Boost Software License, Version 1.0. (See
 * accompanying file LICENSE_1_0.txt or copy at
 * http://www.boost.org/LICENSE_1_0.txt)
 *
 * See http://www.boost.org for most recent version including documentation.
 */

#ifndef BOOST_RANDOM_DETAIL_SIGNED_UNSIGNED_TOOLS
#define BOOST_RANDOM_DETAIL_SIGNED_UNSIGNED_TOOLS

#include <boost/limits.hpp>
#include <boost/config.hpp>
#include <boost/random/traits.hpp>

namespace boost {
namespace random {
namespace detail {

/*
 * Compute x - y, we know that x >= y, return an unsigned value.
 */

template<class T, bool sgn = std::numeric_limits<T>::is_signed && std::numeric_limits<T>::is_bounded>
struct subtract { };

template<class T>
struct subtract<T, /* signed */ false>
{
  typedef T result_type;
  result_type operator()(T x, T y) { return x - y; }
};

template<class T>
struct subtract<T, /* signed */ true>
{
  typedef typename boost::random::traits::make_unsigned_or_unbounded<T>::type result_type;
  result_type operator()(T x, T y)
  {
    if (y >= 0)   // because x >= y, it follows that x >= 0, too
      return result_type(x) - result_type(y);
    if (x >= 0)   // y < 0
      // avoid the nasty two's complement case for y == min()
      return result_type(x) + result_type(-(y+1)) + 1;
    // both x and y are negative: no signed overflow
    return result_type(x - y);
  }
};

/*
 * Compute x + y, x is unsigned, result fits in type of "y".
 */

template<class T1, class T2, bool sgn = (std::numeric_limits<T2>::is_signed && (std::numeric_limits<T1>::digits >= std::numeric_limits<T2>::digits))>
struct add { };

template<class T1, class T2>
struct add<T1, T2, /* signed or else T2 has more digits than T1 so the cast always works - needed when T2 is a multiprecision type and T1 is a native integer */ false>
{
  typedef T2 result_type;
  result_type operator()(T1 x, T2 y) { return T2(x) + y; }
};

template<class T1, class T2>
struct add<T1, T2, /* signed */ true>
{
  typedef T2 result_type;
  result_type operator()(T1 x, T2 y)
  {
    if (y >= 0)
      return T2(x) + y;
    // y < 0
    if (x > T1(-(y+1)))  // result >= 0 after subtraction
      // avoid the nasty two's complement edge case for y == min()
      return T2(x - T1(-(y+1)) - 1);
    // abs(x) < abs(y), thus T2 able to represent x
    return T2(x) + y;
  }
};

} // namespace detail
} // namespace random
} // namespace boost

#endif // BOOST_RANDOM_DETAIL_SIGNED_UNSIGNED_TOOLS


#include <boost/test/unit_test.hpp>

#include <qtm-calc/data.hpp>

/**
 * qtm::qtm_data tests
 */
BOOST_AUTO_TEST_SUITE(test_suite_data)

/**
 * qtm::qtm_data calcs tests
 */
BOOST_AUTO_TEST_SUITE(test_suite_data_calc)

/**
 * qtm::qtm_data::calc_avg_queue() test
 */
BOOST_AUTO_TEST_CASE(check_avg_queue) {
  auto comp = 0.2857142857142857;

  qtm::qtm qtm(2, 1, 10., 5.);
  qtm.calc_final_states();
  auto ret = qtm::qtm_data::calc_avg_queue(qtm);

  BOOST_TEST(comp == ret);
};

/**
 * qtm::qtm_data::check_ete() test
 */
BOOST_AUTO_TEST_CASE(check_ete) {
  auto comp = 0.71428571428571419;

  qtm::qtm qtm(2, 1, 10., 5.);
  qtm.calc_final_states();
  auto ret = qtm::qtm_data::calc_ete(qtm);

  BOOST_TEST(comp == ret);
};

/**
 * qtm::qtm_data::check_avg_time_queue() test
 */
BOOST_AUTO_TEST_CASE(check_avg_time_queue) {
  auto comp = 0.71428571428571419;

  qtm::qtm qtm(2, 1, 10., 5.);
  qtm.calc_final_states();
  auto ret = qtm::qtm_data::calc_avg_time_queue(qtm);

  BOOST_TEST(comp == ret);
};

/**
 * qtm::qtm_data::calc_perc_served_req() test
 */
BOOST_AUTO_TEST_CASE(check_perc_served_req) {
  auto comp = 0.7142857142857143;

  qtm::qtm qtm(2, 1, 10., 5.);
  qtm.calc_final_states();
  auto ret = qtm::qtm_data::calc_perc_served_req(qtm);

  BOOST_TEST(comp == ret);
};

/**
 * qtm::qtm_data::check_avg_count_served_req() test
 */
BOOST_AUTO_TEST_CASE(check_avg_count_served_req) {
  auto comp = 1.7142857142857142;

  qtm::qtm qtm(2, 1, 10., 5.);
  qtm.calc_final_states();
  auto ret = qtm::qtm_data::calc_avg_count_served_req(qtm);

  BOOST_TEST(comp == ret);
};

/**
 * qtm::qtm_data::check_avg_count_req() test
 */
BOOST_AUTO_TEST_CASE(check_avg_count_req) {
  auto comp = 1.7142857142857142;

  qtm::qtm qtm(2, 1, 10., 5.);
  qtm.calc_final_states();
  auto ret = qtm::qtm_data::calc_avg_count_req(qtm);

  BOOST_TEST(comp == ret);
};

/**
 * qtm::qtm_data::check_avg_count_unserved_req() test
 */
BOOST_AUTO_TEST_CASE(check_avg_count_unserved_req) {
  auto comp = 1.5760463582836595;

  qtm::qtm qtm(4, 20, 37.75, 12);
  qtm.calc_final_states();
  auto ret = qtm::qtm_data::calc_avg_count_unserved_req(qtm);

  BOOST_TEST(comp == ret);
};

BOOST_AUTO_TEST_SUITE_END() // test_suite_data_calc

BOOST_AUTO_TEST_SUITE_END() // test_suite_data


/* Copyright (C) 2013 Ion Torrent Systems, Inc. All Rights Reserved */

#include <iostream>
#include <vector>
#include <string>
#include <stdio.h>
#include "Utils.h"
#include "Image.h"
#include "Mask.h"
#include "PcaSpline.h"
#include "SampleQuantiles.h"
#include "OptArgs.h"
#include "ComparatorNoiseCorrector.h"
//#define EIGEN_USE_MKL_ALL 1
#include <malloc.h>
#include <Eigen/Dense>
#include <Eigen/LU>
#define likely(x)       __builtin_expect((x),1)
#define unlikely(x)     __builtin_expect((x),0)
using namespace Eigen;
using namespace std;

namespace pca_spline_driver {

  void CalcDcOffset(RawImage *raw, Mask &mask, int * __restrict good, int &good_wells, short *dc_offset) {
    //Calculate dc offset for each well. Currently hardcoded at 4
    int n_wells = raw->cols * raw->rows;
    short * __restrict s_zeros_start_1 = raw->image;
    short * __restrict s_zeros_start_2 = raw->image + n_wells;
    short * __restrict s_zeros_start_3 = s_zeros_start_2 + n_wells;
    short * __restrict s_zeros_start_4 = s_zeros_start_3 + n_wells;
    short * __restrict p_dc_start = dc_offset;
    short * __restrict p_dc_end = dc_offset + n_wells;
    int mask_skip = MaskPinned | MaskIgnore;
    const unsigned short *__restrict masked = mask.GetMask();
    good_wells = 0;
    while((p_dc_start != p_dc_end)) {
      if (likely((*masked++ & mask_skip) == 0)) {
	good_wells++;
	*good++ = 1;
      }
      else {
	*good++ = 0;
      }
      *p_dc_start++ = (*s_zeros_start_1++ + *s_zeros_start_2++ + *s_zeros_start_3++ + *s_zeros_start_4++) >> 2;
    }
  }

  void CalcColumnAverage(int n_wells, short * __restrict s_start, short * __restrict s_end,
			 int * __restrict good, short * __restrict dc_offset, short & avg) {
    int64_t sum = 0;
    while ((s_start != s_end)) {
      short x = *s_start - *dc_offset++;
      *s_start++ = x;
      sum += x;
    }
    avg = (short)((float)sum / n_wells + .5);
  }

  void CalcFrameAvg(RawImage *raw, Mask &mask, int * __restrict good,
		    short *__restrict dc_offset, short *__restrict frame_avg) {
    int64_t frame_sum_int[raw->frames];
    int n_wells = raw->cols * raw->rows;
    memset(frame_sum_int, 0, sizeof(frame_sum_int));
    memset(frame_avg, 0, sizeof(short) * raw->frames);
    for (int i = 0; i < raw->frames; i++) {
      short *__restrict s_start = raw->image + i * n_wells;
      short *__restrict s_end = s_start + n_wells;
      CalcColumnAverage(n_wells, s_start, s_end, good, dc_offset, frame_avg[i]);
    }
  }

  void LoadMatrixColumn(const short *__restrict s_start, const short *__restrict s_end,
			float *__restrict mem, float *__restrict mem_sub,
			int sample_rate, short favg, int n_sub_rows,
			const int * __restrict good) {
    int good_count = sample_rate - 1;
    while (likely(s_start != s_end)) {
      float x = float(*s_start++ - favg);
      *mem++ = x;
      if (likely(*good++) && unlikely(++good_count == sample_rate)) {
	  *mem_sub++ = x;
	  n_sub_rows--;
	  good_count = 0;
      }
    }
    assert(n_sub_rows == 0);
  }

  void LoadMatrices(RawImage *raw, const short *frame_avg, Mask &mask, int sample_rate, int * __restrict good, MatrixXf &Y, MatrixXf &Ysub) {
    float * __restrict mem = Y.data();
    float * __restrict mem_sub = Ysub.data();
    int n_wells = raw->rows * raw->cols;
    for (int i = 0; i < raw->frames; i++) {
      short * __restrict s_start = raw->image + i * n_wells;
      short * __restrict s_end = s_start + n_wells;
      short favg = frame_avg[i];
      LoadMatrixColumn(s_start, s_end, mem, mem_sub, sample_rate, favg, Ysub.rows(), good);
      mem += n_wells;
      mem_sub += Ysub.rows();
    }
  }
}

using namespace pca_spline_driver;

void help_abort() {
  cout << "PcaSplineDriver - Try compressing data using our modules." << endl;
  cout << "options: " << endl;
  cout << "  -d,--dat-file   dat file to try and compress." << endl;
  cout << "  -k,--knots      knot strategy description." << endl;
  cout << "     --num-pca    number of pca basis vectors to use." << endl;
  cout << "     --do-stats   should we calculate summary stats." << endl;
  cout << "     --mask-file  mask file. " << endl;
  exit(1);
}

int main(int argc, const char *argv[]) {
  OptArgs opts;
  opts.ParseCmdLine(argc, argv);
  string dat_file;
  //  bool help = false;
  string strategy = "no-knots";
  int num_pca_vectors = 5;
  int do_stats = 0;
  string mask_file;
  double tik_k;
  opts.GetOption(dat_file, "", 'd', "dat-file");
  opts.GetOption(strategy, "no-knots", 'k', "knots");
  opts.GetOption(num_pca_vectors, "5", '-', "num-pca");
  opts.GetOption(do_stats, "0", '-', "do-stats");
  opts.GetOption(mask_file, "", '-', "mask-file");
  opts.GetOption(tik_k, "0", '-', "tik_k");
  Eigen::initParallel();
  if (mask_file.empty() || dat_file.empty()) {
    help_abort();
  }
  //  Eigen::setNbThreads(1);
  Mask mask(mask_file.c_str());
  Image img;
  img.SetImgLoadImmediate (false);
  bool loaded = img.LoadRaw (dat_file.c_str());
  assert(loaded);
  RawImage *raw = img.raw;
  ComparatorNoiseCorrector cnc;
  cnc.CorrectComparatorNoise(raw, &mask, false, true);

  size_t n_wells = raw->cols * raw->rows;
  short dc_offset[n_wells];
  short frame_avg[raw->frames];
  memset(frame_avg, 0, raw->frames * sizeof(short));
  int good_wells = 0;

  ClockTimer timer;
  int *good = (int *)memalign(32, sizeof(int) *n_wells);
  memset(good, 0, n_wells * sizeof(int));
  CalcDcOffset(raw, mask, good, good_wells, dc_offset);
  timer.PrintMicroSeconds(cout, "To DC offset took: ");
  CalcFrameAvg(raw, mask, good, dc_offset, frame_avg);
  //  memset(frame_avg, 0, raw->frames * sizeof(short));
  timer.PrintMicroSeconds(cout, "To calc frame avg took: ");
  Matrix<float, Dynamic, Dynamic, AutoAlign | ColMajor> Y(n_wells, raw->frames);
  Matrix<float, Dynamic, Dynamic, AutoAlign | ColMajor> Yh(n_wells, raw->frames);
  int target_sample = 500;
  int sample_rate = max(1,good_wells/target_sample);
  int n_sample_rows = (int)(ceil((float)good_wells/sample_rate));
  Matrix<float, Dynamic, Dynamic, AutoAlign | ColMajor> Ysub(n_sample_rows, raw->frames);
  Y.fill(0);
  Ysub.fill(0);
  LoadMatrices(raw, frame_avg, mask, sample_rate, good, Y, Ysub);
  timer.PrintMicroSeconds(cout, "To Load Matrices took: ");

  PcaSpline compressor(num_pca_vectors, strategy);
  compressor.SetTikhonov(tik_k);
  PcaSplineRegion compressed;
  compressed.n_wells = raw->rows * raw->cols;
  compressed.n_frames = raw->frames;
  compressed.n_basis = compressor.NumBasisVectors(num_pca_vectors, compressed.n_frames,
						  compressor.GetOrder(), strategy);
  cout << "Num basis: " << compressed.n_basis << endl;
  compressed.basis_vectors = (float *)memalign(32, sizeof(float) * compressed.n_frames * compressed.n_basis);
  //  memset(compressed.basis_vectors, 0, sizeof(float) * compressed.n_frames * compressed.n_basis);
  compressed.coefficients = (float *)memalign(32, sizeof(float) * compressed.n_wells * compressed.n_basis);
  //  memset(compressed.coefficients, 0, sizeof(float) * compressed.n_wells * compressed.n_basis);
  float *ysub2_mem;
  int num_sample2;
  compressor.SampleImageMatrix(n_wells, raw->frames, Y.data(), good, target_sample, num_sample2, &ysub2_mem);
  timer.PrintMicroSeconds(cout, "To compressor took: ");
  ClockTimer compress_timer;
   compressor.LossyCompress(raw->rows * raw->cols, raw->frames, Y.data(), num_sample2, ysub2_mem, compressed);
  compress_timer.PrintMicroSeconds(cout, "Compressing traces took: ");
   //  compressor.LossyCompress(raw->rows * raw->cols, raw->frames, Y.data(), Ysub.rows(), Ysub.data(), compressed);
  timer.PrintMicroSeconds(cout, "Full load and compression: ");
  compressor.LossyUncompress(raw->rows * raw->cols, raw->frames, Yh.data(), compressed);
  if(do_stats > 0) {
    ofstream o("error.txt");
    vector<SampleQuantiles<float> > frame_quantiles(raw->frames);
    for (size_t i = 0; i < frame_quantiles.size(); i++) {
      frame_quantiles[i].Init(10000);
    }
    SampleQuantiles<float> total_mad(10000);
    for (int i = 0; i < Y.cols(); i++) {
      SampleQuantiles<float> &frame = frame_quantiles[i];
      float *start = Yh.data() + n_wells * i;
      float *end = start + n_wells;
      short *i_start = raw->image + n_wells * i;
      short favg = frame_avg[i];
      while (start != end) {
        float value = *start + favg - *i_start;
        frame.AddValue(fabs(value));
        total_mad.AddValue(fabs(value));
        start++;
        if (start != end) {
          o << value << "\t";
        }
        else {
          o << value;
        }
        i_start++;
      }
      o << endl;
    }
    cout << "Total error: " << total_mad.GetMedian() << " +/- " << total_mad.GetIqrSd() << endl;
    SampleStats<double> frame_mean_err;
    for (size_t i = 0; i < frame_quantiles.size(); i++) {
      frame_mean_err.AddValue(frame_quantiles[i].GetMedian());
    }
    cout << "Mean of median frame errors: " << frame_mean_err.GetMean()  << " +/- " << frame_mean_err.GetSD() << endl;
    //   cout << "Frame mean error: " << total_mad.GetMedian() << " +/- " << total_mad.GetIqrSd() << endl;
    if (do_stats > 1) {
      cout << "Frames: " <<endl;
      for (size_t i = 0; i < frame_quantiles.size(); i++) {
        cout << i << "\t" << frame_quantiles[i].GetMedian() << endl;
      }
    }
}
return 0;
}


#include "Format.hxx"
#include "Zeros.hxx"
#include "../sdp_read.hxx"
#include "../sdp_convert.hxx"
#include "../sdp_solve.hxx"
#include "../read_vector.hxx"

#include <boost/filesystem.hpp>

void handle_arguments(const int &argc, char **argv, El::BigFloat &threshold,
                      El::BigFloat &mesh_threshold, Format &format,
                      boost::filesystem::path &input_path,
                      boost::filesystem::path &solution_path,
                      boost::filesystem::path &output_path, bool &need_lambda);

std::vector<El::Matrix<El::BigFloat>>
read_x(const boost::filesystem::path &solution_path,
       const std::vector<Polynomial_Vector_Matrix> &matrices);

std::vector<El::Matrix<El::BigFloat>>
read_x(const boost::filesystem::path &solution_path,
       const std::vector<Positive_Matrix_With_Prefactor> &matrices);

El::Matrix<El::BigFloat>
read_y(const boost::filesystem::path &solution_path, const size_t &y_height);

void write_spectrum(const boost::filesystem::path &output_path,
                    const size_t &num_blocks,
                    const std::vector<Zeros> &zeros_blocks);

std::vector<Zeros> compute_spectrum_pmp(
  const std::vector<El::BigFloat> &normalization,
  const El::Matrix<El::BigFloat> &y,
  const std::vector<Positive_Matrix_With_Prefactor> &matrices,
  const std::vector<El::Matrix<El::BigFloat>> &x,
  const El::BigFloat &threshold, El::BigFloat &mesh_threshold,
  const bool &need_lambda);

std::vector<Zeros>
compute_spectrum_pvm(const El::Matrix<El::BigFloat> &y,
                     const std::vector<Polynomial_Vector_Matrix> &matrices,
                     const std::vector<El::Matrix<El::BigFloat>> &x,
                     const El::BigFloat &threshold,
                     El::BigFloat &mesh_threshold, const bool &need_lambda);

int main(int argc, char **argv)
{
  El::Environment env(argc, argv);

  try
    {
      El::BigFloat threshold, mesh_threshold;
      Format format;
      boost::filesystem::path input_path, solution_dir, output_path;
      bool need_lambda;
      handle_arguments(argc, argv, threshold, mesh_threshold, format,
                       input_path, solution_dir, output_path, need_lambda);

      switch(format)
        {
          case Format::Polynomial_Vector_Matrix: {
            std::vector<El::BigFloat> objectives;
            std::vector<Polynomial_Vector_Matrix> matrices;
            size_t num_blocks(0);
            read_pvm_input({input_path}, objectives, matrices, num_blocks);
            El::Matrix<El::BigFloat> y(objectives.size() - 1, 1);
            read_text_block(y, solution_dir / "y.txt");
            std::vector<El::Matrix<El::BigFloat>> x(
              read_x(solution_dir, matrices));
            const std::vector<Zeros> zeros_blocks(compute_spectrum_pvm(
              y, matrices, x, threshold, mesh_threshold, need_lambda));
            write_spectrum(output_path, num_blocks, zeros_blocks);
          }
          break;
          case Format::Positive_Matrix_with_Prefactor: {
            std::vector<El::BigFloat> objectives, normalization;
            std::vector<Positive_Matrix_With_Prefactor> matrices;
            size_t num_blocks;
            read_input(input_path, objectives, normalization, matrices,
                       num_blocks);
            El::Matrix<El::BigFloat> y(objectives.size() - 1, 1);
            read_text_block(y, solution_dir / "y.txt");
            std::vector<El::Matrix<El::BigFloat>> x(
              read_x(solution_dir, matrices));
            const std::vector<Zeros> zeros_blocks(
              compute_spectrum_pmp(normalization, y, matrices, x, threshold,
                                   mesh_threshold, need_lambda));
            write_spectrum(output_path, num_blocks, zeros_blocks);
          }
          break;
        default: throw std::runtime_error("INTERNAL ERROR");
        }
    }
  catch(std::exception &e)
    {
      std::cerr << "Error: " << e.what() << "\n" << std::flush;
      El::mpi::Abort(El::mpi::COMM_WORLD, 1);
    }
  catch(...)
    {
      std::cerr << "Unknown Error\n" << std::flush;
      El::mpi::Abort(El::mpi::COMM_WORLD, 1);
    }
}


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

    @copyright 2016 NumScale SAS

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

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{

  /*!
    @ingroup group-exponential
    This function object returns the rounding to n significants digits of its argument.

    @par Header <boost/simd/function/signiicants.hpp>

    @par Example:

      @snippet significants.cpp significants

    @par Possible output:

      @snippet significants.txt significants

  **/
  IEEEValue significants(IEEEValue const& x, IntegerValue const& n);
} }
#endif

#include <boost/simd/function/scalar/significants.hpp>
#include <boost/simd/function/simd/significants.hpp>

#endif


#include <iostream>
#include <math.h>

#include <Eigen/Dense>

#include "color.h"
#include "renderer.h"
#include "part.h"

void BWCNC::Renderer::render_all( const BWCNC::PartContext & k, const std::string * /* filename */ )
{
    print_start( k.bbox );

    // c++11 forall construct. p is an element of partlist, a pointer
    for( const auto & part_ptr : k.partlist )
        part_ptr->render( this );

    print_end();
}

void BWCNC::Renderer::render_all_z_order( BWCNC::PartContext & k )
{
    k.refresh_z_ascending();

    print_start( k.bbox );

    for( const auto & part_ptr : k.partlist_z_ascending )
        part_ptr.second->render( this );

    print_end();
}

#if 0
protected:

    std::ostream & strm;
    Eigen::Vector3d offset;
    BWCNC::Bouningbox bbox;
    const char * eol = "\r\n";
    double scalar = 1;
};
#endif

#if 0
void BWCNC::SVG::arcto( const BWCNC::Command * cmd )
{
    if( ! bool(lineto_color) ) return;
    drawarc( cmd, lineto_color );
}

void BWCNC::SVG::drawarc( const BWCNC::Command & cmd )
{
    td::vector<NumString> begp = VectorToNumStringArray( (cmd.begin + offset) * scalar );
    td::vector<NumString> endp = VectorToNumStringArray( (cmd.end   + offset) * scalar );
    double rdus = radiu * scalar;

    printf("<path d=\"M%s %s A%s,%s 0 %d,%d %s,%s\" style=\"stroke:#%06x;stroke-width:%d;fill:none\" />\n",
            begp[0].str().c_str(), begp[1].str().c_str(),
            rdus, rdus,
            (cmd.largearc ? 1 : 0), (cmd.sweep ? 1 : 0),
            endp[0].str().c_str(), endp[1].str().c_str(),
            clr.to_rgb24(), stroke_width );
}
#endif

void BWCNC::SVG::draw_dot( const BWCNC::Command * cmd )
{
    BWCNC::Color c = cmd->clr;
    if( ! bool(cmd->clr) )
    {
        if( ! bool(dot_color) )
            return;
        c = dot_color;
    }
    //if( ! bool(lineto_color) ) return;

    std::vector<BWCNC::NumString> begp = VectorToNumStringArray( Eigen::Vector3d((cmd->begin + offset) * scalar) );
    std::vector<BWCNC::NumString> endp = VectorToNumStringArray( Eigen::Vector3d((cmd->end   + offset) * scalar) );

    printf( "<circle cx=\"%s\" cy=\"%s\" r=\"1\" fill=\"#%06x\" />\n",
            begp[0].str().c_str(), begp[1].str().c_str(), c.to_rgb24() );
}

void BWCNC::SVG::drawpart( const BWCNC::Part    * /* prt */ )
{
}

void BWCNC::SVG::drawline( const BWCNC::Command * cmd )
{
    BWCNC::Color c = cmd->clr;
    if( ! bool(cmd->clr) )
    {
        if( ! bool(lineto_color) )
            return;
        c = lineto_color;
    }
    //if( ! bool(lineto_color) ) return;

    std::vector<BWCNC::NumString> begp = VectorToNumStringArray( Eigen::Vector3d((cmd->begin + offset) * scalar) );
    std::vector<BWCNC::NumString> endp = VectorToNumStringArray( Eigen::Vector3d((cmd->end   + offset) * scalar) );

    printf( "<line x1=\"%s\" y1=\"%s\" x2=\"%s\" y2=\"%s\" style=\"stroke:#%06x;stroke-width:%d\" />\n",
            begp[0].str().c_str(), begp[1].str().c_str(),
            endp[0].str().c_str(), endp[1].str().c_str(),
            c.to_rgb24(), stroke_width );
}

void BWCNC::SVG::print_end() { printf( "</svg>\n" ); }

void BWCNC::SVG::print_start( const BWCNC::Boundingbox & bounds )
{
    if( ! bounds ) return;

    // svg is 2 dimensional
    long w = ::ceill(bounds.max[0]) + 1;
    long h = ::ceill(bounds.max[1]) + 1;

    printf( "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" );
    printf( "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns=\"http://www.w3.org/2000/svg\" height=\"%ld\" width=\"%ld\">\n", h, w );
    if( bool(backgd_color) )
        printf( "<rect height=\"100%%\" width=\"100%%\" style=\"fill:#%06x;\" />\n", backgd_color.to_rgb24() );
    else
        printf( "<rect height=\"100%%\" width=\"100%%\" />\n" );
}

#if 0
class GCode : public Renderer
{
public:
    GCode(){}
    virtual ~GCode(){}

    void lineto();
    void moveto();

protected:
    void linear_motion();
    void print_start();
    void print_end();
    void print_cmd();
    void cutting_off();
};
#endif


#include <SDL.h>

#include <fanFilm.h>
#include <fanVector.h>
#include <fanPixel.h>
#include <fanCamera.h>
#include <fanTriangleMesh.h>
#include <fanBufferObject.h>
#include <texture/film/SDLFilm.h>
#include <fanLensMovement.h>
#include <lens/OrthogonalLens.h>
#include <lens/PerspectiveLens.h>
#include <camera/PointScannerCamera.h>
#include <camera/WireframeCamera.h>
#include <camera/DepthCamera.h>
#include <objects/TriangleMeshObject.h>

#include <boost/shared_ptr.hpp>

#include <cstdlib>

#include <iostream>

using namespace fan;
using namespace std;

void addTriangle( fanTriangleMesh& cube, size_t startIndex, int& facesNum ) {
    for( size_t i = 0; i < 4; ++i ) {
        for( size_t j = 0; j < 4; ++j ) {
            /*
             * p2--p3
             *  |  |
             * p0--p1
             */
            size_t p0Index = startIndex + i * 5 + j;
            size_t p1Index = startIndex + i * 5 + j + 1;
            size_t p2Index = p0Index + 5;
            size_t p3Index = p1Index + 5;
            // every face has two triangle
            fanTriangle t1( &cube.mVertices->mBuffer[p0Index],
                            &cube.mVertices->mBuffer[p2Index],
                            &cube.mVertices->mBuffer[p1Index],
                            p0Index, p2Index, p1Index );
            fanTriangle t2( &cube.mVertices->mBuffer[p1Index],
                            &cube.mVertices->mBuffer[p2Index],
                            &cube.mVertices->mBuffer[p3Index],
                            p1Index, p2Index, p3Index );
            cube.mFaces->mBuffer[facesNum] = t1;
            ++facesNum;
            cube.mFaces->mBuffer[facesNum] = t2;
            ++facesNum;
        }
    }
}

int main() {
    if ( SDL_Init( SDL_INIT_VIDEO ) != 0 ) {
        return 1;
    }
    fanMatrix<float, 4, 4> cubePos { 1, 0, 0, 0,
                                     0, 1, 0, 0,
                                     0, 0, 1, 0,
                                     0, 0, 0, 1 };
    boost::shared_ptr<fanBufferObject<fanVector3<float> > >
        vertices( new fanBufferObject<fanVector3<float> >( 6*5*5 ) );
    boost::shared_ptr<fanBufferObject<fanTriangle> >
        faces( new fanBufferObject<fanTriangle>( 4*4*2*6 ) );

    boost::shared_ptr<fanTriangleMesh> cube( new fanTriangleMesh( ) );
    cube->mVertices = vertices;
    cube->mFaces = faces;

    boost::shared_ptr<TriangleMeshObject>
        cubeObject( new TriangleMeshObject( cubePos ) );
    cubeObject->mMeshes.push_back( cube );

    fanScene scene;

    int verticesNum = 0;
    for ( size_t i = 0; i <= 200; i += 50 ) {
        for ( size_t j = 0; j <= 200; j += 50 ) {
            cube->mVertices->mBuffer[verticesNum] = fanVector3<float>( i, j, 0 );
            verticesNum++;
        }
    }
    for ( size_t i = 0; i <= 200; i += 50 ) {
        for ( size_t j = 0; j <= 200; j += 50 ) {
            cube->mVertices->mBuffer[verticesNum] = fanVector3<float>( i, 0, j );
            verticesNum++;
        }
    }
    for ( size_t i = 0; i <= 200; i += 50 ) {
        for ( size_t j = 0; j <= 200; j += 50 ) {
            cube->mVertices->mBuffer[verticesNum] = fanVector3<float>( 0, i, j );
            verticesNum++;
        }
    }
    for ( size_t i = 0; i <= 200; i += 50 ) {
        for ( size_t j = 0; j <= 200; j += 50 ) {
            cube->mVertices->mBuffer[verticesNum] = fanVector3<float>( i, j, 200 );
            verticesNum++;
        }
    }
    for ( size_t i = 0; i <= 200; i += 50 ) {
        for ( size_t j = 0; j <= 200; j += 50 ) {
            cube->mVertices->mBuffer[verticesNum] = fanVector3<float>( i, 200, j );
            verticesNum++;
        }
    }
    for ( size_t i = 0; i <= 200; i += 50 ) {
        for ( size_t j = 0; j <= 200; j += 50 ) {
            cube->mVertices->mBuffer[verticesNum] = fanVector3<float>( 200, i, j );
            verticesNum++;
        }
    }
    int facesNum = 0;
    addTriangle( *cube, 0, facesNum );
    addTriangle( *cube, 25, facesNum );
    addTriangle( *cube, 50, facesNum );
    addTriangle( *cube, 75, facesNum );
    addTriangle( *cube, 100, facesNum );
    addTriangle( *cube, 125, facesNum );

    scene.mTriangleMeshObjects.push_back( cubeObject );

    fanVector<int, 2> size;
    size[0] = 800; size[1] = 600;
    SDLFilm sdl( size );

    OrthogonalLens OrthoLens( fanVector3<float>(400, 0, 0),
                         fanVector3<float>(0, 0, 0),
                         fanVector3<float>(0, 0, 1),
                         fanVector3<float>(800, 600, 800) );
    PerspectiveLens PerspLens( fanVector3<float>(400, 0, 0),
                          fanVector3<float>(0, 0, 0),
                          fanVector3<float>(0, 0, 1),
                          fanVector3<float>(800, 600, 800),
                          3000 );

    fanCamera* currentCamera = NULL;
    fanLens* currentLens = NULL;
    PointScannerCamera pointCamera;
    WireframeCamera wireframeCamera;
    DepthCamera depthCamera;

    bool done = false;

    currentCamera = &pointCamera;
    currentLens = &PerspLens;
    Uint32 time;
    while ( !done ) {
        time = SDL_GetTicks();
        currentCamera->takePicture( scene, sdl, *currentLens );
        std::cout << SDL_GetTicks() - time << " ms in takePicture" << std::endl;
        sdl.develope();

        SDL_Event event;
        if (SDL_WaitEvent(&event)) {
            if ( event.type == SDL_QUIT ) {
                done = true;
            }
            switch( event.type ) {
                case SDL_QUIT:
                    done = true;
                break;
                case SDL_KEYDOWN:
                {
                    fanVector<float, 3> move;
                    switch( event.key.keysym.sym ) {
                        case SDLK_w: Pitch(M_PI/360.0f, OrthoLens); Pitch(M_PI/360.0f, PerspLens); break;
                        case SDLK_s: Pitch(-M_PI/360.0f, OrthoLens); Pitch(-M_PI/360.0f, PerspLens); break;
                        case SDLK_a: Yaw(M_PI/360.0f, OrthoLens); Yaw(M_PI/360.0f, PerspLens); break;
                        case SDLK_d: Yaw(-M_PI/360.0f, OrthoLens); Yaw(-M_PI/360.0f, PerspLens); break;
                        case SDLK_q: Roll(M_PI/360.0f, OrthoLens); Roll(M_PI/360.0f, PerspLens); break;
                        case SDLK_e: Roll(-M_PI/360.0f, OrthoLens); Roll(-M_PI/360.0f, PerspLens); break;
                        case SDLK_u: PitchAroundFocus(M_PI/360.0f, OrthoLens); PitchAroundFocus(M_PI/360.0f, PerspLens); break;
                        case SDLK_j: PitchAroundFocus(-M_PI/360.0f, OrthoLens); PitchAroundFocus(-M_PI/360.0f, PerspLens); break;
                        case SDLK_h: YawAroundFocus(M_PI/360.0f, OrthoLens); YawAroundFocus(M_PI/360.0f, PerspLens); break;
                        case SDLK_k: YawAroundFocus(-M_PI/360.0f, OrthoLens); YawAroundFocus(-M_PI/360.0f, PerspLens); break;
                        case SDLK_y: RollAroundFocus(M_PI/360.0f, OrthoLens); RollAroundFocus(M_PI/360.0f, PerspLens); break;
                        case SDLK_i: RollAroundFocus(-M_PI/360.0f, OrthoLens); RollAroundFocus(-M_PI/360.0f, PerspLens); break;
                        case SDLK_ESCAPE: done = true; break;
                        case SDLK_1: currentCamera = &pointCamera; break;
                        case SDLK_2: currentCamera = &wireframeCamera; break;
                        case SDLK_3: currentCamera = &depthCamera; break;
                        case SDLK_o: currentLens = &OrthoLens; break;
                        case SDLK_p: currentLens = &PerspLens; break;
                    }
                }
                break;
            }
        }
    }

    atexit(SDL_Quit);
}