#ifndef SKYLARK_HILBERT_OPTIONS_HPP
#define SKYLARK_HILBERT_OPTIONS_HPP

#ifndef SKYLARK_AVOID_BOOST_PO

#include <boost/program_options.hpp>
namespace po = boost::program_options;

#else

#include <boost/lexical_cast.hpp>

#endif

#define DEFAULT_LAMBDA 0.0
#define DEFAULT_RHO 1.0
#define DEFAULT_THREADS 1
#define DEFAULT_FEATURE_PARTITIONS 1
#define DEFAULT_KERPARAM 1.0
#define DEFAULT_TOL 0.001
#define DEFAULT_MAXITER 100
#define DEFAULT_SEED 12345
#define DEFAULT_RF 100
#define DEFAULT_KERNEL 0
#define DEFAULT_FILEFORMAT 0

enum LossType {SQUARED = 0, LAD = 1, HINGE = 2, LOGISTIC = 3};
std::string Losses[] = {"Squared Loss",
                        "Least Absolute Deviations",
                        "Hinge Loss (SVMs)",
                        "Logistic Loss"};

enum RegularizerType {L2 = 0 , L1 = 1};
std::string Regularizers[] = {"L2", "L1"};

enum ProblemType {REGRESSION = 0, CLASSIFICATION = 1};
std::string Problems[] = {"Regression", "Classification"};

enum SequenceType { MONTECARLO = 0, LEAPED_HALTON = 1};
std::string Sequences[] = {"Monte Carlo", "Leaped Halton"};

enum KernelType {LINEAR = 0, GAUSSIAN = 1, POLYNOMIAL = 2,
                 LAPLACIAN = 3, EXPSEMIGROUP = 4, MATERN = 5};
std::string Kernels[] = {"Linear", "Gaussian",
                         "Polynomial", "Laplacian", "ExpSemigroup", "Matern"};

enum FileFormatType {LIBSVM_DENSE = 0, LIBSVM_SPARSE = 1, HDF5_DENSE = 2, HDF5_SPARSE = 3};
std::string FileFormats[] = {"libsvm-dense", "libsvm-sparse", "hdf5_dense", "hdf5_sparse"};

/**
 * A structure that is used to pass options to the ADMM solver. This structure
 * has default values embedded. No accessor functions are being written.
 */
struct hilbert_options_t {
    /** Solver type options */

    LossType lossfunction;
    RegularizerType regularizer;
    KernelType kernel;

    /** Kernel parameters */
    double kernelparam;
    double kernelparam2;
    double kernelparam3;

    double lambda;

    /** Optimization options */;
    int MAXITER;
    double tolerance;
    double rho;

    /** Randomization options */
    int seed;
    int randomfeatures;
    bool regularmap;
    SequenceType seqtype;
    bool cachetransforms;

    /* parallelization options */
    int numfeaturepartitions;
    int numthreads;
    int nummpiprocesses;

    int fileformat;

    /**  IO */
    std::string trainfile;
    std::string modelfile;
    std::string testfile;
    std::string valfile;
    std::string str = "";

    /** A parameter indicating if we need to continue or not */
    bool exit_on_return;

    /**
     * The constructor takes in all the command line parameters and parses them.
     */
    hilbert_options_t (int argc, char** argv, int nproc) :
        nummpiprocesses(nproc), exit_on_return(false) {

#ifndef SKYLARK_AVOID_BOOST_PO

        po::options_description desc
            ("Usage: skylark_ml [options] --trainfile trainfile --modelfile modelfile\nUsage: skylark_ml --modelfile modelfile --testfile testfile ");

        desc.add_options()
            ("help,h", "produce a help message")
            ("lossfunction,l",
                po::value<int>((int*) &lossfunction)->default_value(SQUARED),
                "Loss function (0:SQUARED, 1:LAD, 2:HINGE, 3:LOGISTIC)")
            ("regularizer,r",
                po::value<int>((int*) &regularizer)->default_value(L2),
                "Regularizer (0:L2, 1:L1)")
            ("kernel,k",
                po::value<int>((int*) &kernel)->default_value(LINEAR),
                "Kernel (0:LINEAR, 1:GAUSSIAN, 2:POLYNOMIAL, "
                "3:LAPLACIAN, 4:EXPSEMIGROUP, 5:MATERN)")
            ("kernelparam,g",
                po::value<double>(&kernelparam)->default_value(DEFAULT_KERPARAM),
                "Kernel Parameter")
            ("kernelparam2,x",
                po::value<double>(&kernelparam2)->default_value(0),
                "If Applicable - Second Kernel Parameter (Polynomial Kernel: c)")
            ("kernelparam3,y",
                po::value<double>(&kernelparam3)->default_value(1),
                "If Applicable - Third Kernel Parameter (Polynomial Kernel: gamma)")
            ("lambda,c",
                po::value<double>(&lambda)->default_value(DEFAULT_LAMBDA),
                "Regularization Parameter")
            ("tolerance,e",
                po::value<double>(&tolerance)->default_value(DEFAULT_TOL),
                "Tolerance")
            ("rho",
                po::value<double>(&rho)->default_value(DEFAULT_RHO),
                "ADMM rho parameter")
            ("seed,s",
                po::value<int>(&seed)->default_value(DEFAULT_SEED),
                "Seed for Random Number Generator")
            ("randomfeatures,f",
                po::value<int>(&randomfeatures)->default_value(DEFAULT_RF),
                "Number of Random Features (default: 100)")
            ("numfeaturepartitions,n",
                po::value<int>(&numfeaturepartitions)->
                default_value(DEFAULT_FEATURE_PARTITIONS),
                "Number of Feature Partitions (default: 1)")
            ("numthreads,t",
                po::value<int>(&numthreads)->default_value(DEFAULT_THREADS),
                "Number of Threads (default: 1)")
            ("regular",
                po::value<bool>(&regularmap)->default_value(true),
                "Default is to use 'fast' feature mapping, if available."
                "Use this flag to force regular mapping (default: false)")
            ("usequasi,q",
                po::value<int>((int*) &seqtype)->default_value(MONTECARLO),
                "If possible, change the underlying sequence of samples"
                " (0:Regular/Monte Carlo, 1:Leaped Halton)")
            ("cachetransforms",
                po::value<bool>(&cachetransforms)->default_value(false),
                "Default is to not cache feature transforms per iteration, but generate on fly"
                 "Use this flag to force transform caching if you have enough memory (default: false)")
            ("fileformat",
                po::value<int>(&fileformat)->default_value(DEFAULT_FILEFORMAT),
                "Fileformat (default: 0 (libsvm->dense), 1 (libsvm->sparse), 2 (hdf5->dense), 3 (hdf5->sparse)")
            ("MAXITER,i",
                po::value<int>(&MAXITER)->default_value(DEFAULT_MAXITER),
                "Maximum Number of Iterations (default: 100)")
            ("trainfile",
                po::value<std::string>(&trainfile)->default_value(""),
                "Training data file (required in training mode)")
            ("modelfile",
                po::value<std::string>(&modelfile)->required(),
                "Model output file")
            ("valfile",
                po::value<std::string>(&valfile)->default_value(""),
                "Validation file (optional)")
            ("testfile",
                po::value<std::string>(&testfile)->default_value(""),
                "Test file (optional in training mode; required in testing mode)")
            ; /* end options */

        po::positional_options_description positionalOptions;
        positionalOptions.add("trainfile", 1);
        positionalOptions.add("modelfile", 1);

        /** create a variable map to hold all these things */
        po::variables_map vm;
        try {
            po::store(po::command_line_parser(argc, argv)
                .options(desc).positional(positionalOptions).run(), vm);

            /** Print help and return if needed */
            if (vm.count ("help")) {
                std::cout << desc;
                exit_on_return = true;
                return;
            }
            po::notify(vm); // throws on error, so do after help in case
            // there are any problems
        }
        catch(po::error& e) {
            std::cerr << e.what() << std::endl;
            std::cerr << desc << std::endl;
            exit_on_return = true;
            return;
        }

#else
        // The following is much less robust, but should work even without
        // Boost::program_options.

        lossfunction = SQUARED;
        regularizer = L2;
        kernel = LINEAR;
        kernelparam = DEFAULT_KERPARAM;
        kernelparam2 = 0;
        kernelparam3 = 1;
        lambda = DEFAULT_LAMBDA;
        tolerance = DEFAULT_TOL;
        rho = DEFAULT_RHO;
        seed = DEFAULT_SEED;
        randomfeatures = DEFAULT_RF;
        numfeaturepartitions = DEFAULT_FEATURE_PARTITIONS;
        numthreads = DEFAULT_THREADS;
        regularmap = true;
        seqtype = MONTECARLO;
        fileformat = DEFAULT_FILEFORMAT;
        MAXITER = DEFAULT_MAXITER;
        valfile = "";
        testfile = "";

        for (int i = 1; i < argc; i += 2) {
            std::string flag = argv[i];
            std::string value = argv[i+1];

            if (flag == "--lossfunction" || flag == "-l")
                lossfunction =
                    static_cast<LossType>(boost::lexical_cast<int>(value));
            if (flag == "--regularizer" || flag == "-r")
                regularizer =
                    static_cast<RegularizerType>(boost::lexical_cast<int>(value));
            if (flag == "--kernel" || flag == "-k")
                kernel =
                    static_cast<KernelType>(boost::lexical_cast<int>(value));
            if (flag == "--kernelparam" || flag == "-g")
                kernelparam = boost::lexical_cast<double>(value);
            if (flag == "--kernelparam2" || flag == "-x")
                kernelparam2 = boost::lexical_cast<double>(value);
            if (flag == "--kernelparam3" || flag == "-y")
                kernelparam3 = boost::lexical_cast<double>(value);
            if (flag == "--lambda" || flag == "-c")
                lambda = boost::lexical_cast<double>(value);
            if (flag == "--tolerance" || flag == "-e")
                tolerance = boost::lexical_cast<double>(value);
            if (flag == "--rho")
                rho = boost::lexical_cast<double>(value);
            if (flag == "--seed" || flag == "-s")
                seed = boost::lexical_cast<int>(value);
            if (flag == "--randomfeatures" || flag == "-f")
                randomfeatures = boost::lexical_cast<int>(value);
            if (flag == "--numfeaturepartitions" || flag == "-n")
                numfeaturepartitions = boost::lexical_cast<int>(value);
            if (flag == "--numthreads" || flag == "-t")
                numthreads = boost::lexical_cast<int>(value);
            if (flag == "--regular")
                regularmap = value == "on";
            if (flag == "--useqausi" || flag == "-q")
                seqtype =
                    static_cast<SequenceType>(boost::lexical_cast<int>(value));
            if (flag == "--fileformat")
                fileformat =
                    static_cast<FileFormatType>(boost::lexical_cast<int>(value));
            if (flag == "--MAXITER" || flag == "-i")
                MAXITER = boost::lexical_cast<int>(value);
            if (flag == "--trainfile")
                trainfile = value;
            if (flag == "--modelfile")
                modelfile = value;
            if (flag == "--valfile")
                valfile = value;
            if (flag == "--testfile")
                testfile = value;
        }
#endif

        for(int i=0;i<argc;i++) {
        	str.append(argv[i]);
        	if (i<argc-1)
        		str.append(" ");
        }

    }

    std::string print () const {
        std::stringstream optionstring;

        optionstring << "# Generated using libSkylark/hilbert ";
        optionstring << "using the following command-line: " << std::endl;
        optionstring << "#\t" << str << std::endl;
        optionstring << "#" << std::endl;
        optionstring << "# Training File = " << trainfile << std::endl;
        optionstring << "# Model File = " << modelfile << std::endl;
        optionstring << "# Validation File = " << valfile << std::endl;
        optionstring << "# Test File = " << testfile << std::endl;
        optionstring << "# File Format = " << fileformat << std::endl;
        optionstring << "# Loss function = " << lossfunction
                     << " ("<< Losses[lossfunction]<< ")" << std::endl;
        optionstring << "# Regularizer = " << regularizer
                     << " ("<< Regularizers[regularizer]<< ")" << std::endl;
        optionstring << "# Kernel = " << kernel
                     << " ("<< Kernels[kernel]<< ")" << std::endl;
        optionstring << "# Kernel Parameter = " << kernelparam << std::endl;
        if (kernelparam2 != -1)
            optionstring << "# Second Kernel Parameter = "
                         << kernelparam2 << std::endl;
        if (kernelparam3 != -1)
            optionstring << "# Third Kernel Parameter = "
                         << kernelparam3 << std::endl;
        optionstring << "# Regularization Parameter = " << lambda << std::endl;
        optionstring << "# Maximum Iterations = " << MAXITER << std::endl;
        optionstring << "# Tolerance = " << tolerance << std::endl;
        optionstring << "# rho = " << rho << std::endl;
        optionstring << "# Seed = " << seed << std::endl;
        optionstring << "# Random Features = " << randomfeatures << std::endl;
        optionstring << "# Caching Transforms = " << cachetransforms << std::endl;
        optionstring << "# Slow/Fast feature mapping = " << regularmap  << std::endl;
        optionstring << "# Sequence = " << seqtype
                     << " (" << Sequences[seqtype] << ")" << std::endl;
        optionstring << "# Number of feature partitions = "
                     << numfeaturepartitions << std::endl;
        optionstring << "# Threads = " << numthreads << std::endl;
        optionstring <<"# Number of MPI Processes = "
                     << nummpiprocesses << std::endl;

        return optionstring.str();
    }
};

