/*
 Copyright 2017 Marius Appel <marius.appel@uni-muenster.de>

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

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

#include <iostream>
#include <boost/program_options.hpp>
#include <boost/filesystem.hpp>
#include <gdal_utils.h>
#include <gdal_priv.h>
#include <ogr_spatialref.h>
#include <thread>
#include "timer.h"

// if true, input image will be cropped and subsampled in the first step at the same time
// as band selection and band rescaling, this adds an additional resammpling and hence might
// leas to slightly smoother results although this should not matter much in practice.
const bool ENABLE_EARLY_SUBSAMPLE = false;

//
struct arg_input {
    std::string output_path;
    std::vector<uint8_t> z;
    std::vector<uint32_t> x;
    std::vector<uint32_t> y;
    std::vector<uint16_t> b;
    std::vector<double> min;
    std::vector<double> max;
    std::string input_file;
    std::uint32_t m;
    bool enable_early_subsample;
    bool verbose;
} args;

typedef struct {
    double left;
    double right;
    double top;
    double bottom;
    std::string srs;
    uint32_t width;
    uint32_t height;
} space_view;

template<typename T>
struct bounds_2d {
    T left, bottom, top, right;
};

template<typename T>
struct coords_2d {
    T x, y;
};

const double EARTH_RADIUS_METERS = 6378137;
const uint16_t TILE_SIZE_PX = 256;

const double EARTH_CIRCUMFERENCE_METERS = 2 * M_PI * EARTH_RADIUS_METERS;
const double WEBMERCATOR_BOUNDS_LEFT = -EARTH_CIRCUMFERENCE_METERS / 2.0; // -20037508.342789244
const double WEBMERCATOR_BOUNDS_LOWER = -EARTH_CIRCUMFERENCE_METERS / 2.0; // -20037508.342789244
const double WEBMERCATOR_BOUNDS_RIGHT = EARTH_CIRCUMFERENCE_METERS / 2.0;  // 20037508.342789244
const double WEBMERCATOR_BOUNDS_UPPER = EARTH_CIRCUMFERENCE_METERS / 2.0;  // 20037508.342789244

/**
 * Returns tile boundaries in web mercator projection
 * @param tile_coords
 * @param zoom_level
 * @return
 */
bounds_2d<double> tile_bounds(coords_2d<uint32_t> tile_coords, uint8_t zoom_level) {
    bounds_2d<double> out;
    out.left = WEBMERCATOR_BOUNDS_LEFT + tile_coords.x * EARTH_CIRCUMFERENCE_METERS / std::pow(2, zoom_level);
    out.right = WEBMERCATOR_BOUNDS_LEFT + (tile_coords.x + 1) * EARTH_CIRCUMFERENCE_METERS / std::pow(2, zoom_level);
    out.top = WEBMERCATOR_BOUNDS_UPPER - tile_coords.y * EARTH_CIRCUMFERENCE_METERS / std::pow(2, zoom_level);
    out.bottom = WEBMERCATOR_BOUNDS_UPPER - (tile_coords.y + 1) * EARTH_CIRCUMFERENCE_METERS / std::pow(2, zoom_level);
    return out;
}

void affine(double a[6], double* iox, double* ioy, uint32_t n) {
    for (uint32_t i=0; i<n; ++i) {
        double x = a[0] + a[1] * iox[i] + a[2] * ioy[i];
        ioy[i] = a[3] + a[4] * iox[i] + a[5] * ioy[i];
        iox[i] = x;
    }
}

void affine_inv(double a[6], double* iox, double* ioy, uint32_t n) {
    double idet=1/(a[1]*a[5] - a[2]*a[4]);
    for (uint32_t i=0; i<n; ++i) {
        double x = idet *  ( a[5] * (iox[i] - a[0])   - a[2] * (ioy[i] - a[3])  );
        ioy[i] = idet *  ( - a[4] * (iox[i] - a[0])   + a[1] * (ioy[i] - a[3])  );
        iox[i] = x;
    }
}

int generate_tile(space_view sview, std::string filename) {

    sview.width = 256;
    sview.height = 256;

    // Step 1: Build in-memory VRT that has
    // - only selected input bands
    // - Byte data type and color scaling information

    std::string orig_vrt_path = args.input_file;

    GDALDataset *orig_vrt = (GDALDataset *) GDALOpenShared(orig_vrt_path.c_str(), GA_ReadOnly);
    if (orig_vrt == NULL) {
        std::cout << "ERROR: GDALOpen failed for original VRT." << std::endl;
        return 1;
    }

    std::string bandsel_vrt_path = "";
    CPLStringList translate_args(NULL);
    translate_args.AddString("-of");
    translate_args.AddString("VRT");

    for (uint8_t i = 0; i < args.b.size(); ++i) {
        translate_args.AddString("-b");
        translate_args.AddString(std::to_string(args.b[i]).c_str());
    }

    translate_args.AddString("-ot");
    translate_args.AddString("Byte");

    // TODO: try out gdal_translate with reduced resolution / cropped window (see below, not sure about performance benefit)
    if ( args.enable_early_subsample) {
        bounds_2d<double> dblcrop;
        dblcrop.left = sview.left - 0.5 * (sview.right - sview.left);
        dblcrop.right = sview.right + 0.5 * (sview.right - sview.left);
        dblcrop.top = sview.top + 0.5 * (sview.top - sview.bottom);
        dblcrop.bottom = sview.bottom - 0.5 * (sview.top - sview.bottom);

        translate_args.AddString("-projwin");
        translate_args.AddString(std::to_string(dblcrop.left).c_str());
        translate_args.AddString(std::to_string(dblcrop.top).c_str());
        translate_args.AddString(std::to_string(dblcrop.right).c_str());
        translate_args.AddString(std::to_string(dblcrop.bottom).c_str());
        translate_args.AddString("-projwin_srs");
        translate_args.AddString(sview.srs.c_str());

        translate_args.AddString("-outsize");
        translate_args.AddString(std::to_string(2*sview.width).c_str());
        translate_args.AddString(std::to_string(2*sview.height).c_str());
    }

    if (args.min.empty()) {
        translate_args.AddString("-scale");
    } else {
        for (uint16_t i = 0; i < args.b.size(); ++i) {
            translate_args.AddString((std::string("-scale_") + std::to_string(i + 1)).c_str());
            translate_args.AddString(std::to_string(args.min[i]).c_str());
            translate_args.AddString(std::to_string(args.max[i]).c_str());
        }
    }
    GDALTranslateOptions *trans_options = GDALTranslateOptionsNew(translate_args.List(), NULL);
    if (trans_options == NULL) {
        std::cout << "ERROR: Cannot create gdal_translate options." << std::endl;
        return 1;
    }

    GDALDatasetH bandsel_vrt = GDALTranslate(bandsel_vrt_path.c_str(), (GDALDatasetH) orig_vrt, trans_options, NULL);
    GDALTranslateOptionsFree(trans_options);

    // Step 2: Call gdalwarp to reproject and rescale the image based on tile parameters extent and zoom level
    // Result will be written to MEM dataset because PNG driver does not support Create (only CreateCopy).

    GDALDriverH mem_driver = GDALGetDriverByName("MEM"); // TODO: error handling
    GDALDatasetH warped_ds = GDALCreate(mem_driver, "", sview.width, sview.height, args.b.size() + 1, GDT_Byte,
                                        NULL); // additional alpha band
    GDALSetProjection(warped_ds, sview.srs.c_str());
    double affine[6];
    affine[0] = sview.left;
    affine[3] = sview.top;
    affine[1] = (sview.right - sview.left) / ((double) sview.width);
    affine[5] = (sview.bottom - sview.top) / ((double) sview.height);
    affine[2] = 0.0;
    affine[4] = 0.0;
    GDALSetGeoTransform(warped_ds, affine);

    CPLStringList warp_args(NULL);
    warp_args.AddString("-of");
    warp_args.AddString("MEM");
    warp_args.AddString("-t_srs");
    warp_args.AddString(sview.srs.c_str());

    warp_args.AddString("-te"); // xmin ymin xmax ymax
    warp_args.AddString(std::to_string(sview.left).c_str());
    warp_args.AddString(std::to_string(sview.bottom).c_str());
    warp_args.AddString(std::to_string(sview.right).c_str());
    warp_args.AddString(std::to_string(sview.top).c_str());

    warp_args.AddString("-te_srs");
    warp_args.AddString(sview.srs.c_str());

    warp_args.AddString("-ts");
    warp_args.AddString(std::to_string(sview.width).c_str());
    warp_args.AddString(std::to_string(sview.height).c_str());

    warp_args.AddString("-dstalpha");
    warp_args.AddString("-overwrite");
    warp_args.AddString("-multi");

    warp_args.AddString("-wm");
    warp_args.AddString(std::to_string(args.m).c_str());

    GDALWarpAppOptions *warp_opts = GDALWarpAppOptionsNew(warp_args.List(), NULL);
    if (warp_opts == NULL) {
        std::cout << "ERROR: Cannot create gdalwarp options." << std::endl;
        return 1;
    }
    GDALDatasetH inds = (GDALDatasetH) bandsel_vrt;

    //GDALWarp(NULL, warped_ds, 1,&inds, warp_opts,NULL);
    GDALWarp(NULL, warped_ds, 1, &inds, warp_opts, NULL);

    GDALWarpAppOptionsFree(warp_opts);

    // 3. Convert in-memory tile raster to PNG

    std::string png_path = filename;
    CPLStringList translate_png_args(NULL);
    translate_png_args.AddString("-of");
    translate_png_args.AddString("PNG"); // change to png

    GDALTranslateOptions *trans_png_options = GDALTranslateOptionsNew(translate_png_args.List(), NULL);
    if (trans_png_options == NULL) {
        std::cout << "ERROR: Cannot create gdal_translate options." << std::endl;
        return 1;
    }

    GDALTranslate(png_path.c_str(), warped_ds, trans_png_options, NULL);
    GDALTranslateOptionsFree(trans_png_options);

    // Step 4. clean up
    GDALClose(warped_ds);
}

