#ifndef ARRAY_N_DIMENSION_CONSTRUCT_DATA
#define ARRAY_N_DIMENSION_CONSTRUCT_DATA

#include <bredala/data_model/baseconstructdata.hpp>
#include <boost/multi_array.hpp>
#include <boost/serialization/array.hpp>
#include <bredala/data_model/multiarrayserialize.hpp>

// TODO :  See http://www.boost.org/doc/libs/1_46_1/libs/serialization/doc/special.html
// for serialization optimizations

namespace decaf {

template<typename T, int Dim>
class ArrayNDimConstructData : public BaseConstructData {
public:
    ArrayNDimConstructData(mapConstruct map = mapConstruct())
        : BaseConstructData(map){}

    ArrayNDimConstructData(boost::multi_array<T, Dim> value,
                           mapConstruct map = mapConstruct()) :
        BaseConstructData(map), value_(value){}

    virtual ~ArrayNDimConstructData(){}

    virtual bool isBlockSplitable(){ return true; }

    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        boost::serialization::base_object<BaseConstructData>(*this);
        ar & BOOST_SERIALIZATION_NVP(value_);
    }

    virtual boost::multi_array<T, Dim> getArray(){ return value_; }

    virtual int getNbItems(){ return value_.size(); }

	virtual std::string getTypename(){ return std::string("ArrayNDim_" + Dim + "_" + boost::typeindex::type_id<T>().pretty_name()); }

    virtual std::vector<std::shared_ptr<BaseConstructData> > split(
            const std::vector<int>& range,
            std::vector< mapConstruct >& partial_map,
            ConstructTypeSplitPolicy policy = DECAF_SPLIT_DEFAULT )
    {
        std::vector<std::shared_ptr<BaseConstructData> > result;
        std::cout<<"ERROR : the spliting of a n-dimension array is not implemted yet."<<std::endl;
        return result;
    }

    virtual std::vector<std::shared_ptr<BaseConstructData> > split(
            const std::vector< std::vector<int> >& range,
            std::vector< mapConstruct >& partial_map,
            ConstructTypeSplitPolicy policy = DECAF_SPLIT_DEFAULT )
    {
        std::vector<std::shared_ptr<BaseConstructData> > result;
        std::cout<<"ERROR : the spliting of a n-dimension array is not implemted yet."<<std::endl;
        return result;
    }

    virtual std::vector<std::shared_ptr<BaseConstructData> > split(
            const std::vector< block3D >& range,
            std::vector< mapConstruct >& partial_map,
            ConstructTypeSplitPolicy policy = DECAF_SPLIT_DEFAULT)
    {
        std::vector<std::shared_ptr<BaseConstructData> > result;

        std::cout<<"ERROR : using a dimension non defined"<<std::endl;

        return result;
    }

    virtual bool merge( std::shared_ptr<BaseConstructData> other,
                        mapConstruct partial_map,
                        ConstructTypeMergePolicy policy = DECAF_MERGE_DEFAULT)
    {
        return false;
    }

    virtual bool canMerge(std::shared_ptr<BaseConstructData> other)
    {
        std::shared_ptr<ArrayNDimConstructData<T,Dim> >other_ = std::dynamic_pointer_cast<ArrayNDimConstructData<T,Dim> >(other);
        if(!other_)
        {
            std::cout<<"ERROR : trying to merge two objects with different types"<<std::endl;
            return false;
        }
        return true;
    }

protected:
    boost::multi_array<T, Dim> value_;
};

template<typename T>
std::vector<std::shared_ptr<BaseConstructData> > ArrayNDimConstructData<T,2>::split(
        const std::vector< std::vector<int> >& range,
        std::vector< mapConstruct >& partial_map,
        ConstructTypeSplitPolicy policy = DECAF_SPLIT_DEFAULT )
{
    std::vector<std::shared_ptr<BaseConstructData> > result;
    std::cout<<"Specialized version for 2D version"<<std::endl;

    if(value_.num_dimensions() != 3)
    {
        std::cout<<"ERROR : split function is only implemented for dimension 3."<<std::endl;
        return result;
    }

    for(unsigned int i = 0; i < range.size(); i++)
    {
        // Sanity check
        for(unsigned int d = 0 ; d < 3; d++)
        {
            if(range.at(i).extends[d] > value_.shape()[d])
            {
                std::cout<<"ERROR : trying to cut an array out of boundary."
                        <<"("<<range.at(i).extends[d]<<" available, "<<value_.shape()[d]<<" requested on dimension "<<d<<")"<<std::endl;
                return result;
            }

        }
    }

    // Naive method
    for(unsigned int i = 0; i < range.size(); i++)
    {
        boost::multi_array<T, 3> subArray(boost::extents[range.at(i).extends[0]][range.at(i).extends[1]][range.at(i).extends[2]]);
        for(int x = 0; x < range.at(i).extends[0]; x++)
        {
            for(int y = 0; y < range.at(i).extends[1]; y++)
            {
                for(int z = 0; z < range.at(i).extends[2]; z++)
                    subArray[x][y][z] = value_[range.at(i).base[0]+x][range.at(i).base[1]+y][range.at(i).base[2]+z];
            }
        }

        std::shared_ptr<ArrayNDimConstructData<float,3> > data = std::make_shared<ArrayNDimConstructData<float,3> >(
                    subArray);
        result.push_back(data);
    }

    return result;
}

typedef ArrayNDimConstructData<float,2> ArrayNDimConstructDataf2D;
typedef ArrayNDimConstructData<double,2> ArrayNDimConstructDatad2D;
typedef ArrayNDimConstructData<int,2> ArrayNDimConstructDatai2D;
typedef ArrayNDimConstructData<float,3> ArrayNDimConstructDataf3D;
typedef ArrayNDimConstructData<double,3> ArrayNDimConstructDatad3D;
typedef ArrayNDimConstructData<int,3> ArrayNDimConstructDatai3D;

} // namespace

BOOST_CLASS_EXPORT_GUID(decaf::ArrayNDimConstructDataf2D,"ArrayNDimConstructData<float2>")
BOOST_CLASS_EXPORT_GUID(decaf::ArrayNDimConstructDatai2D,"ArrayNDimConstructData<int2>")
BOOST_CLASS_EXPORT_GUID(decaf::ArrayNDimConstructDatad2D,"ArrayNDimConstructData<double2>")
BOOST_CLASS_EXPORT_GUID(decaf::ArrayNDimConstructDataf3D,"ArrayNDimConstructData<float3>")
BOOST_CLASS_EXPORT_GUID(decaf::ArrayNDimConstructDatai3D,"ArrayNDimConstructData<int3>")
BOOST_CLASS_EXPORT_GUID(decaf::ArrayNDimConstructDatad3D,"ArrayNDimConstructData<double3>")

#endif


#include <vector>
#include <string>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <chrono>

//#include <unistd.h>
//#include <sys/wait.h>

#include <boost/python.hpp>

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

#include "../../../core/helper.h"
#include "../../../core/math_helper.h"
#include "../../../stereo_egomotion/base/egomotion_base.h"
#include "../../../stereo_egomotion/base/evaluator.h"
#include "../../../tracker/stereo/experiment_factory.h"
#include "../../../tracker/stereo/stereo_tracker_base.h"
#include "../../../tracker/refiner/feature_refiner_base.cc"
#include "../../../tracker/detector/feature_detector_base.h"
#include "../../../optimization/bundle_adjustment/bundle_adjuster_base.h"
//#include "../../../tracker/stereo/tracker_helper.h"
//#include "../../math_helper.h"
#include "../../../tracker/base/eval_helper.h"
#include "../../../tracker/base/tracker_helper.h"
#include "../../../core/format_helper.h"

using namespace core;