#endif /* SKYLARK_HILBERT_OPTIONS_HPP */


//=======================================================================
// Copyright 2001 Jeremy G. Siek, Andrew Lumsdaine, Lie-Quan Lee,
//
// 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 <boost/graph/graphviz.hpp>
#include <boost/graph/depth_first_search.hpp>

char name[] = "abcdefghij";

struct parenthesis_visitor : public boost::default_dfs_visitor
{
  template <class Vertex, class Graph> void
  start_vertex(Vertex v, const Graph &)
  {
    std::cout << ' ';
  }
  template <class Vertex, class Graph> void
  discover_vertex(Vertex v, const Graph &)
  {
    std::cout << "(" << name[v] << ' ';
  }
  template <class Vertex, class Graph> void
  finish_vertex(Vertex v, const Graph &)
  {
    std::cout << ' ' << name[v] << ")";
  }
};

int
main()
{
  using namespace boost;
  GraphvizGraph g;
  read_graphviz("figs/dfs-example.dot", g);
  graph_traits < GraphvizGraph >::edge_iterator e, e_end;
  for (boost::tie(e, e_end) = edges(g); e != e_end; ++e)
    std::cout << '(' << name[source(*e, g)] << ' '
      << name[target(*e, g)] << ')' << std::endl;
  parenthesis_visitor
    paren_vis;
  depth_first_search(g, visitor(paren_vis));
  std::cout << std::endl;
  return 0;
}


/**
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 **/
/**
 * @file imgAddNoise
 * @ingroup converters
 * @author David Coeurjolly (\c david.coeurjolly@liris.cnrs.fr)
 *
 * @date 2015/03/24
 *
 *
 * This file is part of the DGtal library.
 */

#include <DGtal/base/Common.h>
#include "DGtal/io/readers/GenericReader.h"
#include "DGtal/io/writers/GenericWriter.h"
#include "DGtal/images/ImageContainerBySTLVector.h"
#include "DGtal/images/ImageSelector.h"
#include <DGtal/geometry/volumes/KanungoNoise.h>
#include <DGtal/images/IntervalForegroundPredicate.h>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/variables_map.hpp>

#include <vector>
#include <string>
#include <climits>

using namespace DGtal;

///////////////////////////////////////////////////////////////////////////////
namespace po = boost::program_options;

/**
 * Missing parameter error message.
 *
 * @param param
 */
void missingParam ( std::string param )
{
  trace.error() <<" Parameter: "<<param<<" is required..";
  trace.info() <<std::endl;
  exit ( 1 );
}

typedef ImageSelector < Z2i::Domain, unsigned char>::Type MyImage;

int main( int argc, char** argv )
{
  // parse command line ----------------------------------------------
  po::options_description general_opt("Allowed options are: ");
  general_opt.add_options()
    ("help,h", "display this message")
    ("input,i", po::value<std::string>(), "input image file name (any 2D image format accepted by DGtal::GenericReader)")
    ("output,o", po::value<std::string>(), "output image file name (any 2D image format accepted by DGtal::GenericWriter)")
    ("noise,n", po::value<double>()->default_value(0.5), "Kanungo noise level in ]0,1[ (default 0.5)")  ;

  bool parseOK=true;
  po::variables_map vm;
  try{
    po::store(po::parse_command_line(argc, argv, general_opt), vm);
  }catch(const std::exception& ex){
    trace.info()<< "Error checking program options: "<< ex.what()<< std::endl;
    parseOK=false;
  }
  po::notify(vm);
  if(vm.count("help")||argc<=1|| !parseOK)
    {
      trace.info()<< "Add Kanungo noise to a binary object with 0 values as background points and values >0 for the foreground ones." <<std::endl << "Basic usage: "<<std::endl
      << "\t imgAddNoise [options] --input <imageName> --output <outputImage> -noise 0.3"<<std::endl
      << general_opt << "\n";
      return 0;
    }

  //Parameters
  if ( ! ( vm.count ( "input" ) ) ) missingParam ( "--input" );
  const std::string input = vm["input"].as<std::string>();
  if ( ! ( vm.count ( "output" ) ) ) missingParam ( "--output" );
  const std::string  output = vm["output"].as<std::string>();
  const double noise = vm["noise"].as<double>();

  typedef functors::IntervalForegroundPredicate<MyImage> Binarizer;
  MyImage image = GenericReader<MyImage>::import( input );
  trace.info() <<"Input image: "<< image<<std::endl;
  Binarizer predicate(image, 0,255);

  KanungoNoise<Binarizer, Z2i::Domain> kanungo(predicate, image.domain(), noise);

  MyImage result(image.domain());
  for(Z2i::Domain::ConstIterator it = image.domain().begin(), itend = image.domain().end(); it!= itend; ++it)
  {
    if (kanungo(*it))
      result.setValue(*it, 255);
    else
      result  .setValue(*it, 0);
  }

  result >> output;

  return 0;
}


#include "gl-visualizer.hh"

#include "gl/gl-utils.hpp"
#include "perceive/geometry/vector-3.hpp"
#include "perceive/utils/file-system.hpp"

#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>

#include <QMouseEvent>
#include <QOpenGLPaintDevice>
#include <QOpenGLWidget>
#include <QPixmap>
#include <QTimer>

#include <GL/gl.h>
#include <GL/glu.h>

#include <Eigen/Dense>

#define This GLVisualizer

using Eigen::Vector3d;
using std::string;
using namespace perceive;

static void glColour(const Vector3& k) { glColor3d(k.x, k.y, k.z); }

static void glColour(uint32_t k)
{
   glColor3d(((k >> 16) & 0xff) / 255.0,
             ((k >> 8) & 0xff) / 255.0,
             ((k >> 0) & 0xff) / 255.0);
}
static void glVertex(const Vector3& X) { glVertex3dv(X.ptr()); }

static int64_t milli_clock()
{
   struct timeval tv;
   gettimeofday(&tv, NULL);
   return int64_t(1000) * int64_t(tv.tv_sec) + int64_t(tv.tv_usec) / 1000;
}

struct This::Pimpl
{
   Pimpl(This* parent_)
       : parent(parent_)
       , capture_counter(0)
       , animationTimer(new QTimer)
       , xRot(0)
       , yRot(0)
       , zRot(0)
       , lastPos()
       , translate(0, 0, 0)
       , scale(1, 1, 1)
       , fov(25.0)
       , znear(0.5)
       , zfar(5000.0)
       , eye(-5, 0, 5)
       , centre(0, 0, 0)
       , up(0, 0, 1)
       , delta(0.0)
       , camera_speed(64.0)
       , // 64s for one revolution
       time0(0)
       , last_delta(0)
       , did_capture(false)
       , capture_one_rev(false)
   {}

   This* parent;

   uint capture_counter;

   QTimer* animationTimer;

   // Mouse control changes viewing angle
   int xRot;
   int yRot;
   int zRot;
   QPoint lastPos;
   bool mouseIsDown = false;

   // Translate and scale object
   Vector3d translate;
   Vector3d scale; // in the x, y, and z dimensions

   double fov; // options().info.fov() * 180.0 / M_PI;
   double znear;
   double zfar;

   Vector3d eye, centre, up{0.0, 0.0, 1.0};

   Vector3d centre_delta{0.0, 0.0, 0.0};
   double theta_delta{0.0};
   double helicopter_theta_delta{0.0};

   double rot_theta{NAN};

   double delta;
   double camera_speed; // 1/Hz... i.e, num of seconds per complete revolution
   int64_t time0;
   int64_t last_delta;

   bool did_capture;
   bool capture_one_rev;

   double radius() const { return (eye - centre).norm(); }
   void set_radius(double r)
   {
      Vector3d n = eye - centre;
      eye        = centre + r * n / n.norm();
   }

   void set_up(double x, double y, double z)
   {
      double norm = sqrt(x * x + y * y + z * z);
      up(0)       = x / norm;
      up(1)       = y / norm;
      up(2)       = z / norm;
   }

   double calc_delta()
   {
      auto time_now            = milli_clock();
      last_delta               = time_now - time0;
      const double ellapsed_ms = last_delta;
      const double speed       = camera_speed; // 4s for one revolution
      delta                    = fmod(ellapsed_ms * 0.001, speed) / speed;
      assert(delta >= 0.0 && delta <= 1.0);

      if(capture_one_rev && ellapsed_ms * 0.001 >= camera_speed)
         parent->emit_oneRevolutionCaptured();

      return delta;
   }
};

void glVertex3(const Vector3d& X) { glVertex3d(X(0), X(1), X(2)); }

void glDrawAxis(double line_len = 2.0, double line_width = 4.0)
{
   glPushMatrix();
   glScaled(line_len, line_len, line_len);
   Vector3d axis_origin(0.0, 0.0, 0.0);
   glLineWidth(line_width);
   glBegin(GL_LINES);
   glColor3d(1, 0, 0);
   glVertex3(axis_origin);
   glVertex3(axis_origin + Vector3d(1.0, 0.0, 0.0));
   glColor3d(0, 1, 0);
   glVertex3(axis_origin);
   glVertex3(axis_origin + Vector3d(0.0, 1.0, 0.0));
   glColor3d(0, 0, 1);
   glVertex3(axis_origin);
   glVertex3(axis_origin + Vector3d(0.0, 0.0, 1.0));
   glEnd();
   glLineWidth(1.0);
   glPopMatrix();
}

void glDrawNiceCube()
{
   glBegin(GL_QUADS);              // Draw A Quad
   glColor3f(0.0f, 1.0f, 0.0f);    // Set The Color To Green
   glVertex3f(1.0f, 1.0f, -1.0f);  // Top Right Of The Quad (Top)
   glVertex3f(-1.0f, 1.0f, -1.0f); // Top Left Of The Quad (Top)
   glVertex3f(-1.0f, 1.0f, 1.0f);  // Bottom Left Of The Quad (Top)
   glVertex3f(1.0f, 1.0f, 1.0f);   // Bottom Right Of The Quad (Top)

   glColor3f(1.0f, 0.5f, 0.0f);     // Set The Color To Orange
   glVertex3f(1.0f, -1.0f, 1.0f);   // Top Right Of The Quad (Botm)
   glVertex3f(-1.0f, -1.0f, 1.0f);  // Top Left Of The Quad (Botm)
   glVertex3f(-1.0f, -1.0f, -1.0f); // Bottom Left Of The Quad (Botm)
   glVertex3f(1.0f, -1.0f, -1.0f);  // Bottom Right Of The Quad (Botm)

   glColor3f(1.0f, 0.0f, 0.0f);    // Set The Color To Red
   glVertex3f(1.0f, 1.0f, 1.0f);   // Top Right Of The Quad (Front)
   glVertex3f(-1.0f, 1.0f, 1.0f);  // Top Left Of The Quad (Front)
   glVertex3f(-1.0f, -1.0f, 1.0f); // Bottom Left Of The Quad (Front)
   glVertex3f(1.0f, -1.0f, 1.0f);  // Bottom Right Of The Quad (Front)

   glColor3f(1.0f, 1.0f, 0.0f);     // Set The Color To Yellow
   glVertex3f(1.0f, -1.0f, -1.0f);  // Bottom Left Of The Quad (Back)
   glVertex3f(-1.0f, -1.0f, -1.0f); // Bottom Right Of The Quad (Back)
   glVertex3f(-1.0f, 1.0f, -1.0f);  // Top Right Of The Quad (Back)
   glVertex3f(1.0f, 1.0f, -1.0f);   // Top Left Of The Quad (Back)

   glColor3f(0.0f, 0.0f, 1.0f);     // Set The Color To Blue
   glVertex3f(-1.0f, 1.0f, 1.0f);   // Top Right Of The Quad (Left)
   glVertex3f(-1.0f, 1.0f, -1.0f);  // Top Left Of The Quad (Left)
   glVertex3f(-1.0f, -1.0f, -1.0f); // Bottom Left Of The Quad (Left)
   glVertex3f(-1.0f, -1.0f, 1.0f);  // Bottom Right Of The Quad (Left)

   glColor3f(1.0f, 0.0f, 1.0f);    // Set The Color To Violet
   glVertex3f(1.0f, 1.0f, -1.0f);  // Top Right Of The Quad (Right)
   glVertex3f(1.0f, 1.0f, 1.0f);   // Top Left Of The Quad (Right)
   glVertex3f(1.0f, -1.0f, 1.0f);  // Bottom Left Of The Quad (Right)
   glVertex3f(1.0f, -1.0f, -1.0f); // Bottom Right Of The Quad (Right)
   glEnd();                        // Done Drawing The Quad
}

inline double deg2rad(double deg) { return deg * M_PI / 180.0; }