int main(int ac, char *av[]) {

    // input args: input file, output directory, output format,  zoom levels, resampling,
    namespace po = boost::program_options;
    namespace fs = boost::filesystem;

    po::options_description desc("Input arguments");
    desc.add_options()
            ("help,h", "print usage")
            ("output-dir", po::value<std::string>()->default_value(""),
             "output directory")
            ("zoom,z", po::value<std::string>(),
             "Zoom level(s)")
            ("tx,x", po::value<std::string>(),
             "tile x cooordinate(s)")
            ("ty,y", po::value<std::string>(),
             "tile y cooordinate(s)")
            ("bands,b", po::value<std::string>(),
             "Bands of the input dataset, either one or three must be selected")
            ("min", po::value<std::string>(),
             "minimum values that will be scaled to 0 for each band")
            ("max", po::value<std::string>(),
             "maximum values that will be scaled to 255 for each band")
            ("memory,m", po::value<uint32_t >()->default_value(256),
             "maximum memory in MiB for gdalwarp and GDAL cache")
            ("verbose,v", "verbose output")
            //("xx-ees", "enable early subsampling")
            ("input-file", po::value<std::string>());

    po::positional_options_description p;
    p.add("input-file", 1);
    p.add("output-dir", 1);
    po::variables_map vm;
    po::store(po::command_line_parser(ac, av).
            options(desc).positional(p).run(), vm);
    po::notify(vm);

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

    args.verbose = false;
    if (vm.count("verbose")) {
        args.verbose = true;
    }

    args.enable_early_subsample = false;
//    if (vm.count("xx-ees")) {
//        args.enable_early_subsample = true;
//    }

    args.m = vm["memory"].as<uint32_t>();

    args.output_path = vm["output-dir"].as<std::string>();
    if (args.output_path.compare("") == 0) {
        fs::path p = fs::current_path();
        args.output_path = fs::absolute(p).string();
        std::cout << "INFO: using '" << args.output_path << "' as output directory" << std::endl;
    }
    fs::path out_path{args.output_path};
    if (!fs::exists(out_path)) {
        if (args.verbose) {
            std::cout << "Creating output directory '" << args.output_path << "." << std::endl;
        }
        // try to create
        if (!fs::create_directory(out_path)) {
            std::cout << "ERROR: failed to create output directory '" << args.output_path << "'." << std::endl;
        }
    }

    {
        std::istringstream str(vm["zoom"].as<std::string>());
        std::vector<std::string> tokens{std::istream_iterator<std::string>{str}, std::istream_iterator<std::string>{}};
        std::vector<uint8_t> vec;
        for (uint8_t i = 0; i < tokens.size(); ++i) {
            vec.push_back((uint8_t) std::stoi(tokens[i]));
        }
        args.z = vec;
    }

    {
        std::istringstream str(vm["bands"].as<std::string>());
        std::vector<std::string> tokens{std::istream_iterator<std::string>{str}, std::istream_iterator<std::string>{}};
        std::vector<uint16_t> vec;
        for (uint8_t i = 0; i < tokens.size(); ++i) {
            vec.push_back((uint16_t) std::stoi(tokens[i]));
        }
        args.b = vec;
    }

    {
        std::istringstream str(vm["tx"].as<std::string>());
        std::vector<std::string> tokens{std::istream_iterator<std::string>{str}, std::istream_iterator<std::string>{}};
        std::vector<uint32_t> vec;
        for (uint8_t i = 0; i < tokens.size(); ++i) {
            vec.push_back((uint32_t) std::stoi(tokens[i]));
        }
        args.x = vec;
    }

    {
        std::istringstream str(vm["ty"].as<std::string>());
        std::vector<std::string> tokens{std::istream_iterator<std::string>{str}, std::istream_iterator<std::string>{}};
        std::vector<uint32_t> vec;
        for (uint8_t i = 0; i < tokens.size(); ++i) {
            vec.push_back((uint32_t) std::stoi(tokens[i]));
        }
        args.y = vec;
    }

    if (vm.count("min") > 0) {
        std::istringstream str(vm["min"].as<std::string>());
        std::vector<std::string> tokens{std::istream_iterator<std::string>{str}, std::istream_iterator<std::string>{}};
        std::vector<double> vec;
        for (uint8_t i = 0; i < tokens.size(); ++i) {
            vec.push_back((double) std::stod(tokens[i]));
        }
        args.min = vec;
    }

    if (vm.count("max") > 0) {
        std::istringstream str(vm["max"].as<std::string>());
        std::vector<std::string> tokens{std::istream_iterator<std::string>{str}, std::istream_iterator<std::string>{}};
        std::vector<double> vec;
        for (uint8_t i = 0; i < tokens.size(); ++i) {
            vec.push_back((double) std::stod(tokens[i]));
        }
        args.max = vec;
    }

    if (!(args.z.size() == args.x.size() && args.x.size() == args.y.size())) {
        std::cout << "ERROR: input must have the same number of x,y,z tile coordinates." << std::endl;
    }
    if (args.z.size() < 1) {
        std::cout << "ERROR: at least one tile coordinate must be provided." << std::endl;
    }

    if (args.b.size() != 1 && args.b.size() != 3) {
        std::cout << "ERROR: either one or three bands must be selected." << std::endl;
    }
    if (args.min.size() != args.max.size()) {
        std::cout << "ERROR: expected exactly 0, 1, or 3 pairs of scaling parameters." << std::endl;
    }
    if (args.min.size() != args.b.size()) {
        if (args.min.size() == 1) { // use the same value for all bands
            args.min.push_back(args.min[0]);
            args.min.push_back(args.min[0]);
        } else if (args.min.size() != 0) {
            std::cout << "ERROR: expected 0, 1, or 3 scale (min and max) parameters." << std::endl;
        }
    }
    if (args.max.size() != args.b.size()) {
        if (args.max.size() == 1) { // use the same value for all bands
            args.max.push_back(args.max[0]);
            args.max.push_back(args.max[0]);
        } else if (args.max.size() != 0) {
            std::cout << "ERROR: expected 0, 1, or 3 scale parameters." << std::endl;
        }
    }

    if (!vm.count("input-file")) {
        std::cout << "ERROR: input file missing" << std::endl;
        return 1;
    }
    args.input_file = vm["input-file"].as<std::string>();

    GDALAllRegister();
    CPLSetConfigOption("GDAL_MAX_DATASET_POOL_SIZE", "400");
    GDALSetCacheMax(1024 * 1024 * args.m);
    CPLSetConfigOption("GDAL_PAM_ENABLED", "NO"); // avoid aux files for PNG tiles

    timer t, t1;
    t.start();
    for (uint16_t i = 0; i < args.z.size(); ++i) {
        t1.start();

        space_view sview;
        sview.srs = "EPSG:3857";

        coords_2d<uint32_t> tc;
        tc.x = args.x[i];
        tc.y = args.y[i];
        bounds_2d<double> ext = tile_bounds(tc, args.z[i]);

        sview.bottom = ext.bottom;
        sview.top = ext.top;
        sview.left = ext.left;
        sview.right = ext.right;
        sview.height = 256;
        sview.width = 256;

        std::string filename =
                std::to_string(args.z[i]) + "_" + std::to_string(args.x[i]) + "_" + std::to_string(args.y[i]) + ".png";
        generate_tile(sview, (fs::path{args.output_path} / fs::path{filename}).string());
        std::cout << "Tile (z/x/y) = (" << std::to_string(args.z[i]) << "," << args.x[i] << "," << args.y[i] << ") finished in " << t.time() << "s. " << std::endl;

    }
    std::cout << "DONE (" << t.time() << "s) ." << std::endl;
    return 0;
}


#ifndef MY_RANSAC_ABSTRACT_MODEL_H
#define MY_RANSAC_ABSTRACT_MODEL_H

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

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

#include <iostream>
#include <vector>
#include <stdexcept>

template <typename Datum, typename Param>
class AbstractModel
{
    typedef std::vector<Datum> Data;

public:
    AbstractModel() {}
    virtual ~AbstractModel() {}

public:
    virtual void fit(const Data &data) = 0;
    virtual Param getParam() const = 0;
    virtual void printParam() const = 0;
    virtual double calcError(const Datum &datum) const = 0;
    std::vector<double> calcErrors(const Data &data) const
    {
        std::vector<double> errors;
        errors.reserve(data.size());
        for (const Datum &data : data)
            errors.push_back(this->calcError(data));
        return errors;
    }

protected:
    bool is_fitted_ = false;
};

#endif


#include <vector>
#include <string>
#include <memory>
#include <iomanip>

#include <cassert>

#include <fmt/format.h>
#include <fmt/printf.h>

#include <catch2/catch.hpp>

#include <boost/filesystem.hpp>
#include <boost/system/error_code.hpp>

#include "test_config.hpp"
#include "results/csv_writer.hpp"
#include "results/hdf5_writer.hpp"
#include "results/console_logger.hpp"

// concrete subjects

class Motion : public Results::Subject {
public:

  Motion( double const acceleration_
          , double const target_position_ )
    : acceleration{ acceleration_ }
    , target_position{ target_position_ }
    , time{ 0.0 }
    , position{ 0.0 }
    , velocity{ 0.0 }
  {
    RegisterResults();
  }

  void Compute() {
    time += 1.0;
    velocity = acceleration * time;
    position = velocity * time;
    distance_to_target = target_position - position;
    // notify
    Results::Subject::NotifyObserver();
  }

private:

  double acceleration;
  double target_position;
  double time;
  double position;
  double velocity;
  double distance_to_target;

  void RegisterResults() {
    // file name
    Results::Subject::results_meta.SetFileName( "motion" );

    // results
    {
      std::string const group( "Main results" );
      Results::Subject::results_meta.Register( group, "Time"    , "Time"     , "t", "s"   , &time     );
      Results::Subject::results_meta.Register( group, "Position", "Position" , "x", "m"   , &position );
      Results::Subject::results_meta.Register( group, "Velocity", "Velocity" , "V", "m/s" , &velocity );
    }

    // other results
    Results::Subject::results_meta.Register( "Auxiliary results"
                                             , "DistToTarg"
                                             , "Distance to target"
                                             , "Dx"
                                             , "m"
                                             , &distance_to_target );

  }

};

TEST_CASE( "Results can be observed", "[results]" ) {

  std::string const path = "./";

  Motion motion( 0.25       // acceleration
                 , 200.0 ); // target position
  size_t attached_observers = 0;
  {
    attached_observers = motion.AttachedObservers();
    REQUIRE( 0 == attached_observers );
    if( TestConfig::verbose ) fmt::print( "Motion attached observer(s) = {} \n"
                                          , attached_observers );

    std::unique_ptr<Results::CSVWriter> csv_writer;
    std::unique_ptr<Results::HDF5Writer> hdf5_writer;
    std::unique_ptr<Results::ConsoleLogger> console_logger;

    try {
      // attach to csv writer
      csv_writer = std::make_unique<Results::CSVWriter>( motion, path );
      attached_observers = motion.AttachedObservers();
      REQUIRE( 1 == attached_observers );
      if( TestConfig::verbose ) fmt::print( "Motion attached observer(s) = {} \n"
                                            , attached_observers );

      // attach to hdf5 writer
      hdf5_writer = std::make_unique<Results::HDF5Writer>( motion, path );
      hdf5_writer->SetCompressionLevel( 4 );
      attached_observers = motion.AttachedObservers();
      REQUIRE( 2 == attached_observers );
      if( TestConfig::verbose ) fmt::print( "Motion attached observer(s) = {} \n"
                                            , attached_observers );

      // attach to console logger
      console_logger = std::make_unique<Results::ConsoleLogger>( motion );
      attached_observers = motion.AttachedObservers();
      REQUIRE( 3 == attached_observers );
      if( TestConfig::verbose ) fmt::print( "Motion attached observer(s) = {} \n"
                                            , attached_observers );
    } catch( boost::filesystem::filesystem_error const& fs_err ) {
      fmt::print( "{}\n", fs_err.what() );
      std::exit( EXIT_FAILURE );
    }

    size_t const n_iter     = 30; // number of iterations
    size_t const i_detach   = 9;  // index of iteration where observer is detached
    size_t const i_reattach = 19; // index of iteration where observer is reattached

    for( size_t i = 0; i < n_iter; i++ ) {
      if( i == i_detach ) {
        // detach from csv writer
        motion.DetachObserver( *csv_writer.get() );
        attached_observers = motion.AttachedObservers();
        REQUIRE( 2 == attached_observers );
        if( TestConfig::verbose ) fmt::print( "Motion attached observer(s) = {} \n"
                                              , attached_observers );
      } else if( i == i_reattach ) {
        // reattach from csv writer
        motion.AttachObserver( *csv_writer.get() );
        attached_observers = motion.AttachedObservers();
        REQUIRE( 3 == attached_observers );
        if( TestConfig::verbose ) fmt::print( "Motion attached observer(s) = {} \n"
                                              , attached_observers );
      }
      motion.Compute();
    }
  }

  attached_observers = motion.AttachedObservers();
  REQUIRE( 0 == attached_observers );
  if( TestConfig::verbose ) fmt::print( "Motion attached observer(s) = {} \n"
                                        , attached_observers );
}


/**
 * \file dcs/math/random/uniform_int_adaptor.hpp
 *
 * \brief Adaptor for generating uniformly distributed integer random numbers
 *  from a base generator.
 *
 * \author Marco Guazzone (marco.guazzone@gmail.com)
 *
 * <hr/>
 *
 * Copyright 2009 Marco Guazzone (marco.guazzone@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef DCS_MATH_RANDOM_UNIFORM_INT_ADAPTOR_HPP
#define DCS_MATH_RANDOM_UNIFORM_INT_ADAPTOR_HPP

#include <dcs/detail/config/boost.hpp>

#if !DCS_DETAIL_CONFIG_BOOST_CHECK_VERSION(101500) // 1.15
#	error "Required Boost library version >= 1.15"
#endif // DCS_DETAIL_CONFIG_BOOST_CHECK_VERSION

#include <boost/random/uniform_int_distribution.hpp>
#include <dcs/assert.hpp>
#include <dcs/math/random/base_generator.hpp>
#include <dcs/math/random/generator_traits.hpp>
#include <dcs/type_traits/remove_reference.hpp>
#include <dcs/util/holder.hpp>
#include <limits>

namespace dcs { namespace math { namespace random {

/**
 * \brief Adaptor for generating random numbers in [0,1] from a base random
 *  number generator.
 *
 * \tparam BaseBaseRandomGeneratorT The base random number generator type.
 * \tparam RealT The result type.
 *
 * Wraps a random number generator of type \a BaseGeneratorT for generating
 * random numbers in [0,1].
 * Random numbers are generator according the algorithm provided by
 * \a BaseRandomGeneratorT.
 *
 * \author Marco Guazzone (marco.guazzone@gmail.com)
 */
template <typename BaseRandomGeneratorT, typename IntT>
class uniform_int_adaptor: public base_generator<IntT>
{
	private: typedef base_generator<IntT> base_type;
    public: typedef BaseRandomGeneratorT base_engine_type;
    public: typedef typename ::dcs::type_traits::remove_reference<base_engine_type>::type::result_type input_type;
    public: typedef typename base_type::result_type result_type;
    public: typedef typename base_type::ulonglong_type ulonglong_type;
	private: typedef ::boost::random::uniform_int_distribution<result_type> adaptor_impl_type;

	/// Default constructor.
    public: explicit uniform_int_adaptor(result_type a = 0,
										 result_type b = ::std::numeric_limits<result_type>::max())
        : base_type(),
		  rng_(),
		  impl_(a, b)
    {
		// pre: a_ <= b_
		DCS_ASSERT(
			a <= b,
			::std::invalid_argument("[dcs::math::random::uniform_int_adaptor::ctor] Bad range.")
		);
    }