void RunEgomotion(const std::string& cam_params_file, const std::string& dataset_name,
                  const std::string& left_folder, const std::string& right_folder,
                  size_t start_num, size_t end_num,
                  size_t num_width, std::shared_ptr<track::StereoTrackerBase>& stereo_tracker,
                  std::vector<Eigen::Matrix4d>& egomotion_poses) {
  double cam_params[5];
  FormatHelper::readCameraParams(cam_params_file, cam_params);

  //Eigen::VectorXd camera_params(5);
  //for (int i = 0; i < 5; i++)
  //  camera_params[i] = cam_params[i];
  bool debug = false;
  //debug = true;

  std::string output_folder = ".";
  cv::Mat img_left_prev, img_right_prev;

  std::stringstream first_filename;
  first_filename << std::setw(num_width) << std::setfill('0') << start_num << ".png";
  cv::Mat img_left = cv::imread(left_folder + first_filename.str(), CV_LOAD_IMAGE_GRAYSCALE);
  cv::Mat img_right = cv::imread(right_folder + first_filename.str(), CV_LOAD_IMAGE_GRAYSCALE);
  if (img_left.empty()) {
    std::cout << "Error: no images!\n";
    throw 1;
  }
  bool use_ba = false; // use bundle adjustment

  std::shared_ptr<optim::BundleAdjusterBase> bundle_adjuster;
  std::shared_ptr<egomotion::EgomotionBase> egomotion;
  int ransac_iters = 1000;
  double ransac_threshold = 1.5;
  std::string loss_function_type = "Squared";
  double robust_loss_scale = 0;
  bool use_weighting = true;

  egomotion::EgomotionRansac::Parameters params;
  params.ransac_iters = ransac_iters;
  params.inlier_threshold = ransac_threshold;
  params.loss_function_type = loss_function_type;
  params.robust_loss_scale = robust_loss_scale;
  params.use_weighting = use_weighting;
  params.calib.f = cam_params[0];
  params.calib.cx = cam_params[2];
  params.calib.cy = cam_params[3];
  params.calib.b = cam_params[4];
  egomotion = std::make_shared<egomotion::EgomotionRansac>(params);

  if (use_ba) {
    throw 1;
    //bundle_adjuster->SetCameraParams(camera_params);
  }

  Eigen::Matrix4d pose = Eigen::Matrix4d::Identity();
  Eigen::Matrix4d Rt = Eigen::Matrix4d::Identity();

  // init the tracker
  stereo_tracker->init(img_left, img_right);

  // matrix transforms points in current camera coordinate system to world coord system
  egomotion_poses.push_back(pose);

  //std::vector<cv::Mat> gt_world_motion, gt_camera_motion;
  //if (have_gt) {
  //  int num_of_motions = ((imagelist.size()/2) - 1);
  //  core::FormatHelper::Read2FrameMotionFromAccCameraMotion(gt_filepath, num_of_motions,
  //                                                          gt_world_motion, gt_camera_motion);
  //}
  int frame_num = 0;
  auto start = std::chrono::system_clock::now();
  for (size_t i = start_num+1; i <= end_num; i++) {
    frame_num++;
    std::cout << "\rImage num: " << i << " / " << end_num << "\n";
    //if (have_gt)
    //  std::cout << "motion_idx: " << motion_idx << " / " << (gt_world_motion.size() - 1) << "\n";
    cv::swap(img_left_prev, img_left);
    cv::swap(img_right_prev, img_right);

    std::stringstream filename;
    filename << std::setw(num_width) << std::setfill('0') << i << ".png";
    img_left = cv::imread(left_folder + filename.str(), CV_LOAD_IMAGE_GRAYSCALE);
    img_right = cv::imread(right_folder + filename.str(), CV_LOAD_IMAGE_GRAYSCALE);

    stereo_tracker->track(img_left, img_right);

    // default prior
    double min_disp = 0.01;
    double max_disp_diff = 40.0;
    // filter tracks with prior knowledge
    track::TrackerHelper::FilterTracksWithPriorOld(*stereo_tracker, max_disp_diff, min_disp);

    // draw tracks before ransac
    if (debug)
      track::EvalHelper::DrawStereoTracks(*stereo_tracker, img_left_prev, img_right_prev,
                                          "all_left", "all_right");

    // run egomotion optimization
    // Rt is the motion of world points with respect to the camera
    bool success = egomotion->GetMotion(*stereo_tracker, Rt);

    if (success) {
      // filter outlier tracks
      //std::cout << "Tracks before RANSAC: " << stereo_tracker->countActiveTracks() << "\n";
      std::vector<int> outliers = egomotion->GetTrackerOutliers();
      for (size_t j = 0; j < outliers.size(); j++)
        stereo_tracker->removeTrack(outliers[j]);
      //std::cout << "Tracks after RANSAC: " << stereo_tracker->countActiveTracks() << "\n";

      pose = pose * Rt.inverse();
      egomotion_poses.push_back(pose);

      //std::cout << "Two-frame odometry:\n" << Rt << std::endl;
      if (use_ba) {
        bundle_adjuster->UpdateTracks(*stereo_tracker, Eigen::Matrix4d::Identity());
        if (frame_num >= (bundle_adjuster->num_frames() - 1)) {
          if(!bundle_adjuster->Optimize())
            return;
          if (use_ba) {
            std::cout << "BA odometry:\n" << bundle_adjuster->camera_motions().back() << "\n";
          }
        }
      }
      //std::cout << "-----------------------------------------------------------------------\n\n";
    } else {
      std::cout << "Egomotion failed!\n";
      throw 1;
    }
  }
  auto end = std::chrono::system_clock::now();
  std::chrono::duration<double> elapsed = end - start;
  std::cout << "[Egomotion]: elapsed time = " << elapsed.count() << " sec\n";

  //std::cout << "Writing track " << dataset_name << " to files\n";
  //std::ofstream egomotion_ofile(output_folder + '/' + dataset_name + ".txt");

  //std::ofstream ba_ofile;
  //std::ofstream ba_ofile2;
  ////cv::Mat sba_pose;
  //if (use_ba) {
  //  std::string ba_folder = output_folder.substr(0, output_folder.size()-1) + "_ba/";
  //  ba_ofile.open(ba_folder + dataset_name + ".txt");
  //}
  //pose = Eigen::Matrix4d::Identity();
  //std::vector<Eigen::Matrix4d> bundle_adjusted_motions;
  //if (use_ba) {
  //  bundle_adjusted_motions = bundle_adjuster->camera_motions();
  //  if (egomotion_poses.size() != bundle_adjusted_motions.size()+1) {
  //    std::cout << "[egomotion.cc] " << egomotion_poses.size() << "!="
  //              << bundle_adjusted_motions.size()+1 << "\n";
  //    return;
  //  }
  //}
}

double run_orb(const std::string config_file, int patch_size, int num_levels, float scale_factor,
               int max_dist_stereo, int max_dist_mono) {
  std::string cam_params_file;
  std::string dataset_name;
  std::string left_folder;
  std::string right_folder;
  std::string gt_filepath;
  std::size_t start_num, end_num, num_width;

  core::FormatHelper::ParseKITTIDatasetConfig(config_file, cam_params_file, dataset_name,
                                              left_folder, right_folder, gt_filepath, start_num,
                                              end_num, num_width);
  std::vector<Eigen::Matrix4d> poses;
  std::shared_ptr<track::StereoTrackerBase> stereo_tracker;
  bool status = track::ExperimentFactory::CreateValidationORB(
      patch_size, num_levels, scale_factor, max_dist_stereo, max_dist_mono,
      &stereo_tracker);
  if (!status) {
    std::cout << "Invalid experiment config!\n";
    throw 1;
  }
  RunEgomotion(cam_params_file, dataset_name, left_folder, right_folder, start_num, end_num,
               num_width, stereo_tracker, poses);
  double trans_error, rot_error;
  egomotion::Evaluator::Eval(gt_filepath, poses, trans_error, rot_error);
  return trans_error;

  return 0;
}