This::This(QWidget* parent)
    : QOpenGLWidget(parent)
    , pimpl_(make_unique<Pimpl>(this))
{
   onDrawThunk                     = []() {};
   draw_gl_axis                    = false;
   draw_3d_cube                    = false;
   capture_frames                  = false;
   rotating                        = false;
   helicopter_rotation_inclination = 0.0;

   make_ui();

   auto animationTimer = pimpl_->animationTimer;
   animationTimer->setSingleShot(false);
   connect(animationTimer, SIGNAL(timeout()), this, SLOT(update()));
   animationTimer->start(1000.0 / 33.0); // i.e. 33 frames per second
   setRotating(false);
}

This::~This()
{
   if(pimpl_->did_capture)
      INFO("convert -delay 10 -loop 0 /tmp/gl-capture/*.png "
           "$HOME/animaion.gif");
}

QSize This::minimumSizeHint() const { return QSize(50, 50); }

QSize This::sizeHint() const { return QSize(400, 400); }

void This::make_ui() {}

static void qNormalizeAngle(int& angle)
{
   while(angle < 0) angle += 360 * 16;
   while(angle > 360 * 16) angle -= 360 * 16;
}

bool This::isRotating() const { return rotating; }

bool This::isCapturing() const { return capture_frames; }

void This::setXRotation(int angle)
{
   qNormalizeAngle(angle);
   if(angle != pimpl_->xRot) {
      pimpl_->xRot = angle;
      emit xRotationChanged(angle);
   }
}

void This::setYRotation(int angle)
{
   qNormalizeAngle(angle);
   if(angle != pimpl_->yRot) {
      pimpl_->yRot = angle;
      emit yRotationChanged(angle);
   }
}

void This::setZRotation(int angle)
{
   qNormalizeAngle(angle);
   if(angle != pimpl_->zRot) {
      pimpl_->zRot = angle;
      emit zRotationChanged(angle);
   }
}

void This::setZoom(double scalev)
{
   const double min_scale = 0.001;
   const double max_scale = 1000.0;
   if(scalev < min_scale) {
      printf("WARNING: scale (%g) less than floor (%g), using floor\n",
             scalev,
             min_scale);
      scalev = min_scale;
   }
   if(scalev > max_scale) {
      printf("WARNING: scale (%g) greater than ceil (%g), using ceil\n",
             scalev,
             max_scale);
      scalev = max_scale;
   }
   pimpl_->scale = (pimpl_->scale / pimpl_->scale.norm()) * scalev;

   emit(zoomChanged(scalev));
}

void This::setScale(double dx, double dy, double dz)
{
   pimpl_->scale(0) = dx;
   pimpl_->scale(1) = dy;
   pimpl_->scale(2) = dz;
}

void This::setRotTheta(double rot) { pimpl_->rot_theta = rot; }

void This::setCapture(bool capture, int frame_counter_begin)
{
   if(capture && !capture_frames) pimpl_->capture_counter = frame_counter_begin;
   capture_frames = capture;
}

void This::setDrawAxis(bool draw) { draw_gl_axis = draw; }

void This::setDraw3dCube(bool draw) { draw_3d_cube = draw; }

void This::setRotating(bool rotate)
{
   if(rotate && !rotating) pimpl_->time0 = milli_clock();
   rotating = rotate;
}

void This::setRotateDelta(double delta) { pimpl_->delta = delta; }

void This::setHelicopterRotateInclination(double value)
{
   helicopter_rotation_inclination = value;
}

void This::setRotateUp(double x, double y, double z)
{
   pimpl_->set_up(x, y, z);
}

void This::setRotationSpeed(double secs_per_revolusion)
{
   pimpl_->camera_speed = secs_per_revolusion;
}

void This::setEye(double x, double y, double z)
{
   pimpl_->eye(0) = x;
   pimpl_->eye(1) = y;
   pimpl_->eye(2) = z;
}

void This::setCentre(double x, double y, double z)
{
   auto& P     = *pimpl_;
   auto vec    = P.eye - P.centre;
   P.centre(0) = x;
   P.centre(1) = y;
   P.centre(2) = z;
   P.eye       = P.centre + vec;
}

void This::zoomIn() { setZoom(pimpl_->scale.norm() * 1.1); }

void This::zoomOut() { setZoom(pimpl_->scale.norm() * 0.9); }

double This::getZoom() { return pimpl_->scale.norm(); }

void This::captureOneRevolution()
{
   setRotating(true);
   setCapture(true);
   pimpl_->capture_one_rev = true;
}

void This::initializeGL()
{
   glShadeModel(GL_SMOOTH);
   glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
   glClearDepth(1.0f);
   glEnable(GL_DEPTH_TEST);
   glDepthFunc(GL_LEQUAL);
   glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
   glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
   glDisable(GL_CULL_FACE);
}

void This::paintGL()
{
   static bool first_run = true;
   if(first_run) {
      initializeGL();
      first_run = false;
   }

   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

   glLoadIdentity();

   { // Locate the current eye position...
      Pimpl& P              = *pimpl_;
      const Vector3d centre = P.centre + P.centre_delta;
      const double radius   = (centre - P.eye).norm() * P.scale.norm();
      Vector3d at           = (centre - P.eye);
      at /= at.norm();
      Vector3d up = P.up;
      up /= up.norm();
      Vector3d yaxis  = at.cross(up);
      Vector3d xaxis  = up.cross(yaxis);
      Vector3d& zaxis = up;

      if(rotating) P.calc_delta();
      const double theta0 = (std::isfinite(P.rot_theta))
                                ? P.rot_theta
                                : 2.0 * (P.delta - 0.5) * M_PI; // 2pi radians
      const double theta  = theta0 + P.theta_delta;

      auto normalize_theta = [&](double theta) { // [-pi..pi)
         if(theta >= M_PI) return std::fmod(theta + M_PI, 2.0 * M_PI) - M_PI;
         if(theta < -M_PI) return std::fmod(theta - M_PI, 2.0 * M_PI) + M_PI;
         return theta;
      };

      const double inclination = std::fabs(normalize_theta(
          helicopter_rotation_inclination + P.helicopter_theta_delta));

      const double sin_inc = sin(inclination);
      const double cos_inc = cos(inclination);
      const double sin_azi = sin(theta);
      const double cos_azi = cos(theta);

      Vector3d eye = centre                               //
                     + xaxis * radius * sin_inc * cos_azi //
                     + yaxis * radius * sin_inc * sin_azi //
                     + zaxis * radius * cos_inc;          //

      if(true) {
         glu_look_at(eye(0) - centre(0),
                     eye(1) - centre(1),
                     eye(2) - centre(2),
                     0.0,
                     0.0,
                     0.0,
                     up(0),
                     up(1),
                     up(2));

         // Change the rotation, keeping the centre constant
         glRotated(double(pimpl_->xRot) / 16.0, 1.0, 0.0, 0.0);
         glRotated(double(pimpl_->yRot) / 16.0, 0.0, 1.0, 0.0);
         glRotated(double(pimpl_->zRot) / 16.0, 0.0, 0.0, 1.0);

         // Now translate "away" from the centre
         glTranslated(-centre(0), -centre(1), -centre(2));
      } else {
         glu_look_at(eye(0),
                     eye(1),
                     eye(2),
                     centre(0),
                     centre(1),
                     centre(2),
                     up(0),
                     up(1),
                     up(2));
      }
   }

   // -- Draw the axis
   if(draw_gl_axis) glDrawAxis();

   // Draw a quad -- testing
   if(draw_3d_cube) glDrawNiceCube();

   if(onDrawThunk) onDrawThunk();

   // Save a frame to disk (if requested)
   if(capture_frames) {
      const uint buflen = 2048;

      static uint capture_counter = 0;
      static int t0               = 0;
      static int frame_counter    = 0;
      static char buffer[buflen];
      static uint8_t* pixels = nullptr;
      static uint pixels_sz  = 0;

      capture_counter = (pimpl_->capture_counter)++;
      // convert -delay 10 -loop 0 inputfiles*.png animaion.gif

      string outdir = "/tmp/gl-capture";
      {
         if(!is_directory(outdir) && !is_readable_file(outdir)) {
            mkdir(outdir.c_str(), 0700);
         }

         // Process a screen capture
         auto sz       = size();
         auto w        = sz.width();
         auto h        = sz.height();
         uint n_pixels = 2 * 4 * w * h;
         if(pixels_sz < n_pixels) {
            pixels_sz = n_pixels;
            delete[] pixels;
            pixels = new uint8_t[pixels_sz];
         }

         glReadPixels(0, 0, w, h, GL_BGRA, GL_UNSIGNED_BYTE, pixels);

         // flip the y-axis of the output buffer
         uint bytes    = 4 * w;
         uint8_t* pix2 = pixels + 4 * w * h;
         for(int y = 0; y < h; ++y)
            memcpy(&pix2[(h - y - 1) * bytes], &pixels[y * bytes], bytes);

         snprintf(buffer, buflen, "/tmp/gl-capture/%06d.png", capture_counter);

         // Save the image
         QImage im(pix2, w, h, QImage::Format_ARGB32);
         im.save(buffer);

         printf("capture %s\n", buffer);

         pimpl_->did_capture = true;

         emit oneFrameCaptured();
      }
   }

   if(onDrawThunk2) onDrawThunk2();
}

void This::resizeGL(int width, int height)
{
   auto fov   = pimpl_->fov;
   auto znear = pimpl_->znear;
   auto zfar  = pimpl_->zfar;

   // Protect against a divide by zero
   if(height == 0) height = 1;

   // Setup our viewport.
   glViewport(0, 0, GLint(width), GLint(height));

   // change to the projection matrix and set our viewing volume.
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   glu_perspective(fov, float(width) / float(height), znear, zfar);

   // Make sure we're chaning the model view and not the projection
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
}

void This::mousePressEvent(QMouseEvent* event)
{
   pimpl_->lastPos     = event->pos();
   pimpl_->mouseIsDown = true;
}

void This::mouseReleaseEvent(QMouseEvent* event)
{
   pimpl_->mouseIsDown = false;
}

void This::mouseMoveEvent(QMouseEvent* event)
{
   auto& P = *pimpl_;

   const int dx = event->x() - pimpl_->lastPos.x();
   const int dy = event->y() - pimpl_->lastPos.y();

   const bool is_left_btn   = event->buttons() & Qt::LeftButton;
   const bool is_right_btn  = event->buttons() & Qt::RightButton;
   const bool is_shift_down = event->modifiers() & Qt::ShiftModifier;

   if(pan_mode == MODE_XY_XZ) {
      if(is_right_btn) {
         setXRotation(P.xRot + 8 * dy);
         setYRotation(P.yRot + 8 * dx);
      } else if(is_left_btn) {
         setXRotation(P.xRot + 8 * dy);
         setZRotation(P.zRot + 8 * dx);
      }
   } else {
      if(is_shift_down and is_left_btn) {
         P.centre_delta(0) += 0.02 * dx;
         P.centre_delta(1) += 0.02 * dy;
      } else if(is_shift_down and is_right_btn) {
         P.centre_delta(2) += 0.02 * dy;
      } else if(!is_shift_down and is_left_btn) {
         P.theta_delta += to_radians(0.1 * dx);
      } else if(!is_shift_down and is_right_btn) {
         P.helicopter_theta_delta += to_radians(0.1 * dy);
      }
   }

   pimpl_->lastPos = event->pos();
}

void This::keyPressEvent(QKeyEvent* event) {}

void This::wheelEvent(QWheelEvent* event)
{
   auto d_angle = event->angleDelta();
   if(d_angle.y() < 0) zoomIn();
   if(d_angle.y() > 0) zoomOut();
}

void This::emit_oneRevolutionCaptured()
{
   setCapture(false);
   setRotating(false);
   pimpl_->capture_one_rev = false;
   emit oneRevolutionCaptured();
}

void This::print() const
{
   const auto& P = *pimpl_;
   printf("Rot    = { %d, %d, %d }\n", P.xRot, P.yRot, P.zRot);
   printf("Centre = {%f, %f, %f}\n", P.centre(0), P.centre(1), P.centre(2));
   printf("Eye    = {%f, %f, %f}\n", P.eye(0), P.eye(1), P.eye(2));
   printf("Up     = {%f, %f, %f}\n", P.up(0), P.up(1), P.up(2));
   printf("Scale  = {%f, %f, %f}\n", P.scale(0), P.scale(1), P.scale(2));
   printf("Transl = {%f, %f, %f}\n",
          P.translate(0),
          P.translate(1),
          P.translate(2));
   printf("\n\n");
}


#include "catch.hpp"
#include "fakeit.hpp"

#include <vector>
#include <limits>

#include <libArrhenius/Integration/ArrheniusIntegral.hpp>
#include <libArrhenius/Fitting/ArrheniusFit.hpp>
#include <libArrhenius/ThresholdCalculator.hpp>

#include<boost/optional/optional_io.hpp>

#include <boost/multiprecision/cpp_dec_float.hpp>
using namespace boost::multiprecision;

using namespace libArrhenius;

typedef cpp_dec_float_100 DataType;