	/// Seed constructor.
    public: uniform_int_adaptor(input_type seed,
								result_type a,
								result_type b)
        : base_type(),
		  rng_(seed),
		  impl_(a, b)
    {
		// pre: a_ <= b_
		DCS_ASSERT(
			a <= b,
			::std::invalid_argument("[dcs::math::random::uniform_int_adaptor::ctor] Bad range.")
		);
    }

	/// Copy constructor.
    public: uniform_int_adaptor(uniform_int_adaptor const& that)
        : base_type(that),
		  rng_(that.rng_),
		  impl_(that.a_, that.b_)
    {
		// empty
    }

	/// A Constructor: copy the base generator.
	public: explicit uniform_int_adaptor(base_engine_type rng,
										 result_type a = 0,
										 result_type b = ::std::numeric_limits<result_type>::max())
		: base_type(),
		  rng_(rng),
		  impl_(a, b)
	{
		// pre: a_ <= b_
		DCS_ASSERT(
			a <= b,
			::std::invalid_argument("[dcs::math::random::uniform_int_adaptor::ctor] Bad range.")
		);
	}

// Support of reference-to-refernce qualifier is only available from C++0x.
#if __cplusplus > 201103L

	/// A constructor: move the base generator.
    //public: uniform_int_adaptor(base_engine_type&& rng)
    public: explicit uniform_int_adaptor(base_engine_type& rng,
										 result_type a = 0,
										 result_type b = ::std::numeric_limits<result_type>::max())
        : base_type(),
		  rng_(rng),
		  impl_(a, b)
    {
		// pre: a_ <= b_
		DCS_ASSERT(
			a <= b,
			::std::invalid_argument("[dcs::math::random::uniform_int_adaptor::ctor] Bad range.")
		);
    }

#else

	/// A constructor: move the base generator.
    public: explicit uniform_int_adaptor(::dcs::util::holder<base_engine_type>& rng,
										 result_type a = 0,
										 result_type b = ::std::numeric_limits<result_type>::max())
        : base_type(),
		  rng_(rng),
		  impl_(a, b)
    {
		// pre: a_ <= b_
		DCS_ASSERT(
			a <= b,
			::std::invalid_argument("[dcs::math::random::uniform_int_adaptor::ctor] Bad range.")
		);
    }

#endif

	public: const base_engine_type base() const
	{
		return rng_;
	}

    private: result_type do_generate()
    {
		return impl_(rng_);
    }

	private: void do_seed()
	{
		this->rng_.seed();
	}

	private: void do_seed(result_type s)
	{
		rng_.seed(s);
		//generator_traits<base_engine_type>::seed(rng_, s);
	}

	private: void do_discard(ulonglong_type z)
	{
		while (z--)
		{
			rng_();
		}
	}

	private: result_type do_min() const
	{
		return impl_.min();
	}

	private: result_type do_max() const
	{
		return impl_.max();
	}

	private: base_engine_type rng_;
	private: adaptor_impl_type impl_;
};

// Template specialization for avoid auto-wrapping (i.e., when the base
// generator is itself a uniform_int_adaptor)
template <typename RandomNumberGeneratorT, typename IntT>
class uniform_int_adaptor< uniform_int_adaptor<RandomNumberGeneratorT,IntT>, IntT >: public uniform_int_adaptor<RandomNumberGeneratorT,IntT>
{
    public: typedef RandomNumberGeneratorT base_engine_type;
    public: typedef typename ::dcs::type_traits::remove_reference<RandomNumberGeneratorT>::type::result_type input_type;
    public: typedef IntT result_type;
};

}}} // Namespace dcs::math::random

#endif // DCS_MATH_RANDOM_UNIFORM_INT_ADAPTOR_HPP


/*
 * MutableBooleanNetwork.hpp
 *
 *  Created on: Nov 18, 2010
 *      Author: stewie
 */

#ifndef MUTABLEBOOLEANNETWORK_HPP_
#define MUTABLEBOOLEANNETWORK_HPP_

#include <cassert>
#include <cstddef>
#include <iosfwd>
#include <vector>

#include <boost/graph/graph_selectors.hpp>
#include <boost/graph/adjacency_list.hpp>

#include "BooleanDynamics.hpp"
#include "BooleanFunction.hpp"

namespace bn {

class MutableBooleanNetwork : public BooleanDynamics {
private:
	class Updater;
	friend class Updater;

public:
	typedef std::vector<int> TruthTable;
	typedef boost::adjacency_list<boost::vecS, boost::vecS,
			boost::bidirectionalS, TruthTable> Network;

	using BooleanDynamics::update; // make update(size_t) visible

	/**
	 * Returns the number of nodes in the network.
	 * @return the number of nodes
	 */
	size_t size() const {
		return boost::num_vertices(net);
	}

	MutableBooleanNetwork* clone() const {
		return new MutableBooleanNetwork(*this);
	}

	Network& topology() {
		return net;
	}

	const Network& topology() const {
		return net;
	}

	/**
	 * Sets the state of this network.
	 * @param s the new state
	 */
	void setState(const State& s) {
		state = s;
		assert(state.size() == size());
	}

	/**
	 * Returns a reference to the current state of this network.
	 * @return the state of this network
	 */
	const State& getState() const {
		return state;
	}

	void update();

	BooleanFunction getFunction(const size_t i) const;

	std::vector<BooleanFunction> getFunctions() const;

	BooleanDynamics* simulate() {
		return new Updater(*this);
	}

	MutableBooleanNetwork& operator=(MutableBooleanNetwork other) {
		swap(other);
		return *this;
	}

	void swap(MutableBooleanNetwork& other) {
		using std::swap;
		swap(state, other.state);
		swap(net, other.net);
	}

	void update(State& s);

	static MutableBooleanNetwork makeNetwork(const char topologyFilename[],
			const char functionFilename[]);

	friend std::ostream& operator<<(std::ostream& out,
			const MutableBooleanNetwork& rbn);

protected:
	/**
	 * The current state of the netwotk.
	 */
	State state;

	Network net;

private:
	class Updater : public BooleanDynamics {
	public:
		using BooleanDynamics::update;

		Updater(MutableBooleanNetwork& bn) :
			bn(&bn) {
		}

		Updater* clone() const {
			return new Updater(*this);
		}

		const State& getState() const {
			return state;
		}

		void update();

		void setState(const State& s) {
			state = s;
		}

		void update(State& s);

		size_t size() const {
			return bn->size();
		}

		BooleanFunction getFunction(const size_t i) const {
			return bn->getFunction(i);
		}

	private:
		MutableBooleanNetwork* bn;

		State state;
	};
};

void swap(MutableBooleanNetwork& a, MutableBooleanNetwork& b);

} // namespace bn

#endif /* MUTABLEBOOLEANNETWORK_HPP_ */


#ifndef NETWORKS_HPP
#define NETWORKS_HPP

#include <tuple>
#include <vector>
// For External Library
#include <torch/torch.h>
#include <boost/program_options.hpp>

// Define Namespace
namespace nn = torch::nn;
namespace po = boost::program_options;

// Function Prototype
void weights_init(nn::Module &m);

// --------------------------------
// struct{SegNetImpl}(nn::Module)
// --------------------------------
struct SegNetImpl : nn::Module{
private:
    size_t num_downs;
    nn::ModuleList encoder, decoder;
    nn::Sequential classifier;
public:
    SegNetImpl(){}
    SegNetImpl(po::variables_map &vm);
    torch::Tensor forward(torch::Tensor x);
};

// -------------------------------------
// struct{DownSamplingImpl}(nn::Module)
// -------------------------------------
struct DownSamplingImpl : nn::Module{
private:
    nn::Sequential features;
    nn::MaxPool2d pool{nullptr};
public:
    DownSamplingImpl(){}
    DownSamplingImpl(const size_t in_nc, const size_t out_nc, const size_t n_layers=2, const bool use_dropout=false);
    std::tuple<torch::Tensor, torch::Tensor, std::vector<long int>> forward(torch::Tensor x);
};

// -----------------------------------
// struct{UpSamplingImpl}(nn::Module)
// -----------------------------------
struct UpSamplingImpl : nn::Module{
private:
    nn::Sequential features;
    nn::MaxUnpool2d unpool{nullptr};
public:
    UpSamplingImpl(){}
    UpSamplingImpl(const size_t in_nc, const size_t out_nc, const size_t n_layers=2, const bool use_dropout=false);
    torch::Tensor forward(torch::Tensor x, torch::Tensor indices, const std::vector<long int> unpool_sizes);
};

TORCH_MODULE(SegNet);
TORCH_MODULE(DownSampling);
TORCH_MODULE(UpSampling);

#endif


/* ----------------------------------------------------------------------------

 * GTSAM Copyright 2010, Georgia Tech Research Corporation,
 * Atlanta, Georgia 30332-0415
 * All Rights Reserved
 * Authors: Frank Dellaert, et al. (see THANKS for the full author list)

 * See LICENSE for the license information

 * -------------------------------------------------------------------------- */

/*
 * testDecisionTreeFactor.cpp
 *
 *  @date Feb 5, 2012
 *  @author Frank Dellaert
 *  @author Duy-Nguyen Ta
 */

#include <gtsam/discrete/Signature.h>
#include <gtsam/discrete/DecisionTreeFactor.h>
#include <gtsam/base/Testable.h>
#include <CppUnitLite/TestHarness.h>
#include <boost/assign/std/map.hpp>
using namespace boost::assign;

using namespace std;
using namespace gtsam;

/* ************************************************************************* */
TEST( DecisionTreeFactor, constructors)
{
  // Declare a bunch of keys
  DiscreteKey X(0,2), Y(1,3), Z(2,2);

  // Create factors
  DecisionTreeFactor f1(X, {2, 8});
  DecisionTreeFactor f2(X & Y, "2 5 3 6 4 7");
  DecisionTreeFactor f3(X & Y & Z, "2 5 3 6 4 7 25 55 35 65 45 75");
  EXPECT_LONGS_EQUAL(1,f1.size());
  EXPECT_LONGS_EQUAL(2,f2.size());
  EXPECT_LONGS_EQUAL(3,f3.size());

  DiscreteValues values;
  values[0] = 1; // x
  values[1] = 2; // y
  values[2] = 1; // z
  EXPECT_DOUBLES_EQUAL(8, f1(values), 1e-9);
  EXPECT_DOUBLES_EQUAL(7, f2(values), 1e-9);
  EXPECT_DOUBLES_EQUAL(75, f3(values), 1e-9);
}

/* ************************************************************************* */
TEST_UNSAFE( DecisionTreeFactor, multiplication)
{
  DiscreteKey v0(0,2), v1(1,2), v2(2,2);

  DecisionTreeFactor f1(v0 & v1, "1 2 3 4");
  DecisionTreeFactor f2(v1 & v2, "5 6 7 8");

  DecisionTreeFactor expected(v0 & v1 & v2, "5 6 14 16 15 18 28 32");

  DecisionTreeFactor actual = f1 * f2;
  CHECK(assert_equal(expected, actual));
}

/* ************************************************************************* */
TEST( DecisionTreeFactor, sum_max)
{
  DiscreteKey v0(0,3), v1(1,2);
  DecisionTreeFactor f1(v0 & v1, "1 2  3 4  5 6");

  DecisionTreeFactor expected(v1, "9 12");
  DecisionTreeFactor::shared_ptr actual = f1.sum(1);
  CHECK(assert_equal(expected, *actual, 1e-5));

  DecisionTreeFactor expected2(v1, "5 6");
  DecisionTreeFactor::shared_ptr actual2 = f1.max(1);
  CHECK(assert_equal(expected2, *actual2));

  DecisionTreeFactor f2(v1 & v0, "1 2  3 4  5 6");
  DecisionTreeFactor::shared_ptr actual22 = f2.sum(1);
}

/* ************************************************************************* */
// Check enumerate yields the correct list of assignment/value pairs.
TEST(DecisionTreeFactor, enumerate) {
  DiscreteKey A(12, 3), B(5, 2);
  DecisionTreeFactor f(A & B, "1 2  3 4  5 6");
  auto actual = f.enumerate();
  std::vector<std::pair<DiscreteValues, double>> expected;
  DiscreteValues values;
  for (size_t a : {0, 1, 2}) {
    for (size_t b : {0, 1}) {
      values[12] = a;
      values[5] = b;
      expected.emplace_back(values, f(values));
    }
  }
  EXPECT(actual == expected);
}

/* ************************************************************************* */
// Check markdown representation looks as expected.
TEST(DecisionTreeFactor, markdown) {
  DiscreteKey A(12, 3), B(5, 2);
  DecisionTreeFactor f(A & B, "1 2  3 4  5 6");
  string expected =
      "|A|B|value|\n"
      "|:-:|:-:|:-:|\n"
      "|0|0|1|\n"
      "|0|1|2|\n"
      "|1|0|3|\n"
      "|1|1|4|\n"
      "|2|0|5|\n"
      "|2|1|6|\n";
  auto formatter = [](Key key) { return key == 12 ? "A" : "B"; };
  string actual = f.markdown(formatter);
  EXPECT(actual == expected);
}