double run_agast_freak(const std::string config_file, int agast_threshold, std::string agast_type,
                       std::string normalize_orientation, float freak_size,
                       int max_xdiff, int max_dist_stereo, int max_dist_mono) {
  std::string cam_params_file;
  std::string dataset_name;
  std::string left_folder;
  std::string right_folder;
  std::string gt_filepath;
  std::size_t start_num, end_num, num_width;
  bool norm_orient = false;
  if (normalize_orientation == "YES")
    norm_orient = true;

  core::FormatHelper::ParseKITTIDatasetConfig(config_file, cam_params_file, dataset_name,
                                              left_folder, right_folder, gt_filepath, start_num,
                                              end_num, num_width);
  std::vector<Eigen::Matrix4d> poses;
  std::shared_ptr<track::StereoTrackerBase> stereo_tracker;
  bool status = track::ExperimentFactory::CreateValidationAgastFreak(
      agast_threshold, agast_type, norm_orient, freak_size, max_xdiff,
      max_dist_stereo, max_dist_mono, &stereo_tracker);
  if (!status) {
    std::cout << "Invalid experiment config!\n";
    throw 1;
  }
  RunEgomotion(cam_params_file, dataset_name, left_folder, right_folder, start_num,
               end_num, num_width, stereo_tracker, poses);
  double trans_error, rot_error;
  egomotion::Evaluator::Eval(gt_filepath, poses, trans_error, rot_error);
  return trans_error;

  return 0;
}

double run_harris_freak(const std::string config_file, const std::string normalize_orientation,
                        const float freak_size, const int max_xdiff, const int max_dist_stereo,
                        const int max_dist_mono) {
  const int block_sz = 3;
  const int filter_sz = 3;
  const double k = 0.04;
  const double thr = 1e-07;
  const int margin = block_sz;
  const int max_features = 20000;
  const int max_tracks = 10000;

  std::string cam_params_file;
  std::string dataset_name;
  std::string left_folder;
  std::string right_folder;
  std::string gt_filepath;
  std::size_t start_num, end_num, num_width;
  bool norm_orient = false;
  if (normalize_orientation == "YES")
    norm_orient = true;

  core::FormatHelper::ParseKITTIDatasetConfig(config_file, cam_params_file, dataset_name,
      left_folder, right_folder, gt_filepath, start_num, end_num, num_width);
  std::vector<Eigen::Matrix4d> poses;
  std::shared_ptr<track::StereoTrackerBase> stereo_tracker;
  bool status = track::ExperimentFactory::CreateValidationHarrisFreak(
      block_sz, filter_sz, k, thr, margin, max_features, max_tracks, norm_orient,
      freak_size, max_xdiff, max_dist_stereo, max_dist_mono, &stereo_tracker);
  if (!status) {
    std::cout << "Invalid experiment config!\n";
    throw 1;
  }
  RunEgomotion(cam_params_file, dataset_name, left_folder, right_folder, start_num,
               end_num, num_width, stereo_tracker, poses);
  double trans_error, rot_error;
  egomotion::Evaluator::Eval(gt_filepath, poses, trans_error, rot_error);
  return trans_error;

  return 0;
}

BOOST_PYTHON_MODULE(libegomotion) {
  boost::python::def("run_orb", run_orb);
  boost::python::def("run_harris_freak", run_harris_freak);
  boost::python::def("run_agast_freak", run_agast_freak);
}


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

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

     Represents the mantissa function in generic contexts.

     @par Models:
        Hierarchy
   **/
    struct mantissa_ : ext::elementwise_<mantissa_>
    {
      /// @brief Parent hierarchy
      typedef ext::elementwise_<mantissa_> parent;
      template<class... Args>
      static BOOST_FORCEINLINE BOOST_AUTO_DECLTYPE dispatch(Args&&... args)
      BOOST_AUTO_DECLTYPE_BODY( dispatching_mantissa_( ext::adl_helper(), static_cast<Args&&>(args)... ) )
    };
  }
  namespace ext
  {
   template<class Site, class... Ts>
   BOOST_FORCEINLINE generic_dispatcher<tag::mantissa_, Site> dispatching_mantissa_(adl_helper, boost::dispatch::meta::unknown_<Site>, boost::dispatch::meta::unknown_<Ts>...)
   {
     return generic_dispatcher<tag::mantissa_, Site>();
   }
   template<class... Args>
   struct impl_mantissa_;
  }
  /*!
    Returns the signed mantissa of the floating input.
    \par
    The exponent e and signed mantissa m of a floating point entry a are related by
    \f$a = m\times 2^e\f$, with the absolute value of m between one (included) ans two (excluded)

    @par Semantic:

    @code
    T r = mantissa(a0);
    @endcode

    is similar to:

    @code
    T r = a0*pow(2, -exponent(a0));
    @endcode

    @param a0

    @return a value of same type as the input
  **/
  BOOST_DISPATCH_FUNCTION_IMPLEMENTATION(tag::mantissa_, mantissa, 1)
} }

#endif


#include <csv.h>
#include <ratings_parser.h>
#include <stringtools.h>

#include <Eigen/Sparse>
#include <fstream>
#include <iostream>
#include <set>
using Eigen::Triplet;
static std::set<int> users;
//------------------------------------------------------------------------------
bool csvitem_tovector(TripletVector<uint8_t>& v,
                      const std::vector<std::string>& item,
                      std::pair<int, int>& dim, int limit, int filter_divisor,
                      int filter_modulo) {
    try {
        Triplet<uint8_t> t(std::stoul(item[0]) - 1, std::stoul(item[1]) - 1,
                           int(std::stof(item[2]) * 2));
        if (limit > 0) {
            users.insert(t.row());
            if (users.size() > limit) return false;
        }
        if (filter_modulo < 0 || filter_divisor == 0 ||
            t.row() % filter_divisor == filter_modulo) {
            dim.first = std::max(dim.first, t.row() + 1);
            dim.second = std::max(dim.second, t.col() + 1);
            v.push_back(t);
        }
    } catch (const std::invalid_argument& e) {
        std::cerr << "Unable to convert <" << item[0] << "," << item[1] << ","
                  << item[2] << ">" << std::endl;
        return false;
    }
    return true;
}

//------------------------------------------------------------------------------
struct FileBuckets {
    FileBuckets() : joiner(",") {}
    std::map<unsigned, std::ofstream> buckets;
    Joiner joiner;

    void closeall() {
        for (auto& b : buckets) {
            b.second.close();
        }
        buckets.clear();
    }

    ~FileBuckets() { closeall(); }
};

//------------------------------------------------------------------------------
bool csvitem_touserbucket(const std::vector<std::string>& item,
                          FileBuckets& fb) {
    try {
        unsigned user = std::stoul(item[0]) - 1;
        if (fb.buckets.find(user) == fb.buckets.end()) {
            fb.closeall();  // keep only one file opened at a time
                            // it's fine when input is sorted
            fb.buckets[user].open(item[0] + ".csv");
        }
        fb.buckets[user] << fb.joiner.join(item) << std::endl;
    } catch (const std::invalid_argument& e) {
        std::cerr << "Unable to convert <" << item[0] << "," << item[1] << ","
                  << item[2] << ">" << std::endl;
        return false;
    }
    return true;
}

//------------------------------------------------------------------------------
bool csv_ratings_triplet_vector(const std::string fname,
                                TripletVector<uint8_t>& v) {
    std::pair<int, int> dim(0, 0);
    if (!csv_parse(fname, std::bind(csvitem_tovector, std::ref(v),
                                    std::placeholders::_1, std::ref(dim), -1, 0,
                                    -1))) {
        return false;
    }
    return true;
}