TEST_CASE( "ArrheniusFitter Usage", "[usage]" )
{

  std::vector<double> taus = { 0.001, 0.01, 0.1, 1.0, 10.0 };
  std::vector<std::shared_ptr<DataType>> ts,Ts;
  std::vector<size_t> Ns;

  double A = 3.1e99;
  double Ea = 6.28e5;

  ThresholdCalculator< ArrheniusIntegral<DataType> > calc(A,Ea);

  for( auto tau : taus )
  {
    double dt = tau / 20;
    double total_time = 4*tau;
    size_t N = total_time / dt;

    Ns.push_back( N );
    ts.push_back( std::shared_ptr<DataType>( new DataType[N], [](DataType* p){delete[] p;} ) );
    Ts.push_back( std::shared_ptr<DataType>( new DataType[N], [](DataType* p){delete[] p;} ) );
    int j = Ns.size()-1;

    for( size_t i = 0; i < Ns[j]; i++ )
    {
      ts[j].get()[i] = dt*i;
      Ts[j].get()[i] = 310;
      if( ts[j].get()[i] > tau/2 )
        Ts[j].get()[i] = 10 + 310;
      if( ts[j].get()[i] > tau + tau/2 )
        Ts[j].get()[i] = 310;
    }

    auto Threshold = calc(Ns[j],ts[j].get(),Ts[j].get());
    for( size_t i = 0; i < Ns[j]; i++ )
    {
      Ts[j].get()[i] = Threshold*(Ts[j].get()[i] - Ts[j].get()[0]) + Ts[j].get()[0];
    }

    auto Omega = calc.Omega(Ns[j],ts[j].get(),Ts[j].get());
    CHECK( static_cast<double>(Omega) == Approx(1) );

  }

  SECTION("Minimize Scaling Factors Method")
  {
    ArrheniusFit< DataType, MinimizeScalingFactors > fit;

    for( size_t j = 0; j < Ns.size(); ++j )
    {
      fit.addProfile( Ns[j], ts[j].get(), Ts[j].get() );
    }

    fit.exec();
  }

  SECTION("Minimize logA and Scaling Factors Method")
  {
    ArrheniusFit< DataType > fit;

    for( size_t j = 0; j < Ns.size(); ++j )
    {
      fit.addProfile( Ns[j], ts[j].get(), Ts[j].get() );
    }

    SECTION("No limits")
    {
      auto ret = fit.exec();

      CHECK( static_cast<double>(ret.A.get()) == Approx(3.1e99) );
      CHECK( static_cast<double>(ret.Ea.get()) == Approx(6.28e5) );
    }

    SECTION("upper bound on Ea")
    {
      fit.setMaxEa(6e5);
      auto ret = fit.exec();

      CHECK( static_cast<double>(ret.A.get()) < 3.1e99 );
      CHECK( static_cast<double>(ret.Ea.get()) == Approx(6e5) );
    }

    SECTION("lower bound on Ea")
    {
      fit.setMinEa(6.5e5);
      auto ret = fit.exec();

      CHECK( static_cast<double>(ret.A.get()) > 3.1e99 );
      CHECK( static_cast<double>(ret.Ea.get()) == Approx(6.5e5) );
    }

    SECTION("upper bound on A")
    {
      fit.setMaxA(3e99);
      auto ret = fit.exec();

      CHECK( static_cast<double>(ret.A.get()) == Approx( 3.e99) );
      CHECK( static_cast<double>(ret.Ea.get()) == Approx(6.28e5) );
    }

    SECTION("lower bound on A")
    {
      fit.setMinA(3.2e99);
      fit.setMaxA(3.3e99);
      auto ret = fit.exec();

      CHECK( static_cast<double>(ret.A.get()) == Approx( 3.2e99) );
      CHECK( static_cast<double>(ret.Ea.get()) == Approx(6.28e5) );
    }
  }

}

TEST_CASE( "ArrheniusFitter Usage (effective exposures method)", "[usage]" ) {

  std::vector<double> taus = { 0.001, 0.01, 0.1, 1.0, 10.0 };
  std::vector<std::shared_ptr<DataType>> ts,Ts;
  std::vector<size_t> Ns;

  double A = 3.1e99;
  double Ea = 6.28e5;

  ThresholdCalculator< ArrheniusIntegral<DataType> > calc(A,Ea);
  ArrheniusFit< DataType, EffectiveExposuresLinearRegression > fit;

  for( auto tau : taus )
  {
    double dt = tau / 20;
    double total_time = 4*tau;
    size_t N = total_time / dt;

    Ns.push_back( N );
    ts.push_back( std::shared_ptr<DataType>( new DataType[N], [](DataType* p){delete[] p;} ) );
    Ts.push_back( std::shared_ptr<DataType>( new DataType[N], [](DataType* p){delete[] p;} ) );
    int j = Ns.size()-1;

    for( size_t i = 0; i < Ns[j]; i++ )
    {
      ts[j].get()[i] = dt*i;
      Ts[j].get()[i] = 310;
      if( ts[j].get()[i] > tau/2 )
        Ts[j].get()[i] = 10 + 310;
      if( ts[j].get()[i] > tau + tau/2 )
        Ts[j].get()[i] = 310;
    }

    auto Threshold = calc(Ns[j],ts[j].get(),Ts[j].get());
    for( size_t i = 0; i < Ns[j]; i++ )
    {
      Ts[j].get()[i] = Threshold*(Ts[j].get()[i] - Ts[j].get()[0]) + Ts[j].get()[0];
    }

    auto Omega = calc.Omega(Ns[j],ts[j].get(),Ts[j].get());
    CHECK( static_cast<double>(Omega) == Approx(1) );

    fit.addProfile( Ns[j], ts[j].get(), Ts[j].get() );
  }

  auto ret = fit.exec();

  CHECK( static_cast<double>(ret.A.get()) == Approx(3.1e99).epsilon(0.3) ); // within 30%
  CHECK( static_cast<double>(ret.Ea.get()) == Approx(6.28e5).epsilon(0.1) ); // within 10%

}

TEST_CASE( "ArrheniusFitter Usage (standard method)", "[usage]" ) {

  std::vector<double> taus = { 0.001, 0.01, 0.1, 1.0, 10.0 };
  std::vector<std::shared_ptr<DataType>> ts,Ts;
  std::vector<size_t> Ns;

  double A = 3.1e99;
  double Ea = 6.28e5;

  ThresholdCalculator< ArrheniusIntegral<DataType> > calc(A,Ea);
  ArrheniusFit< DataType, ConstantTemperatureLinearRegression > fit;

  for( auto tau : taus )
  {
    double dt = tau / 200;
    double total_time = 4*tau;
    size_t N = total_time / dt;

    Ns.push_back( N );
    ts.push_back( std::shared_ptr<DataType>( new DataType[N], [](DataType* p){delete[] p;} ) );
    Ts.push_back( std::shared_ptr<DataType>( new DataType[N], [](DataType* p){delete[] p;} ) );
    int j = Ns.size()-1;

    for( size_t i = 0; i < Ns[j]; i++ )
    {
      ts[j].get()[i] = dt*i;
      Ts[j].get()[i] = 310;
      if( ts[j].get()[i] > tau/2 )
        Ts[j].get()[i] = 10 + 310;
      if( ts[j].get()[i] > tau + tau/2 )
        Ts[j].get()[i] = 310;
    }

    auto Threshold = calc(Ns[j],ts[j].get(),Ts[j].get());
    for( size_t i = 0; i < Ns[j]; i++ )
    {
      Ts[j].get()[i] = Threshold*(Ts[j].get()[i] - Ts[j].get()[0]) + Ts[j].get()[0];
    }

    auto Omega = calc.Omega(Ns[j],ts[j].get(),Ts[j].get());
    CHECK( static_cast<double>(Omega) == Approx(1) );

    fit.addProfile( Ns[j], ts[j].get(), Ts[j].get() );
  }

  auto ret = fit.exec();

  CHECK( static_cast<double>(ret.A.get()) == Approx(3.1e99).epsilon(0.01) );
  CHECK( static_cast<double>(ret.Ea.get()) == Approx(6.28e5) );

}

TEST_CASE( "ArrheniusFitterInterface Usage", "[usage]" ) {

  std::vector<double> taus = { 0.001, 0.01, 0.1, 1.0, 10.0 };
  std::vector<std::shared_ptr<DataType>> ts,Ts;
  std::vector<size_t> Ns;

  double A = 3.1e99;
  double Ea = 6.28e5;

  ThresholdCalculator< ArrheniusIntegral<DataType> > calc(A,Ea);
  std::shared_ptr< ArrheniusFitInterface< DataType > > fit( new ArrheniusFit<DataType>() );

  for( auto tau : taus )
  {
    double dt = tau / 20;
    double total_time = 4*tau;
    size_t N = total_time / dt;

    Ns.push_back( N );
    ts.push_back( std::shared_ptr<DataType>( new DataType[N], [](DataType* p){delete[] p;} ) );
    Ts.push_back( std::shared_ptr<DataType>( new DataType[N], [](DataType* p){delete[] p;} ) );
    int j = Ns.size()-1;

    for( size_t i = 0; i < Ns[j]; i++ )
    {
      ts[j].get()[i] = dt*i;
      Ts[j].get()[i] = 310;
      if( ts[j].get()[i] > tau/2 )
        Ts[j].get()[i] = 10 + 310;
      if( ts[j].get()[i] > tau + tau/2 )
        Ts[j].get()[i] = 310;
    }

    auto Threshold = calc(Ns[j],ts[j].get(),Ts[j].get());
    for( size_t i = 0; i < Ns[j]; i++ )
    {
      Ts[j].get()[i] = Threshold*(Ts[j].get()[i] - Ts[j].get()[0]) + Ts[j].get()[0];
    }

    auto Omega = calc.Omega(Ns[j],ts[j].get(),Ts[j].get());
    CHECK( static_cast<double>(Omega) == Approx(1) );

    fit->addProfile( Ns[j], ts[j].get(), Ts[j].get() );
  }

  auto ret = fit->exec();

  CHECK( static_cast<double>(ret.A.get()) == Approx(3.1e99) );
  CHECK( static_cast<double>(ret.Ea.get()) == Approx(6.28e5) );

}


// Copyright (c) 2017 Graphcore Ltd. All rights reserved.
#define BOOST_TEST_MODULE ConvUtilTest
#include "ConvUtilInternal.hpp"
#include <boost/test/unit_test.hpp>
#include <poplibs_support/TestDevice.hpp>
#include <poplin/ConvUtil.hpp>
#include <poplin/Convolution.hpp>
#include <poputil/TileMapping.hpp>
#include <poputil/exceptions.hpp>

using namespace poplar;
using namespace poplibs_support;

BOOST_AUTO_TEST_CASE(getInputRangeFlipActsAndWeights) {
  poplin::ConvParams params{
      poplar::FLOAT, // data type,
      1,             // batch size
      {3, 1},        // input size
      {5, 1},        // kernel size
      1,             // input channels
      1,             // output channels
      1              // conv groups
  };
  params.inputTransform.paddingLower = {2, 0};
  params.inputTransform.paddingUpper = {2, 0};
  params.inputTransform.flip = {true, false};
  params.kernelTransform.flip = {true, false};

  auto inRange = poplin::getInputRange(0, {0, 2}, 1, params);
  BOOST_CHECK_EQUAL(inRange.first, 0);
  BOOST_CHECK_EQUAL(inRange.second, 2);
}

BOOST_AUTO_TEST_CASE(getKernelRangeTruncateInput) {
  poplin::ConvParams params{
      poplar::FLOAT, // data type,
      1,             // batch size
      {3},           // input size
      {4},           // kernel size
      1,             // input channels
      1,             // output channels
      1              // conv groups
  };
  params.inputTransform.truncationUpper = {1};
  params.inputTransform.dilation = {2};
  params.inputTransform.paddingLower = {1};

  auto kernelRange = poplin::getKernelRange(0, {0, 1}, {0, 3}, params);
  BOOST_CHECK_EQUAL(kernelRange.first, 1);
  BOOST_CHECK_EQUAL(kernelRange.second, 4);
}

BOOST_AUTO_TEST_CASE(SplitActivationIntoGroups) {
  auto device = createTestDevice(TEST_TARGET);
  Graph graph(device.getTarget());

  const auto g = 4;
  const auto n = 100;
  const auto h = 101;
  const auto w = 102;
  const auto c = 16;
  const auto uut = graph.addVariable(HALF, {g, n, h, w, c});

  const auto cgpg = 4;
  const auto cpg = 8;

  BOOST_TEST(g % cgpg == 0);
  BOOST_TEST(c % cpg == 0);

  const auto result = poplin::splitActivationIntoGroups(uut, cgpg, cpg);
  const auto &resultShape = result.shape();
  BOOST_TEST(resultShape.size() == uut.shape().size() + 2);

  std::vector<std::size_t> expected{g / cgpg, c / cpg, n, h, w, cgpg, cpg};
  BOOST_TEST(resultShape == expected, boost::test_tools::per_element());

  const auto t = poplin::unsplitActivationFromGroups(result);
  BOOST_TEST(t.shape() == uut.shape(), boost::test_tools::per_element());
  BOOST_TEST(t.getVarRegions() == uut.getVarRegions());
}