/* ************************************************************************* */
int main() {
  TestResult tr;
  return TestRegistry::runAllTests(tr);
}
/* ************************************************************************* */


//---------------------------------------------------------------------------//
// Copyright (c) 2013-2014 Kyle Lutz <kyle.r.lutz@gmail.com>
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt
//
// See http://boostorg.github.com/compute for more information.
//---------------------------------------------------------------------------//

#define BOOST_TEST_MODULE TestFunctionalPopcount
#include <boost/test/unit_test.hpp>

#include <boost/compute/algorithm/transform.hpp>
#include <boost/compute/container/vector.hpp>
#include <boost/compute/functional/popcount.hpp>

#include "check_macros.hpp"
#include "context_setup.hpp"

namespace compute = boost::compute;

typedef boost::mpl::list<
    compute::uchar_, compute::ushort_, compute::uint_, compute::ulong_
> popcount_test_types;

BOOST_AUTO_TEST_CASE_TEMPLATE(popcount, T, popcount_test_types)
{
    compute::vector<T> vec(context);
    vec.push_back(0x00, queue);
    vec.push_back(0x01, queue);
    vec.push_back(0x03, queue);
    vec.push_back(0xff, queue);

    compute::vector<int> popcounts(vec.size(), context);
    compute::transform(
        vec.begin(), vec.end(), popcounts.begin(), compute::popcount<T>(), queue
    );
    CHECK_RANGE_EQUAL(int, 4, popcounts, (0, 1, 2, 8));
}

BOOST_AUTO_TEST_SUITE_END()


#include "Reader.h"
#include <algorithm>
#include <boost/algorithm/string.hpp>
#include <fstream>
#include <iostream>
#include <unordered_map>
#include <sstream>

using namespace std;

void Reader::read_word_vector(string folder, unordered_map<string,vector<double>> &word_embeddings){
  ifstream glove_file;
  glove_file.open(folder);
  if(!glove_file){
    cout << "unable to word vector file" << '\n';
    exit(0);
  }
  cout << "Reading Word Embeddings" << endl;
  string word;
  string str;
  while(getline( glove_file,str)){
    istringstream ss(str);
    ss >> word;
    vector<double> embed;
    double elem;
    for(int s = 0 ; s < 300 ; s++) {
      ss >> elem;
      embed.push_back(elem);
    }
    word_embeddings[word] = embed;
  }
}

void Reader::calculate_word_vector(const unordered_map<string,vector<double>> &word_embeddings,const string& word,
                                   vector<double> &v_g_word_vector)
{

  vector<string> each_word;
  boost::split(each_word, word, boost::is_any_of(" "));
  unsigned int cnt = 0;
  for(const string& token : each_word){
    if(!token.empty()){
      if(word_embeddings.find(token) != word_embeddings.end()){
        cnt++;
        const vector<double>& each_word_vector = word_embeddings.at(token);
        if(!v_g_word_vector.empty())
          std::transform (v_g_word_vector.begin(), v_g_word_vector.end(),
                          each_word_vector.begin(), v_g_word_vector.begin(),
                          std::plus<double>());
        else
          v_g_word_vector = each_word_vector;
      }
    }
  }
  return;
  /*if(v_g_word_vector.empty()) return;
  cnt = each_word.size();
  transform(v_g_word_vector.begin(), v_g_word_vector.end(), v_g_word_vector.begin(), [cnt](double &c){ return c/cnt; });*/

  //if (v_g_word_vector.empty()) return;
  /*if (each_word.size() != cnt){
    vector<double> rand_vector (word_embeddings.begin()->second.size(),0);
    srand(time(0));
    generate(rand_vector.begin(), rand_vector.end(), rand);
    std::transform(rand_vector.begin(), rand_vector.end(), rand_vector.begin(),
                   [](double d) -> double { return d / RAND_MAX; });

    std::transform (v_g_word_vector.begin(), v_g_word_vector.end(),
                    rand_vector.begin(), v_g_word_vector.begin(),
                    std::plus<double>());

    int size = each_word.size();
    transform(v_g_word_vector.begin(), v_g_word_vector.end(), v_g_word_vector.begin(), [size](double &c){ return c/size; });
  }else{
    transform(v_g_word_vector.begin(), v_g_word_vector.end(), v_g_word_vector.begin(), [cnt](double &c){ return c/cnt; });
  }*/
}


#pragma once

// Armadillo
#include <armadillo>

// Mantella
#include "mantella_bits/optimisationProblem.hpp"

namespace mant {
  namespace krm {
    class KinematicallyRedundantMachines : public OptimisationProblem {
     public:
      const arma::uword numberOfWorkspaceDimensions_;

      explicit KinematicallyRedundantMachines(
          const arma::uword numberOfProblemDimensions,
          const arma::uword numberOfWorkspaceDimensions);

      void setEndEffectorTrajectory(
          const arma::mat& endEffectorTrajectory);

      arma::mat getEndEffectorTrajectory() const;

     protected:
      arma::mat endEffectorTrajectory_;
    };
  }
}


//==================================================================================================
/**
  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_DEINTERLEAVE_SECOND_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_DEINTERLEAVE_SECOND_HPP_INCLUDED

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{
/*!
    @ingroup group-swar
    This function object performs the extraction of the second part of a boost::simd::pack
    containing two channels of data.

    @par Semantic:
    For any type @c T and any integral constant @c N greater than 1, let's consider two values @c x
    and @c y of type @c boost::simd::pack<T,N> , the following code:
    @code
    boost::simd::pack<T,N> r = deinterleave_second(x,y);
    @endcode
    is equivalent to :
    @code
    boost::simd::pack<T,N> r{x[N/2], x[N/2+1], ..., x[N-1], y[N/2], y[N/2+1], ..., y[N-1]};
    @endcode

    @param  v0  First part of the data to deinterleave
    @param  v1  Second part of the data to deinterleave
    @return A boost::simd::pack containing the deinterleaved value of the second data channel.
  **/
  Value deinterleave_second(Value const& v0, Value const& v1);
} }
#endif

#include <boost/simd/function/simd/deinterleave_second.hpp>

#endif


#include "timer.hpp"

#include <Eigen/SparseCore>

#include <random>
#include <iostream>

using real = double;
using cmat = Eigen::SparseMatrix<real, Eigen::ColMajor>;
using rmat = Eigen::SparseMatrix<real, Eigen::RowMajor>;
using triplet = Eigen::Triplet<real>;

static std::default_random_engine& engine() {
  static std::random_device dev;
  static std::default_random_engine res(dev());
  return res;
}

template<class T> struct uniform;

template<> struct uniform<int> {
  using type = std::uniform_int_distribution<int>;
};

template<> struct uniform<real> {
  using type = std::uniform_real_distribution<real>;
};

// sum duplicate triplets
template<class Iterator>
static Iterator merge_sorted_triplets(Iterator first, Iterator last) {
  struct stealer : triplet {
    using triplet::m_value;
  };

  return std::unique(first, last, [](triplet& lhs, const triplet& rhs) {
      const bool res = lhs.row() == rhs.row() && lhs.col() == rhs.col();
      if(res) static_cast<stealer&>(lhs).m_value += rhs.value();
      return res;
    });

}

namespace v1 {

  // reserve nnz directly + insertBack, std::sort + merge

  template<class Iterator>
  static void set_from_sorted_unique_triplets(rmat& matrix, Iterator first, Iterator last) {
    matrix.reserve(last - first);

    int last_row = -1;
    for(Iterator it = first; it != last; ++it) {

      while(last_row < it->row()) {
        matrix.startVec(++last_row);
      }

      matrix.insertBack(it->row(), it->col()) = it->value();
    }

    matrix.finalize();
  }

  template<class Iterator>
  static void set_from_triplets(rmat& matrix, Iterator first, Iterator last) {
    std::sort(first, last, [](const triplet& lhs, const triplet& rhs) {
        return lhs.row() < rhs.row() || (lhs.row() == rhs.row() && lhs.col() < rhs.col());
      });

    last = merge_sorted_triplets(first, last);
    set_from_sorted_unique_triplets(matrix, first, last);
  }

}

namespace v2 {
  // reserve nnz by rows + insertBackUncompressed, std::sort + merge

  template<class Iterator>
  static void set_from_sorted_unique_triplets(rmat& matrix, Iterator first, Iterator last,
                                              std::vector<rmat::StorageIndex>&& nnz = {}) {

    // reserve exactly nnz count per outer index
    nnz.clear(); nnz.resize(matrix.rows(), 0);

    for(Iterator it = first; it != last; ++it) {
      ++nnz[it->row()];
    }

    matrix.reserve(nnz);

    for(Iterator it = first; it != last; ++it) {
      matrix.insertBackUncompressed(it->row(), it->col()) = it->value();
    }

    matrix.finalize();
  }

  template<class Iterator>
  static void set_from_triplets(rmat& matrix, Iterator first, Iterator last) {
    std::sort(first, last, [](const triplet& lhs, const triplet& rhs) {
        return lhs.row() < rhs.row() || (lhs.row() == rhs.row() && lhs.col() < rhs.col());
      });

    last = merge_sorted_triplets(first, last);
    set_from_sorted_unique_triplets(matrix, first, last);
  }

}

namespace v3 {
  // counting sort + insertBackUncompressed

  template<class Triplets, class F>
  static void sort_triplets(std::size_t n, const F f,
                            const Triplets& input, Triplets& output,
                            std::vector<rmat::StorageIndex>&& count = {}) {
    // TODO memset?
    count.clear(); count.resize(n, 0);

    for(const triplet& t : input) {
      ++count[ f(t) ];
    }

    rmat::StorageIndex total = 0;
    for(rmat::StorageIndex& c : count) {
      rmat::StorageIndex old = c;
      c = total;
      total += old;
    }

    output.resize(input.size());
    for(const triplet& t : input ) {
      const rmat::StorageIndex at = count[ f(t) ]++;
      output[ at ] = t;
    }
  }

  static void set_from_triplets(rmat& matrix, std::vector<triplet>& input) {

    std::vector<triplet> tmp;

    std::vector<rmat::StorageIndex> count( std::max(matrix.rows(), matrix.cols()) );

    sort_triplets(matrix.cols(), [](const triplet& t) { return t.col(); }, input, tmp, std::move(count) );
    sort_triplets(matrix.rows(), [](const triplet& t) { return t.row(); }, tmp, input, std::move(count) );

    auto last = merge_sorted_triplets(input.begin(), input.end());
    v2::set_from_sorted_unique_triplets(matrix, input.begin(), last, std::move(count));
  }

}

namespace v4 {
  // counting sort + insertBack

  static void set_from_triplets(rmat& matrix, std::vector<triplet>& input) {
    std::vector<triplet> tmp;

    std::vector<rmat::StorageIndex> count( std::max(matrix.rows(), matrix.cols()) );

    v3::sort_triplets(matrix.cols(), [](const triplet& t) { return t.col(); }, input, tmp, std::move(count) );
    v3::sort_triplets(matrix.rows(), [](const triplet& t) { return t.row(); }, tmp, input, std::move(count) );

    auto last = merge_sorted_triplets(input.begin(), input.end());
    v1::set_from_sorted_unique_triplets(matrix, input.begin(), last);
  }

}

namespace v5 {
  // counting sort + custom insertion

  template<class Iterator>
  static void set_from_sorted_unique_triplets(rmat& matrix, Iterator first, Iterator last,
                                              std::vector<rmat::StorageIndex>&& positions = {}) {

    struct stealer : rmat {
      using rmat::rmat;
      using rmat::m_outerIndex;
      using rmat::m_data;
    };

    stealer dest(matrix.rows(), matrix.cols());

    // reset non-zeros
    const std::size_t n = dest.outerSize();

    // TODO use Eigen map to exploit alignment?
    std::fill(dest.m_outerIndex, dest.m_outerIndex + n, 0);

    // build histogram
    for(Iterator it = first; it != last; ++it) {
      ++dest.m_outerIndex[it->row()];
    }

    // prefix sum
    rmat::StorageIndex count = 0;
    for(std::size_t i = 0; i < n; ++i) {
      const rmat::StorageIndex old = dest.m_outerIndex[i];
      dest.m_outerIndex[i] = count;
      count += old;
    }

    // sentinel?
    dest.m_outerIndex[n] = count;

    // alloc
    positions.resize(n);

    // TODO exploit alignment ?
    std::copy(dest.m_outerIndex, dest.m_outerIndex + n, positions.begin());

    // fill data
    dest.m_data.resize( count );
    for(Iterator it = first; it != last; ++it) {
      const rmat::StorageIndex pos = positions[it->row()]++;
      dest.m_data.index(pos) = it->col();
      dest.m_data.value(pos) = it->value();
    }

    // dest.finalize();
    dest.swap(matrix);
  }