//------------------------------------------------------------------------------
bool csv_ratings_sparse_matrix(const std::string fname,
                               Eigen::SparseMatrix<uint8_t>& m) {
    TripletVector<uint8_t> v;
    std::pair<int, int> dim(0, 0);
    if (!csv_parse(fname, std::bind(csvitem_tovector, std::ref(v),
                                    std::placeholders::_1, std::ref(dim), -1, 0,
                                    -1))) {
        return false;
    }
    m.reserve(v.size());
    m.resize(dim.first, dim.second);
    m.setFromTriplets(v.begin(), v.end());
    return true;
}

//------------------------------------------------------------------------------
bool csv_bucketize_byuser(const std::string& fname) {
    FileBuckets filebuckets;
    if (!csv_parse(fname, std::bind(csvitem_touserbucket, std::placeholders::_1,
                                    std::ref(filebuckets)))) {
        return false;
    }
    return true;
}

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


//////////////////////////////////////////////////////////////////////////////////
// survival::modelss::exponential::scalar::log_rate::identity::include.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_MODELS_EXPONENTIAL_SCALAR_LOG_RATE_IDENTITY_INCLUDE_HPP_ER_2009
#define BOOST_STATISTICS_DETAIL_DISTRIBUTION_SURVIVAL_MODELS_EXPONENTIAL_SCALAR_LOG_RATE_IDENTITY_INCLUDE_HPP_ER_2009

#include <boost/statistics/detail/distribution/survival/models/exponential/scalar/log_rate/identity/identity.hpp>

#endif


#include "../../sdp_read.hxx"
#include "../../Timers.hxx"
#include "../../max_normalization_index.hxx"
#include "../../sdp_convert.hxx"

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

std::vector<Polynomial> bilinear_basis(const Damped_Rational &damped_rational,
                                       const size_t &half_max_degree);

void write_output(const boost::filesystem::path &output_path,
                  const std::vector<std::string> &command_arguments,
                  const std::vector<El::BigFloat> &objectives,
                  const std::vector<El::BigFloat> &normalization,
                  const std::vector<Positive_Matrix_With_Prefactor> &matrices,
                  Timers &timers)
{
  auto &objectives_timer(timers.add_and_start("write_output.objectives"));

  const size_t max_index(max_normalization_index(normalization));

  El::BigFloat objective_const(objectives.at(max_index)
                               / normalization.at(max_index));
  std::vector<El::BigFloat> dual_objective_b;
  dual_objective_b.reserve(normalization.size() - 1);
  for(size_t index = 0; index < normalization.size(); ++index)
    {
      if(index != max_index)
        {
          dual_objective_b.push_back(
            objectives.at(index) - normalization.at(index) * objective_const);
        }
    }

  objectives_timer.stop();

  auto &matrices_timer(timers.add_and_start("write_output.matrices"));
  std::vector<Dual_Constraint_Group> dual_constraint_groups;
  int rank(El::mpi::Rank(El::mpi::COMM_WORLD)),
    num_procs(El::mpi::Size(El::mpi::COMM_WORLD));
  std::vector<size_t> indices;
  for(size_t index = rank; index < matrices.size(); index += num_procs)
    {
      auto &scalings_timer(timers.add_and_start(
        "write_output.matrices.scalings_" + std::to_string(index)));
      const size_t max_degree([&]() {
        int64_t result(0);
        for(auto &pvv : matrices[index].polynomials)
          for(auto &pv : pvv)
            for(auto &polynomial : pv)
              {
                result = std::max(result, polynomial.degree());
              }
        return result;
      }());
      std::vector<Boost_Float> points(sample_points(max_degree + 1)),
        scalings(sample_scalings(points, matrices[index].damped_rational));
      scalings_timer.stop();

      Polynomial_Vector_Matrix pvm;
      pvm.rows = matrices[index].polynomials.size();
      pvm.cols = matrices[index].polynomials.front().size();

      auto &bilinear_basis_timer(timers.add_and_start(
        "write_output.matrices.bilinear_basis_" + std::to_string(index)));

      pvm.bilinear_basis
        = bilinear_basis(matrices[index].damped_rational, max_degree / 2);

      bilinear_basis_timer.stop();

      pvm.sample_points.reserve(points.size());
      for(auto &point : points)
        {
          pvm.sample_points.emplace_back(to_string(point));
        }
      pvm.sample_scalings.reserve(scalings.size());
      for(auto &scaling : scalings)
        {
          pvm.sample_scalings.emplace_back(to_string(scaling));
        }

      auto &pvm_timer(timers.add_and_start("write_output.matrices.pvm_"
                                           + std::to_string(index)));
      pvm.elements.reserve(pvm.rows * pvm.cols);
      for(auto &pvv : matrices[index].polynomials)
        for(auto &pv : pvv)
          {
            pvm.elements.emplace_back();
            auto &pvm_polynomials(pvm.elements.back());
            pvm_polynomials.reserve(pv.size());
            pvm_polynomials.push_back(pv.at(max_index)
                                      / normalization.at(max_index));
            auto &pvm_constant(pvm_polynomials.back());

            for(size_t index = 0; index < normalization.size(); ++index)
              {
                if(index != max_index)
                  {
                    pvm_polynomials.emplace_back(0, 0);
                    auto &pvm_poly(pvm_polynomials.back());
                    pvm_poly.coefficients.reserve(pv.at(index).degree() + 1);
                    size_t coefficient(0);
                    for(; coefficient < pv.at(index).coefficients.size()
                          && coefficient < pvm_constant.coefficients.size();
                        ++coefficient)
                      {
                        pvm_poly.coefficients.push_back(
                          pv.at(index).coefficients[coefficient]
                          - normalization.at(index)
                              * pvm_constant.coefficients[coefficient]);
                      }
                    for(; coefficient < pv.at(index).coefficients.size();
                        ++coefficient)
                      {
                        pvm_poly.coefficients.push_back(
                          pv.at(index).coefficients[coefficient]);
                      }
                    for(; coefficient < pvm_constant.coefficients.size();
                        ++coefficient)
                      {
                        pvm_poly.coefficients.push_back(
                          -normalization.at(index)
                          * pvm_polynomials.at(0).coefficients[coefficient]);
                      }
                  }
              }
          }
      pvm_timer.stop();
      auto &dual_constraint_timer(timers.add_and_start(
        "write_output.matrices.dual_constraint_" + std::to_string(index)));
      dual_constraint_groups.emplace_back(index, pvm);
      dual_constraint_timer.stop();
    }
  matrices_timer.stop();

  auto &write_timer(timers.add_and_start("write_output.write"));
  write_sdpb_input_files(output_path, rank, matrices.size(), command_arguments,
                         objective_const, dual_objective_b,
                         dual_constraint_groups);
  write_timer.stop();
}


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

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

      Represents the correct_fma function in generic contexts.

      @par Models:
      Hierarchy
    **/
    struct correct_fma_ : ext::elementwise_<correct_fma_>
    {
     /// @brief Parent hierarchy
     typedef ext::elementwise_<correct_fma_> parent;
      template<class... Args>
      static BOOST_FORCEINLINE BOOST_AUTO_DECLTYPE dispatch(Args&&... args)
      BOOST_AUTO_DECLTYPE_BODY( dispatching_correct_fma_( ext::adl_helper(), static_cast<Args&&>(args)... ) )
    };
  }
  namespace ext
  {
    template<class Site>
    BOOST_FORCEINLINE generic_dispatcher<tag::correct_fma_, Site> dispatching_correct_fma_(adl_helper, boost::dispatch::meta::unknown_<Site>, ...)
    {
      return generic_dispatcher<tag::correct_fma_, Site>();
    }
    template<class... Args>
    struct impl_correct_fma_;
  }
  /*!
    Computes fused multiply/add of its parameter.

    @par semantic:
    For any given value @c a, @c b, @c c of type @c T:

    @code
    T r = correct_fma(a, b, c);
    @endcode

    is similar to:

    @code
    T r = a*b+c;
    @endcode

    but with only one rounding

    @par Note:

    For integer a*b+c is performed

    For floating points numbers, always compute the correct fused multiply add,
    this means the computation of a0*a1+a2 with only one rounding operation.
    On machines not possessing this hard wired capability this can be very
    expansive.

    @c correct_fma is in fact a transitory function that allows to ensure
    strict fma capabilities,  i.e. only one rounding operation and no undue
    overflow in intermediate computations.

    If you are using this function for a system with no hard wired fma
    and are sure that overflow is not a problem
    you can define BOOST_SIMD_DONT_CARE_CORRECT_FMA_OVERFLOW to get better
    performances

    This function is never used internally in boost/simd. See also
    the @funcref{fma} function.

    @see  @funcref{fma}
    @param  a0

    @param  a1

    @param  a2

    @return      a value of the same type as the input.

  **/
  BOOST_DISPATCH_FUNCTION_IMPLEMENTATION(tag::correct_fma_, correct_fma, 3)
} }