BOOST_AUTO_TEST_CASE(SplitWeightsIntoGroups) {
  auto device = createTestDevice(TEST_TARGET);
  Graph graph(device.getTarget());

  const auto g = 4;
  const auto h = 101;
  const auto w = 102;
  const auto co = 15;
  const auto ci = 16;
  const auto uut = graph.addVariable(HALF, {g, h, w, co, ci});

  const auto cgpg = 4;
  const auto copg = 3;
  const auto cipg = 8;

  BOOST_TEST(g % cgpg == 0);
  BOOST_TEST(co % copg == 0);
  BOOST_TEST(ci % cipg == 0);

  const auto result = poplin::splitWeightsIntoGroups(uut, cgpg, cipg, copg);
  const auto &resultShape = result.shape();
  BOOST_TEST(resultShape.size() == uut.shape().size() + 3);

  std::vector<std::size_t> expected{g / cgpg, co / copg, ci / cipg, h,
                                    w,        cgpg,      copg,      cipg};
  BOOST_TEST(resultShape == expected, boost::test_tools::per_element());

  const auto t = poplin::unsplitWeightsFromGroups(result);
  BOOST_TEST(t.shape() == uut.shape(), boost::test_tools::per_element());
  BOOST_TEST(t.getVarRegions() == uut.getVarRegions());
}

BOOST_AUTO_TEST_CASE(DetectActivationChannelGrouping) {
  auto device = createTestDevice(TEST_TARGET);
  Graph graph(device.getTarget());

  const auto numConvGroupGroups = 5;
  const auto numInChanGroups = 7;

  const auto n = 100;
  const auto h = 101;
  const auto w = 102;

  const auto convGroupsPerGroup = 4;
  const auto inChansPerGroup = 16;

  // how the weights are laid out in memory.
  auto t = graph.addVariable(HALF, {numConvGroupGroups, numInChanGroups, n, h,
                                    w, convGroupsPerGroup, inChansPerGroup});
  graph.setTileMapping(t, 0);

  // reshape to internal shape.
  t = poplin::unsplitActivationFromGroups(t);

  auto detectedGrouping = poplin::detectChannelGrouping(graph, t);
  BOOST_CHECK_EQUAL(convGroupsPerGroup, detectedGrouping.convGroupsPerGroup);
  BOOST_CHECK_EQUAL(inChansPerGroup, detectedGrouping.chansPerGroup);
}

BOOST_AUTO_TEST_CASE(DetectWeightsChannelGrouping) {
  auto device = createTestDevice(TEST_TARGET);
  Graph graph(device.getTarget());

  const auto numConvGroupGroups = 5;
  const auto numOutChanGroups = 6;
  const auto numInChanGroups = 7;

  const auto kh = 10;
  const auto kw = 11;

  const auto convGroupsPerGroup = 4;
  const auto outChansPerGroup = 8;
  const auto inChansPerGroup = 16;

  // how the weights are laid out in memory.
  auto t = graph.addVariable(HALF, {numConvGroupGroups, numOutChanGroups,
                                    numInChanGroups, kh, kw, convGroupsPerGroup,
                                    outChansPerGroup, inChansPerGroup});
  graph.setTileMapping(t, 0);

  // reshape to internal shape.
  t = poplin::unsplitWeightsFromGroups(t);

  auto detectedGrouping = poplin::detectWeightsChannelGrouping(graph, t);
  BOOST_CHECK_EQUAL(convGroupsPerGroup, detectedGrouping.convGroupsPerGroup);
  BOOST_CHECK_EQUAL(outChansPerGroup, detectedGrouping.outChansPerGroup);
  BOOST_CHECK_EQUAL(inChansPerGroup, detectedGrouping.inChansPerGroup);
}

BOOST_AUTO_TEST_CASE(SplitTilesByComp) {
  BOOST_CHECK_THROW(poplin::splitElementsInWeightedGroups({1, 1}, 1),
                    poputil::poplibs_error);
  {
    const auto tiles =
        poplin::splitElementsInWeightedGroups({123, 456, 789}, 1216);
    std::vector<unsigned> expectation = {109, 406, 701};
    BOOST_TEST(tiles == expectation);
  }
  {
    const auto tiles = poplin::splitElementsInWeightedGroups({1, 4, 1}, 1216);
    std::vector<unsigned> expectation = {203, 810, 203};
    BOOST_TEST(tiles == expectation);
  }
  {
    const auto tiles =
        poplin::splitElementsInWeightedGroups({1, 1, 1, 1, 4, 10}, 1216);
    std::vector<unsigned> expectation = {68, 67, 68, 67, 270, 676};
    BOOST_TEST(tiles == expectation);
  }
  {
    const auto tiles =
        poplin::splitElementsInWeightedGroups({1, 10000, 1}, 1216);
    std::vector<unsigned> expectation = {1, 1214, 1};
    BOOST_TEST(tiles == expectation);
  }
  {
    const auto tiles =
        poplin::splitElementsInWeightedGroups({1, 1, 1, 1, 10, 10000}, 1216);
    std::vector<unsigned> expectation = {1, 1, 1, 1, 2, 1210};
    BOOST_TEST(tiles == expectation);
  }
  BOOST_CHECK_THROW(poplin::splitTilesByComp({1, 1, 1}, 3),
                    poputil::poplibs_error);
  {
    const auto tiles = poplin::splitTilesByComp({123, 456, 789}, 1216);
    std::vector<unsigned> expectation = {110, 404, 702};
    BOOST_TEST(tiles == expectation);
  }
  {
    const auto tiles = poplin::splitTilesByComp({1, 1, 1, 1, 10, 1000}, 1216);
    std::vector<unsigned> expectation = {2, 2, 2, 2, 12, 1196};
    BOOST_TEST(tiles == expectation);
  }
  {
    const auto tiles = poplin::splitTilesByComp({1, 1, 1, 1, 10, 10000}, 1216);
    std::vector<unsigned> expectation = {2, 2, 2, 2, 2, 1206};
    BOOST_TEST(tiles == expectation);
  }
  {
    const auto tiles = poplin::splitTilesByComp({4294967296, 1}, 1216);
    std::vector<unsigned> expectation = {1214, 2};
    BOOST_TEST(tiles == expectation);
  }
  {
    const std::vector<unsigned> groups{3, 0, 4, 1};
    BOOST_TEST(poplin::getGroupIndex(groups, 0) == 0);
    BOOST_TEST(poplin::getGroupIndex(groups, 2) == 0);
    BOOST_TEST(poplin::getGroupIndex(groups, 3) == 2);
    BOOST_TEST(poplin::getGroupIndex(groups, 6) == 2);
    BOOST_TEST(poplin::getGroupIndex(groups, 7) == 3);
    BOOST_CHECK_THROW(poplin::getGroupIndex(groups, 8), poputil::poplibs_error);
  }
}


#include "testsuite.h"
#include <blitz/array.h>

BZ_USING_NAMESPACE(blitz)

int main()
{
    Array<float,1> A(4), B(4);
    firstIndex i;
    A = i;
    B = 0, 1, 2, 3;
    BZTEST(count(A==B)==4);
    return 0;
}


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

  @copyright 2016 NumScale SAS

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

#include <boost/simd/meta/as_logical.hpp>
#include <boost/simd/constant/smallestposval.hpp>
#include <boost/simd/function/abs.hpp>
#include <boost/simd/function/is_less.hpp>
#include <boost/simd/function/is_nez.hpp>
#include <boost/simd/function/logical_and.hpp>
#include <boost/simd/constant/false.hpp>
#include <boost/simd/logical.hpp>
#include <boost/simd/detail/dispatch/function/overload.hpp>
#include <boost/config.hpp>

namespace boost { namespace simd { namespace ext
{
  namespace bd = boost::dispatch;
  namespace bs = boost::simd;
  BOOST_DISPATCH_OVERLOAD_IF ( is_denormal_
                          , (typename A0, typename X)
                          , (detail::is_native<X>)
                          , bd::cpu_
                          , bs::pack_< bd::arithmetic_<A0>, X>
                          )
  {
    using result = bs::as_logical_t<A0>;
    BOOST_FORCEINLINE  result  operator() ( A0 ) const BOOST_NOEXCEPT
    {
      return False<result>();
    }
  };
  BOOST_DISPATCH_OVERLOAD_IF ( is_denormal_
                          , (typename A0, typename X)
                          , (detail::is_native<X>)
                          , bd::cpu_
                          , bs::pack_< bd::floating_<A0>, X>
                          )
  {
    BOOST_FORCEINLINE  bs::as_logical_t<A0> operator() ( A0 const& a0) const BOOST_NOEXCEPT
    {
      return logical_and(is_nez(a0), is_less(bs::abs(a0), Smallestposval<A0>()));
    }
  };
} } }

#endif