  static void set_from_triplets(rmat& matrix, std::vector<triplet>& input) {

    std::vector<triplet> tmp;

    std::vector<rmat::StorageIndex> count( std::max(matrix.rows(), matrix.cols()) );

    v3::sort_triplets(matrix.cols(), [](const triplet& t) { return t.col(); }, input, tmp, std::move(count) );
    v3::sort_triplets(matrix.rows(), [](const triplet& t) { return t.row(); }, tmp, input, std::move(count) );

    auto last = merge_sorted_triplets(input.begin(), input.end());
    set_from_sorted_unique_triplets(matrix, input.begin(), last, std::move(count) );
  }

}

int main(int, char**) {

  unsigned m = 5000, n = 1000;
  unsigned nnz = (m * n) / 100;

  std::vector<triplet> values;

  uniform<int>::type m_rand(0, m - 1);
  uniform<int>::type n_rand(0, n - 1);
  uniform<real>::type value_rand(0, 1);

  for(unsigned i = 0; i < nnz; ++i) {

    values.emplace_back(m_rand(engine()),
                        n_rand(engine()),
                        value_rand(engine()));

  }

  auto check = [&](const rmat& matrix) {
    // std::cerr << "check: " << matrix.sum() << std::endl;
  };

  auto ref = [&] {
    const real t = timer([&] {
        rmat A(m, n);
        std::vector<triplet> triplets = values;
        A.setFromTriplets(triplets.begin(), triplets.end());

        check(A);
      });

    std::clog << "eigen: " << t << std::endl;
  };

  auto v1 = [&] {
    const real t1 = timer([&] {
        rmat A(m, n);
        std::vector<triplet> triplets = values;
        v1::set_from_triplets(A, triplets.begin(), triplets.end());

        check(A);
      });

    std::clog << "v1: " << t1 << std::endl;
  };

  auto v2 = [&] {
    const real t2 = timer([&] {
        rmat A(m, n);
        std::vector<triplet> triplets = values;
        v2::set_from_triplets(A, triplets.begin(), triplets.end());

        check(A);
      });

    std::clog << "v2: " << t2 << std::endl;
  };

  auto v3 = [&] {
    const real t3 = timer([&] {
        rmat A(m, n);
        std::vector<triplet> triplets = values;
        v3::set_from_triplets(A, triplets);

        check(A);
      });

    std::clog << "v3: " << t3 << std::endl;
  };

  auto v4 = [&] {
    const real t4 = timer([&] {
        rmat A(m, n);
        std::vector<triplet> triplets = values;
        v4::set_from_triplets(A, triplets);

        check(A);
      });

    std::clog << "v4: " << t4 << std::endl;
  };

  static const auto v5 = [&] {
    const real t5 = timer([&] {
        rmat A(m, n);
        std::vector<triplet> triplets = values;
        v5::set_from_triplets(A, triplets);

        check(A);
      });

    std::clog << "v5: " << t5 << std::endl;
  };

  auto warm = ref;

  warm();

  ref();
  v1();
  v2();
  v3();
  v4();
  v5();

  return 0;
}


#ifndef WAVE_PLANE_PARAMETERIZATION_HPP
#define WAVE_PLANE_PARAMETERIZATION_HPP

#include <Eigen/Core>
#include <ceres/ceres.h>
#include <ceres/local_parameterization.h>
#include "wave/utils/math.hpp"

namespace wave {

class PlaneParameterization : public ceres::LocalParameterization {
 public:
    virtual ~PlaneParameterization() {}
    virtual bool Plus(const double* x, const double* delta, double* x_plus_delta) const;
    virtual bool ComputeJacobian(const double* x, double* jacobian) const;
    virtual int GlobalSize() const { return 6; }
    virtual int LocalSize() const { return 3; }
};

}

#endif //WAVE_PLANE_PARAMETERIZATION_HPP


#include <assert.h>
#include <cmath>
#include <fstream>

#include <boost/format.hpp>

#include "DT_DAG.h"

//
// DAG member functions
//

void DAG::dfsearch(Triangle *a, Vertex *p, Vertex *e, bool leftRecursive)
{
    if (nullptr == a || nullptr == p) {
        return;
    } else if (true == a->isValid() && isContained(a, p)) {
        if (nullptr == e) {
             adjList.add(a);
        } else {
            if (isContained(a, e))
                adjList.add(a);
        }
    } else {
        int seq[3] = {0, 1, 2};
        if (!leftRecursive) {
            seq[0] = 2;
            seq[1] = 1;
            seq[2] = 0;
        }
        Triangle * child = nullptr;

        child = a->getChild(seq[0]);
        dfsearch(child,p, e, leftRecursive);

        child = a->getChild(seq[1]);
        dfsearch(child,p, e, leftRecursive);

        child = a->getChild(seq[2]);
        dfsearch(child,p, e, leftRecursive);
    }
}

void DAG::findSharedEdge(Triangle *ta,
                         Triangle *tb,
                         Vertex **va,
                         Vertex **vb,
                         Vertex *ve[2])
{
    // find the edge shared between the 2 triangles
    Vertex ** avt = ta->getVertices();
    Vertex ** bvt = tb->getVertices();

    Vertex * ep[2] = {nullptr,nullptr};
    Vertex * ap = nullptr;
    Vertex * bp = nullptr;

    int order_a[3] = {-1,-1,-1};
    int order_b[3] = {-1,-1,-1};
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            if (avt[i] == bvt[j]) {
                order_a[i] = j;
                order_b[j] = i;
            }
        }
    }

    for (int i = 0, j = 0; i < 3; i++) {
        if (order_a[i] < 0) {
            ap = avt[i];
        } else {
            ep[j] = avt[i];
            j += 1;
        }
        if (order_b[i] < 0)
            bp = bvt[i];
    }

    *va = ap;
    *vb = bp;
    ve[0] = ep[0];
    ve[1] = ep[1];
}

void DAG::findTriangleContainingPoint(Vertex *p, bool leftRecursive)
{
    Triangle * head = tri[0];
    adjList.clear();
    dfsearch(head, p, nullptr, leftRecursive);
}

void DAG::findAdjacentTriangle(Triangle *a, Vertex *p, bool leftRecursive)
{
    Vertex * ap[2];
    a->getVerticesNotContainingPoint(p, ap);

    Triangle * head = tri[0];
    adjList.clear();
    adjList.add(a);
    dfsearch(head, ap[0], ap[1], leftRecursive);
}

void DAG::divideOnInterior(Triangle * a, Vertex * p)
{
    json j;
    logStep("divide_interior", "pre", p, j, false);

    // simple case, pr lies on interior
    // split a into a1,a2,a3
    Vertex ** v = a->getVertices();

    Triangle * a1 = tri.get();
    Triangle * a2 = tri.get();
    Triangle * a3 = tri.get();

    a1->setVertices(v[0], v[1], p);
    a2->setVertices(v[1], v[2], p);
    a3->setVertices(v[2], v[0], p);

    a->addChild(a1);
    a->addChild(a2);
    a->addChild(a3);
    a->setValid(false);

    splitList.clear();
    splitList.add(a1);
    splitList.add(a2);
    splitList.add(a3);

    logStep("divide_interior", "post", p, j, true);
}

void DAG::divideOnEdge(Triangle * a, Triangle * b, Vertex * p)
{
    json j;
    logStep("divide_edge", "pre", p, j, false);

    Vertex * ep[2] = {nullptr,nullptr};
    Vertex * ap = nullptr;
    Vertex * bp = nullptr;

    findSharedEdge(a, b, &ap, &bp, ep);

    Triangle * c1 = tri.get();
    Triangle * c2 = tri.get();
    Triangle * c3 = tri.get();
    Triangle * c4 = tri.get();

    c1->setVertices(p, ep[0], ap);
    c2->setVertices(p, ep[1], ap);

    a->addChild(c1);
    a->addChild(c2);
    a->setValid(false);

    c3->setVertices(p, ep[0], bp);
    c4->setVertices(p, ep[1], bp);

    b->addChild(c3);
    b->addChild(c4);
    b->setValid(false);

    splitList.clear();
    splitList.add(c1);
    splitList.add(c2);
    splitList.add(c3);
    splitList.add(c4);

    logStep("divide_edge", "post", p, j, true);
}

void DAG::flip(Triangle *a, Triangle *b, Vertex *pr,
                      Triangle *n[2])
{
    json j;
    logStep("flip", "pre", pr, j, false);

    Vertex * ep[2] = {nullptr,nullptr};
    Vertex * ap = nullptr;
    Vertex * bp = nullptr;

    findSharedEdge(a, b, &ap, &bp, ep);

    n[0] = tri.get();
    n[1] = tri.get();

    n[0]->setVertices(ap, bp, ep[0]);
    n[1]->setVertices(ap, bp, ep[1]);

    a->addChild(n[0]);
    a->addChild(n[1]);
    a->setValid(false);

    b->addChild(n[0]);
    b->addChild(n[1]);
    b->setValid(false);

    logStep("flip", "post", pr, j, true);
}

Triangle * DAG::get()
{
    Triangle *t = tri.get();
    if (t == nullptr)
        fprintf(stdout, "WARNING: DAG::get() pool is empty!\n");

    return t;
}

void DAG::removeTriangleContainingPoint(Vertex & a)
{
    for (int i = 0; i < tri.len(); i++) {
        if (0 <= tri[i]->containsPoint(a))
            tri[i]->setValid(false);
    }
}

json DAG::to_json() {
    json j;
    for (int i = 0; i < tri.len(); i++) {
        Triangle *a = tri[i];
        j.push_back( a->to_json() );
    }
    return j;
}

void DAG::logStep(const char * name,
                  const char * type,
                  Vertex * p,
                  json & j,
                  bool dump_to_file)
{
    static int len = -1;
    json j1;
    j1["name"] = name;
    j1["type"] = type;
    j1["point"] = p->to_json();
    j1["length"] = tri.len();
    j1["dag"] = to_json();

    j.push_back(j1);

    if (true == dump_to_file) {
        std::ofstream log;
        log.open( str( boost::format("step_%05d_%s_%s.json") % len % type % name ) );
        log << j.dump();
        log.close();
    } else {
        len = tri.len();
    }
}

void DAG::logError(const char * name,
                   const char * type,
                   Vertex * p)
{
    json j1;
    j1["name"] = name;
    j1["type"] = type;
    j1["point"] = p->to_json();
    j1["length"] = tri.len();
    j1["dag"] = to_json();
    for (int i = 0; i < adjList.len; i++) {
        j1["error"].push_back(adjList[i]->to_json());
    }

    std::ofstream log;
    log.open( str( boost::format("step_%05d_%s_%s.json") % tri.len() % type % name ) );
    log << j1.dump();
    log.close();
}


/* ----------------------------------------------------------------------------
 * Copyright 2020, Jesus Tordesillas Torres, Aerospace Controls Laboratory
 * Massachusetts Institute of Technology
 * All Rights Reserved
 * Authors: Jesus Tordesillas, et al.
 * See LICENSE file for the license information
 * -------------------------------------------------------------------------- */

#pragma once

#include <array>
#include <vector>
#include <Eigen/Dense>
#include <memory>

namespace separator
{
class Separator
{
public:
  Separator();
  ~Separator();

  bool solveModel(Eigen::Vector3d& solution, double& d, const std::vector<Eigen::Vector3d>& pointsA,
                  const std::vector<Eigen::Vector3d>& pointsB);

  bool solveModel(Eigen::Vector3d& solutionN, double& solutionD,
                  const Eigen::Matrix<double, 3, Eigen::Dynamic>& pointsA,
                  const Eigen::Matrix<double, 3, Eigen::Dynamic>& pointsB);

  long int getNumOfLPsRun();

  double meanSolveTimeMs();

private:
  // PImpl idiom, https://www.geeksforgeeks.org/pimpl-idiom-in-c-with-examples/
  struct PImpl;
  std::unique_ptr<PImpl> pm_;  // Opaque pointer
};

}  // namespace separator


//==============================================================================
//         Copyright 2003 - 2012 LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2012 LRI    UMR 8623 CNRS/Univ Paris Sud XI
//         Copyright 2012        MetaScale SAS
//
//          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_SDK_UNIT_DETAILS_ULP_HPP_INCLUDED
#define NT2_SDK_UNIT_DETAILS_ULP_HPP_INCLUDED

#include <nt2/sdk/unit/config.hpp>
#include <nt2/include/functions/value.hpp>
#include <nt2/sdk/unit/details/is_sequence.hpp>
#include <nt2/sdk/unit/details/smallest_type.hpp>

#include <boost/dispatch/attributes.hpp>
#include <boost/fusion/include/is_sequence.hpp>
#include <boost/foreach.hpp>
#include <algorithm>
#include <vector>
#include <iostream>

namespace nt2 { namespace details
{
  /// Hold which value was faulty and by how much
  template<class A, class B> struct failed_value
  {
    A           value;
    B           desired_value;
    double      ulp_error;
    std::size_t index;
  };