#endif


///////////////////////////////////////////////////////////////////////////////
// non_empty_cells_count.hpp                                                 //
//                                                                           //
//  Copyright 2010 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_NON_PARAMETRIC_CONTINGENCY_TABLE_CELLS_NON_EMPTY_CELLS_COUNT_HPP_ER_2010
#define BOOST_STATISTICS_DETAIL_NON_PARAMETRIC_CONTINGENCY_TABLE_CELLS_NON_EMPTY_CELLS_COUNT_HPP_ER_2010
#include <boost/range.hpp>
#include <boost/statistics/detail/non_parametric/contingency_table/cells/cells.hpp>

namespace boost{
namespace statistics{
namespace detail{
namespace contingency_table{

  	template<typename Keys,typename AccSet>
    std::size_t non_empty_cells_count(AccSet const& acc){
        namespace ns = contingency_table;
        return ns::extract::cells<Keys>( acc ).size() ;
    }

}// contingency_table
}// detail
}// statistics
}// boost

#endif


#ifdef STAN_OPENCL

#include <stan/math/opencl/kernel_generator.hpp>
#include <stan/math/opencl/matrix_cl.hpp>
#include <stan/math/opencl/copy.hpp>
#include <test/unit/math/opencl/kernel_generator/reference_kernel.hpp>
#include <test/unit/util.hpp>
#include <Eigen/Dense>
#include <gtest/gtest.h>
#include <string>

using Eigen::Matrix;
using Eigen::MatrixXd;
using Eigen::MatrixXi;
using stan::math::matrix_cl;

TEST(KernelGenerator, block_errors) {
  using stan::math::block;

  matrix_cl<double> m(7, 9);

  EXPECT_NO_THROW(block(m, 0, 0, 1, 1));
  EXPECT_NO_THROW(block(m, 6, 8, 1, 1));
  EXPECT_NO_THROW(block(m, 0, 0, 7, 9));
  EXPECT_THROW(block(m, 0, 0, 8, 1), std::invalid_argument);
  EXPECT_THROW(block(m, 0, 0, 1, 10), std::invalid_argument);
  EXPECT_THROW(block(m, 6, -1, 1, 1), std::invalid_argument);
  EXPECT_THROW(block(m, -1, 5, 1, 1), std::invalid_argument);
  EXPECT_THROW(block(m, 0, 0, -1, 1), std::invalid_argument);
  EXPECT_THROW(block(m, 0, 0, 1, -1), std::invalid_argument);
  EXPECT_THROW(block(m, 0, 9, 0, 1), std::invalid_argument);

  EXPECT_NO_THROW(block(m, 0, 0, 7, 9) = m);
  EXPECT_THROW(block(m, 0, 0, 7, 8) = m, std::invalid_argument);
  EXPECT_THROW(block(m, 0, 0, 6, 9) = m, std::invalid_argument);
  EXPECT_THROW(block(m, 0, 0, 7, 8) = m, std::invalid_argument);
  EXPECT_THROW(block(m, 0, 0, 6, 9) = m, std::invalid_argument);
}

TEST(KernelGenerator, block_test) {
  using stan::math::block;
  std::string kernel_filename = "block.cl";
  MatrixXd m = MatrixXd::Random(7, 9);

  matrix_cl<double> m_cl(m);

  auto tmp = block(m_cl, 2, 4, 3, 5);
  matrix_cl<double> res_cl;
  std::string kernel_src = tmp.get_kernel_source_for_evaluating_into(res_cl);
  stan::test::store_reference_kernel_if_needed(kernel_filename, kernel_src);
  std::string expected_kernel_src
      = stan::test::load_reference_kernel(kernel_filename);
  EXPECT_EQ(expected_kernel_src, kernel_src);

  res_cl = tmp;
  MatrixXd res = stan::math::from_matrix_cl(res_cl);

  MatrixXd correct = m.block(2, 4, 3, 5);
  EXPECT_MATRIX_NEAR(res, correct, 1e-9);
}

TEST(KernelGenerator, block_multiple_operations_test) {
  using stan::math::block;
  MatrixXd m = MatrixXd::Random(7, 9);

  matrix_cl<double> m_cl(m);

  auto tmp = block(block(m_cl, 1, 1, 5, 5), 2, 0, 2, 3);
  matrix_cl<double> res_cl = tmp;
  MatrixXd res = stan::math::from_matrix_cl(res_cl);

  MatrixXd tmp_block = m.block(1, 1, 5, 5);
  MatrixXd correct = tmp_block.block(2, 0, 2, 3);
  EXPECT_MATRIX_NEAR(res, correct, 1e-9);
}

TEST(KernelGenerator, block_multiple_operations_accept_lvalue_test) {
  using stan::math::block;
  MatrixXd m = MatrixXd::Random(7, 9);

  matrix_cl<double> m_cl(m);

  auto tmp2 = block(m_cl, 1, 1, 5, 5);
  auto tmp = block(tmp2, 2, 0, 2, 3);
  matrix_cl<double> res_cl = tmp;
  MatrixXd res = stan::math::from_matrix_cl(res_cl);

  MatrixXd tmp_block = m.block(1, 1, 5, 5);
  MatrixXd correct = tmp_block.block(2, 0, 2, 3);
  EXPECT_MATRIX_NEAR(res, correct, 1e-9);
}

TEST(KernelGenerator, lhs_block_test) {
  using stan::math::block;
  MatrixXd m1(2, 3);
  m1 << 1, 2, 3, 4, 5, 6;
  MatrixXd m2 = MatrixXd::Constant(5, 7, 9);

  matrix_cl<double> m1_cl(m1);
  matrix_cl<double> m2_cl(m2);

  block(m2_cl, 1, 1, 2, 3) = m1_cl;

  MatrixXd res = stan::math::from_matrix_cl(m2_cl);

  MatrixXd correct = m2;
  correct.block(1, 1, 2, 3) = m1;
  EXPECT_MATRIX_NEAR(res, correct, 1e-9);
}

TEST(KernelGenerator, block_to_lhs_block_test) {
  using stan::math::block;
  MatrixXd m1(2, 3);
  m1 << 1, 2, 3, 4, 5, 6;
  MatrixXd m2 = MatrixXd::Constant(5, 7, 9);

  matrix_cl<double> m1_cl(m1);
  matrix_cl<double> m2_cl(m2);

  block(m2_cl, 1, 1, 2, 3) = block(m1_cl, 0, 0, 2, 3);

  MatrixXd res = stan::math::from_matrix_cl(m2_cl);

  MatrixXd correct = m2;
  correct.block(1, 1, 2, 3) = m1;
  EXPECT_MATRIX_NEAR(res, correct, 1e-9);
}