#include "f1_datalogger/post_processing/post_processing_utils.h"
#include <boost/filesystem.hpp>
#include <iostream>
#include <google/protobuf/util/json_util.h>
#include <alglib/interpolation.h>
namespace fs = boost::filesystem;
namespace deepf1
{
namespace post_processing
{
	PostProcessingUtils::PostProcessingUtils()
	{
	}
	PostProcessingUtils::~PostProcessingUtils()
	{
	}
	void get_all(const fs::path& root, const std::string& ext, std::vector<fs::path>& ret)
	{
		if (!fs::exists(root) || !fs::is_directory(root)) return;

		fs::recursive_directory_iterator it(root);
		fs::recursive_directory_iterator endit;

		while (it != endit)
		{
			if (fs::is_regular_file(*it) && it->path().extension() == ext) ret.push_back(it->path().filename());
			++it;

		}

	}
	bool udpComp(const deepf1::protobuf::F1UDPData& a, const deepf1::protobuf::F1UDPData& b)
	{
		return a.logger_time() < b.logger_time();
	}
	bool imageComp(const deepf1::protobuf::TimestampedImage& a, const deepf1::protobuf::TimestampedImage& b)
	{
		return a.timestamp() < b.timestamp();
	}
	unsigned int closestValueHelper(const std::vector<deepf1::protobuf::F1UDPData>& sorted_data,
	 int64_t search, unsigned int left, unsigned int right)
	{
		if(left == right)
		{
			return left;

		}
		else if((right-left)==1)
		{
			if(std::abs(sorted_data.at(left).logger_time() - search) < std::abs(sorted_data.at(right).logger_time() - search))
			{
				return left;
			}
			else
			{
				return right;
			}
		}
		else
		{
			unsigned int middle = (left + right)/2;
			int64_t query_val = sorted_data.at(middle).logger_time();
			if(query_val == search)
			{
				return middle;
			}
			else if(query_val < search)
			{
				return closestValueHelper(sorted_data, search, middle, right);
			}
			else
			{
				return closestValueHelper(sorted_data, search, left, middle);
			}
		}
	}
	std::pair<deepf1::protobuf::F1UDPData, unsigned int> PostProcessingUtils::closestValue(const std::vector<deepf1::protobuf::F1UDPData>& sorted_data, int64_t search)
	{
		unsigned int index = closestValueHelper(sorted_data, search, 0, sorted_data.size()-1);

		return std::pair<deepf1::protobuf::F1UDPData, unsigned int>(sorted_data.at(index), index);
	}
	std::vector<float> interp(const std::vector<deepf1::protobuf::F1UDPData>& udp_data, unsigned int closest_index, unsigned int interpolation_order, int64_t image_timestamp)
	{
		std::vector<float> rtn(3);

		alglib::barycentricinterpolant steering_interp, brake_interp, throttle_interp;
		alglib::real_1d_array timestamps, steering, brake, throttle;
		timestamps.setlength(interpolation_order);
		steering.setlength(interpolation_order);
		brake.setlength(interpolation_order);
		throttle.setlength(interpolation_order);
		unsigned int lower_bound = closest_index - interpolation_order / 2;
		unsigned int upper_bound = closest_index + interpolation_order / 2 ;
		unsigned int idx = 0;
//		printf("Building alglib vectors at index %d. \n", closest_index);
		for (unsigned int i = lower_bound; i <= upper_bound; i++)
		{
			timestamps(idx) = (double)udp_data.at(i).logger_time();
			steering(idx) = (double)udp_data.at(i).steering();
			brake(idx) = (double)udp_data.at(i).brake();
			throttle(idx) = (double)udp_data.at(i).throttle();
			idx++;
		}
	//	printf("Built alglib vectors. \n");
		alglib::polynomialbuild(timestamps, steering, interpolation_order, steering_interp);
		alglib::polynomialbuild(timestamps, brake, interpolation_order, brake_interp);
		alglib::polynomialbuild(timestamps, throttle, interpolation_order, throttle_interp);
	//	printf("Built alglib models. \n");

		double t = (double)image_timestamp;
		rtn[0] = (float)alglib::barycentriccalc( steering_interp,  t);
		rtn[1] = (float)alglib::barycentriccalc( throttle_interp, t);
		rtn[2] = (float)alglib::barycentriccalc( brake_interp,  t);

		return rtn;
	}
	std::vector<deepf1::protobuf::LabeledImage> PostProcessingUtils::labelImages(std::vector<deepf1::protobuf::F1UDPData>& udp_data, std::vector<deepf1::protobuf::TimestampedImage>& image_data, unsigned int interpolation_order)
	{
		printf("Labeling points.\n");
		std::vector<deepf1::protobuf::LabeledImage> rtn;
		std::sort(udp_data.begin(), udp_data.end(), &udpComp);
		std::sort(image_data.begin(), image_data.end(), &imageComp);

		rtn.resize(image_data.size());
		for(unsigned int i = 0; i < image_data.size(); i ++)
		{
			deepf1::protobuf::TimestampedImage image_point = image_data.at(i);

			//printf("Processing image with filename: %s", image_point.image_file().c_str());

			std::pair<deepf1::protobuf::F1UDPData, unsigned int> pair = closestValue(udp_data, image_point.timestamp());
			deepf1::protobuf::F1UDPData closest_packet = udp_data.at(pair.second);
			if ( pair.second < interpolation_order || pair.second >(udp_data.size() - interpolation_order) )
				continue;

		//	printf("Image file %s with index #%u and timestamp %ld is closest to udp index %u with timestamp %ld. Delta=%ld\n",
			//	image_point.image_file().c_str(), i, image_point.timestamp(), pair.second, closest_packet.logger_time(), closest_packet.logger_time()-image_point.timestamp());
			deepf1::protobuf::LabeledImage im;

		//	printf("Interpolating on order %u \n", interpolation_order);
			std::vector<float> interp_results = interp(udp_data, pair.second, interpolation_order, image_point.timestamp());
			rtn.at(i).set_image_file(std::string(image_point.image_file()));
			rtn.at(i).set_steering(interp_results[0]);
			rtn.at(i).set_throttle(interp_results[1]);
			rtn.at(i).set_brake(interp_results[2]);
		//	printf("Done interpolating \n");
			/*
			std::string json;
			google::protobuf::util::MessageToJsonString(rtn.at(i), &json);
			std::cout << "Labeled image: " << json << std::endl;
			*/
			//rtn.push_back(deepf1::protobuf::LabeledImage(im));
		}
	//	rtn.shrink_to_fit();

		return rtn;
	}
	std::vector<deepf1::protobuf::F1UDPData> PostProcessingUtils::parseUDPDirectory(const std::string& directory)
	{
		std::vector<deepf1::protobuf::F1UDPData> rtn;

		std::vector<fs::path> paths;
		fs::path udp_dir(directory);
		get_all(udp_dir, ".pb", paths);
		std::ifstream stream_in;
		for (fs::path path : paths)
		{
			fs::path current_path = udp_dir / path;
	//		std::cout << "Loading file: " << current_path.string() << std::endl;
			stream_in.open(current_path.string().c_str());
			deepf1::protobuf::F1UDPData data_in;
			bool success = data_in.ParseFromIstream(&stream_in);
			stream_in.close();
			if (!success || data_in.logger_time()==0)
			{

				std::cout << "FOUND EMPTY UDP PACKET" << std::endl;
				continue;
			}

			//std::string json;
			//google::protobuf::util::MessageToJsonString(data_in, &json);
			//std::cout << "Got data: " << std::endl << json << std::endl;
			rtn.push_back(data_in);
		}

		return rtn;
	}
	std::vector<deepf1::protobuf::TimestampedImage> PostProcessingUtils::parseImageDirectory(const std::string& directory)
	{
		std::vector<deepf1::protobuf::TimestampedImage> rtn;

		std::vector<fs::path> paths;
		fs::path udp_dir(directory);
		get_all(udp_dir, ".pb", paths);
		std::ifstream stream_in;
		for (fs::path path : paths)
		{
			fs::path current_path = udp_dir / path;
			//		std::cout << "Loading file: " << current_path.string() << std::endl;
			stream_in.open(current_path.string().c_str());
			deepf1::protobuf::TimestampedImage data_in;
			bool success = data_in.ParseFromIstream(&stream_in);
			stream_in.close();
			if (!success || data_in.image_file().empty())
			{
				std::cout << "FOUND EMPTY IMAGE FILENAME" << std::endl;
				continue;
			}
			/*
			std::string json;
			google::protobuf::util::MessageToJsonString(data_in, &json);
			std::cout << "Got data: " << std::endl << json << std::endl;
			*/
			rtn.push_back(data_in);
		}

		return rtn;

	}
}
}


#include <sstream>
#include <boost/program_options.hpp>

#include <frovedis/ml/fm/fm.hpp>
#include <frovedis/matrix/crs_matrix.hpp>

using namespace boost;
using namespace frovedis;
using namespace std;

int main(int argc, char* argv[]) {
  use_frovedis use(argc, argv);

  // debug option
  // set_loglevel(TRACE);

  using namespace boost::program_options;

  string model_path, input_path, label_path;
  bool is_binary_model, is_binary_data;

  options_description opt("option");
  opt.add_options()
    ("help,h", "print help")
    ("model,m", value<string>(&model_path)->required(), "filename for trained model, fm_model")
    ("input,i", value<string>(&input_path)->required(), "filename for test data, crs matrix format")
    ("label,l", value<string>(&label_path)->required(), "filename for test label, dvector format")
    ("binary-model", bool_switch(&is_binary_model)->default_value(false, "false"), "use binary for trained model")
    ("binary-data", bool_switch(&is_binary_data)->default_value(false, "false"), "use binary for test data and label")
    ;

  variables_map argmap;
  store(command_line_parser(argc,argv).options(opt).allow_unregistered().run(), argmap);
  if(argmap.count("help")){
    cerr << opt << endl;
    exit(1);
  }

  notify(argmap);

  fm::fm_model<double> trained_model;
  if (is_binary_model) {
    throw std::runtime_error("Loading binary fm model is not implemented yet.");
  } else {
    trained_model = fm::load_fm_model<double>(model_path);
  }

  crs_matrix_local<double> test_data;
  std::vector<double> label;
  if (is_binary_data) {
    test_data = make_crs_matrix_local_loadbinary<double>(input_path);
    label = make_dvector_loadbinary<double>(label_path).gather();
  } else {
    test_data = make_crs_matrix_local_load<double>(input_path);
    label = make_dvector_loadline<double>(label_path).gather();
  }

  auto result = fm_test(trained_model, test_data, label);
  std::cout << "# of samples: " << test_data.local_num_row << std::endl;
  if (trained_model.config.is_regression) {
    std::cout << "RMSE: " << result << std::endl;
  } else {
    std::cout << "Accuracy: " << result << std::endl;
  }

  return 0;
}


// Boost.Geometry (aka GGL, Generic Geometry Library)

// Copyright (c) 2014-2020, Oracle and/or its affiliates.

// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle

// Licensed under the Boost Software License version 1.0.
// http://www.boost.org/users/license.html

#ifndef BOOST_GEOMETRY_TEST_GET_TURNS_LL_INVARIANCE_HPP
#define BOOST_GEOMETRY_TEST_GET_TURNS_LL_INVARIANCE_HPP

#include <vector>

#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/size.hpp>

#include <boost/geometry/algorithms/reverse.hpp>

#include <boost/geometry/algorithms/detail/signed_size_type.hpp>

#include <boost/geometry/algorithms/detail/relate/turns.hpp>

#include <boost/geometry/algorithms/detail/turns/compare_turns.hpp>
#include <boost/geometry/algorithms/detail/turns/print_turns.hpp>
#include <boost/geometry/algorithms/detail/turns/filter_continue_turns.hpp>
#include <boost/geometry/algorithms/detail/turns/remove_duplicate_turns.hpp>

#include <boost/geometry/io/wkt/write.hpp>

namespace bg = ::boost::geometry;
namespace bg_detail = ::boost::geometry::detail;
namespace bg_turns = bg_detail::turns;

template
<
    bool Enable = true,
    bool EnableRemoveDuplicateTurns = true,
    bool EnableDegenerateTurns = true
>
class test_get_turns_ll_invariance
{
private:
    struct assign_policy
    {
        static bool const include_no_turn = false;
        static bool const include_degenerate = EnableDegenerateTurns;
        static bool const include_opposite = false;
        static bool const include_start_turn = false;

        template
        <
            typename Info,
            typename Point1,
            typename Point2,
            typename IntersectionInfo
        >
        static inline void apply(Info& , Point1 const& , Point2 const& ,
                                 IntersectionInfo const&)
        {
        }
    };

    template
    <
        typename Turns,
        typename LinearGeometry1,
        typename LinearGeometry2
    >
    static inline void compute_turns(Turns& turns,
                                     LinearGeometry1 const& linear1,
                                     LinearGeometry2 const& linear2)
    {
        turns.clear();
        bg_detail::relate::turns::get_turns
            <
                LinearGeometry1,
                LinearGeometry2,
                bg_detail::get_turns::get_turn_info_type
                <
                    LinearGeometry1,
                    LinearGeometry2,
                    assign_policy
                >
            >::apply(turns, linear1, linear2);
    }

public:
    template <typename Linear1, typename Linear2>
    static inline void apply(Linear1 const& lineargeometry1,
                             Linear2 const& lineargeometry2)
    {
        typedef typename bg::strategy::relate::services::default_strategy
            <
                Linear1, Linear2
            >::type strategy_type;

        typedef typename bg_detail::relate::turns::get_turns
            <
                Linear1, Linear2
            >::template turn_info_type<strategy_type>::type turn_info;

        typedef std::vector<turn_info> turns_container;

        typedef bg_turns::filter_continue_turns
            <
                turns_container, true
            > filter_continue_turns;

        typedef bg_turns::remove_duplicate_turns
            <
                turns_container, EnableRemoveDuplicateTurns
            > remove_duplicate_turns;

        turns_container turns;

        Linear1 linear1(lineargeometry1);
        Linear2 linear2(lineargeometry2);

        Linear2 linear2_reverse(linear2);
        boost::geometry::reverse(linear2_reverse);

        turns_container turns_all, rturns_all;
        compute_turns(turns_all, linear1, linear2);
        compute_turns(rturns_all, linear1, linear2_reverse);

        turns_container turns_wo_cont(turns_all);
        turns_container rturns_wo_cont(rturns_all);

        filter_continue_turns::apply(turns_wo_cont);
        filter_continue_turns::apply(rturns_wo_cont);

        std::sort(boost::begin(turns_all), boost::end(turns_all),
                  bg_turns::less_seg_fraction_other_op<>());

        std::sort(boost::begin(turns_wo_cont), boost::end(turns_wo_cont),
                  bg_turns::less_seg_fraction_other_op<>());

        std::sort(boost::begin(rturns_all), boost::end(rturns_all),
                  bg_turns::less_seg_fraction_other_op<std::greater<boost::geometry::signed_size_type> >());

        std::sort(boost::begin(rturns_wo_cont), boost::end(rturns_wo_cont),
                  bg_turns::less_seg_fraction_other_op<std::greater<boost::geometry::signed_size_type> >());

        remove_duplicate_turns::apply(turns_all);
        remove_duplicate_turns::apply(turns_wo_cont);
        remove_duplicate_turns::apply(rturns_all);
        remove_duplicate_turns::apply(rturns_wo_cont);

#ifdef BOOST_GEOMETRY_TEST_DEBUG
        std::cout << std::endl << std::endl;
        std::cout << "### ORIGINAL TURNS ###" << std::endl;
        bg_turns::print_turns(linear1, linear2, turns_all);
        std::cout << std::endl << std::endl;
        std::cout << "### ORIGINAL REVERSE TURNS ###" << std::endl;
        bg_turns::print_turns(linear1, linear2_reverse, rturns_all);
        std::cout << std::endl << std::endl;
        std::cout << "### TURNS W/O CONTINUE TURNS ###" << std::endl;
        bg_turns::print_turns(linear1, linear2, turns_wo_cont);
        std::cout << std::endl << std::endl;
        std::cout << "### REVERSE TURNS W/O CONTINUE TURNS ###" << std::endl;
        bg_turns::print_turns(linear1, linear2_reverse, rturns_wo_cont);
        std::cout << std::endl << std::endl;
#endif

        BOOST_CHECK_MESSAGE(boost::size(turns_wo_cont) == boost::size(rturns_wo_cont),
                            "Incompatible turns count: " << boost::size(turns_wo_cont) <<
                            " and " << boost::size(rturns_wo_cont) <<
                            " for L1: " << bg::wkt(lineargeometry1) <<
                            ", L2: " << bg::wkt(lineargeometry2));
    }
};

template <bool EnableRemoveDuplicateTurns, bool EnableDegenerateTurns>
class test_get_turns_ll_invariance
<
    false, EnableRemoveDuplicateTurns, EnableDegenerateTurns
>
{
public:
    template <typename Linear1, typename Linear2>
    static inline void apply(Linear1 const&, Linear2 const&)
    {
    }
};

#endif // BOOST_GEOMETRY_TEST_GET_TURNS_LL_INVARIANCE_HPP


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

#include <nt2/include/functor.hpp>
#include <boost/simd/constant/hierarchy.hpp>
#include <boost/simd/constant/register.hpp>