  /// ULP test is a success
  NT2_TEST_UNIT_DECL void ulp_pass(const char* desc, double ulpd, double N);

  /// ULP test is a failure
  NT2_TEST_UNIT_DECL
  void ulp_fail ( const char* desc, const char* f, int line
                , std::size_t size, double N, bool ok
                );

  /// Default implementation of max_ulps forward to generic ulpdist
  template<class A,class B>
  BOOST_FORCEINLINE double max_ulps(A const& a, B const& b )
  {
    return (a<b) ? b-a : a-b;
  }

  /// Precompiled implementation max_ulps on double
  NT2_TEST_UNIT_DECL double max_ulps(double a, double b);

  /// Precompiled implementation max_ulps on float
  NT2_TEST_UNIT_DECL double max_ulps(float  a, float  b);

  /// Perform a test of equality on A and B with a given ulp tolerance
  /// Detects if A and/or B is actually a Fusion sequence or not
  template< class A, class B
          , bool IsAFusionSeq=boost::fusion::traits::is_sequence<A>::value
          , bool IsBFusionSeq=boost::fusion::traits::is_sequence<A>::value
          >
  struct max_ulp_value_;

  /// A and B are not Fusion Sequence, call max_ulps on the value
  template< class A, class B>
  struct max_ulp_value_<A,B,false,false>
  {
    typedef failed_value<A,B> failure_type;

    template<class VF>
    BOOST_FORCEINLINE bool
    operator()( A const& a, B const& b
              , double max_ulpd, VF& fails, std::size_t i
              , double& ru
              ) const
    {
      double d = max_ulps ( nt2::details::smallest_a( nt2::value(a)
                                                    , nt2::value(b)
                                                    )
                          , nt2::details::smallest_b( nt2::value(a)
                                                    , nt2::value(b)
                                                    )
                          );
      if(!(d <= max_ulpd) )
      {
        failure_type f = { a, b, d, i };
        fails.push_back(f);
      }

      ru = d;

      return true;
    }
  };

  /// A and B are Fusion Sequence, call max_ulps on every elements and
  /// compute the maximum
  template< class A, class B>
  struct max_ulp_value_<A,B,true,true>
  {
    template<int I, int N> struct max_ulp_seq_
    {
      template<class X, class Y>
      BOOST_FORCEINLINE double
      operator()( X const& a, Y const& b, double z ) const
      {
        return max_ulp_seq_<I+1,N>()
              ( a , b
              , std::max( z
                        , max_ulps( nt2::details::
                                    smallest_a( boost::fusion::at_c<I>(a)
                                              , boost::fusion::at_c<I>(b)
                                              )
                                  , nt2::details::
                                    smallest_b( boost::fusion::at_c<I>(a)
                                              , boost::fusion::at_c<I>(b)
                                              )
                                  )
                        )
              );
      }
    };

    template<int N> struct max_ulp_seq_<N,N>
    {
      template<class X, class Y>
      BOOST_FORCEINLINE double
      operator()( X const&, Y const&, double z ) const
      {
        return z;
      }
    };

    typedef failed_value<A,B> failure_type;

    template<class VF>
    BOOST_FORCEINLINE bool
    operator()( A const& a, B const& b
              , double max_ulpd, VF& fails, std::size_t i
              , double& ru
              ) const
    {
      double d =  max_ulp_seq_<0,boost::fusion::result_of
                                              ::size<A>::value>()(a,b,0.);

      if(!(d <=  max_ulpd))
      {
        failure_type f = {a, b, d, i};
        fails.push_back(f);
      }

      ru =  d;

      return true;
    }
  };

  /// Perform a test of equality on A and B with a given ulp tolerance
  /// Detects if A and/or B is actually a sequence and apply max_ulps on
  /// every elements of said sequences
  template< class A, class B
          , bool IsASeq=nt2::details::is_sequence<A>::value
          , bool IsBSeq=nt2::details::is_sequence<B>::value
          >
  struct max_ulp_
  {
    typedef typename max_ulp_value_<A,B>::failure_type failure_type;

    /// Main operator() checks of A and B are Fusion Sequence then
    /// jump into the proper eval() member functions depending on this
    /// status.
    template<class VF>
    BOOST_FORCEINLINE bool
    operator()( A const& a, B const& b
              , double max_ulpd, VF& fails, std::size_t i
              , double& ru
              ) const
    {
      return max_ulp_value_<A,B>()(a, b, max_ulpd, fails, i, ru);
    }
  };

  /// Both A and B are sequence, so we iterate over elements using the single
  /// value version of max_ulp_
  template<class A, class B>
  struct max_ulp_< A, B, true,true>
  {
    typedef failed_value< typename A::value_type
                        , typename B::value_type
                        >                         failure_type;

    template<class VF>
    BOOST_FORCEINLINE bool
    operator()( A const& a, B const& b
              , double max_ulpd, VF& fails, std::size_t i
              , double& ru
              ) const
    {
      if( std::distance(b.begin(),b.end()) == std::distance(a.begin(),a.end()))
      {
        double res = 0;

        typename A::const_iterator ab = a.begin();
        typename A::const_iterator ae = a.end();
        typename B::const_iterator bb = b.begin();

        while(ab != ae)
        {
          double r;
          max_ulp_< typename A::value_type
                  , typename B::value_type
                  >()(*ab,*bb,max_ulpd,fails,i++,r);

          res = std::max(res,r);
          ab++;
          bb++;
        }

        ru = res;

        return true;
      }
      else
      {
        return false;
      }
    }
  };

  /// A is a sequence while B is not, this means B has exactly one element
  template<class A, class B>
  struct max_ulp_< A, B, true, false>
  {
    typedef failed_value<typename A::value_type, B> failure_type;

    template<class VF>
    BOOST_FORCEINLINE bool
    operator()( A const& a, B const& b
              , double max_ulpd, VF& fails, std::size_t i
              , double& ru
              ) const
    {
      if( std::distance(a.begin(),a.end()) == 1)
      {
        return max_ulp_<typename A::value_type,B>() ( *a.begin(),b
                                                    , max_ulpd,fails,i, ru
                                                    );
      }
      else
      {
        return false;
      }
    }
  };

  /// B is a sequence while A is not, this means B has exactly one element
  template<class A, class B>
  struct max_ulp_< A, B, false, true>
  {
    typedef failed_value<A, typename B::value_type> failure_type;

    template<class VF>
    BOOST_FORCEINLINE bool
    operator()( A const& a, B const& b
              , double max_ulpd, VF& fails , std::size_t i
              , double& ru
              ) const
    {
      if( std::distance(b.begin(),b.end()) == 1)
      {
        return max_ulp_<typename A::value_type,B>() ( a,*b.begin()
                                                    , max_ulpd,fails,i, ru
                                                    );
      }
      else
      {
        return false;
      }
    }
  };
} }

namespace nt2 { namespace unit
{
  /// INTERNAL ONLY Main test for equality over any types A and B within a
  /// given ulp tolerance
  template<class A, class B, class VF>
  BOOST_FORCEINLINE
  bool max_ulp( A const& a, B const& b, double max_ulpd, VF& fails, double& ru )
  {
    return details::max_ulp_<A,B>()(a,b,max_ulpd,fails,0,ru);
  }
} }

#endif


//==================================================================================================
/*
  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)
*/
//==================================================================================================
#include <boost/simd/pack.hpp>
#include <algorithm>
#include <numeric>
#include <simd_test.hpp>

namespace bs = boost::simd;

template <typename T, std::size_t N, typename Env>
void test_not(Env& $)
{
  std::array<T, N> a;
  std::iota(a.begin(), a.end(), T(1));
  bs::pack<T, N> p0(a.begin(), a.end()), ref(a.begin(), a.end());

  bool supports_logical_not = true;
  for (auto it0 = p0.begin(); it0 != p0.end(); ++it0)
    supports_logical_not = supports_logical_not && !(!(*it0));

  STF_EXPECT( supports_logical_not );
}

STF_CASE_TPL( "Check proxy bitwise negation" , STF_NUMERIC_TYPES)
{
  test_not<T,  1>($);
  test_not<T,  2>($);
  test_not<T,  4>($);
  test_not<T,  8>($);
  test_not<T, 16>($);
  test_not<T, 32>($);
}


///////////////////////////////////////////////////////////////////////////////
// statistics::model::prior::wrap::unary::prior.hpp                          //
//                                                                           //
//  Copyright 2009 Erwann Rogard. 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)         //
///////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_STATISTICS_MODEL_WRAP_UNARY_PRIOR_HPP_ER_2009
#define BOOST_STATISTICS_MODEL_WRAP_UNARY_PRIOR_HPP_ER_2009
#include <boost/statistics/model/wrap/unary/detail/base.hpp>

namespace boost{
namespace statistics{
namespace model{

    template<typename D>
    class prior_wrapper : detail::base_<D>{
        typedef detail::base_<D> base_;
    public:
        typedef D                 prior_type;
        typedef prior_wrapper<D>  prior_wrapper_type;

        // Construction
        prior_wrapper();
        prior_wrapper(const D& d);
        prior_wrapper(const prior_wrapper&);

        // Access
        const D& prior()const;
    };

    // Implementation //

    template<typename D>
    prior_wrapper<D>::prior_wrapper() : base_(){}

    template<typename D>
    prior_wrapper<D>::prior_wrapper( const D& d ) : base_(d){}

    template<typename D>
    prior_wrapper<D>::prior_wrapper(const prior_wrapper& that)
    :base_(that){}

    template<typename D>
    const D& prior_wrapper<D>::prior()const{ return (this->value); }

    // Free functions

    template<typename D>
    prior_wrapper<D>
    make_prior_wrapper(const D& d){
        typedef prior_wrapper<D> result_type;
        return result_type(d);
    }

}// prior
}// statistics
}// boost

#endif


//---------------------------------------------------------------------------//
//!
//! \file   tstElectronVoltUnit.cpp
//! \author Alex Robinson
//! \brief  Electron volt unit unit test
//!
//---------------------------------------------------------------------------//

// Boost Includes
#include <boost/units/quantity.hpp>
#include <boost/units/systems/cgs/energy.hpp>
#include <boost/units/systems/si/energy.hpp>

// Trilinos Includes
#include <Teuchos_UnitTestHarness.hpp>

// FRENSIE Includes
#include "Utility_ElectronVoltUnit.hpp"
#include "Utility_UnitTraits.hpp"

using namespace Utility::Units;
using boost::units::quantity;
namespace si = boost::units::si;
namespace cgs = boost::units::cgs;

//---------------------------------------------------------------------------//
// Tests
//---------------------------------------------------------------------------//
// Check that the electron volt unit can be initialized
TEUCHOS_UNIT_TEST( ElectronVolt, initialize )
{
  quantity<ElectronVolt> ev_energy( 1*eV );

  TEST_EQUALITY_CONST( ev_energy.value(), 1.0 );

  ev_energy = 2.0*eV;

  TEST_EQUALITY_CONST( ev_energy.value(), 2.0 );
}

//---------------------------------------------------------------------------//
// Check that electron volt unit can be initialized from cgs and si units
TEUCHOS_UNIT_TEST( ElectronVolt, initialize_from_cgs_si )
{
  quantity<ElectronVolt> ev_energy_cgs( 1.0*cgs::erg );

  quantity<ElectronVolt> ev_energy_si( 1.0*si::joule );

  TEST_FLOATING_EQUALITY( ev_energy_cgs.value(),
			  6.24150934326017944e+11,
			  1e-15 );
  TEST_FLOATING_EQUALITY( ev_energy_si.value(),
			  1.0/1.602176565e-19,
			  1e-15 );
}

//---------------------------------------------------------------------------//
// Check that the electron volt unit can be initialized from keV and MeV
TEUCHOS_UNIT_TEST( ElectronVolt, initialize_from_kev_mev )
{
  quantity<ElectronVolt> ev_energy_kev( 1.0*keV );

  quantity<ElectronVolt> ev_energy_mev( 1.0*MeV );

  TEST_EQUALITY_CONST( ev_energy_kev.value(), 1000.0 );
  TEST_EQUALITY_CONST( ev_energy_mev.value(), 1e6 );
}

//---------------------------------------------------------------------------//
// Check that the cgs and si units can be initialized from the electron volt
TEUCHOS_UNIT_TEST( ElectronVolt, initialize_cgs_si )
{
  quantity<cgs::energy> cgs_energy( 1*eV );

  quantity<si::energy> si_energy( 1*eV );

  TEST_FLOATING_EQUALITY( cgs_energy.value(), 1.602176565e-12, 1e-15 );
  TEST_FLOATING_EQUALITY( si_energy.value(), 1.602176565e-19, 1e-15 );
}

//---------------------------------------------------------------------------//
// Check that the keV unit can be initialized
TEUCHOS_UNIT_TEST( KiloElectronVolt, initialize )
{
  quantity<KiloElectronVolt> kev_energy( 1*keV );

  TEST_EQUALITY_CONST( kev_energy.value(), 1.0 );

  kev_energy = 2.0*keV;

  TEST_EQUALITY_CONST( kev_energy.value(), 2.0 );
}