TEST(KernelGenerator, block_repeat_lhs_rhs_test) {
  using stan::math::block;
  MatrixXd m1(2, 3);
  m1 << 1, 2, 3, 4, 5, 6;

  matrix_cl<double> m1_cl(m1);

  auto b = block(m1_cl, 0, 1, 2, 2);

  b = b + 1;

  MatrixXd res = stan::math::from_matrix_cl(m1_cl);

  MatrixXd correct = m1;
  correct.block(0, 1, 2, 2).array() += 1;
  EXPECT_MATRIX_NEAR(res, correct, 1e-9);
}

TEST(KernelGenerator, two_blocks_of_same_expression) {
  using stan::math::block;
  MatrixXd m(2, 3);
  m << 1, 2, 3, 4, 5, 6;

  matrix_cl<double> m_cl(m);

  auto tmp = m_cl + 1;
  auto tmp2 = block(tmp, 0, 0, 2, 2) + block(tmp, 0, 1, 2, 2);

  matrix_cl<double> res_cl = tmp2;

  MatrixXd res = stan::math::from_matrix_cl(res_cl);
  MatrixXd correct = (m.block(0, 0, 2, 2) + m.block(0, 1, 2, 2)).array() + 2;

  EXPECT_MATRIX_NEAR(res, correct, 1e-9);
}

TEST(MathMatrixCL, block_view_test) {
  using stan::math::block;
  matrix_cl<double> m(4, 4, stan::math::matrix_cl_view::Diagonal);
  matrix_cl<double> res = block(m, 0, 0, 2, 2);
  EXPECT_EQ(res.view(), stan::math::matrix_cl_view::Diagonal);
  res = block(m, 1, 0, 2, 2);
  EXPECT_EQ(res.view(), stan::math::matrix_cl_view::Upper);
  res = block(m, 0, 1, 2, 2);
  EXPECT_EQ(res.view(), stan::math::matrix_cl_view::Lower);
  res = block(m, 0, 2, 2, 2);
  EXPECT_EQ(res.view(), stan::math::matrix_cl_view::Diagonal);
  res = block(m, 2, 0, 2, 2);
  EXPECT_EQ(res.view(), stan::math::matrix_cl_view::Diagonal);

  res = block(cos(m), 1, 0, 2, 2);
  EXPECT_EQ(res.view(), stan::math::matrix_cl_view::Entire);
}

#endif


/*
 [auto_generated]
 boost/numeric/odeint/iterator/const_step_time_iterator.hpp

 [begin_description]
 Iterator for iterating throught the solution of an ODE with constant step size. The dereferences types containes also the time.
 [end_description]

 Copyright 2009-2011 Karsten Ahnert
 Copyright 2009-2011 Mario Mulansky

 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_NUMERIC_ODEINT_ITERATOR_CONST_STEP_TIME_ITERATOR_HPP_INCLUDED
#define BOOST_NUMERIC_ODEINT_ITERATOR_CONST_STEP_TIME_ITERATOR_HPP_INCLUDED

#include <boost/numeric/odeint/stepper/stepper_categories.hpp>
#include <boost/numeric/odeint/util/stepper_traits.hpp>

namespace boost {
namespace numeric {
namespace odeint {

// initial decalaration, defining specializations in impl/const_step_time_iterator_impl.hpp
    template< class Stepper , class System ,
              class StepperTag = typename base_tag< typename traits::stepper_category< Stepper >::type >::type >
    class const_step_time_iterator;

    template< class Stepper , class System >
    const_step_time_iterator< Stepper , System > make_const_step_time_iterator_begin(
        Stepper stepper ,
        System system ,
        typename traits::state_type< Stepper >::type &x ,
        typename traits::time_type< Stepper >::type t ,
        typename traits::time_type< Stepper >::type t_end ,
        typename traits::time_type< Stepper >::type dt )
    {
        return const_step_time_iterator< Stepper , System >( stepper , system , x , t , t_end , dt );
    }

    template< class Stepper , class System >
    const_step_time_iterator< Stepper , System > make_const_step_time_iterator_end(
        Stepper stepper ,
        System system ,
        typename traits::state_type< Stepper >::type &x )
    {
        return const_step_time_iterator< Stepper , System >( stepper , system , x );
    }

    template< class Stepper , class System >
    std::pair< const_step_time_iterator< Stepper , System > , const_step_time_iterator< Stepper , System > >
    make_const_step_time_range(
        Stepper stepper ,
        System system ,
        typename traits::state_type< Stepper >::type &x ,
        typename traits::time_type< Stepper >::type t_start ,
        typename traits::time_type< Stepper >::type t_end ,
        typename traits::time_type< Stepper >::type dt )
    {
        return std::make_pair(
            const_step_time_iterator< Stepper , System >( stepper , system , x , t_start , t_end , dt ) ,
            const_step_time_iterator< Stepper , System >( stepper , system , x ) );
    }

    /**
     * \fn make_const_step_time_iterator_begin( Stepper stepper , System system , typename Stepper::state_type &x , typename Stepper::time_type t , typename Stepper::time_type t_end , typename Stepper::time_type dt )
     *
     * \brief Factory function for const_step_time_iterator. Constructs a begin iterator.
     *
     * \param stepper The stepper to use during the iteration.
     * \param system The system function (ODE) to solve.
     * \param x The initial state. const_step_time_iterator stores a reference of s and changes its value during the iteration.
     * \param t The initial time.
     * \param t_end The end time, at which the iteration should stop.
     * \param dt The initial time step.
     * \returns The const step time iterator.
     */

    /**
     * \fn make_const_step_time_iterator_end( Stepper stepper , System system , typename Stepper::state_type &x )
     * \brief Factory function for const_step_time_iterator. Constructs a end iterator.
     *
     * \param stepper The stepper to use during the iteration.
     * \param system The system function (ODE) to solve.
     * \param x The initial state. const_step_time_iterator store a reference of s and changes its value during the iteration.
     * \returns The const step time iterator.
     */

    /**
     * \fn make_const_step_time_range( Stepper stepper , System system , typename Stepper::state_type &x , typename Stepper::time_type t_start , typename Stepper::time_type t_end , typename Stepper::time_type dt )
     *
     * \brief Factory function to construct a single pass range of const_step_time_iterator. A range is here a pair of const_step_time_iterator.
     *
     * \param stepper The stepper to use during the iteration.
     * \param system The system function (ODE) to solve.
     * \param x The initial state. const_step_time_iterator stores a reference of s and changes its value during the iteration.
     * \param t The initial time.
     * \param t_end The end time, at which the iteration should stop.
     * \param dt The initial time step.
     * \returns The const step time range.
     */

} // namespace odeint
} // namespace numeric
} // namespace boost

#include <boost/numeric/odeint/iterator/impl/const_step_time_iterator_impl.hpp>
#include <boost/numeric/odeint/iterator/impl/const_step_time_iterator_dense_output_impl.hpp>

#endif // BOOST_NUMERIC_ODEINT_ITERATOR_CONST_STEP_TIME_ITERATOR_HPP_INCLUDED


/*
 * Copyright Andrey Semashev 2020
 *
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or copy at
 * https://www.boost.org/LICENSE_1_0.txt)
 */
/*!
 * \file has_single_bit.hpp
 *
 * This header defines \c has_single_bit algorithm, which returns \c true if the passed integer has a single non-zero bit.
 * This algorithm is functionally equivalent to \c is_power_of_2.
 */

#ifndef BOOST_BIT_OPS_POW2_HAS_SINGLE_BIT_HPP_INCLUDED_
#define BOOST_BIT_OPS_POW2_HAS_SINGLE_BIT_HPP_INCLUDED_