namespace nt2
{
  namespace tag
  {
    /*!
      @brief log_2hi generic tag

      Represents the log_2hi constant in generic contexts.

      @par Models:
      Hierarchy
    **/
    BOOST_SIMD_CONSTANT_REGISTER( Log_2hi, double
                                , 0, 0x3f318000UL        //0.693359375f
                                , 0x3fe62e42fee00000ULL  //6.93147180369123816490e-01
                                )
  }
  namespace ext
  {
    template<class Site>
    BOOST_FORCEINLINE generic_dispatcher<tag::Log_2hi, Site> dispatching_Log_2hi(adl_helper, boost::dispatch::meta::unknown_<Site>, ...)
    {
      return generic_dispatcher<tag::Log_2hi, Site>();
    }
    template<class... Args>
    struct impl_Log_2hi;
  }
 /*!
    Generates constant Log_2hi.This constant is coupled with Log2_lo and is
    used in the float logarithms computations
    We have double(Log_2hi<float>())+double(Log2_lo<float>()) == Log_2<double>()

    @par Semantic:

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

  **/
  BOOST_SIMD_CONSTANT_IMPLEMENTATION(tag::Log_2hi, Log_2hi);
}

#endif


#include "../include/hal.hpp"
#include <armadillo>
#include <map>
#include "../include/corpus.hpp"
#include "../include/constants.hpp"
#include "../include/vocabulary.hpp"

using namespace wove;

hal::hal(std::string corpus_name)
{
	this->corpus_name = corpus_name;
	nlp::corpus corpus(this->corpus_name);
	std::vector<std::string> tokens = corpus.get_tokens(0,0);
    nlp::vocabulary vocabulary (tokens,50);
	std::vector<std::string> vocab;
	vocab = vocabulary.get();
	word_vectors(tokens,vocab, true);
}

hal::~hal()
{
}

void hal::word_vectors(std::vector<std::string> tokens, std::vector<std::string> vocab, bool save)
{
	int size = tokens.size();
	int repeat_reading = 1;
	arma::sp_mat hal_matrix(vocab.size(),vocab.size());

	nlp::corpus corpus(this->corpus_name);
	corpus.seek_position = 0;

	if ( size > nlp::TOKENS_TO_READ )
		repeat_reading = size/nlp::TOKENS_TO_READ;

	for (int i = 0; i < repeat_reading; i++)
        {
            std::vector<std::string> partial_tokens = corpus.get_tokens(corpus.seek_position, nlp::TOKENS_TO_READ);
            if (i % 1000 == 0)
                std::cerr << "read = " << partial_tokens.size() << std::endl;
            hal_matrix += build_hal(partial_tokens, vocab, false);
        }
        std::cerr << "Completed l2r" << std::endl;

        corpus.seek_position = 0;

        for (long long i = 0; i < repeat_reading; i++)
        {
            std::vector<std::string> partial_tokens = corpus.get_tokens(corpus.seek_position, nlp::TOKENS_TO_READ);
            if (i % repeat_reading == 0)
                std::cerr << "count = " << partial_tokens.size() << std::endl;
            hal_matrix += build_hal(partial_tokens, vocab, true);
        }
        std::cerr << "Completed r2l" << std::endl;

        hal_matrix = hal_matrix / 2.0;
		if ( save )
			hal_matrix.save("hal_matrix.bin");
		else
		{
			arma::cout << hal_matrix;
		}
}

arma::sp_mat hal::build_hal(std::vector<std::string> tokens, std::vector<std::string> vocab, bool reverse_tokens)
{
	arma::sp_mat hal_matrix(vocab.size(), vocab.size());

	std::map<std::string, int> vocab_map;
	int i = 0;
	for (auto const &x : vocab)
	{
		vocab_map[x] = i++;
	}

	int gram_size = 11;

	std::vector<int> ramp;
	ramp.push_back(0);
	for (int i = gram_size - 1; i > 0; i--)
		ramp.push_back(i);

    std::vector<std::string> vec;
    auto first = tokens.begin();
    auto last = tokens.end();
    for (auto i = first; i < (last - gram_size+1); i++)
	{
		std::vector<std::string> grams;
		grams.assign(i, i + gram_size);

		if (reverse_tokens)
			reverse(grams.begin(), grams.end());

		long long m = 0;
		if (vocab_map.count(grams[0]))
		{
			m = vocab_map[grams[0]];
		}
		else
		{
			continue;
		}

		unsigned long k = 0;

		for (unsigned long j = 0; j < grams.size(); j++)
		{
			long n = 0;
			if (vocab_map.count(grams[j]) == 0)
			{
				continue;
			}
			else
			{
				n = vocab_map[grams[j]];
			}
			if ( ramp[k] > 0 ){
				hal_matrix(m, n) += ramp[k++];
			}
			else
				k++;
		}
	}

	return hal_matrix;
}


//=======================================================================
// Copyright (c)
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================
/**
 * @file bounded_degree_mst.hpp
 * @brief
 * @author Piotr Godlewski
 * @version 1.0
 * @date 2013-06-03
 */
#ifndef PAAL_BOUNDED_DEGREE_MST_HPP
#define PAAL_BOUNDED_DEGREE_MST_HPP

#include "paal/iterative_rounding/bounded_degree_min_spanning_tree/bounded_degree_mst_oracle.hpp"
#include "paal/iterative_rounding/ir_components.hpp"
#include "paal/iterative_rounding/iterative_rounding.hpp"
#include "paal/lp/lp_row_generation.hpp"

#include <boost/bimap.hpp>
#include <boost/range/as_array.hpp>
#include <boost/graph/connected_components.hpp>

namespace paal {
namespace ir {

namespace {
struct bounded_degree_mst_compare_traits {
    static const double EPSILON;
};

const double bounded_degree_mst_compare_traits::EPSILON = 1e-10;
}

/**
 * @class bounded_degree_mst
 * @brief The class for solving the Bounded Degree MST problem using Iterative
* Rounding.
 *
 * @tparam Graph input graph
 * @tparam DegreeBounds map from Graph vertices to degree bounds
 * @tparam CostMap map from Graph edges to costs
 * @tparam VertexIndex map from Graph vertices to indices
 * @tparam SpanningTreeOutputIterator
 * @tparam Oracle separation oracle
 */
template <typename Graph, typename DegreeBounds, typename CostMap,
          typename VertexIndex, typename SpanningTreeOutputIterator,
          typename Oracle = paal::lp::random_violated_separation_oracle>
class bounded_degree_mst {
  public:
    /**
     * Constructor.
     */
    bounded_degree_mst(const Graph & g, const DegreeBounds & deg_bounds,
                    CostMap cost_map, VertexIndex index,
                    SpanningTreeOutputIterator result_spanning_tree, Oracle oracle = Oracle{}) :
              m_g(g), m_cost_map(cost_map), m_index(index), m_deg_bounds(deg_bounds),
              m_result_spanning_tree(result_spanning_tree),
              m_compare(bounded_degree_mst_compare_traits::EPSILON),
              m_oracle(oracle)
    {}

    using Edge = typename boost::graph_traits<Graph>::edge_descriptor;
    using Vertex = typename boost::graph_traits<Graph>::vertex_descriptor;

    using EdgeMap = boost::bimap<Edge, lp::col_id>;
    using VertexMap = std::unordered_map<lp::row_id, Vertex>;

    using EdgeMapOriginal = std::vector<std::pair<lp::col_id, Edge>>;

    using ErrorMessage = boost::optional<std::string>;

    /**
     * Checks if the input graph is connected.
     */
    ErrorMessage check_input_validity() {
        // Is g connected?
        std::vector<int> components(num_vertices(m_g));
        int num = boost::connected_components(m_g, &components[0]);

        if (num > 1) {
            return ErrorMessage{ "The graph is not connected." };
        }

        return ErrorMessage{};
    }

    /**
     * @brief
     *
     * @tparam LP
     * @param lp
     *
     * @return
     */
    template <typename LP>
    auto get_find_violation(LP & lp) {
        using candidate = bdmst_violation_checker::Candidate;
        return m_oracle([&](){return m_violation_checker.get_violation_candidates(*this, lp);},
                        [&](candidate c){return m_violation_checker.check_violation(c, *this);},
                        [&](candidate c){return m_violation_checker.add_violated_constraint(c, *this, lp);});
    }

    /**
     * Returns the input graph.
     */
    const Graph &get_graph() const { return m_g; }

    /**
     * Returns the vertex index.
     */
    const VertexIndex &get_index() const { return m_index; }

    /**
     * Removes an LP column and the graph edge corresponding to it.
     */
    void remove_column(lp::col_id col_id) {
        auto ret = m_edge_map.right.erase(col_id);
        assert(ret == 1);
    }

    /**
     * Binds a graph edge to a LP column.
     */
    void bind_edge_to_col(Edge e, lp::col_id col) {
        m_edge_map_original.push_back(
            typename EdgeMapOriginal::value_type(col, e));
        m_edge_map.insert(typename EdgeMap::value_type(e, col));
    }

    /**
     * Returns the cost of a given edge.
     */
    decltype(get(std::declval<CostMap>(), std::declval<Edge>()))
        get_cost(Edge e) {
        return get(m_cost_map, e);
    }

    /**
     * Returns the degree bound of a vertex.
     */
    decltype(std::declval<DegreeBounds>()(get(std::declval<VertexIndex>(),
                                              std::declval<Vertex>())))
        get_degree_bound(Vertex v) {
        return m_deg_bounds(get(m_index, v));
    }

    /**
     * Returns the LP column corresponding to an edge, if it wasn't deleted from
     * the LP.
     */
    boost::optional<lp::col_id> edge_to_col(Edge e) const {
        auto i = m_edge_map.left.find(e);
        if (i != m_edge_map.left.end()) {
            return i->second;
        } else {
            return boost::none;
        }
    }

    /**
     * Returns a bimap between edges and LP column IDs.
     */
    const EdgeMap &get_edge_map() const { return m_edge_map; }

    /**
     * Returns a mapping between LP column IDs and edges in the original graph.
     */
    const EdgeMapOriginal &get_original_edges_map() const {
        return m_edge_map_original;
    }

    /**
     * Adds an edge to the result spanning tree.
     */
    void add_to_result_spanning_tree(Edge e) {
        *m_result_spanning_tree = e;
        ++m_result_spanning_tree;
    }

    /**
     * Returns the double comparison object.
     */
    utils::compare<double> get_compare() const {
        return m_compare;
    }

    /**
     * Binds a graph vertex to an LP row.
     */
    void bind_vertex_to_row(Vertex v, lp::row_id row) {
        m_vertex_map.insert(typename VertexMap::value_type(row, v));
    }

    /**
     * Unbinds the graph vertex from its corresponding (deleted) LP row.
     */
    void remove_row(lp::row_id row_id) {
        auto ret = m_vertex_map.erase(row_id);
        assert(ret == 1);
    }

    /**
     * Returns the graph vertex corresponding to a given LP row,
     *        unless the row doen't correspond to any vertex.
     */
    boost::optional<Vertex> row_to_vertex(lp::row_id row) {
        auto i = m_vertex_map.find(row);
        if (i != m_vertex_map.end()) {
            return i->second;
        } else {
            return boost::none;
        }
    }

  private:
    Edge col_to_edge(lp::col_id col) {
        auto i = m_edge_map.right.find(col);
        assert(i != m_edge_map.right.end());
        return i->second;
    }

    const Graph &m_g;
    CostMap m_cost_map;
    VertexIndex m_index;
    const DegreeBounds &m_deg_bounds;
    SpanningTreeOutputIterator m_result_spanning_tree;
    bdmst_violation_checker m_violation_checker;

    EdgeMapOriginal m_edge_map_original;
    EdgeMap m_edge_map;
    VertexMap m_vertex_map;

    const utils::compare<double>   m_compare;