//---------------------------------------------------------------------------//
// Check that the keV unit can be initialized from cgs and si units
TEUCHOS_UNIT_TEST( KiloElectronVolt, initialize_from_cgs_si )
{
  quantity<KiloElectronVolt> kev_energy_cgs( 1.0*cgs::erg );

  quantity<KiloElectronVolt> kev_energy_si( 1.0*si::joule );

  TEST_FLOATING_EQUALITY( kev_energy_cgs.value(),
			  6.24150934326017976e8,
			  1e-15 );
  TEST_FLOATING_EQUALITY( kev_energy_si.value(),
			  1.0/1.602176565e-16,
			  1e-15 );
}

//---------------------------------------------------------------------------//
// Check that the keV unit can bin intialized from eV and MeV units
TEUCHOS_UNIT_TEST( KiloElectronVolt, initialize_from_ev_mev )
{
  quantity<KiloElectronVolt> kev_energy_ev( 1.0*eV );

  quantity<KiloElectronVolt> kev_energy_mev( 1.0*MeV );

  TEST_EQUALITY_CONST( kev_energy_ev.value(), 1e-3 );
  TEST_EQUALITY_CONST( kev_energy_mev.value(), 1e3 );
}

//---------------------------------------------------------------------------//
// Check that the cgs and si units can be initialized from the keV unit
TEUCHOS_UNIT_TEST( KiloElectronVolt, initialize_cgs_si )
{
  quantity<cgs::energy> cgs_energy( 1.0*keV );

  quantity<si::energy> si_energy( 1.0*keV );

  TEST_FLOATING_EQUALITY( cgs_energy.value(),1.602176565e-09, 1e-15 );
  TEST_FLOATING_EQUALITY( si_energy.value(), 1.602176565e-16, 1e-15 );
}

//---------------------------------------------------------------------------//
// Check that the mega-electron volt unit can be initialized
TEUCHOS_UNIT_TEST( MegaElectronVolt, initialize )
{
  quantity<MegaElectronVolt> mev_energy( 1*MeV );

  TEST_EQUALITY_CONST( mev_energy.value(), 1.0 );

  mev_energy = 2.0*MeV;

  TEST_EQUALITY_CONST( mev_energy.value(), 2.0 );
}

//---------------------------------------------------------------------------//
// Check that the MeV unit can be initialized from cgs and si units
TEUCHOS_UNIT_TEST( MegaElectronVolt, initialize_from_cgs_si )
{
  quantity<MegaElectronVolt> mev_energy_cgs( 1.0*cgs::erg );

  quantity<MegaElectronVolt> mev_energy_si( 1.0*si::joule );

  TEST_FLOATING_EQUALITY( mev_energy_cgs.value(),
			  6.24150934326017974e5,
			  1e-15 );
  TEST_FLOATING_EQUALITY( mev_energy_si.value(),
			  1.0/1.602176565e-13,
			  1e-15 );
}

//---------------------------------------------------------------------------//
// Check that the MeV unit can bin intialized from eV and keV units
TEUCHOS_UNIT_TEST( MegaElectronVolt, initialize_from_ev_kev )
{
  quantity<MegaElectronVolt> mev_energy_ev( 1.0*eV );

  quantity<MegaElectronVolt> mev_energy_kev( 1.0*keV );

  TEST_EQUALITY_CONST( mev_energy_ev.value(), 1e-6 );
  TEST_EQUALITY_CONST( mev_energy_kev.value(), 1e-3 );
}

//---------------------------------------------------------------------------//
// Check that the cgs and si units can be initialized from the MeV unit
TEUCHOS_UNIT_TEST( MegaElectronVolt, initialize_cgs_si )
{
  quantity<cgs::energy> cgs_energy( 1.0*MeV );

  quantity<si::energy> si_energy( 1.0*MeV );

  TEST_FLOATING_EQUALITY( cgs_energy.value(), 1.602176565e-06, 1e-15 );
  TEST_FLOATING_EQUALITY( si_energy.value(), 1.602176565e-13, 1e-15 );
}

//---------------------------------------------------------------------------//
// end tstElectronVoltUnit.cpp
//---------------------------------------------------------------------------//


/*
 * typedefs.hpp
 *
 *  Created on: Mar 19, 2020
 *      Author: jelavice
 */

#pragma once
#include <Eigen/Dense>

namespace pure_pursuit {

using Vector = Eigen::Vector2d;
using Point = Eigen::Vector2d;
using Matrix = Eigen::Matrix2d;

}  // namespace pure_pursuit


#include <cassert>
#include <exception>
#include <iostream>

#include <boost/safe_numerics/safe_integer.hpp>

int main(int, const char *[]){
    std::cout << "example 2:";
    std::cout << "undetected overflow in data type" << std::endl;
    // problem: undetected overflow
    std::cout << "Not using safe numerics" << std::endl;
    try{
        int x = INT_MAX;
        // the following silently produces an incorrect result
        ++x;
        std::cout << x << " != " << INT_MAX << " + 1" << std::endl;
        std::cout << "error NOT detected!" << std::endl;
    }
    catch(std::exception){
        std::cout << "error detected!" << std::endl;
    }
    // solution: replace int with safe<int>
    std::cout << "Using safe numerics" << std::endl;
    try{
        using namespace boost::safe_numerics;
        safe<int> x = INT_MAX;
        // throws exception when result is past maximum possible
        ++x;
        assert(false); // never arrive here
    }
    catch(std::exception & e){
        std::cout << e.what() << std::endl;
        std::cout << "error detected!" << std::endl;
    }
    return 0;
}


// Author(s): Jeroen van der Wulp, Wieger Wesselink
// Copyright: see the accompanying file COPYING or copy at
// https://svn.win.tue.nl/trac/MCRL2/browser/trunk/COPYING
//
// 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 <functional>
#include <string>
#include <utility>

#include <boost/test/minimal.hpp>

#include "mcrl2/data/assignment.h"
#include "mcrl2/data/detail/concepts.h"
#include "mcrl2/data/expression_traits.h"
#include "mcrl2/data/lambda.h"
#include "mcrl2/data/parse.h"
#include "mcrl2/data/replace.h"
#include "mcrl2/data/replace.h"
#include "mcrl2/data/standard_utility.h"
#include "mcrl2/data/substitutions/assignment_sequence_substitution.h"
#include "mcrl2/data/substitutions/enumerator_substitution.h"
#include "mcrl2/data/substitutions/mutable_indexed_substitution.h"
#include "mcrl2/data/substitutions/mutable_map_substitution.h"
#include "mcrl2/data/substitutions/mutable_substitution_composer.h"
#include "mcrl2/data/substitutions/sort_expression_assignment.h"

using namespace mcrl2;
using namespace mcrl2::data;

inline
data_expression operator+(data_expression const& l, data_expression const& r)
{
  return sort_nat::plus(l, r);
}

inline
data_expression operator*(data_expression const& l, data_expression const& r)
{
  return sort_nat::times(l, r);
}

void test_basic()
{
  using namespace mcrl2::data::sort_nat;

  variable        x("x", sort_nat::nat());
  variable        y("y", sort_nat::nat());
  data_expression e(variable("z", sort_nat::nat()) + (x + y));

  using mcrl2::data::concepts::MutableSubstitution;

//  BOOST_CONCEPT_ASSERT((MutableSubstitution< mutable_map_substitution< std::map< variable, data_expression > > >));
//  BOOST_CONCEPT_ASSERT((MutableSubstitution< mutable_map_substitution< std::map< variable, variable > > >));

  mutable_map_substitution< std::map< variable, data_expression > > s;

  BOOST_CHECK(static_cast< variable >(s(x)) == x);
  BOOST_CHECK(static_cast< variable >(s(y)) != x);

  data::function_symbol c("c", sort_nat::nat());

  BOOST_CHECK(c + x == c + x);
  BOOST_CHECK(data::replace_free_variables(data_expression(c + x), s) == c + x);
  BOOST_CHECK(data::replace_free_variables(data_expression(c + x * y), s) == c + x * y);

  s[y] = c;

  BOOST_CHECK(data::replace_free_variables(static_cast<const data_expression&>(x), s) == x);
  BOOST_CHECK(data::replace_free_variables(static_cast<const data_expression&>(y), s) == c);

  BOOST_CHECK(data::replace_free_variables(c + x * y, s) == c + x * c);
  BOOST_CHECK(data::replace_free_variables(lambda(x,x), s) == lambda(x,x));
  BOOST_CHECK(data::replace_free_variables(lambda(x,y), s) == lambda(x,c));

  // Replacing free variables only
  mutable_map_substitution< std::map< variable, data_expression > > sb;

  sb[y] = c;

  BOOST_CHECK(data::replace_free_variables(lambda(y,y), sb) == lambda(y,y));
  BOOST_CHECK(data::replace_free_variables(lambda(y,y)(x) + y, sb) == lambda(y,y)(x) + c);
}

void test_indexed_substitution()
{
  using namespace mcrl2::data::sort_nat;

  variable        x("x", sort_nat::nat());
  variable        y("y", sort_nat::nat());
  data_expression e(variable("z", sort_nat::nat()) + (x + y));

  using mcrl2::data::concepts::MutableSubstitution;

//  BOOST_CONCEPT_ASSERT((MutableSubstitution< mutable_indexed_substitution< variable, std::vector< data_expression > > >));
//  BOOST_CONCEPT_ASSERT((MutableSubstitution< mutable_indexed_substitution< variable, std::vector< variable > > >));

  mutable_indexed_substitution< variable, data_expression > s;

  BOOST_CHECK(static_cast< variable >(s(x)) == x);
  BOOST_CHECK(static_cast< variable >(s(y)) != x);

  data::function_symbol c("c", sort_nat::nat());

  BOOST_CHECK(c + x == c + x);
  BOOST_CHECK(data::replace_free_variables(data_expression(c + x), s) == c + x);
  BOOST_CHECK(data::replace_free_variables(data_expression(c + x * y), s) == c + x * y);

  s[y] = c;

#ifdef MCRL2_NEW_REPLACE_VARIABLES
// in the old version this fails due to the unfortunate interface of replace_free_variables
  BOOST_CHECK(data::replace_free_variables(x, s) == x);
  BOOST_CHECK(data::replace_free_variables(y, s) == c);
#endif
std::cerr << "TEST " << x << "  " << y << " OUD \n";
std::cerr << "RES " << c + x * c << "\n";
std::cerr << "RES " << data::replace_free_variables(c + x * y, s) << "\n";
  BOOST_CHECK(data::replace_free_variables(c + x * y, s) == c + x * c);
  BOOST_CHECK(data::replace_free_variables(lambda(x,x), s) == lambda(x,x));
  BOOST_CHECK(data::replace_free_variables(lambda(x,y), s) == lambda(x,c));

  // Replacing free variables only
  mutable_indexed_substitution< variable, data_expression > sb;

  sb[y] = c;

  BOOST_CHECK(data::replace_free_variables(lambda(y,y), sb) == lambda(y,y));
  BOOST_CHECK(data::replace_free_variables(lambda(y,y)(x) + y, sb) == lambda(y,y)(x) + c);
}

struct my_assignment_sequence_substitution: public std::unary_function<variable, data_expression>
{
  typedef variable variable_type;
  typedef data_expression expression_type;

  assignment_list assignments;

  my_assignment_sequence_substitution(assignment_list assignments_)
    : assignments(assignments_)
  {}

  data_expression operator()(const variable& v) const
  {
    for (const auto & assignment : assignments)
    {
      if (assignment.lhs() == v)
      {
        return assignment.rhs();
      }
    }
    return v;
  }
};

void test_my_assignment_sequence_substitution()
{
  variable x("x", sort_nat::nat());
  variable y("y", sort_nat::nat());
  variable z("z", sort_nat::nat());
  variable u("u", sort_nat::nat());

  assignment xy(x,y);
  assignment uz(u,z);
  assignment_list l({ xy, uz });

  my_assignment_sequence_substitution f(l);

  BOOST_CHECK(f(x) == y);
  BOOST_CHECK(f(y) == y);
  BOOST_CHECK(f(z) == z);
  BOOST_CHECK(f(u) == z);

  assignment yz(y,z);
  l = { xy, uz, yz };
  my_assignment_sequence_substitution g(l);

  BOOST_CHECK(g(x) == y); // Assignments are not simultaneous, hence we expect y
  BOOST_CHECK(g(y) == z);
  BOOST_CHECK(g(z) == z);
  BOOST_CHECK(g(u) == z);
}