#include <boost/bit_ops/detail/config.hpp>
#include <boost/bit_ops/detail/type_traits/enable_if.hpp>
#include <boost/bit_ops/detail/type_traits/is_integral.hpp>
#include <boost/bit_ops/detail/type_traits/is_unsigned.hpp>
#include <boost/bit_ops/pow2/is_power_of_2.hpp>

namespace boost {
namespace bit_ops {

//! Tests if the integer has a single non-zero bit
template< typename T >
inline BOOST_CONSTEXPR typename bit_ops::detail::enable_if<
    bit_ops::detail::is_integral< T >::value && bit_ops::detail::is_unsigned< T >::value,
    bool
>::type has_single_bit(T value) BOOST_NOEXCEPT
{
    return bit_ops::is_power_of_2(value);
}

} // namespace bit_ops
} // namespace boost

#endif // BOOST_BIT_OPS_POW2_HAS_SINGLE_BIT_HPP_INCLUDED_


//=======================================================================
// Copyright 1997, 1998, 1999, 2000 University of Notre Dame.
// Authors: Andrew Lumsdaine, Lie-Quan Lee, Jeremy G. Siek
//
// This file is part of the Boost Graph Library
//
// You should have received a copy of the License Agreement for the
// Boost Graph Library along with the software; see the file LICENSE.
// If not, contact Office of Research, University of Notre Dame, Notre
// Dame, IN 46556.
//
// Permission to modify the code and to distribute modified code is
// granted, provided the text of this NOTICE is retained, a notice that
// the code was modified is included with the above COPYRIGHT NOTICE and
// with the COPYRIGHT NOTICE in the LICENSE file, and that the LICENSE
// file is distributed with the modified code.
//
// LICENSOR MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.
// By way of example, but not limitation, Licensor MAKES NO
// REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
// PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE COMPONENTS
// OR DOCUMENTATION WILL NOT INFRINGE ANY PATENTS, COPYRIGHTS, TRADEMARKS
// OR OTHER RIGHTS.
//=======================================================================
/*

  Paul Moore's request:

  As an example of a practical problem which is not restricted to graph
  "experts", consider file dependencies. It's basically graph construction,
  plus topological sort, but it might make a nice "tutorial" example. Build a
  dependency graph of files, then use the algorithms to do things like

  1. Produce a full recompilation order (topological sort, by modified date)
  2. Produce a "parallel" recompilation order (same as above, but group files
  which can be built in parallel)
  3. Change analysis (if I change file x, which others need recompiling)
  4. Dependency changes (if I add a dependency between file x and file y, what
  are the effects)

*/

#include <boost/config.hpp> // put this first to suppress some VC++ warnings

#include <iostream>
#include <iterator>
#include <algorithm>
#include <time.h>

#include <boost/utility.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/topological_sort.hpp>
#include <boost/graph/depth_first_search.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/visitors.hpp>

using namespace std;
using namespace boost;

enum files_e { dax_h, yow_h, boz_h, zow_h, foo_cpp,
               foo_o, bar_cpp, bar_o, libfoobar_a,
               zig_cpp, zig_o, zag_cpp, zag_o,
                 libzigzag_a, killerapp, N };
const char* name[] = { "dax.h", "yow.h", "boz.h", "zow.h", "foo.cpp",
                       "foo.o", "bar.cpp", "bar.o", "libfoobar.a",
                       "zig.cpp", "zig.o", "zag.cpp", "zag.o",
                       "libzigzag.a", "killerapp" };

struct print_visitor : public bfs_visitor<> {
  template <class Vertex, class Graph>
  void discover_vertex(Vertex v, Graph&) {
    cout << name[v] << " ";
  }
};

struct cycle_detector : public dfs_visitor<>
{
  cycle_detector(bool& has_cycle)
    : m_has_cycle(has_cycle) { }

  template <class Edge, class Graph>
  void back_edge(Edge, Graph&) { m_has_cycle = true; }
protected:
  bool& m_has_cycle;
};

int main(int,char*[])
{

  typedef pair<int,int> Edge;
  Edge used_by[] = {
    Edge(dax_h, foo_cpp), Edge(dax_h, bar_cpp), Edge(dax_h, yow_h),
    Edge(yow_h, bar_cpp), Edge(yow_h, zag_cpp),
    Edge(boz_h, bar_cpp), Edge(boz_h, zig_cpp), Edge(boz_h, zag_cpp),
    Edge(zow_h, foo_cpp),
    Edge(foo_cpp, foo_o),
    Edge(foo_o, libfoobar_a),
    Edge(bar_cpp, bar_o),
    Edge(bar_o, libfoobar_a),
    Edge(libfoobar_a, libzigzag_a),
    Edge(zig_cpp, zig_o),
    Edge(zig_o, libzigzag_a),
    Edge(zag_cpp, zag_o),
    Edge(zag_o, libzigzag_a),
    Edge(libzigzag_a, killerapp)
  };
  const std::size_t nedges = sizeof(used_by)/sizeof(Edge);
  int weights[nedges];
  std::fill(weights, weights + nedges, 1);

  typedef adjacency_list<vecS, vecS, directedS,
      property<vertex_color_t, default_color_type>,
      property<edge_weight_t, int>
    > Graph;
#if defined(BOOST_MSVC) && BOOST_MSVC <= 1300
  // VC++ can't handle the iterator constructor
  Graph g(N);
  property_map<Graph, edge_weight_t>::type weightmap = get(edge_weight, g);
  for (std::size_t j = 0; j < nedges; ++j) {
    graph_traits<Graph>::edge_descriptor e; bool inserted;
    tie(e, inserted) = add_edge(used_by[j].first, used_by[j].second, g);
    weightmap[e] = weights[j];
  }
#else
  Graph g(used_by, used_by + nedges, weights, N);
#endif
  typedef graph_traits<Graph>::vertex_descriptor Vertex;

  typedef property_map<Graph, vertex_color_t>::type Color;

  // Determine ordering for a full recompilation
  {
    typedef list<Vertex> MakeOrder;
    MakeOrder make_order;
    topological_sort(g, std::front_inserter(make_order));

    cout << "make ordering: ";
    for (MakeOrder::iterator i = make_order.begin();
         i != make_order.end(); ++i)
      cout << name[*i] << " ";
    cout << endl;
  }
  cout << endl;

  // Recompilation order with files that can be compiled in parallel
  // grouped together
  {
    // Set up the necessary graph properties.
    vector<int> time(N);
    typedef vector<int>::iterator Time;
    property_map<Graph, edge_weight_t>::type weight = get(edge_weight, g);

    // Calculate the in_degree for each vertex.
    vector<int> in_degree(N, 0);
    graph_traits<Graph>::vertex_iterator i, iend;
    graph_traits<Graph>::out_edge_iterator j, jend;
    for (tie(i, iend) = vertices(g); i != iend; ++i)
      for (tie(j, jend) = out_edges(*i,g); j != jend; ++j)
        in_degree[target(*j,g)] += 1;

    std::greater<int> compare;
    closed_plus<int> combine;

    // Run best-first-search from each vertex with zero in-degree.
    for (tie(i, iend) = vertices(g); i != iend; ++i) {
      if (in_degree[*i] == 0) {
        std::vector<graph_traits<Graph>::vertex_descriptor>
          pred(num_vertices(g));
        property_map<Graph, vertex_index_t>::type
          indexmap = get(vertex_index, g);
        dijkstra_shortest_paths_no_init
          (g, *i, &pred[0], &time[0], weight, indexmap,
           compare, combine, 0, 0, default_dijkstra_visitor());
      }
    }

    cout << "parallel make ordering, " << endl
         << "vertices with same group number can be made in parallel" << endl;
    for (tie(i,iend) = vertices(g); i != iend; ++i)
      cout << "time_slot[" << name[*i] << "] = " << time[*i] << endl;
  }
  cout << endl;

  // if I change yow.h what files need to be re-made?
  {
    cout << "A change to yow.h will cause what to be re-made?" << endl;
    print_visitor vis;
    breadth_first_search(g, vertex(yow_h, g), visitor(vis));
    cout << endl;
  }
  cout << endl;

  // are there any cycles in the graph?
  {
    bool has_cycle = false;
    cycle_detector vis(has_cycle);
    depth_first_search(g, visitor(vis));
    cout << "The graph has a cycle? " << has_cycle << endl;
  }
  cout << endl;

  // add a dependency going from bar.cpp to dax.h
  {
    cout << "adding edge bar_cpp -> dax_h" << endl;
    add_edge(bar_cpp, dax_h, g);
  }
  cout << endl;

  // are there any cycles in the graph?
  {
    typedef property_map<Graph,vertex_color_t>::type Color;
    bool has_cycle = false;
    cycle_detector vis(has_cycle);
    depth_first_search(g, visitor(vis));
    cout << "The graph has a cycle now? " << has_cycle << endl;
  }

  return 0;
}


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

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

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

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