    Oracle m_oracle;
};

namespace detail {
/**
 * @brief Creates a bounded_degree_mst object. Non-named version.
 *
 * @tparam Oracle
 * @tparam Graph
 * @tparam DegreeBounds
 * @tparam CostMap
 * @tparam VertexIndex
 * @tparam SpanningTreeOutputIterator
 * @param g
 * @param degBoundMap
 * @param cost_map
 * @param vertex_index
 * @param result_spanning_tree
 * @param oracle
 *
 * @return bounded_degree_mst object
 */
template <typename Oracle = lp::random_violated_separation_oracle,
          typename Graph,
          typename DegreeBounds, typename CostMap, typename VertexIndex,
          typename SpanningTreeOutputIterator>
bounded_degree_mst<Graph, DegreeBounds, CostMap, VertexIndex, SpanningTreeOutputIterator, Oracle>
make_bounded_degree_mst(const Graph & g, const DegreeBounds & deg_bounds,
                      CostMap cost_map, VertexIndex vertex_index,
                      SpanningTreeOutputIterator result_spanning_tree,
                      Oracle oracle = Oracle()) {
    return bounded_degree_mst<Graph, DegreeBounds, CostMap, VertexIndex,
                SpanningTreeOutputIterator, Oracle>(g, deg_bounds, cost_map, vertex_index,
                    result_spanning_tree, oracle);
}
} // detail

/**
 * Creates a bounded_degree_mst object. Named version.
 * The returned object can be used to check input validity or to get a lower
* bound on the
 * optimal solution cost.
 *
 * @tparam Oracle
 * @tparam Graph
 * @tparam DegreeBounds
 * @tparam SpanningTreeOutputIterator
 * @tparam P
 * @tparam T
 * @tparam R
 * @param g
 * @param deg_bounds
 * @param params
 * @param result_spanning_tree
 * @param oracle
 *
 * @return bounded_degree_mst object
 */
template <typename Oracle = lp::random_violated_separation_oracle,
          typename Graph,
          typename DegreeBounds, typename SpanningTreeOutputIterator,
          typename P, typename T, typename R>
auto
make_bounded_degree_mst(const Graph & g,
                      const DegreeBounds & deg_bounds,
                      const boost::bgl_named_params<P, T, R> & params,
                      SpanningTreeOutputIterator result_spanning_tree,
                      Oracle oracle = Oracle())
        -> bounded_degree_mst<Graph, DegreeBounds,
                decltype(choose_const_pmap(get_param(params, boost::edge_weight), g, boost::edge_weight)),
                decltype(choose_const_pmap(get_param(params, boost::vertex_index), g, boost::vertex_index)),
                SpanningTreeOutputIterator, Oracle> {

    return detail::make_bounded_degree_mst(g, deg_bounds,
                choose_const_pmap(get_param(params, boost::edge_weight), g, boost::edge_weight),
                choose_const_pmap(get_param(params, boost::vertex_index), g, boost::vertex_index),
                result_spanning_tree, oracle);
}

/**
 * Creates a bounded_degree_mst object. All default parameters.
 * The returned object can be used to check input validity or to get a lower
* bound on the
 * optimal solution cost.
 *
 * @tparam Oracle
 * @tparam Graph
 * @tparam DegreeBounds
 * @tparam SpanningTreeOutputIterator
 * @param g
 * @param deg_bounds
 * @param result_spanning_tree
 * @param oracle
 *
 * @return bounded_degree_mst object
 */
template <typename Oracle = lp::random_violated_separation_oracle,
          typename Graph, typename DegreeBounds, typename SpanningTreeOutputIterator>
auto
make_bounded_degree_mst(const Graph & g, const DegreeBounds & deg_bounds,
                      SpanningTreeOutputIterator result_spanning_tree,
                      Oracle oracle = Oracle()) ->
        decltype(make_bounded_degree_mst(g, deg_bounds, boost::no_named_parameters(), result_spanning_tree, oracle)) {
    return make_bounded_degree_mst(g, deg_bounds, boost::no_named_parameters(), result_spanning_tree, oracle);
}

/**
 * Round Condition of the IR Bounded Degree MST algorithm.
 */
struct bdmst_round_condition {
    /**
     * Constructor. Takes epsilon used in double comparison.
     */
    bdmst_round_condition(double epsilon =
                              bounded_degree_mst_compare_traits::EPSILON)
        : m_round_zero(epsilon) {}

    /**
     * Checks if a given column of the LP can be rounded to 0.
     * If the column is rounded, the corresponding edge is removed from the
     * graph.
     */
    template <typename Problem, typename LP>
    boost::optional<double> operator()(Problem &problem, const LP &lp,
                                       lp::col_id col) {
        auto ret = m_round_zero(problem, lp, col);
        if (ret) {
            problem.remove_column(col);
        }
        return ret;
    }

  private:
    round_condition_equals<0> m_round_zero;
};

/**
 * Relax Condition of the IR Bounded Degree MST algorithm.
 */
struct bdmst_relax_condition {
    /**
     * Checks if a given row of the LP corresponds to a degree bound and can be
     * relaxed.
     * If the row degree is not greater than the corresponding degree bound + 1,
     * it is relaxed
     * and the degree bound is deleted from the problem.
     */
    template <typename Problem, typename LP>
    bool operator()(Problem &problem, const LP &lp, lp::row_id row) {
        auto vertex = problem.row_to_vertex(row);
        if (vertex) {
            auto ret = (lp.get_row_degree(row) <=
                        problem.get_degree_bound(*vertex) + 1);
            if (ret) {
                problem.remove_row(row);
            }
            return ret;
        } else
            return false;
    }
};

/**
 * Initialization of the IR Bounded Degree MST algorithm.
 */
struct bdmst_init {
    /**
     * Initializes the LP: variables for edges, degree bound constraints
     * and constraint for all edges.
     */
    template <typename Problem, typename LP>
    void operator()(Problem &problem, LP &lp) {
        lp.set_lp_name("bounded degree minimum spanning tree");
        lp.set_optimization_type(lp::MINIMIZE);

        add_variables(problem, lp);
        add_degree_bound_constraints(problem, lp);
        add_all_set_equality(problem, lp);
    }

  private:
    /**
     * Adds a variable to the LP for each edge in the input graph.
     * Binds the LP columns to edges.
     */
    template <typename Problem, typename LP>
    void add_variables(Problem & problem, LP & lp) {
        for (auto e : boost::as_array(edges(problem.get_graph()))) {
            auto col = lp.add_column(problem.get_cost(e), 0, 1);
            problem.bind_edge_to_col(e, col);
        }
    }

    /**
     * Adds a degree bound constraint to the LP for each vertex in the input
     * graph
     * and binds vertices to rows.
     */
    template <typename Problem, typename LP>
    void add_degree_bound_constraints(Problem &problem, LP &lp) {
        auto const &g = problem.get_graph();

        for (auto v : boost::as_array(vertices(g))) {
            lp::linear_expression expr;

            for (auto e : boost::as_array(out_edges(v, g))) {
                expr += *(problem.edge_to_col(e));
            }

            auto row =
                lp.add_row(std::move(expr) <= problem.get_degree_bound(v));
            problem.bind_vertex_to_row(v, row);
        }
    }

    /**
     * Adds an equality constraint to the LP for the set of all edges in the
     * input graph.
     */
    template <typename Problem, typename LP>
    void add_all_set_equality(Problem &problem, LP &lp) {
        lp::linear_expression expr;
        for (auto col : lp.get_columns()) {
            expr += col;
        }
        lp.add_row(std::move(expr) == num_vertices(problem.get_graph()) - 1);
    }
};

/**
 * Set Solution component of the IR Bounded Degree MST algorithm.
 */
struct bdmst_set_solution {
    /**
     * Constructor. Takes epsilon used in double comparison.
     */
    bdmst_set_solution(double epsilon =
                           bounded_degree_mst_compare_traits::EPSILON)
        : m_compare(epsilon) {}

    /**
     * Creates the result spanning tree form the LP (all edges corresponding to
     * columns with value 1).
     */
    template <typename Problem, typename GetSolution>
    void operator()(Problem & problem, const GetSolution & solution) {
        for (auto col_and_edge : problem.get_original_edges_map()) {
            if (m_compare.e(solution(col_and_edge.first), 1)) {
                problem.add_to_result_spanning_tree(col_and_edge.second);
            }
        }
    }

private:
    const utils::compare<double>   m_compare;
};

template <typename Init = bdmst_init,
          typename RoundCondition = bdmst_round_condition,
          typename RelaxContition = bdmst_relax_condition,
          typename SetSolution = bdmst_set_solution,
          typename SolveLPToExtremePoint = ir::row_generation_solve_lp<>,
          typename ResolveLpToExtremePoint = ir::row_generation_solve_lp<>>
using bdmst_ir_components =
    IRcomponents<Init, RoundCondition, RelaxContition, SetSolution,
                 SolveLPToExtremePoint, ResolveLpToExtremePoint>;

namespace detail {
/**
 * @brief Solves the Bounded Degree MST problem using Iterative Rounding.
* Non-named version.
 *
 * @tparam Oracle
 * @tparam Graph
 * @tparam DegreeBounds
 * @tparam CostMap
 * @tparam VertexIndex
 * @tparam SpanningTreeOutputIterator
 * @tparam IRcomponents
 * @tparam Visitor
 * @param g
 * @param degBoundMap
 * @param cost_map
 * @param vertex_index
 * @param result_spanning_tree
 * @param components
 * @param oracle
 * @param visitor
 *
 * @return solution status
 */
template <typename Oracle = lp::random_violated_separation_oracle,
          typename Graph,
          typename DegreeBounds, typename CostMap, typename VertexIndex,
          typename SpanningTreeOutputIterator,
          typename IRcomponents = bdmst_ir_components<>,
          typename Visitor = trivial_visitor>
IRResult bounded_degree_mst_iterative_rounding(
        const Graph & g,
        const DegreeBounds & deg_bounds,
        CostMap cost_map,
        VertexIndex vertex_index,
        SpanningTreeOutputIterator result_spanning_tree,
        IRcomponents components = IRcomponents(),
        Oracle oracle = Oracle(),
        Visitor visitor = Visitor()) {

    auto bdmst = make_bounded_degree_mst(g, deg_bounds, cost_map, vertex_index, result_spanning_tree, oracle);
    return solve_iterative_rounding(bdmst, std::move(components), std::move(visitor));
}
} // detail

/**
 * @brief Solves the Bounded Degree MST problem using Iterative Rounding. Named
* version.
 *
 * @tparam Oracle
 * @tparam Graph
 * @tparam DegreeBounds
 * @tparam SpanningTreeOutputIterator
 * @tparam IRcomponents
 * @tparam Visitor
 * @tparam P
 * @tparam T
 * @tparam R
 * @param g
 * @param deg_bounds
 * @param result_spanning_tree
 * @param params
 * @param components
 * @param oracle
 * @param visitor
 *
 * @return solution status
 */
template <typename Oracle = lp::random_violated_separation_oracle,
          typename Graph,
          typename DegreeBounds, typename SpanningTreeOutputIterator,
          typename IRcomponents = bdmst_ir_components<>,
          typename Visitor = trivial_visitor, typename P, typename T,
          typename R>
IRResult bounded_degree_mst_iterative_rounding(
            const Graph & g,
            const DegreeBounds & deg_bounds,
            const boost::bgl_named_params<P, T, R> & params,
            SpanningTreeOutputIterator result_spanning_tree,
            IRcomponents components = IRcomponents(),
            Oracle oracle = Oracle(),
            Visitor visitor = Visitor()) {

        return detail::bounded_degree_mst_iterative_rounding(g, deg_bounds,
                    choose_const_pmap(get_param(params, boost::edge_weight), g, boost::edge_weight),
                    choose_const_pmap(get_param(params, boost::vertex_index), g, boost::vertex_index),
                    std::move(result_spanning_tree), std::move(components),
                    std::move(oracle), std::move(visitor));
}

/**
 * @brief Solves the Bounded Degree MST problem using Iterative Rounding. All
* default parameters.
 *
 * @tparam Oracle
 * @tparam Graph
 * @tparam DegreeBounds
 * @tparam SpanningTreeOutputIterator
 * @tparam IRcomponents
 * @tparam Visitor
 * @param g
 * @param deg_bounds
 * @param result_spanning_tree
 * @param components
 * @param oracle
 * @param visitor
 *
 * @return solution status
 */
template <typename Oracle = lp::random_violated_separation_oracle, typename Graph,
          typename DegreeBounds, typename SpanningTreeOutputIterator,
          typename IRcomponents = bdmst_ir_components<>,
          typename Visitor = trivial_visitor>
IRResult bounded_degree_mst_iterative_rounding(
            const Graph & g,
            const DegreeBounds & deg_bounds,
            SpanningTreeOutputIterator result_spanning_tree,
            IRcomponents components = IRcomponents(),
            Oracle oracle = Oracle(),
            Visitor visitor = Visitor()) {

        return bounded_degree_mst_iterative_rounding(g, deg_bounds,
                    boost::no_named_parameters(), std::move(result_spanning_tree),
                    std::move(components), std::move(oracle), std::move(visitor));
}

} //! ir
} //! paal
#endif // PAAL_BOUNDED_DEGREE_MST_HPP


#include "phase_solver.h"
#include <boost/numeric/odeint.hpp>

using namespace boost::numeric::odeint;

PhaseSolver::PhaseSolver(std::shared_ptr<PhaseExpression> expression, initial_value initial)
: expr(expression),
 _initial(initial) { }

double PhaseSolver::solve(double end, double step) {
    auto stepper = boost::numeric::odeint::runge_kutta4<state_type>();
    state_type x { _initial.first };
    boost::numeric::odeint::integrate_const(stepper, *expr, x, _initial.second, end, step);
    return x[x.size() - 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_ATAND_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_ATAND_HPP_INCLUDED

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

  /*!
    @ingroup group-trigonometric
    This function object returns the inverse tangent in degree.

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

    @par Note

      For every parameter of floating type `atand(x)`
      returns the arc @c r in the interval  \f$[-90, 90[\f$
      such that <tt>tand(r) == x</tt>.

    @see atan2d, atan2, atan, atanpi, tand

    @par Example:

      @snippet atand.cpp atand

    @par Possible output:

      @snippet atand.txt atand

  **/
  IEEEValue atand(IEEEValue const& x);
} }
#endif

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

#endif