void test_my_list_substitution()
{
  variable x("x", sort_nat::nat());
  variable y("y", sort_nat::nat());
  variable z("z", sort_nat::nat());
  variable u("u", sort_nat::nat());
  variable v("v", sort_nat::nat());

  const data_expression& y1 = y;
  // const data_expression& z1 = z;
  // const data_expression& u1 = u;
  // const data_expression& v1 = v;

  assignment xy(x,y);
  assignment uz(u,z);
  assignment_list l = { xy, uz };
  assignment_list r = { assignment(x, y1) };

// TODO: This does not longer work, can it be fixed?
//  BOOST_CHECK(replace_variables(x,  my_assignment_sequence_substitution(r)) == v1);
//  BOOST_CHECK(replace_variables(y,  my_assignment_sequence_substitution(r)) == y1);
//  BOOST_CHECK(replace_variables(z,  my_assignment_sequence_substitution(r)) == z1);
//  BOOST_CHECK(replace_variables(u,  my_assignment_sequence_substitution(r)) == u1);
//  BOOST_CHECK(replace_variables(uz, my_assignment_sequence_substitution(r)) == uz);
}

void test_assignment_sequence_substitution()
{
  variable x("x", sort_nat::nat());
  variable y("y", sort_nat::nat());
  variable z("z", sort_nat::nat());
  variable u("u", sort_nat::nat());

  assignment xy(x,y);
  assignment uz(u,z);
  assignment_list l = { xy, uz };

  assignment_sequence_substitution f(l);

  BOOST_CHECK(f(x) == y);
  BOOST_CHECK(f(y) == y);
  BOOST_CHECK(f(z) == z);
  BOOST_CHECK(f(u) == z);

  assignment yz(y,z);
  l = { xy, uz, yz };
  assignment_sequence_substitution g(l);

  BOOST_CHECK(g(x) == y); // Assignments are not simultaneous, hence we expect y
  BOOST_CHECK(g(y) == z);
  BOOST_CHECK(g(z) == z);
  BOOST_CHECK(g(u) == z);
}

void test_list_substitution()
{
  variable x("x", sort_nat::nat());
  variable y("y", sort_nat::nat());
  variable z("z", sort_nat::nat());
  variable u("u", sort_nat::nat());

  variable y1("y1", sort_nat::nat());

  assignment xy(x,y);
  assignment uz(u,z);
  assignment_list l = { xy, uz };
  assignment_list r = { assignment(x, y1) };

// TODO: This does not longer work, can it be fixed?
//  BOOST_CHECK(replace_variables(x, assignment_sequence_substitution(r)) == y1);
//  BOOST_CHECK(replace_variables(y, assignment_sequence_substitution(r)) == y);
//  BOOST_CHECK(replace_variables(z, assignment_sequence_substitution(r)) == z);
//  BOOST_CHECK(replace_variables(u, assignment_sequence_substitution(r)) == u);
//  BOOST_CHECK(replace_variables(uz, assignment_sequence_substitution(r)) == uz);
}

void test_mutable_substitution_composer()
{
  mutable_map_substitution< > f;
  variable x("x", sort_nat::nat());
  variable y("y", sort_nat::nat());
  variable z("z", sort_nat::nat());
  f[x] = y;

  mutable_substitution_composer<mutable_map_substitution< > > g(f);
  BOOST_CHECK(g(x) == y);
}

struct my_sort_substitution: public std::unary_function<data::basic_sort, data::sort_expression>
{
  data::sort_expression operator()(const data::basic_sort& s)
  {
    if (s == sort_nat::nat()) {
      return sort_pos::pos();
    }
    return s;
  }
};

void test_sort_substitution()
{
  using namespace mcrl2::data;
  using namespace mcrl2::data::sort_list;
  using namespace mcrl2::data::sort_set;
  using namespace mcrl2::data::sort_bag;

  data_specification specification = parse_data_specification("sort A = struct f(A) | g;");

  // s0 = A
  sort_expression s0 = basic_sort("A");

  // s1 = f(A)|g
  std::vector< structured_sort_constructor_argument > arguments1;
  arguments1.push_back(structured_sort_constructor_argument(basic_sort("A")));
  std::vector< structured_sort_constructor > constructors1;
  constructors1.push_back(structured_sort_constructor("f", arguments1));
  constructors1.push_back(structured_sort_constructor("g"));
  sort_expression s1=structured_sort(constructors1);

  // s2 = f(struct f(A)|g) |g
  std::vector< structured_sort_constructor_argument > arguments2;
  arguments2.push_back(structured_sort_constructor_argument(s1));
  std::vector< structured_sort_constructor > constructors2;
  constructors2.push_back(structured_sort_constructor("f", arguments2));
  constructors2.push_back(structured_sort_constructor("g"));
  sort_expression s2=structured_sort(constructors2);

  std::cout << "s0 = " << data::pp(s0) << std::endl;
  std::cout << "s1 = " << data::pp(s1) << std::endl;
  std::cout << "s2 = " << data::pp(s2) << std::endl;

  // apply the substitution s1 := s0 innermost to s2
  sort_expression s3 = data::replace_sort_expressions(s2, sort_expression_assignment(s1, s0), true);
  std::cout << "s3 = " << data::pp(s3) << std::endl;
  BOOST_CHECK(s3 == s0);

  // apply the substitution s1 := s0 to s2
  sort_expression s4 = data::replace_sort_expressions(s2, sort_expression_assignment(s1, s0), false);
  std::cout << "s4 = " << data::pp(s4) << std::endl;
  BOOST_CHECK(s4 == s2);

}

// returns the function composition f o g
inline
enumerator_substitution compose(const enumerator_substitution& f, const enumerator_substitution& g)
{
  return enumerator_substitution(g.variables + f.variables, g.expressions + f.expressions);
}

void test_enumerator_substitution(const enumerator_substitution& sigma, const variable& x, const data_expression& expected_result)
{
  data_expression result = sigma(x);
  if (result != expected_result)
  {
    std::cerr << "ERROR: sigma = " << sigma << "(" << x << ") = " << result << " expected_result = " << expected_result << std::endl;
  }
  BOOST_CHECK(result == expected_result);
}

void test_enumerator_substitution()
{
  typedef core::term_traits<data_expression> tr;

  variable x = parse_variable("x: Bool");
  variable y = parse_variable("y: Bool");
  variable z = parse_variable("z: Bool");
  data_expression x_and_y = tr::and_(x, y);
  data_expression y_and_y = tr::and_(y, y);

  enumerator_substitution sigma1;
  sigma1.add_assignment(x, y);

  enumerator_substitution sigma2;
  sigma2.add_assignment(y, z);

  enumerator_substitution sigma3;
  sigma3.add_assignment(z, x_and_y);

  enumerator_substitution sigma;
  data::data_expression expected_result;

  sigma = compose(sigma1, sigma2);
  expected_result = y;
  test_enumerator_substitution(sigma, x, expected_result);

  sigma = compose(sigma2, sigma1);
  expected_result = z;
  test_enumerator_substitution(sigma, x, expected_result);

  sigma = compose(sigma1, sigma3);
  expected_result = y_and_y;
  test_enumerator_substitution(sigma, z, expected_result);

  variable n = parse_variable("n: Nat");
  variable q = parse_variable("q: Pos");
  data_expression one = parse_data_expression("1");
  enumerator_substitution rho1;
  rho1.add_assignment(n, q);
  rho1.add_assignment(q, one);
  rho1.revert();
  test_enumerator_substitution(rho1, n, one);
}

void test_mutable_indexed_substitution()
{
  std::cout << "test_mutable_indexed_substitution" << std::endl;
  mutable_indexed_substitution<> sigma;
  variable b = parse_variable("b: Bool");
  data_expression T = parse_data_expression("true");
  sigma[b] = T;
  std::string s = sigma.to_string();
  std::cout << "s = " << s << std::endl;
  BOOST_CHECK(s == "[b := true]");
}

int test_main(int /* a */, char**  /* aa */)
{
  test_my_assignment_sequence_substitution();
  test_my_list_substitution();
  test_basic();
  test_assignment_sequence_substitution();
  test_list_substitution();
  test_sort_substitution();
  test_indexed_substitution();
  test_enumerator_substitution();
  test_mutable_indexed_substitution();

  return EXIT_SUCCESS;
}


///////////////////////////////////////////////////////////////////////////////
// distribution::survival::record::function::lower_bound.hpp                 //
//                                                                           //
//  Copyright 2009 Erwann Rogard. 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)         //
///////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_STATISTICS_DETAIL_DISTRIBUTION_SURVIVAL_RECORD_FUNCTION_LOWER_BOUND_HPP_ER_2009
#define BOOST_STATISTICS_DETAIL_DISTRIBUTION_SURVIVAL_RECORD_FUNCTION_LOWER_BOUND_HPP_ER_2009
#include <algorithm>
#include <boost/fusion/include/make_map.hpp>
#include <boost/statistics/detail/distribution/survival/record/function/lower_bound.hpp>
#include <boost/statistics/detail/distribution/survival/record/function/less_than.hpp>

namespace boost{
namespace statistics{
namespace detail{
namespace distribution{
namespace survival{
namespace record{

	template<typename It,typename T>
    It lower_bound(It b,It e,const T& time_analysis)
    {
    	typedef record::tag::entry_time tag_;
    	return std::lower_bound(
            b,
            e,
            boost::fusion::make_map<tag_>( time_analysis ),
            record::less_than()
        );
    }

}// record
}// survival
}// distribution
}// detail
}// statistics
}// boost

#endif


// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

#include <iostream>
#include <random>
#include <vector>

#include "cutil.hpp"
#include "util.hpp"
#include "zipf_dist.hpp"

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

namespace po = boost::program_options;
namespace fs = boost::filesystem;

po::variables_map parse_cmdargs(int argc, char const* argv[])
{
    po::variables_map vm;
    po::options_description desc("Allowed options");
    // clang-format off
    desc.add_options()
        ("help,h", "produce help message")
        ("text,t", "text (default is uint32_t binary)")
        ("num,n",po::value<size_t>()->required(), "number of integers per file")
        ("output,o",po::value<std::string>()->required(), "output path");
    // clang-format on
    try {
        po::store(po::parse_command_line(argc, argv, desc), vm);
        if (vm.count("help")) {
            std::cout << desc << "\n";
            exit(EXIT_SUCCESS);
        }
        po::notify(vm);
    } catch (const po::required_option& e) {
        std::cout << desc;
        std::cerr << "Missing required option: " << e.what() << std::endl;
        exit(EXIT_FAILURE);
    } catch (po::error& e) {
        std::cout << desc;
        std::cerr << "Error parsing cmdargs: " << e.what() << std::endl;
        exit(EXIT_FAILURE);
    }

    return vm;
}

template <class t_dist>
void generate_data(
    const t_dist& dist, size_t n, std::string out_file, bool write_text)
{
    std::string file_name = out_file;
    std::cout << "generating file " << file_name << std::endl;
    std::vector<uint32_t> nums;
    std::mt19937 gen(0);
    t_dist dst = dist;
    for (size_t i = 0; i < n; i++) {
        nums.push_back(dst(gen));
    }
    if (write_text)
        write_file_text(nums, file_name + ".txt");
    else
        write_file_u32(nums, file_name + ".u32");
}

int main(int argc, char const* argv[])
{
    auto cmdargs = parse_cmdargs(argc, argv);
    auto output_path = cmdargs["output"].as<std::string>();
    auto n = cmdargs["num"].as<size_t>();
    auto write_text = cmdargs.count("text") != 0;

    generate_data(std::uniform_int_distribution<uint32_t>(0, (1 << 8) - 1), n,
        output_path + "/uniform08", write_text);
    generate_data(std::uniform_int_distribution<uint32_t>(0, (1 << 12) - 1), n,
        output_path + "/uniform12", write_text);
    generate_data(std::uniform_int_distribution<uint32_t>(0, (1 << 16) - 1), n,
        output_path + "/uniform16", write_text);
    generate_data(std::uniform_int_distribution<uint32_t>(0, (1 << 20) - 1), n,
        output_path + "/uniform20", write_text);

    generate_data(std::geometric_distribution<uint32_t>(0.01), n,
        output_path + "/geom0.01", write_text);
    generate_data(std::geometric_distribution<uint32_t>(0.1), n,
        output_path + "/geom0.1", write_text);
    generate_data(std::geometric_distribution<uint32_t>(0.2), n,
        output_path + "/geom0.2", write_text);
    generate_data(std::geometric_distribution<uint32_t>(0.4), n,
        output_path + "/geom0.4", write_text);
    generate_data(std::geometric_distribution<uint32_t>(0.6), n,
        output_path + "/geom0.6", write_text);
    generate_data(std::geometric_distribution<uint32_t>(0.8), n,
        output_path + "/geom0.8", write_text);
    generate_data(std::geometric_distribution<uint32_t>(0.9), n,
        output_path + "/geom0.9", write_text);
    generate_data(std::geometric_distribution<uint32_t>(0.99), n,
        output_path + "/geom0.99", write_text);

    generate_data(zipf_distribution<uint32_t>(1 << 12), n,
        output_path + "/zipf12", write_text);
    generate_data(zipf_distribution<uint32_t>(1 << 20), n,
        output_path + "/zipf20", write_text);
}