#include <iostream>

#ifndef BOOST_TEST_MODULE
#define BOOST_TEST_MODULE test_intersection_pointlike_pointlike
#endif

#ifdef BOOST_GEOMETRY_TEST_DEBUG
#define BOOST_GEOMETRY_DEBUG_TURNS
#define BOOST_GEOMETRY_DEBUG_SEGMENT_IDENTIFIER
#endif

#include <boost/test/included/unit_test.hpp>

#include "../test_set_ops_pointlike.hpp"

#include <boost/geometry/geometries/multi_point.hpp>

typedef bg::model::point<double,2,bg::cs::cartesian>  point_type;
typedef bg::model::multi_point<point_type>  multi_point_type;

//===========================================================================
//===========================================================================
//===========================================================================

BOOST_AUTO_TEST_CASE( test_intersection_point_point )
{
#ifdef BOOST_GEOMETRY_TEST_DEBUG
    std::cout << std::endl << std::endl << std::endl;
    std::cout << "*** POINT / POINT INTERSECTION ***" << std::endl;
    std::cout << std::endl;
#endif

    typedef point_type P;
    typedef multi_point_type MP;

    typedef test_set_op_of_pointlike_geometries
        <
            P, P, MP, bg::overlay_intersection
        > tester;

    tester::apply
        ("ppi01",
         from_wkt<P>("POINT(0 0)"),
         from_wkt<P>("POINT(1 1)"),
         from_wkt<MP>("MULTIPOINT()")
         );

    tester::apply
        ("ppi02",
         from_wkt<P>("POINT(0 0)"),
         from_wkt<P>("POINT(0 0)"),
         from_wkt<MP>("MULTIPOINT(0 0)")
         );
}

BOOST_AUTO_TEST_CASE( test_intersection_multipoint_point )
{
#ifdef BOOST_GEOMETRY_TEST_DEBUG
    std::cout << std::endl << std::endl << std::endl;
    std::cout << "*** MULTIPOINT / POINT INTERSECTION ***" << std::endl;
    std::cout << std::endl;
#endif

    typedef point_type P;
    typedef multi_point_type MP;

    typedef test_set_op_of_pointlike_geometries
        <
            MP, P, MP, bg::overlay_intersection
        > tester;

    tester::apply
        ("mppi01",
         from_wkt<MP>("MULTIPOINT(0 0)"),
         from_wkt<P>("POINT(1 1)"),
         from_wkt<MP>("MULTIPOINT()")
         );

    tester::apply
        ("mppi02",
         from_wkt<MP>("MULTIPOINT(0 0)"),
         from_wkt<P>("POINT(0 0)"),
         from_wkt<MP>("MULTIPOINT(0 0)")
         );

    tester::apply
        ("mppi03",
         from_wkt<MP>("MULTIPOINT(0 0,0 0)"),
         from_wkt<P>("POINT(1 1)"),
         from_wkt<MP>("MULTIPOINT()")
         );

    tester::apply
        ("mppi04",
         from_wkt<MP>("MULTIPOINT(0 0,0 0)"),
         from_wkt<P>("POINT(0 0)"),
         from_wkt<MP>("MULTIPOINT(0 0)")
         );

    tester::apply
        ("mppi05",
         from_wkt<MP>("MULTIPOINT(0 0,0 0,1 0)"),
         from_wkt<P>("POINT(1 1)"),
         from_wkt<MP>("MULTIPOINT()")
         );

    tester::apply
        ("mppi06",
         from_wkt<MP>("MULTIPOINT(0 0,0 0,1 0)"),
         from_wkt<P>("POINT(1 0)"),
         from_wkt<MP>("MULTIPOINT(1 0)")
         );

    tester::apply
        ("mppi07",
         from_wkt<MP>("MULTIPOINT(0 0,0 0,1 0)"),
         from_wkt<P>("POINT(0 0)"),
         from_wkt<MP>("MULTIPOINT(0 0)")
         );

    tester::apply
        ("mppi08",
         from_wkt<MP>("MULTIPOINT()"),
         from_wkt<P>("POINT(0 0)"),
         from_wkt<MP>("MULTIPOINT()")
         );
}

BOOST_AUTO_TEST_CASE( test_intersection_multipoint_multipoint )
{
#ifdef BOOST_GEOMETRY_TEST_DEBUG
    std::cout << std::endl << std::endl << std::endl;
    std::cout << "*** MULTIPOINT / MULTIPOINT INTERSECTION ***" << std::endl;
    std::cout << std::endl;
#endif

    typedef multi_point_type MP;

    typedef test_set_op_of_pointlike_geometries
        <
            MP, MP, MP, bg::overlay_intersection
        > tester;

    tester::apply
        ("mpmpi01",
         from_wkt<MP>("MULTIPOINT(2 2,3 3,0 0,0 0,2 2,1 1,1 1,1 0,1 0)"),
         from_wkt<MP>("MULTIPOINT(1 0,1 1,1 1,1 1)"),
         from_wkt<MP>("MULTIPOINT(1 0,1 1,1 1,1 1)")
         );

    tester::apply
        ("mpmp02",
         from_wkt<MP>("MULTIPOINT(0 0,1 1,1 0,1 1)"),
         from_wkt<MP>("MULTIPOINT(1 0,0 0,1 1,0 0)"),
         from_wkt<MP>("MULTIPOINT(0 0,1 1,1 0,1 1)"),
         from_wkt<MP>("MULTIPOINT(1 0,0 0,1 1,0 0)")
         );

    tester::apply
        ("mpmpi03",
         from_wkt<MP>("MULTIPOINT()"),
         from_wkt<MP>("MULTIPOINT(1 0,0 0,1 1,0 0)"),
         from_wkt<MP>("MULTIPOINT()")
         );

    tester::apply
        ("mpmpi04",
         from_wkt<MP>("MULTIPOINT(0 0,1 1,1 0,1 1)"),
         from_wkt<MP>("MULTIPOINT()"),
         from_wkt<MP>("MULTIPOINT()")
         );

    tester::apply
        ("mpmpi05",
         from_wkt<MP>("MULTIPOINT()"),
         from_wkt<MP>("MULTIPOINT()"),
         from_wkt<MP>("MULTIPOINT()")
         );

    tester::apply
        ("mpmpi06",
         from_wkt<MP>("MULTIPOINT(0 0,1 0,2 0,3 0,0 0,1 0,2 0)"),
         from_wkt<MP>("MULTIPOINT(0 1,0 2,1 0,0 0,2 0)"),
         from_wkt<MP>("MULTIPOINT(1 0,0 0,2 0)")
         );
}