// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2011 Gael Guennebaud <g.gael@free.fr>
// Copyright (C) 2012 Kolja Brix <brix@igpm.rwth-aaachen.de>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include "../../test/sparse_solver.h"
#include <Eigen/IterativeSolvers>

template <typename T> void test_gmres_T() {
    GMRES<SparseMatrix<T>, DiagonalPreconditioner<T>> gmres_colmajor_diag;
    GMRES<SparseMatrix<T>, IdentityPreconditioner> gmres_colmajor_I;
    GMRES<SparseMatrix<T>, IncompleteLUT<T>> gmres_colmajor_ilut;
    // GMRES<SparseMatrix<T>, SSORPreconditioner<T> >     gmres_colmajor_ssor;

    CALL_SUBTEST(check_sparse_square_solving(gmres_colmajor_diag));
    //   CALL_SUBTEST( check_sparse_square_solving(gmres_colmajor_I)     );
    CALL_SUBTEST(check_sparse_square_solving(gmres_colmajor_ilut));
    // CALL_SUBTEST( check_sparse_square_solving(gmres_colmajor_ssor)     );
}

void test_gmres() {
    for (int i = 0; i < g_repeat; i++) {
        CALL_SUBTEST_1(test_gmres_T<double>());
        CALL_SUBTEST_2(test_gmres_T<std::complex<double>>());
    }
}


//
// Created by qin on 3/9/22.
//

#include "myslam/eth3d_dataset.h"
#include "myslam/frame.h"

#include <boost/format.hpp>
#include <fstream>
#include <opencv2/opencv.hpp>

using namespace std;

//void tokenize(std::string const &str, const char delim,
//              std::vector<std::string> &out) {
//    size_t start;
//    size_t end = 0;
//
//    while ((start = str.find_first_not_of(delim, end)) != std::string::npos) {
//        end = str.find(delim, start);
//        out.push_back(str.substr(start, end - start));
//    }
//}

namespace myslam {

Dataset::Dataset(const std::string &dataset_path)
    : dataset_path_(dataset_path) {}

bool Dataset::Init() {
  // read 1st camera intrinsics
  std::ifstream intrinsic_left(dataset_path_ + "/calibration2.txt");
  if (!intrinsic_left) {
    LOG(ERROR) << "cannot find " << dataset_path_ << "/calibration2.txt";
    return false;
  }
  double K1[4];
  for (double &i : K1) {
    intrinsic_left >> i;
  }
  Vec3 t1;
  t1 << 0, 0, 0;
  Camera::Ptr camera_left(
      new Camera(K1[0], K1[1], K1[2], K1[3], t1.norm(), SE3(SO3(), t1)));
  cameras_.push_back(camera_left);

  // read 2nd camera intrinsics and extrinsics
  std::ifstream intrinsic_right(dataset_path_ + "/calibration.txt");
  if (!intrinsic_right) {
    LOG(ERROR) << "cannot find " << dataset_path_ << "/calibration.txt";
    return false;
  }
  double K2[4];
  for (double &i : K2) {
    intrinsic_right >> i;
  }
  std::ifstream extrinsic_left2right(dataset_path_ + "/extrinsics_1_2.txt");
  if (!extrinsic_left2right) {
    LOG(ERROR) << "cannot find " << dataset_path_ << "/extrinsics_1_2.txt";
    return false;
  }
  double projection_data[12]; // T from right to left camera
  for (double &i : projection_data) {
    extrinsic_left2right >> i;
  }
  Mat33 R2;
  R2 << projection_data[0], projection_data[1], projection_data[2],
      projection_data[4], projection_data[5], projection_data[6],
      projection_data[8], projection_data[9], projection_data[10];
  Eigen::JacobiSVD<Eigen::MatrixXd> SO3_R(R2, Eigen::ComputeThinU | Eigen::ComputeThinV);
  R2 = SO3_R.matrixU().inverse() * SO3_R.matrixV().transpose().inverse();
  Mat33 R = R2.inverse();
  Vec3 t2;
  t2 << projection_data[3], projection_data[7], projection_data[11];
  t2 = -1 * (R * t2);
  Camera::Ptr camera_right(
      new Camera(K2[0], K2[1], K2[2], K2[3], t2.norm(), SE3(R, t2)));
  cameras_.push_back(camera_right);

//        LOG(INFO) << "Camera extrinsics: " << t.transpose();
  extrinsic_left2right.close();
  intrinsic_left.close();
  intrinsic_right.close();

  std::cout << "left K:\n" << camera_left->K().matrix() << std::endl;
  std::cout << "left t:\n" << camera_left->pose().matrix() << std::endl;
  std::cout << "right K:\n" << camera_right->K().matrix() << std::endl;
  std::cout << "right t:\n" << camera_right->pose().matrix() << std::endl;

  // read image pairs
  std::ifstream dataset(dataset_path_ + "/associated.txt");
  if (!dataset) {
    LOG(ERROR) << "cannot find " << dataset_path_ << "/associated.txt";
    return false;
  }
  std::string line;
  while (getline(dataset, line)) {
    char *cstr = new char[line.length() + 1];
    std::strcpy(cstr, line.c_str());
    char *p = std::strtok(cstr + 15, " ");
//    for (int i = 1; i < 2; i++){
//      p = std::strtok(nullptr, " ");
//    }
    std::string each_in_line = p;
    left_images_.push_back(dataset_path_ + "/" + each_in_line.insert(3, "2"));
    right_images_.push_back(dataset_path_ + "/" + each_in_line);
    LOG(INFO) << (dataset_path_ + "/" + each_in_line);
  }

  current_image_index_ = 0;
  return true;

}

Frame::Ptr Dataset::NextFrame() {
  cv::Mat image_left, image_right, image_right_original;
  // read images
  image_left = cv::imread(left_images_[current_image_index_], cv::IMREAD_GRAYSCALE);
  image_right_original = cv::imread(right_images_[current_image_index_], cv::IMREAD_GRAYSCALE);

  cv::resize(image_right_original, image_right, cv::Size(image_left.cols,
                                                         image_left.rows), cv::INTER_LINEAR);

  if (image_left.data == nullptr || image_right.data == nullptr) {
    LOG(WARNING) << "cannot find images at index " << current_image_index_;
    return nullptr;
  }

//        cv::Mat image_left_resized, image_right_resized;
//        cv::resize(image_left, image_left_resized, cv::Size(), 0.5, 0.5, cv::INTER_NEAREST);
//        cv::resize(image_right, image_right_resized, cv::Size(), 0.5, 0.5, cv::INTER_NEAREST);

  auto new_frame = Frame::CreateFrame();
//        new_frame->left_img_ = image_left_resized;
//        new_frame->right_img_ = image_right_resized;
  new_frame->left_img_ = image_left;
  new_frame->right_img_ = image_right;
  current_image_index_++;
  return new_frame;
}

}  // namespace myslam


/*    This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT.
 *    See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details.
 *    Author(s):       Siargey Kachanovich
 *
 *    Copyright (C) 2016 Inria
 *
 *    Modification(s):
 *      - YYYY/MM Author: Description of the modification
 */

// #ifdef _DEBUG
// # define TBB_USE_THREADING_TOOL
// #endif

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE Subsampling - test choose_n_farthest_points
#include <boost/test/unit_test.hpp>
#include <boost/mpl/list.hpp>

#include <gudhi/choose_n_farthest_points.h>
#include <vector>
#include <iterator>

#include <CGAL/Epick_d.h>

typedef CGAL::Epick_d<CGAL::Dynamic_dimension_tag> K;
typedef typename K::FT FT;
typedef typename K::Point_d Point_d;

typedef boost::mpl::list<CGAL::Epick_d<CGAL::Dynamic_dimension_tag>, CGAL::Epick_d<CGAL::Dimension_tag<4>>> list_of_tested_kernels;

BOOST_AUTO_TEST_CASE_TEMPLATE(test_choose_farthest_point, Kernel, list_of_tested_kernels) {
  typedef typename Kernel::FT FT;
  typedef typename Kernel::Point_d Point_d;
  std::vector< Point_d > points, landmarks;
  // Add grid points (625 points)
  for (FT i = 0; i < 5; i += 1.0)
    for (FT j = 0; j < 5; j += 1.0)
      for (FT k = 0; k < 5; k += 1.0)
        for (FT l = 0; l < 5; l += 1.0) {
          std::vector<FT> point({i, j, k, l});
          points.emplace_back(point.begin(), point.end());
        }

  landmarks.clear();
  Kernel k;
  Gudhi::subsampling::choose_n_farthest_points(k, points, 100, Gudhi::subsampling::random_starting_point, std::back_inserter(landmarks));

  BOOST_CHECK(landmarks.size() == 100);
  for (auto landmark : landmarks)
  {
    // Check all landmarks are in points
    BOOST_CHECK(std::find (points.begin(), points.end(), landmark) != points.end());
  }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(test_choose_farthest_point_limits, Kernel, list_of_tested_kernels) {
  typedef typename Kernel::FT FT;
  typedef typename Kernel::Point_d Point_d;
  std::vector< Point_d > points, landmarks;
  std::vector< FT > distances;
  landmarks.clear();
  Kernel k;
  // Choose -1 farthest points in an empty point cloud
  Gudhi::subsampling::choose_n_farthest_points(k, points, -1, -1, std::back_inserter(landmarks), std::back_inserter(distances));
  BOOST_CHECK(landmarks.size() == 0);
  landmarks.clear(); distances.clear();
  // Choose 0 farthest points in an empty point cloud
  Gudhi::subsampling::choose_n_farthest_points(k, points, 0, -1, std::back_inserter(landmarks), std::back_inserter(distances));
  BOOST_CHECK(landmarks.size() == 0);
  landmarks.clear(); distances.clear();
  // Choose 1 farthest points in an empty point cloud
  Gudhi::subsampling::choose_n_farthest_points(k, points, 1, -1, std::back_inserter(landmarks), std::back_inserter(distances));
  BOOST_CHECK(landmarks.size() == 0);
  landmarks.clear(); distances.clear();

  std::vector<FT> point({0.0, 0.0, 0.0, 0.0});
  points.emplace_back(point.begin(), point.end());
  // Choose -1 farthest points in a one point cloud
  Gudhi::subsampling::choose_n_farthest_points(k, points, -1, -1, std::back_inserter(landmarks), std::back_inserter(distances));
  BOOST_CHECK(landmarks.size() == 1 && distances.size() == 1);
  BOOST_CHECK(distances[0] == std::numeric_limits<FT>::infinity());
  landmarks.clear(); distances.clear();
  // Choose 0 farthest points in a one point cloud
  Gudhi::subsampling::choose_n_farthest_points(k, points, 0, -1, std::back_inserter(landmarks), std::back_inserter(distances));
  BOOST_CHECK(landmarks.size() == 0 && distances.size() == 0);
  landmarks.clear(); distances.clear();
  // Choose 1 farthest points in a one point cloud
  Gudhi::subsampling::choose_n_farthest_points(k, points, 1, -1, std::back_inserter(landmarks), std::back_inserter(distances));
  BOOST_CHECK(landmarks.size() == 1 && distances.size() == 1);
  BOOST_CHECK(distances[0] == std::numeric_limits<FT>::infinity());
  landmarks.clear(); distances.clear();

  std::vector<FT> point2({1.0, 0.0, 0.0, 0.0});
  points.emplace_back(point2.begin(), point2.end());
  // Choose all farthest points among 2 points
  Gudhi::subsampling::choose_n_farthest_points(k, points, -1, -1, std::back_inserter(landmarks), std::back_inserter(distances));
  BOOST_CHECK(landmarks.size() == 2 && distances.size() == 2);
  BOOST_CHECK(distances[0] == std::numeric_limits<FT>::infinity());
  BOOST_CHECK(distances[1] == 1);
  landmarks.clear(); distances.clear();

  // Ignore duplicated points
  points.emplace_back(point.begin(), point.end());
  Gudhi::subsampling::choose_n_farthest_points(k, points, -1, -1, std::back_inserter(landmarks), std::back_inserter(distances));
  BOOST_CHECK(landmarks.size() == 2 && distances.size() == 2);
  BOOST_CHECK(distances[0] == std::numeric_limits<FT>::infinity());
  BOOST_CHECK(distances[1] == 1);
  landmarks.clear(); distances.clear();
}


#ifndef DELTA_BSPLINES_HPP
#define DELTA_BSPLINES_HPP

#include <Eigen/Core>

class BSplines {
    template <unsigned P>
    static int find_span(int n, double u, const Eigen::Matrix<double, P + 1, 1>& U);

    template <unsigned P>
    static Eigen::Matrix<double, P + 1, 1> basis_funs(int i, double u, int p, const Eigen::Matrix<double, P + 1, 1>& U);
};

template<unsigned int P>
int BSplines::find_span(int n, double u, const Eigen::Matrix<double, P + 1, 1> &U) {
    return 0;
}

template<unsigned int P>
Eigen::Matrix<double, P + 1, 1> BSplines::basis_funs(int i, double u, int p, const Eigen::Matrix<double, P + 1, 1> &U) {
    return Eigen::Matrix<double, P + 1, 1>();
}

#endif //DELTA_BSPLINES_HPP


//==============================================================================
//         Copyright 2009 - 2013 LRI    UMR 8623 CNRS/Univ Paris Sud XI
//         Copyright 2012 - 2014 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
//==============================================================================

#include <boost/simd/sdk/simd/pack.hpp>
#include <boost/simd/include/functions/any.hpp>
#include <boost/simd/include/functions/seladd.hpp>
#include <boost/simd/include/functions/plus.hpp>
#include <boost/simd/include/functions/minus.hpp>
#include <boost/simd/include/functions/times.hpp>
#include <boost/simd/include/functions/is_less.hpp>
#include <boost/simd/include/functions/aligned_store.hpp>
#include <boost/simd/include/constants/zero.hpp>
#include <boost/simd/include/constants/one.hpp>
#include <boost/simd/memory/allocator.hpp>
#include <boost/fusion/include/at.hpp>
#include <boost/dispatch/meta/as_integer.hpp>
#include <boost/dispatch/meta/strip.hpp>
#include <vector>
#include <iostream>

#include <nt2/sdk/bench/benchmark.hpp>
#include <nt2/sdk/bench/protocol/max_duration.hpp>
#include <nt2/sdk/bench/metric/cycles_per_element.hpp>
#include <nt2/sdk/bench/stats/median.hpp>
#include <nt2/sdk/bench/setup/geometric.hpp>
#include <nt2/sdk/bench/setup/combination.hpp>
#include <nt2/sdk/bench/setup/constant.hpp>

using namespace nt2::bench;
using namespace nt2;

namespace mandelbrot
{
  struct step
  {
    template<class Sig> struct result;
    template<class This, class A0, class A1>
    struct result<This(A0,A1)>
    {
      typedef typename boost::dispatch::meta::
              as_integer<typename boost::dispatch::meta::strip<A0>::type
                        >::type type;
    };

    step(std::size_t const& n) : max_iter_(n) {}

    template<class T>
    typename result<step(T,T)>::type operator()(T const& a, T const& b) const
    {
      typedef typename result<step(T const&, T const&)>::type iter_type;
      typedef typename boost::simd::meta::scalar_of<T>::type s_type;
      iter_type iter = boost::simd::Zero<iter_type>();
      iter_type const o = boost::simd::One<iter_type>();
      T x = boost::simd::Zero<T>();
      T y = boost::simd::Zero<T>();
      T x2, y2, xy, m2;
      typename boost::simd::meta::as_logical<T>::type mask;
      std::size_t i = 0;
      bool flag;
      do
      {
        x2 = x * x;
        y2 = y * y;
        xy = s_type(2) * x * y;
        x = x2 - y2 + a;
        y = xy + b;
        m2 = x2 + y2;
        mask = m2 < s_type(4);
        iter = boost::simd::seladd(mask, iter, o);
        flag = boost::simd::any(mask);
        i++;
      }
      while(flag && i < 256);

      return iter;
    }

    std::size_t max_iter_;
  };
}

template<typename T> struct mandelbrot_simd
{
  typedef T value_type;
  template<typename Setup>
  mandelbrot_simd(Setup const& s)
                    :  h_(boost::fusion::at_c<0>(s))
                    ,  w_(boost::fusion::at_c<1>(s))
                    ,  a0_(boost::fusion::at_c<2>(s))
                    ,  a1_(boost::fusion::at_c<3>(s))
                    ,  b0_(boost::fusion::at_c<4>(s))
                    ,  b1_(boost::fusion::at_c<5>(s))
                    ,  max_iter_(boost::fusion::at_c<6>(s))
                    ,  size_(h_*w_)
                    ,  julia(max_iter_)

  {
    A.resize(size_);
    B.resize(size_);
    C.resize(size_);

    T interval_A=(a1_-a0_)/(h_-1);
    T new_val=a0_;
    for (std::size_t jj=0;jj<w_;jj++)
    {
      for (std::size_t ii=0;ii<h_;ii++)
      {
        A[jj*h_+ii]=new_val;
        new_val+=interval_A;
      }
      new_val=a0_;
    }
    new_val=b0_;
    T interval_B=(b1_-b0_)/(w_-1);
    for (std::size_t jj=0;jj<w_;jj++)
    {
      for (std::size_t ii=0;ii<h_;ii++)
      {
        B[jj*h_+ii]=new_val;
      }
      new_val+=interval_B;
    }
  }

  void operator()()
  {
    using boost::simd::pack;
    using boost::simd::aligned_store;

    typedef pack<value_type> type;

    std::size_t step_size_ = boost::simd::meta::cardinal_of<type>::value;
    std::size_t aligned_sz = size_ & ~(step_size_-1);
    std::size_t it = 0;

    for(std::size_t m=aligned_sz; it != m; it+=step_size_)
    {
      type A_pack = (&A[it]);
      type B_pack = (&B[it]);
      aligned_store(julia(A_pack, B_pack), &C[it]);
    }

    for(std::size_t m=size_; it != m; it++)
      C[it]=julia(A[it],B[it]);
  }

  friend std::ostream& operator<<(std::ostream& os, mandelbrot_simd<T> const& p)
  {
    return os << "(" << p.h_ << " x " << p.w_ << ")";
  }

  std::size_t size() const { return size_; }

private:
  std::size_t h_, w_;
  value_type a0_, a1_, b0_, b1_;
  std::size_t max_iter_, size_;
  mandelbrot::step julia;
  std::vector<value_type, boost::simd::allocator<value_type> > A, B;
  std::vector<int, boost::simd::allocator<int> > C;
};

NT2_REGISTER_BENCHMARK_TPL( mandelbrot_simd, (float) )
{
  std::size_t hmin = args("hmin", 100);
  std::size_t hmax = args("hmax", 1600);
  std::size_t hstep = args("hstep", 2);
  std::size_t wmin = args("wmin", 100);
  std::size_t wmax = args("wmax",1600);
  std::size_t wstep = args("wstep", 2);
  T xmin = args("xmin", -1.5);
  T xmax = args("xmax", 1.5);
  T ymin = args("ymin", -1.5);
  T ymax = args("ymax", 1.5);
  T max_iter = args("max_iter", 256);

  run_during_with< mandelbrot_simd<float> > ( 1.
                                            , and_( geometric(hmin,hmax,hstep)
                                                  , geometric(wmin,wmax,wstep)
                                                  , constant(xmin)
                                                  , constant(xmax)
                                                  , constant(ymin)
                                                  , constant(ymax)
                                                  , constant(max_iter)
                                                  )
                                            , cycles_per_element<stats::median_>()
                                            );
}


// Boost.Geometry
// Unit Test

// Copyright (c) 2021 Barend Gehrels, Amsterdam, the Netherlands.

// Use, modification and distribution is subject to 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 <geometry_test_common.hpp>

#include <boost/geometry/algorithms/detail/distance/point_to_geometry.hpp>
#include <boost/geometry/algorithms/detail/overlay/approximately_equals.hpp>

#include <boost/geometry/strategies/strategies.hpp>
#include <boost/geometry/geometries/geometries.hpp>
#include <boost/geometry/io/wkt/wkt.hpp>

template <typename P>
auto get_distance(P const& p1, P const& p2)
{
    using namespace boost::geometry;

    using tag = typename cs_tag<P>::type;
    using strategy_type
    = typename strategy::distance::services::default_strategy
        <
            point_tag, point_tag, P, P, tag, tag
        >::type;

    strategy_type s;
    return detail::distance::point_to_point<P, P, strategy_type>::apply(p1, p2, s);
}

template <typename P, typename E>
bool test_approximately_equal(P const& p1, P const& p2, E const m,
                              bool debug)
{
    const bool result = bg::detail::overlay::approximately_equals(p1, p2, m);
    if (debug)
    {
        const auto d = get_distance(p1, p2);
        std::cout << std::boolalpha
                << " " << result
                << " " << d
                << std::endl;
    }
    return result;
}

template <typename P, typename E>
void test_all(E const multiplier, std::size_t expected_index)
{
    constexpr bool debug = false;

    if (debug)
    {
        using ct = typename bg::coordinate_type<P>::type;
        std::cout << "EPSILON: " << std::setprecision(32)
                  << std::numeric_limits<ct>::epsilon() << std::endl;
    }

    P p1;
    bg::read_wkt("POINT(1 1)", p1);
    std::size_t index = 0;
    for (double d = 1.0; d > 0; d /= 2.0, index++)
    {
        if (debug) { std::cout << index; }

        P p2 = p1;
        bg::set<0>(p2, bg::get<0>(p1) + d);

        if (test_approximately_equal(p1, p2, multiplier, debug))
        {
            BOOST_CHECK_MESSAGE(index == expected_index,
               "Expected: " << expected_index << " Detected: " << index);
            return;
        }
    }
}

int test_main(int, char* [])
{
    double m = 1000.0;
    test_all<bg::model::point<long double, 2, bg::cs::cartesian>>(m, 54);
    test_all<bg::model::point<double, 2, bg::cs::cartesian>>(m, 43);
    test_all<bg::model::point<float, 2, bg::cs::cartesian>>(m, 24);

    m *= 1000.0;
    test_all<bg::model::point<long double, 2, bg::cs::cartesian>>(m, 44);
    test_all<bg::model::point<double, 2, bg::cs::cartesian>>(m, 33);
    test_all<bg::model::point<float, 2, bg::cs::cartesian>>(m, 24);

    m *= 1000.0;
    test_all<bg::model::point<long double, 2, bg::cs::cartesian>>(m, 34);
    test_all<bg::model::point<double, 2, bg::cs::cartesian>>(m, 23);
    test_all<bg::model::point<float, 2, bg::cs::cartesian>>(m, 23);

    return 0;
}


/* I was unable to find good information on how the PLL dividers work
in the ECP5 PLL Design and Usage Guide, so I ran several frequencies
through Lattice's clarity designer, and was surprised with what I
found:
| Input | Output | refclk | feedback | output | fvco |
|    12 |     48 |      1 |        4 |     12 |  576 |
|    12 |     60 |      1 |        5 |     10 |  600 |
|    20 |     30 |      2 |        3 |     20 |  600 |
|    45 |     30 |      3 |        2 |     20 |  600 |
|   100 |    400 |      1 |        4 |      1 |  400 |
|   200 |    400 |      1 |        2 |      2 |  800 |
|    50 |    400 |      1 |        8 |      2 |  800 |
|    70 |     40 |      7 |        4 |     15 |  600 |
|    12 |     36 |      1 |        3 |     18 |  648 |
|    12 |     96 |      1 |        8 |      6 |  576 |
|    90 |     40 |      9 |        4 |     15 |  600 |
|    90 |     50 |      9 |        5 |     13 |  650 |
|    43 |     86 |      1 |        2 |      7 |  602 |

it appears that
f_pfd = f_in/refclk
f_vco = f_pfd * feedback * output
f_out = f_vco / output
*/

#define INPUT_MIN 8.0f
#define INPUT_MAX 400.0f
#define OUTPUT_MIN 10.0f
#define OUTPUT_MAX 400.0f
#define PFD_MIN 3.125f
#define PFD_MAX 400.0f
#define VCO_MIN 400.0f
#define VCO_MAX 800.0f
#include <iostream>
#include <limits>
#include <fstream>
#include <string>
#include <boost/program_options.hpp>
#include "version.hpp"
#include "wasmexcept.hpp"
using namespace std;

enum class pll_mode{
  SIMPLE,
  HIGHRES
};

struct secondary_params{
  bool enabled;
  int div;
  int cphase;
  int fphase;
  string name;

  float freq;
  float phase;
};

struct pll_params{
  pll_mode mode;
  int refclk_div;
  int feedback_div;
  int output_div;
  int primary_cphase;
  string clkin_name;
  string clkout0_name;
  int dynamic;
  int reset, standby;
  int feedback_clkout, internal_feedback, internal_feedback_wake;
  string feedback_name[4], feedback_wname[4];

  float clkin_frequency;

  secondary_params secondary[3];

  float fout;
  float fvco;

  pll_params() :mode(pll_mode::SIMPLE) {
    for(int i=0;i<3;i++){
      secondary[i].enabled = false;
      primary_cphase = 9;
    }
  }
};

void calc_pll_params(pll_params &params, float input, float output);
void calc_pll_params_highres(pll_params &params, float input, float output);
void generate_secondary_output(pll_params &params, int channel, string name, float frequency, float phase);
void write_pll_config(const pll_params & params, const string &name, ofstream& file);

int main(int argc, char** argv){
  namespace po = boost::program_options;
  po::options_description options("Allowed options");

  options.add_options()("help,h", "show help");
  options.add_options()("module,n", po::value<string>(), "module name");
  options.add_options()("clkin_name", po::value<string>(), "Input signal name");
  options.add_options()("clkin,i", po::value<float>(), "Input frequency in MHz");
  options.add_options()("clkout0_name", po::value<string>(), "Primary Output(0) signal name");
  options.add_options()("clkout0,o", po::value<float>(), "Primary Output(0) frequency in MHz");
  options.add_options()("clkout1_name", po::value<string>(), "Secondary Output(1) signal name");
  options.add_options()("clkout1", po::value<float>(), "Secondary Output(1) frequency in MHz");
  options.add_options()("phase1", po::value<float>()->default_value(0), "Secondary Output(1) phase in degrees");
  options.add_options()("clkout2_name", po::value<string>(), "Secondary Output(2) signal name");
  options.add_options()("clkout2", po::value<float>(), "Secondary Output(2) frequency in MHz");
  options.add_options()("phase2", po::value<float>()->default_value(0), "Secondary Output(2) phase in degrees");
  options.add_options()("clkout3_name", po::value<string>(), "Secondary Output(3) signal name");
  options.add_options()("clkout3", po::value<float>(), "Secondary Output(3) frequency in MHz");
  options.add_options()("phase3", po::value<float>()->default_value(0), "Secondary Output(3) phase in degrees");
  options.add_options()("file,f", po::value<string>(), "Output to file");
  options.add_options()("highres", "Use secondary PLL output for higher frequency resolution");
  options.add_options()("dynamic", "Use dynamic clock control");
  options.add_options()("reset", "Enable reset input");
  options.add_options()("standby", "Enable standby input");
  options.add_options()("feedback_clkout", po::value<string>(), "Use Nth Output as feedback signal");
  options.add_options()("internal_feedback", "Use internal feedback (instead of external)");
  options.add_options()("internal_feedback_wake", "Wake internal feedback");

  po::variables_map vm;
  po::parsed_options parsed = po::command_line_parser(argc, argv).options(options).run();
  po::store(parsed, vm);
  po::notify(vm);

  if(vm.count("help")){
    cerr << "Project Trellis - Open Source Tools for ECP5 FPGAs" << endl;
    cerr << argv[0] << ": ECP5 PLL Configuration Calculator" << endl;
    cerr << "Version " << git_describe_str << endl;
    cerr << endl;
    cerr << "This tool is experimental! Use at your own risk!" << endl;
    cerr << endl;
    cerr << "Copyright (C) 2018-2019 David Shah <david@symbioticeda.com>" << endl;
    cerr << endl;
    cerr << options << endl;
    return 1;
  }
  if(vm.count("clkin") != 1 || vm.count("clkout0") != 1){
    cerr << "Error: missing input or output frequency!\n";
    return 1;
  }
  float inputf = vm["clkin"].as<float>();
  float outputf = vm["clkout0"].as<float>();
  if(inputf < INPUT_MIN || inputf > INPUT_MAX){
    cerr << "Warning: Input frequency " << inputf << "MHz not in range (" << INPUT_MIN << "MHz, " << INPUT_MAX << "MHz)\n";
  }
  if(outputf < OUTPUT_MIN || outputf > OUTPUT_MAX){
    cerr << "Warning: Output frequency " << outputf << "MHz not in range (" << OUTPUT_MIN << "MHz, " << OUTPUT_MAX << "MHz)\n";
  }

  pll_params params = {};

  string module_name = "pll";
  if(vm.count("module"))
    module_name = vm["module"].as<string>();
  params.clkin_frequency = inputf;

  params.clkin_name = "clkin";
  if(vm.count("clkin_name"))
    params.clkin_name = vm["clkin_name"].as<string>();

  params.clkout0_name = "clkout0";
  if(vm.count("clkout0_name"))
    params.clkout0_name = vm["clkout0_name"].as<string>();

  params.secondary[0].name = "clkout[1]";
  params.secondary[1].name = "clkout[2]";
  params.secondary[2].name = "clkout[3]";

  if(vm.count("highres"))
  {
    if(vm.count("clkout1") > 0)
    {
      cerr << "Cannot specify secondary frequency in highres mode\n";
    }
    params.secondary[0].name = "clkout1";
    if(vm.count("clkout1_name"))
      params.secondary[0].name = vm["clkout1_name"].as<string>();
    calc_pll_params_highres(params, inputf, outputf);
  }
  else
  {
    calc_pll_params(params, inputf, outputf);
    if(vm.count("clkout1"))
    {
      string clkout1_name = "clkout1";
      if(vm.count("clkout1_name"))
        clkout1_name = vm["clkout1_name"].as<string>();
      generate_secondary_output(params, 0, clkout1_name, vm["clkout1"].as<float>(), vm["phase1"].as<float>());
    }
    if(vm.count("clkout2"))
    {
      string clkout2_name = "clkout2";
      if(vm.count("clkout2_name"))
        clkout2_name = vm["clkout2_name"].as<string>();
      generate_secondary_output(params, 1, clkout2_name, vm["clkout2"].as<float>(), vm["phase2"].as<float>());
    }
    if(vm.count("clkout3"))
    {
      string clkout3_name = "clkout3";
      if(vm.count("clkout3_name"))
        clkout3_name = vm["clkout3_name"].as<string>();
      generate_secondary_output(params, 2, clkout3_name, vm["clkout3"].as<float>(), vm["phase3"].as<float>());
    }
  }

  params.dynamic = 0;
  if(vm.count("dynamic"))
    params.dynamic = 1;
  params.reset = 0;
  if(vm.count("reset"))
    params.reset = 1;
  params.standby = 0;
  if(vm.count("standby"))
    params.standby = 1;
  params.feedback_name[0] = "OP";
  params.feedback_name[1] = "OS";
  params.feedback_name[2] = "OS2";
  params.feedback_name[3] = "OS3";
  params.feedback_wname[0] = params.clkout0_name;
  params.feedback_wname[1] = params.secondary[0].name;
  params.feedback_wname[2] = params.secondary[1].name;
  params.feedback_wname[3] = params.secondary[2].name;
  params.feedback_clkout = 0;
  if(vm.count("feedback_clkout"))
  {
    if(vm["feedback_clkout"].as<string>() == "0")
      params.feedback_clkout = 0;
    if(vm["feedback_clkout"].as<string>() == "1")
      params.feedback_clkout = 1;
    if(vm["feedback_clkout"].as<string>() == "2")
      params.feedback_clkout = 2;
    if(vm["feedback_clkout"].as<string>() == "3")
      params.feedback_clkout = 3;
  }
  params.internal_feedback = 0;
  if(vm.count("internal_feedback"))
    params.internal_feedback = 1;
  params.internal_feedback_wake = 0;
  if(vm.count("internal_feedback_wake"))
    params.internal_feedback_wake = 1;

  cout << "Pll parameters:" << endl;
  cout << "Refclk divisor: " << params.refclk_div << endl;
  cout << "Feedback divisor: " << params.feedback_div << endl;
  cout << "clkout0 divisor: " << params.output_div << "" << endl;
  cout << "clkout0 frequency: " << params.fout << " MHz" << endl;
  if(params.secondary[0].enabled){
    cout << "clkout1 divisor: " << params.secondary[0].div << endl;
    cout << "clkout1 frequency: " << params.secondary[0].freq << " MHz" << endl;
    cout << "clkout1 phase shift: " << params.secondary[0].phase << " degrees" << endl;
  }
  if(params.secondary[1].enabled){
    cout << "clkout2 divisor: " << params.secondary[1].div << endl;
    cout << "clkout2 frequency: " << params.secondary[1].freq << " MHz" << endl;
    cout << "clkout2 phase shift: " << params.secondary[1].phase << " degrees" << endl;
  }
  if(params.secondary[2].enabled){
    cout << "clkout3 divisor: " << params.secondary[2].div << endl;
    cout << "clkout3 frequency: " << params.secondary[2].freq << " MHz" << endl;
    cout << "clkout3 phase shift: " << params.secondary[2].phase << " degrees" << endl;
  }
  cout << "VCO frequency: " << params.fvco << endl;
  if(vm.count("file")){
    ofstream f;
    f.open(vm["file"].as<string>().c_str());
    write_pll_config(params, module_name, f);
    f.close();
  }
}

void calc_pll_params(pll_params &params, float input, float output){
  float error = std::numeric_limits<float>::max();
  for(int input_div=1;input_div <= 128; input_div++){

    float fpfd = input / (float)input_div;
    if(fpfd < PFD_MIN || fpfd > PFD_MAX)
      continue;
    for(int feedback_div=1;feedback_div <= 80; feedback_div++){
      for(int output_div=1;output_div <= 128; output_div++){
	float fvco = fpfd * (float)feedback_div * (float) output_div;

	if(fvco < VCO_MIN || fvco > VCO_MAX)
	  continue;

	float fout = fvco / (float) output_div;
	if(fabsf(fout - output) < error ||
	   (fabsf(fout-output) == error && fabsf(fvco - 600) < fabsf(params.fvco - 600))){
	  error = fabsf(fout-output);
	  params.refclk_div = input_div;
	  params.feedback_div = feedback_div;
	  params.output_div = output_div;
	  params.fout = fout;
	  params.fvco = fvco;
	  // shift the primary by 180 degrees. Lattice seems to do this
	  float ns_phase = 1/(fout * 1e6) * 0.5;
	  params.primary_cphase = ns_phase * (fvco * 1e6);
	}
      }
    }
  }
}

void calc_pll_params_highres(pll_params &params, float input, float output){
  float error = std::numeric_limits<float>::max();
  for(int input_div=1;input_div <= 128; input_div++){

    float fpfd = input / (float)input_div;
    if(fpfd < PFD_MIN || fpfd > PFD_MAX)
      continue;
    for(int feedback_div=1;feedback_div <= 80; feedback_div++){
      for(int output_div=1;output_div <= 128; output_div++){
	float fvco = fpfd * (float)feedback_div * (float) output_div;

	if(fvco < VCO_MIN || fvco > VCO_MAX)
	  continue;
	float ffeedback = fvco / (float) output_div;
	if(ffeedback < OUTPUT_MIN || ffeedback > OUTPUT_MAX)
	  continue;
	for(int secondary_div = 1; secondary_div <= 128; secondary_div++){
	  float fout = fvco / (float) secondary_div;
	  if(fabsf(fout - output) < error ||
	     (fabsf(fout-output) == error && fabsf(fvco - 600) < fabsf(params.fvco - 600))){
	    error = fabsf(fout-output);
	    params.mode = pll_mode::HIGHRES;
	    params.refclk_div = input_div;
	    params.feedback_div = feedback_div;
	    params.output_div = output_div;
	    params.secondary[0].div = secondary_div;
	    params.secondary[0].enabled = true;
	    params.secondary[0].freq = fout;
	    params.fout = fout;
	    params.fvco = fvco;
	  }
	}
      }
    }
  }
}

void generate_secondary_output(pll_params &params, int channel, string name, float frequency, float phase){
  int div = params.fvco/frequency;
  float freq = params.fvco/div;
  cout << "sdiv " << div << endl;

  float ns_shift = 1/(freq * 1e6) * phase /  360.0;
  float phase_count = ns_shift * (params.fvco * 1e6);
  int cphase = (int) phase_count;
  int fphase = (int) ((phase_count - cphase) * 8);

  float ns_actual = 1/(params.fvco * 1e6) * (cphase + fphase/8.0);
  float phase_shift = 360 * ns_actual/ (1/(freq * 1e6));

  params.secondary[channel].enabled = true;
  params.secondary[channel].div = div;
  params.secondary[channel].freq = freq;
  params.secondary[channel].phase = phase_shift;
  params.secondary[channel].cphase = cphase + params.primary_cphase;
  params.secondary[channel].fphase = fphase;
  params.secondary[channel].name = name;
}

void write_pll_config(const pll_params & params, const string &name, ofstream& file)
{
  file << "// diamond 3.7 accepts this PLL\n";
  file << "// diamond 3.8-3.9 is untested\n";
  file << "// diamond 3.10 or higher is likely to abort with error about unable to use feedback signal\n";
  file << "// cause of this could be from wrong CPHASE/FPHASE parameters\n";
  file << "module " << name << "\n(\n";
  if(params.reset)
    file << "    input reset, // 0:inactive, 1:reset\n";
  if(params.standby)
    file << "    input standby, // 0:inactive, 1:standby\n";
  if(params.dynamic)
  {
    file << "    input [1:0] phasesel, // clkout[] index affected by dynamic phase shift (except clkfb), 5 ns min before apply\n";
    file << "    input phasedir, // 0:delayed (lagging), 1:advence (leading), 5 ns min before apply\n";
    file << "    input phasestep, // 45 deg step, high for 5 ns min, falling edge = apply\n";
    file << "    input phaseloadreg, // high for 10 ns min, falling edge = apply\n";
  }
  file << "    input " << params.clkin_name << ", // " << params.clkin_frequency << " MHz, 0 deg\n";
  file << "    output " << params.clkout0_name << ", // " << params.fout << " MHz, 0 deg\n";

  for(int i=0;i<3;i++){
    if(!(i==0 && params.mode == pll_mode::HIGHRES) && params.secondary[i].enabled){
      file << "    output " << params.secondary[i].name << ", // " << params.secondary[i].freq << " MHz, " << params.secondary[i].phase << " deg\n";
    }
  }

  file << "    output locked\n";
  file << ");\n";
  if(params.internal_feedback)
    file << "wire clkfb;\n";
  if(params.dynamic)
  {
    file << "wire [1:0] phasesel_hw;\n";
    file << "assign phasesel_hw = phasesel - 1;\n";
  }
  file << "(* FREQUENCY_PIN_CLKI=\"" << params.clkin_frequency << "\" *)\n";
  file << "(* FREQUENCY_PIN_CLKOP=\"" << params.fout << "\" *)\n";
  if(params.secondary[0].enabled)
    file << "(* FREQUENCY_PIN_CLKOS=\"" << params.secondary[0].freq << "\" *)\n";
  if(params.secondary[1].enabled)
    file << "(* FREQUENCY_PIN_CLKOS2=\"" << params.secondary[1].freq << "\" *)\n";
  if(params.secondary[2].enabled)
    file << "(* FREQUENCY_PIN_CLKOS3=\"" << params.secondary[2].freq << "\" *)\n";
  file << "(* ICP_CURRENT=\"12\" *) (* LPF_RESISTOR=\"8\" *) (* MFG_ENABLE_FILTEROPAMP=\"1\" *) (* MFG_GMCREF_SEL=\"2\" *)\n";
  file << "EHXPLLL #(\n";
  file << "        .PLLRST_ENA(\"" << (params.reset ? "EN" :"DIS") << "ABLED\"),\n";
  file << "        .INTFB_WAKE(\"" << (params.internal_feedback_wake ? "EN" :"DIS") << "ABLED\"),\n";
  file << "        .STDBY_ENABLE(\"" << (params.standby ? "EN" :"DIS") << "ABLED\"),\n";
  file << "        .DPHASE_SOURCE(\"" << (params.dynamic ? "EN" :"DIS") << "ABLED\"),\n";
  file << "        .OUTDIVIDER_MUXA(\"DIVA\"),\n";
  file << "        .OUTDIVIDER_MUXB(\"DIVB\"),\n";
  file << "        .OUTDIVIDER_MUXC(\"DIVC\"),\n";
  file << "        .OUTDIVIDER_MUXD(\"DIVD\"),\n";
  file << "        .CLKI_DIV(" << params.refclk_div <<"),\n";
  file << "        .CLKOP_ENABLE(\"ENABLED\"),\n";
  file << "        .CLKOP_DIV(" << params.output_div << "),\n";
  file << "        .CLKOP_CPHASE(" << params.primary_cphase << "),\n";
  file << "        .CLKOP_FPHASE(0),\n";
  if(params.secondary[0].enabled){
    file << "        .CLKOS_ENABLE(\"ENABLED\"),\n";
    file << "        .CLKOS_DIV(" << params.secondary[0].div << "),\n";
    file << "        .CLKOS_CPHASE(" << params.secondary[0].cphase << "),\n";
    file << "        .CLKOS_FPHASE(" << params.secondary[0].fphase << "),\n";
  }
  if(params.secondary[1].enabled){
    file << "        .CLKOS2_ENABLE(\"ENABLED\"),\n";
    file << "        .CLKOS2_DIV(" << params.secondary[1].div << "),\n";
    file << "        .CLKOS2_CPHASE(" << params.secondary[1].cphase << "),\n";
    file << "        .CLKOS2_FPHASE(" << params.secondary[1].fphase << "),\n";
  }
  if(params.secondary[2].enabled){
    file << "        .CLKOS3_ENABLE(\"ENABLED\"),\n";
    file << "        .CLKOS3_DIV(" << params.secondary[2].div << "),\n";
    file << "        .CLKOS3_CPHASE(" << params.secondary[2].cphase << "),\n";
    file << "        .CLKOS3_FPHASE(" << params.secondary[2].fphase << "),\n";
  }
  if(params.internal_feedback)
    file << "        .FEEDBK_PATH(\"INT_" <<  params.feedback_name[params.feedback_clkout] << "\"),\n";
  else
    file << "        .FEEDBK_PATH(\"CLK" <<  params.feedback_name[params.feedback_clkout] << "\"),\n";
  file << "        .CLKFB_DIV(" << params.feedback_div << ")\n";
  file << "    ) pll_i (\n";
  if(params.reset)
    file << "        .RST(reset),\n";
  else
    file << "        .RST(1'b0),\n";
  if(params.standby)
    file << "        .STDBY(standby),\n";
  else
    file << "        .STDBY(1'b0),\n";
  file << "        .CLKI(" << params.clkin_name << "),\n";
  file << "        .CLKOP(" << params.clkout0_name << "),\n";
  if(params.secondary[0].enabled){
    if(params.mode == pll_mode::HIGHRES)
      file << "        .CLKOS(" << params.clkout0_name << "),\n";
    else
      file << "        .CLKOS(" << params.secondary[0].name << "),\n";
  }
  if(params.secondary[1].enabled){
    file << "        .CLKOS2(" << params.secondary[1].name << "),\n";
  }
  if(params.secondary[2].enabled){
    file << "        .CLKOS3(" << params.secondary[2].name << "),\n";
  }
  if(params.internal_feedback)
  {
    file << "        .CLKFB(clkfb),\n";
    file << "        .CLKINTFB(clkfb),\n";
  }
  else
  {
    file << "        .CLKFB(" <<  params.feedback_wname[params.feedback_clkout] << "),\n";
    file << "        .CLKINTFB(),\n";
  }
  if(params.dynamic)
  {
    file << "        .PHASESEL0(phasesel_hw[0]),\n";
    file << "        .PHASESEL1(phasesel_hw[1]),\n";
    file << "        .PHASEDIR(phasedir),\n";
    file << "        .PHASESTEP(phasestep),\n";
    file << "        .PHASELOADREG(phaseloadreg),\n";
  }
  else
  {
    file << "        .PHASESEL0(1'b0),\n";
    file << "        .PHASESEL1(1'b0),\n";
    file << "        .PHASEDIR(1'b1),\n";
    file << "        .PHASESTEP(1'b1),\n";
    file << "        .PHASELOADREG(1'b1),\n";
  }
  file << "        .PLLWAKESYNC(1'b0),\n";
  file << "        .ENCLKOP(1'b0),\n";
  file << "        .LOCK(locked)\n";
  file << "	);\n";
  file << "endmodule\n";
}


#include <pokerstove/penum/ShowdownEnumerator.h>

#include <iostream>
#include <vector>

#include <boost/program_options.hpp>

using namespace pokerstove;
namespace po = boost::program_options;
using namespace std;

int main(int argc, char** argv)
{
    po::options_description desc("ps-eval, a poker hand evaluator\n");

    desc.add_options()("help,?", "produce help message")(
        "game,g", po::value<string>()->default_value("h"),
        "game to use for evaluation")("board,b", po::value<string>(),
                                      "community cards for he/o/o8")("hand,h", po::value<vector<string>>(),
                                                                     "a hand for evaluation")("quiet,q", "produces no output");

    // make hand a positional argument
    po::positional_options_description p;
    p.add("hand", -1);

    po::variables_map vm;
    po::store(po::command_line_parser(argc, argv).style(po::command_line_style::unix_style).options(desc).positional(p).run(), vm);
    po::notify(vm);

    // check for help
    if (vm.count("help") || argc == 1)
    {
        cout << desc << endl;
        return 1;
    }

    // extract the options
    string game = vm["game"].as<string>();
    string board = vm.count("board") ? vm["board"].as<string>() : "";
    vector<string> hands = vm["hand"].as<vector<string>>();

    bool quiet = vm.count("quiet") > 0;

    // allocate evaluator and create card distributions
    boost::shared_ptr<PokerHandEvaluator> evaluator = PokerHandEvaluator::alloc(game);
    vector<CardDistribution> handDists;
    for (const string& hand : hands)
    {
        handDists.emplace_back();
        handDists.back().parse(hand);
    }

    // fill with random if necessary
    if (handDists.size() == 1)
    {
        handDists.emplace_back();
        handDists.back().fill(evaluator->handSize());
    }

    // calcuate the results and print them
    ShowdownEnumerator showdown;
    vector<EquityResult> results = showdown.calculateEquity(handDists, CardSet(board), evaluator);

    double total = 0.0;
    for (const EquityResult& result : results)
    {
        total += result.winShares + result.tieShares;
    }

    if (!quiet)
    {
        for (size_t i = 0; i < results.size(); ++i)
        {
            double equity = (results[i].winShares + results[i].tieShares) / total;
            string handDesc = (i < hands.size()) ? "The hand " + hands[i] : "A random hand";
            cout << handDesc << " has " << equity * 100. << " % equity (" << results[i].str() << ")" << endl;
        }
    }
}


/*******************************************************************************
 * Copyright 2013-2014 Sebastian Niemann <niemann@sra.uni-hannover.de>.
 *
 * Licensed under the MIT License (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://opensource.org/licenses/MIT
 *
 * Developers:
 *   Sebastian Niemann - Lead developer
 *   Daniel Kiechle - Unit testing
 ******************************************************************************/
#include <Expected.hpp>
using armadilloJava::Expected;

#include <iostream>
using std::cout;
using std::endl;

#include <algorithm>
using std::min;

#include <utility>
using std::pair;

#include <armadillo>
using arma::Mat;
using arma::Row;

#include <InputClass.hpp>
using armadilloJava::InputClass;

#include <Input.hpp>
using armadilloJava::Input;

namespace armadilloJava {
  class ExpectedInPlaceGenMatRowIndGenRowVec : public Expected {
    public:
      ExpectedInPlaceGenMatRowIndGenRowVec() {
        cout << "Compute ExpectedInPlaceGenMatRowIndGenRowVec(): " << endl;

          vector<vector<pair<string, void*>>> inputs = Input::getTestParameters({
            InputClass::GenMat,
            InputClass::RowInd,
            InputClass::GenRowVec
          });

          for (vector<pair<string, void*>> input : inputs) {
            _fileSuffix = "";

            int n = 0;
            for (pair<string, void*> value : input) {
              switch (n) {
                case 0:
                  _fileSuffix += value.first;
                  _genMat = *static_cast<Mat<double>*>(value.second);
                  break;
                case 1:
                  _fileSuffix += "," + value.first;
                  _rowInd = *static_cast<int*>(value.second);
                  break;
                case 2:
                  _fileSuffix += "," + value.first;
                  _genRowVec = *static_cast<Row<double>*>(value.second);
                  break;
              }
              ++n;
            }

            cout << "Using input: " << _fileSuffix << endl;

            _copyOfGenMat = _genMat;
            _copyOfRowInd = _rowInd;
            _copyOfGenRowVec = _genRowVec;

            expectedMatDiagEqual();

            _genMat = _copyOfGenMat;
            _rowInd = _copyOfRowInd;
            _genRowVec = _copyOfGenRowVec;
            expectedMatDiagPlus();

            _genMat = _copyOfGenMat;
            _rowInd = _copyOfRowInd;
            _genRowVec = _copyOfGenRowVec;
            expectedMatDiagMinus();

            _genMat = _copyOfGenMat;
            _rowInd = _copyOfRowInd;
            _genRowVec = _copyOfGenRowVec;
            expectedMatDiagElemTimes();

            _genMat = _copyOfGenMat;
            _rowInd = _copyOfRowInd;
            _genRowVec = _copyOfGenRowVec;
            expectedMatDiagElemDivide();

            _genMat = _copyOfGenMat;
            _rowInd = _copyOfRowInd;
            _genRowVec = _copyOfGenRowVec;
            expectedMatRowEqual();

            _genMat = _copyOfGenMat;
            _rowInd = _copyOfRowInd;
            _genRowVec = _copyOfGenRowVec;
            expectedMatRowPlus();

            _genMat = _copyOfGenMat;
            _rowInd = _copyOfRowInd;
            _genRowVec = _copyOfGenRowVec;
            expectedMatRowMinus();

            _genMat = _copyOfGenMat;
            _rowInd = _copyOfRowInd;
            _genRowVec = _copyOfGenRowVec;
            expectedMatRowElemTimes();

            _genMat = _copyOfGenMat;
            _rowInd = _copyOfRowInd;
            _genRowVec = _copyOfGenRowVec;
            expectedMatRowElemDivide();
          }

          cout << "done." << endl;
        }

    protected:
      Mat<double> _genMat;
      Mat<double> _copyOfGenMat;

      int _rowInd;
      int _copyOfRowInd;

      Row<double> _genRowVec;
      Row<double> _copyOfGenRowVec;

      void expectedMatDiagEqual() {
        if(_rowInd >= _genMat.n_rows) {
          return;
        }

        if (_genRowVec.n_elem != min(_genMat.n_rows - _rowInd, _genMat.n_cols)) {
          return;
        }

        cout << "- Compute expectedMatDiagEqual() ... ";

        _genMat.diag(-_rowInd) = _genRowVec;
        save<double>("Mat.diagSubEqual", _genMat);

        cout << "done." << endl;
      }

      void expectedMatDiagPlus() {
        if(_rowInd >= _genMat.n_rows) {
          return;
        }

        if (_genRowVec.n_elem != min(_genMat.n_rows - _rowInd, _genMat.n_cols)) {
          return;
        }

        cout << "- Compute expectedMatDiagPlus() ... ";

        _genMat.diag(-_rowInd) += _genRowVec;
        save<double>("Mat.diagSubPlus", _genMat);

        cout << "done." << endl;
      }

      void expectedMatDiagMinus() {
        if(_rowInd >= _genMat.n_rows) {
          return;
        }

        if (_genRowVec.n_elem != min(_genMat.n_rows - _rowInd, _genMat.n_cols)) {
          return;
        }

        cout << "- Compute expectedMatDiagMinus() ... ";

        _genMat.diag(-_rowInd) -= _genRowVec;
        save<double>("Mat.diagSubMinus", _genMat);

        cout << "done." << endl;
      }

      void expectedMatDiagElemTimes() {
        if(_rowInd >= _genMat.n_rows) {
          return;
        }

        if (_genRowVec.n_elem != min(_genMat.n_rows - _rowInd, _genMat.n_cols)) {
          return;
        }

        cout << "- Compute expectedMatDiagElemTimes() ... ";

        _genMat.diag(-_rowInd) %= _genRowVec;
        save<double>("Mat.diagSubElemTimes", _genMat);

        cout << "done." << endl;
      }

      void expectedMatDiagElemDivide() {
        if(_rowInd >= _genMat.n_rows) {
          return;
        }

        if (_genRowVec.n_elem != min(_genMat.n_rows - _rowInd, _genMat.n_cols)) {
          return;
        }

        cout << "- Compute expectedMatDiagElemDivide() ... ";

        _genMat.diag(-_rowInd) /= _genRowVec;
        save<double>("Mat.diagSubElemDivide", _genMat);

        cout << "done." << endl;
      }

      void expectedMatRowEqual() {
        if(_rowInd >= _genMat.n_rows) {
          return;
        }

        if(_genMat.n_cols != _genRowVec.n_cols) {
          return;
        }

        cout << "- Compute expectedMatRowEqual() ... ";

        _genMat.row(_rowInd) = _genRowVec;
        save<double>("Mat.rowEqual", _genMat);

        cout << "done." << endl;
      }

      void expectedMatRowPlus() {
        if(_rowInd >= _genMat.n_rows) {
          return;
        }

        if(_genMat.n_cols != _genRowVec.n_cols) {
          return;
        }

        cout << "- Compute expectedMatRowPlus() ... ";

        _genMat.row(_rowInd) += _genRowVec;
        save<double>("Mat.rowPlus", _genMat);

        cout << "done." << endl;
      }

      void expectedMatRowMinus() {
        if(_rowInd >= _genMat.n_rows) {
          return;
        }

        if(_genMat.n_cols != _genRowVec.n_cols) {
          return;
        }

        cout << "- Compute expectedMatRowMinus() ... ";

        _genMat.row(_rowInd) -= _genRowVec;
        save<double>("Mat.rowMinus", _genMat);

        cout << "done." << endl;
      }

      void expectedMatRowElemTimes() {
        if(_rowInd >= _genMat.n_rows) {
          return;
        }

        if(_genMat.n_cols != _genRowVec.n_cols) {
          return;
        }

        cout << "- Compute expectedMatRowElemTimes() ... ";

        _genMat.row(_rowInd) %= _genRowVec;
        save<double>("Mat.rowElemTimes", _genMat);

        cout << "done." << endl;
      }

      void expectedMatRowElemDivide() {
        if(_rowInd >= _genMat.n_rows) {
          return;
        }

        if(_genMat.n_cols != _genRowVec.n_cols) {
          return;
        }

        cout << "- Compute expectedMatRowElemDivide() ... ";

        _genMat.row(_rowInd) /= _genRowVec;
        save<double>("Mat.rowElemDivide", _genMat);

        cout << "done." << endl;
      }
  };
}


//
// Created by kerin on 2019-02-28.
//

#ifndef LEMMA_EIGEN_UTILS_HPP
#define LEMMA_EIGEN_UTILS_HPP

#include "tools/eigen3.3/Dense"

#include <iostream>
#include <cmath>
#include <map>
#include <vector>
#include <string>
#include <set>

#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/device/file.hpp>
#include <boost/iostreams/filter/gzip.hpp>

namespace boost_io = boost::iostreams;

namespace EigenUtils {

template <typename EigenMat>
void center_matrix(EigenMat& M);

template <typename EigenMat>
void scale_matrix_and_remove_constant_cols(EigenMat &M,
                                           long &n_cols,
                                           std::vector<std::string> &col_names);

template <typename EigenMat>
void scale_matrix_and_remove_constant_cols(EigenMat &M){
	long n_cols = M.cols();
	std::vector<std::string> placeholder;
	for (long ii = 0; ii < n_cols; ii++) {
		placeholder.emplace_back("tmp");
	}
	scale_matrix_and_remove_constant_cols(M, n_cols, placeholder);
}

Eigen::MatrixXf solve(const Eigen::MatrixXf &A, const Eigen::MatrixXf &b);

Eigen::MatrixXd solve(const Eigen::MatrixXd &A, const Eigen::MatrixXd &b);

Eigen::MatrixXd subset_matrix(const Eigen::MatrixXd &orig, const std::vector<int> &valid_points);

template <typename EigenMat>
void read_matrix(const std::string& filename,
                 EigenMat& M,
                 std::vector< std::string >& col_names,
                 std::map<long, bool>& incomplete_row );

template <typename EigenMat>
void read_matrix(const std::string& filename,
                 EigenMat& M,
                 std::vector< std::string >& col_names);

template <typename EigenMat>
void read_matrix(const std::string& filename,
                 EigenMat& M);

void read_matrix_and_skip_cols(const std::string &filename,
                               const int& n_skip_cols,
                               Eigen::MatrixXd &M,
                               std::vector <std::string> &col_names);

template <typename EigenMat>
void write_matrix(boost_io::filtering_ostream& outf,
                  EigenMat& M,
                  std::vector<std::string>& col_names,
                  std::vector<std::string>& row_names);

Eigen::MatrixXd project_out_covars(Eigen::Ref<Eigen::MatrixXd> rhs,
                                   const Eigen::Ref<const Eigen::MatrixXd> &C,
                                   const Eigen::Ref<const Eigen::MatrixXd> &CtC_inv);
}

#endif //LEMMA_EIGEN_UTILS_HPP


//          Copyright Gavin Band 2008 - 2012.
// 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 <Eigen/Core>
#include "components/RelatednessComponent/LapackEigenDecomposition.hpp"
#include <iostream>
#include "config/config.hpp"
#if HAVE_CLAPACK
	#include "clapack.h"
#else

extern "C" {
	/*
	*  function dsyev_ (see http://www.netlib.org/clapack/what/double/dsyev.c)

	-- LAPACK driver routine (version 3.0) --
		Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd.,
		Courant Institute, Argonne National Lab, and Rice University
		June 30, 1999

	*
	* Arguments:
	*
	JOBZ	(input) CHARACTER*1
			= 'N':	Compute eigenvalues only;
			= 'V':	Compute eigenvalues and eigenvectors.

	UPLO	(input) CHARACTER*1
			= 'U':	Upper triangle of A is stored;
			= 'L':	Lower triangle of A is stored.

	N		(input) INTEGER
			The order of the matrix A.	N >= 0.

	A		(input/output) DOUBLE PRECISION array, dimension (LDA, N)
			On entry, the symmetric matrix A.  If UPLO = 'U', the
			leading N-by-N upper triangular part of A contains the
			upper triangular part of the matrix A.	If UPLO = 'L',
			the leading N-by-N lower triangular part of A contains
			the lower triangular part of the matrix A.
			On exit, if JOBZ = 'V', then if INFO = 0, A contains the
			orthonormal eigenvectors of the matrix A.
			If JOBZ = 'N', then on exit the lower triangle (if UPLO='L')
			or the upper triangle (if UPLO='U') of A, including the
			diagonal, is destroyed.

	LDA		(input) INTEGER
			The leading dimension of the array A.  LDA >= max(1,N).

	W		(output) DOUBLE PRECISION array, dimension (N)
			If INFO = 0, the eigenvalues in ascending order.

	WORK	(workspace/output) DOUBLE PRECISION array, dimension (LWORK)
			On exit, if INFO = 0, WORK(1) returns the optimal LWORK.

	LWORK	(input) INTEGER
			The length of the array WORK.  LWORK >= max(1,3*N-1).
			For optimal efficiency, LWORK >= (NB+2)*N,
			where NB is the blocksize for DSYTRD returned by ILAENV.

			If LWORK = -1, then a workspace query is assumed; the routine
			only calculates the optimal size of the WORK array, returns
			this value as the first entry of the WORK array, and no error
			message related to LWORK is issued by XERBLA.

	INFO	(output) INTEGER
			= 0:  successful exit
			< 0:  if INFO = -i, the i-th argument had an illegal value
			> 0:  if INFO = i, the algorithm failed to converge; i
				  off-diagonal elements of an intermediate tridiagonal
				  form did not converge to zero.
	*/
	int dsyev_(char *jobz, char *uplo, int *n, double *a,
		 int *lda, double *w, double *work, int *lwork,
		int *info) ;
}

#endif

#include <vector>

namespace lapack
{
	void compute_eigendecomposition( Eigen::MatrixXd const& matrix, Eigen::VectorXd* eigenvalues, Eigen::MatrixXd* eigenvectors ) {
		int N = matrix.cols() ;
		assert( matrix.rows() == N ) ;
		*eigenvectors = matrix ;
		int LDA = eigenvectors->outerStride() ;
		int LWORK = -1 ;
		int info = 0 ;
		char JOBZ = 'V' ;
		char UPLO = 'L' ;
		double work_size ;
		dsyev_( &JOBZ, &UPLO, &N, 0, &LDA, 0, &work_size, &LWORK, &info ) ;
		LWORK = work_size + 32 ;
		std::vector< double > workspace( work_size ) ;
		dsyev_( &JOBZ, &UPLO, &N, eigenvectors->data(), &LDA, eigenvalues->data(), &workspace[0], &LWORK, &info ) ;
		if( info != 0 ) {
			std::cerr << "!! compute_eigendecomposition(): info = " << info << ".\n" ;
			if( info < 0 ) {
				assert( 0 ) ;
			}
		}
	}
}

#if !HAVE_CLAPACK
extern "C" {
/*
 *	function dsyevr_ (see http://www.netlib.org/clapack/what/double/dsyevr.c)

	Arguments
	=========

	JOBZ	(input) CHARACTER*1
			= 'N':	Compute eigenvalues only;
			= 'V':	Compute eigenvalues and eigenvectors.

	RANGE	(input) CHARACTER*1
			= 'A': all eigenvalues will be found.
			= 'V': all eigenvalues in the half-open interval (VL,VU]
				   will be found.
			= 'I': the IL-th through IU-th eigenvalues will be found.
   ********* For RANGE = 'V' or 'I' and IU - IL < N - 1, DSTEBZ and
   ********* DSTEIN are called

	UPLO	(input) CHARACTER*1
			= 'U':	Upper triangle of A is stored;
			= 'L':	Lower triangle of A is stored.

	N		(input) INTEGER
			The order of the matrix A.	N >= 0.

	A		(input/output) DOUBLE PRECISION array, dimension (LDA, N)
			On entry, the symmetric matrix A.  If UPLO = 'U', the
			leading N-by-N upper triangular part of A contains the
			upper triangular part of the matrix A.	If UPLO = 'L',
			the leading N-by-N lower triangular part of A contains
			the lower triangular part of the matrix A.
			On exit, the lower triangle (if UPLO='L') or the upper
			triangle (if UPLO='U') of A, including the diagonal, is
			destroyed.

	LDA		(input) INTEGER
			The leading dimension of the array A.  LDA >= max(1,N).

	VL		(input) DOUBLE PRECISION
	VU		(input) DOUBLE PRECISION
			If RANGE='V', the lower and upper bounds of the interval to
			be searched for eigenvalues. VL < VU.
			Not referenced if RANGE = 'A' or 'I'.

	IL		(input) INTEGER
	IU		(input) INTEGER
			If RANGE='I', the indices (in ascending order) of the
			smallest and largest eigenvalues to be returned.
			1 <= IL <= IU <= N, if N > 0; IL = 1 and IU = 0 if N = 0.
			Not referenced if RANGE = 'A' or 'V'.

	ABSTOL	(input) DOUBLE PRECISION
			The absolute error tolerance for the eigenvalues.
			An approximate eigenvalue is accepted as converged
			when it is determined to lie in an interval [a,b]
			of width less than or equal to

					ABSTOL + EPS *	 max( |a|,|b| ) ,

			where EPS is the machine precision.	 If ABSTOL is less than
			or equal to zero, then	EPS*|T|	 will be used in its place,
			where |T| is the 1-norm of the tridiagonal matrix obtained
			by reducing A to tridiagonal form.

			See "Computing Small Singular Values of Bidiagonal Matrices
			with Guaranteed High Relative Accuracy," by Demmel and
			Kahan, LAPACK Working Note #3.

			If high relative accuracy is important, set ABSTOL to
			DLAMCH( 'Safe minimum' ).  Doing so will guarantee that
			eigenvalues are computed to high relative accuracy when
			possible in future releases.  The current code does not
			make any guarantees about high relative accuracy, but
			furutre releases will. See J. Barlow and J. Demmel,
			"Computing Accurate Eigensystems of Scaled Diagonally
			Dominant Matrices", LAPACK Working Note #7, for a discussion
			of which matrices define their eigenvalues to high relative
			accuracy.

	M		(output) INTEGER
			The total number of eigenvalues found.	0 <= M <= N.
			If RANGE = 'A', M = N, and if RANGE = 'I', M = IU-IL+1.

	W		(output) DOUBLE PRECISION array, dimension (N)
			The first M elements contain the selected eigenvalues in
			ascending order.

	Z		(output) DOUBLE PRECISION array, dimension (LDZ, max(1,M))
			If JOBZ = 'V', then if INFO = 0, the first M columns of Z
			contain the orthonormal eigenvectors of the matrix A
			corresponding to the selected eigenvalues, with the i-th
			column of Z holding the eigenvector associated with W(i).
			If JOBZ = 'N', then Z is not referenced.
			Note: the user must ensure that at least max(1,M) columns are
			supplied in the array Z; if RANGE = 'V', the exact value of M
			is not known in advance and an upper bound must be used.

	LDZ		(input) INTEGER
			The leading dimension of the array Z.  LDZ >= 1, and if
			JOBZ = 'V', LDZ >= max(1,N).

	ISUPPZ	(output) INTEGER array, dimension ( 2*max(1,M) )
			The support of the eigenvectors in Z, i.e., the indices
			indicating the nonzero elements in Z. The i-th eigenvector
			is nonzero only in elements ISUPPZ( 2*i-1 ) through
			ISUPPZ( 2*i ).
   ********* Implemented only for RANGE = 'A' or 'I' and IU - IL = N - 1

	WORK	(workspace/output) DOUBLE PRECISION array, dimension (LWORK)
			On exit, if INFO = 0, WORK(1) returns the optimal LWORK.

	LWORK	(input) INTEGER
			The dimension of the array WORK.  LWORK >= max(1,26*N).
			For optimal efficiency, LWORK >= (NB+6)*N,
			where NB is the max of the blocksize for DSYTRD and DORMTR
			returned by ILAENV.

			If LWORK = -1, then a workspace query is assumed; the routine
			only calculates the optimal size of the WORK array, returns
			this value as the first entry of the WORK array, and no error
			message related to LWORK is issued by XERBLA.

	IWORK	(workspace/output) INTEGER array, dimension (LIWORK)
			On exit, if INFO = 0, IWORK(1) returns the optimal LWORK.

	LIWORK	(input) INTEGER
			The dimension of the array IWORK.  LIWORK >= max(1,10*N).

			If LIWORK = -1, then a workspace query is assumed; the
			routine only calculates the optimal size of the IWORK array,
			returns this value as the first entry of the IWORK array, and
			no error message related to LIWORK is issued by XERBLA.

	INFO	(output) INTEGER
			= 0:  successful exit
			< 0:  if INFO = -i, the i-th argument had an illegal value
			> 0:  Internal error
*/
	int dsyevr_(
		char *jobz, char *range, char *uplo, int *n,
		double *a, int *lda,
		double *vl, double *vu,
		int *il,
		int *iu,
		double *abstol,
		int *m, double *w,
		double *z__, int *ldz,
		int *isuppz,
		double *work, int *lwork, int *iwork, int *liwork, int *info
	) ;

	double dlamch_( char *cmach ) ;
}
#endif

namespace lapack {
	void compute_eigendecomposition(
		Eigen::MatrixXd const& input_matrix,
		Eigen::VectorXd* eigenvalues,
		Eigen::MatrixXd* eigenvectors,
		double minimum_eigenvalue,
		double maximum_eigenvalue
	) {
		assert( input_matrix.rows() == input_matrix.cols() ) ;
		assert( eigenvalues ) ;
		assert( eigenvectors ) ;
		assert( minimum_eigenvalue == minimum_eigenvalue ) ;
		assert( maximum_eigenvalue == maximum_eigenvalue ) ;
		assert( minimum_eigenvalue < maximum_eigenvalue ) ;

		Eigen::MatrixXd matrix = input_matrix ;
		int N = matrix.cols() ;
		assert( matrix.rows() == N ) ;
		int M_LDA = matrix.outerStride() ;
		int EV_LDA = eigenvectors->outerStride() ;
		int info = 0 ;
		char RANGE = 'V' ;
		char JOBZ = 'V' ;
		char UPLO = 'L' ;
		double ABSTOL = dlamch_( const_cast< char* >( "Safe minimum" ) ) ;
		int number_of_eigenvalues ; // numbers of eigenvectors that are computed

		// set up workspaces
		int LWORK = -1 ;
		double work_size ;
		int LIWORK = -1 ;
		int iwork_size ;

		dsyevr_(
			&JOBZ, &RANGE, &UPLO, &N,
			matrix.data(), &M_LDA,
			&minimum_eigenvalue, &maximum_eigenvalue,
			0, 0,
			&ABSTOL,
			&number_of_eigenvalues, eigenvalues->data(),
			eigenvectors->data(), &EV_LDA,
			0,
			&work_size, &LWORK, &iwork_size, &LIWORK, &info
		) ;
		assert( info == 0 ) ;
		LWORK = work_size + 32 ;
		LIWORK = iwork_size + 32 ;
		std::vector< double > workspace( work_size ) ;
		std::vector< int > iworkspace( iwork_size ) ;

		// Now compute the decomposition.
		eigenvalues->resize( input_matrix.cols() ) ;
		eigenvectors->resize( input_matrix.cols(), input_matrix.cols() ) ;

		dsyevr_(
			&JOBZ, &RANGE, &UPLO, &N,
			const_cast< double* >( matrix.data() ), &M_LDA,
			&minimum_eigenvalue, &maximum_eigenvalue,
			0, 0,
			&ABSTOL,
			&number_of_eigenvalues, eigenvalues->data(),
			eigenvectors->data(), &EV_LDA,
			0,
			&workspace[0],
			&LWORK,
			&iworkspace[0],
			&LIWORK,
			&info
		) ;
		if( info != 0 ) {
			std::cerr << "!! compute_eigendecomposition(): info = " << info << ".\n" ;
			if( info < 0 ) {
				assert( 0 ) ;
			}
		} else {
			eigenvalues->resize( number_of_eigenvalues ) ;
			eigenvectors->resize( eigenvectors->rows(), number_of_eigenvalues ) ;
		}
	}
}

namespace lapack {
	void compute_eigendecomposition(
		Eigen::MatrixXd const& input_matrix,
		Eigen::VectorXd* eigenvalues,
		Eigen::MatrixXd* eigenvectors,
		int number_of_eigenvalues
	) {
		assert( input_matrix.rows() == input_matrix.cols() ) ;
		assert( number_of_eigenvalues > 0 ) ;
		assert( eigenvalues ) ;
		assert( eigenvectors ) ;
		assert( number_of_eigenvalues <= input_matrix.cols() ) ;

		Eigen::MatrixXd matrix = input_matrix ;
		int N = matrix.cols() ;
		assert( matrix.rows() == N ) ;
		int M_LDA = matrix.outerStride() ;
		int EV_LDA = eigenvectors->outerStride() ;
		int info = 0 ;
		char RANGE = 'I' ;
		char JOBZ = 'V' ;
		char UPLO = 'L' ;
		double ABSTOL = dlamch_( const_cast< char* >( "Safe minimum" ) ) ;
		int IL = 1;
		int IU = number_of_eigenvalues + 1 ;

		// set up workspaces
		int LWORK = -1 ;
		double work_size ;
		int LIWORK = -1 ;
		int iwork_size ;

		dsyevr_(
			&JOBZ, &RANGE, &UPLO, &N,
			matrix.data(), &M_LDA,
			0, 0,
			&IL, &IU,
			&ABSTOL,
			&number_of_eigenvalues, eigenvalues->data(),
			eigenvectors->data(), &EV_LDA,
			0,
			&work_size, &LWORK, &iwork_size, &LIWORK, &info
		) ;
		assert( info == 0 ) ;
		LWORK = work_size + 32 ;
		LIWORK = iwork_size + 32 ;
		std::vector< double > workspace( work_size ) ;
		std::vector< int > iworkspace( iwork_size ) ;

		// compute the decomposition
		eigenvalues->resize( number_of_eigenvalues ) ;
		eigenvectors->resize( input_matrix.cols(), number_of_eigenvalues ) ;

		dsyevr_(
			&JOBZ, &RANGE, &UPLO, &N,
			const_cast< double* >( matrix.data() ), &M_LDA,
			0, 0,
			&IL, &IU,
			&ABSTOL,
			&number_of_eigenvalues, eigenvalues->data(),
			eigenvectors->data(), &EV_LDA,
			0,
			&workspace[0],
			&LWORK,
			&iworkspace[0],
			&LIWORK,
			&info
		) ;
		if( info != 0 ) {
			std::cerr << "!! compute_eigendecomposition(): info = " << info << ".\n" ;
			if( info < 0 ) {
				assert( 0 ) ;
			}
		} else {
			eigenvalues->resize( number_of_eigenvalues ) ;
			eigenvectors->resize( eigenvectors->rows(), number_of_eigenvalues ) ;
		}
	}
}


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

  Copyright 2016 NumScale SAS
  Copyright 2016 J.T. Lapreste

  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 <boost/simd/function/round2even.hpp>
#include <boost/simd/function/bits.hpp>
#include <boost/simd/meta/cardinal_of.hpp>
#include <simd_test.hpp>

template <typename T, int N, typename Env>
void test(Env& $)
{
  namespace bs = boost::simd;
  using p_t = bs::pack<T, N>;

  T a1[N], b[N], a2[N], c[N];
  for(int i = 0; i < N; ++i)
  {
    a1[i] = ((i%2) ? T(2*i) : T(-2*i))/T(3);
    a2[i] = T(2*i+1)/T(2);
    b[i] = bs::round2even(a1[i]) ;
    c[i] = bs::round2even(a2[i]) ;
  }
  p_t aa1(&a1[0], &a1[N]);
  p_t aa2(&a2[0], &a2[N]);
  p_t bb (&b[0], &b[N]);
  p_t cc (&c[0], &c[N]);
  STF_IEEE_EQUAL(bs::round2even(aa1), bb);
  STF_IEEE_EQUAL(bs::round2even(aa2), cc);
}

STF_CASE_TPL("Check round2even on pack" , STF_NUMERIC_TYPES)
{
  namespace bs = boost::simd;
  using p_t = bs::pack<T>;
  static const std::size_t N = bs::cardinal_of<p_t>::value;
  test<T, N>($);
  test<T, N/2>($);
  test<T, N*2>($);
}


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

#include <boost/simd/include/functor.hpp>
#include <boost/simd/sdk/meta/float.hpp>
#include <boost/simd/sdk/meta/int_c.hpp>
#include <boost/simd/sdk/meta/double.hpp>
#include <boost/simd/sdk/constant/constant.hpp>

/*!
 * \ingroup boost_simd_constant
 * \defgroup boost_simd_constant_signmask Signmask
 *
 * \par Description
 * Constant Signmask : is a mask with the lone most significand bit set to one
 * (even if the type is unsigned).
 * \par
 * The value of this constant is type dependant. This means that for different
 * types it does not represent the same mathematical number.
 *
 * \par Header file
 *
 * \code
 * #include <nt2/include/functions/signmask.hpp>
 * \endcode
 *
 *
 * \synopsis
 *
 * \code
 * namespace boost::simd
 * {
 *   template <class T,class A0>
 *     meta::call<tag::signmask_(A0)>::type
 *     Signmask();
 * }
 * \endcode
 *
 *
 * \param T template parameter of Signmask
 *
 * \return type T value
 *
 *
**/

namespace boost { namespace simd
{
  namespace tag
  {
    /*!
     * \brief Define the tag Signmask of functor Signmask
     *        in namespace boost::simd::tag for toolbox boost.simd.constant
    **/
    struct Signmask : ext::pure_constant_<Signmask>
    {
      template<class Target, class Dummy=void>
      struct apply : meta::int_c<typename Target::type,0> {};
    };

    template<class T, class Dummy>
    struct  Signmask::apply<boost::dispatch::meta::single_<T>,Dummy>
          : meta::single_<0x80000000UL> {};

    template<class T, class Dummy>
    struct  Signmask::apply<boost::dispatch::meta::double_<T>,Dummy>
          : meta::double_<0x8000000000000000ULL> {};

    template<class T, class Dummy>
    struct  Signmask::apply<boost::dispatch::meta::int8_<T>,Dummy>
          : meta::int_c<boost::simd::int8_t,boost::simd::int8_t(0x80U)> {};

    template<class T, class Dummy>
    struct  Signmask::apply<boost::dispatch::meta::int16_<T>,Dummy>
          : meta::int_c<boost::simd::int16_t,boost::simd::int16_t(0x8000U)> {};

    template<class T, class Dummy>
    struct  Signmask::apply<boost::dispatch::meta::int32_<T>,Dummy>
          : meta::int_c<boost::simd::int32_t,boost::simd::int32_t(0x80000000UL)>
    {};

    template<class T, class Dummy>
    struct  Signmask::apply<boost::dispatch::meta::int64_<T>,Dummy>
          : meta::int_c < boost::simd::int64_t
                        , boost::simd::int64_t(0x8000000000000000ULL)
                        >
    {};
  }

  BOOST_SIMD_CONSTANT_IMPLEMENTATION(boost::simd::tag::Signmask, Signmask)
} }

#include <boost/simd/sdk/constant/common.hpp>

#endif


#ifndef DISCREGRID_COMMON_HPP
#define DISCREGRID_COMMON_HPP

#include <Eigen/Dense>

namespace Discregrid {
#ifdef USE_DOUBLE
typedef double Real;
#else
typedef float Real;
#endif

using Vector3r = Eigen::Matrix<Real, 3, 1, Eigen::DontAlign>;
using Matrix3r = Eigen::Matrix<Real, 3, 3, Eigen::DontAlign>;
using AlignedBox3r = Eigen::AlignedBox<Real, 3>;
}  // namespace Discregrid

#endif


#ifndef ESN_HPP
#define ESN_HPP

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

#ifdef USE_TBB
#include <tbb/mutex.h>
#endif

#include "yaml-cpp/yaml.h"

#include "Reservoir.hpp"
#include "FitnessFunctions.hpp"
#include "ComputeState.hpp"

#include "EchoBay.hpp"
#include "IOUtils.hpp"
#include "DataStorage.hpp"
#include "Readout.hpp"

namespace EchoBay
{
    ArrayBO esn_caller(const Eigen::VectorXd &optParams, YAML::Node confParams,
                       const std::string outputFolder, const DataStorage &store,
                       const bool guessEval, const std::string &computationType,
                       const std::string &matrixFolder);

    Reservoir esn_config(const Eigen::VectorXd &optParams, YAML::Node confParams,
                         const int Nu, const int guess, const std::string folder);

    ArrayBO esn_train(YAML::Node confParams, Reservoir &ESN, const double lambda,
                      const std::string problemType, const std::string fitnessRule,
                      const std::string outputFolder, const DataStorage &store, const bool saveflag,
                      const int guesses);

    ArrayBO esn_compute(YAML::Node confParams, Reservoir &ESN, const DataStorage &store,
                        const std::string &problemType, const std::string &fitnessRule,
                        const int blockStep, Eigen::Ref<MatrixBO> Wout,
                        const std::string &outputFolder, const bool saveflag, const int guesses);

    floatBO esn_compute(const MatrixBO &input_data, const std::string &folder);

    MatrixBO compute_prediction(const int outNr, const int outDimension,
                            const Eigen::Ref<MatrixBO> Wout,
                            Reservoir &ESN, const MatrixBO &src,
                            const Eigen::Ref<const ArrayBO> sampleState);
} // namespace EchoBay

#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/function/scalar/ctz.hpp>
#include <scalar_test.hpp>
#include <boost/simd/detail/dispatch/meta/as_integer.hpp>
#include <boost/simd/constant/inf.hpp>
#include <boost/simd/constant/minf.hpp>
#include <boost/simd/constant/mone.hpp>
#include <boost/simd/constant/nan.hpp>
#include <boost/simd/constant/one.hpp>
#include <boost/simd/constant/zero.hpp>
#include <boost/simd/constant/nbmantissabits.hpp>
#include <boost/simd/constant/signmask.hpp>

STF_CASE_TPL (" ctz real",  STF_IEEE_TYPES)
{
  namespace bs = boost::simd;
  namespace bd = boost::dispatch;
  using bs::ctz;
  using r_t = decltype(ctz(T()));
  // return type conformity test
  STF_EXPR_IS(ctz(T()), bd::as_integer_t<T>);

  // specific values tests
#ifndef BOOST_SIMD_NO_INVALIDS
  STF_EQUAL(ctz(bs::Inf<T>()), r_t(bs::Nbmantissabits<T>()));
  STF_EQUAL(ctz(bs::Minf<T>()), r_t(bs::Nbmantissabits<T>()));
#endif
  STF_EQUAL(ctz(bs::Zero<T>()), r_t(sizeof(T)*8));
  STF_EQUAL(ctz(bs::Signmask<T>()), r_t(sizeof(T)*8-1));
} // end of test for real_

STF_CASE_TPL (" ctz signed_int",  STF_SIGNED_INTEGRAL_TYPES)
{
  namespace bs = boost::simd;
  using bs::ctz;
  using bs::Nbmantissabits;
  using bs::Signmask;

  using r_t = decltype(ctz(T()));

  for(std::size_t j=0; j< (sizeof(T)*CHAR_BIT-1); j++)
  {
  namespace bs = boost::simd;
    // Test 01111 ... 10000b
    T value = ~T(0) & ~((T(1)<<j)-1);
    STF_EQUAL(ctz( value ), r_t(j));
    STF_EQUAL(ctz( T(-value) ), r_t(j));
  }

  STF_EQUAL(ctz(Signmask<T>()) , r_t(sizeof(T)*CHAR_BIT-1) );
}

STF_CASE_TPL(" ctz unsigned_integer", STF_UNSIGNED_INTEGRAL_TYPES )
{
  namespace bs = boost::simd;
  namespace bd = boost::dispatch;
  using bs::ctz;
  using r_t = decltype(ctz(T()));

  // return type conformity test
  STF_EXPR_IS(ctz(T()), bd::as_integer_t<T>);

  // specific values tests
  for(std::size_t j=0; j< sizeof(T)*CHAR_BIT; j++)
  {
  namespace bs = boost::simd;
    // Test 1111 ... 10000b
    T value = ~T(0) & ~((T(1)<<j)-1);
    STF_EQUAL(ctz( value ), r_t(j));
  }
 }


#ifndef SETTINGS_HPP
#define SETTINGS_HPP

#include <boost/lexical_cast.hpp>
#include <clstatphys/ensemble/hybrid_monte_carlo.hpp>
#include <clstatphys/physics/todalattice.hpp>
#include <common/settings_common.hpp>

using EnsemblerOrigin = ensemble::HybridMonteCarlo;
using Hamiltonian = hamiltonian::TodaLattice;

class Ensembler : public EnsemblerOrigin{
public:
  template<class Hamiltonian>
  Ensembler(
            int num_particles, double temperture, double dt_relax,
            double relax_time, int total_accept, int initial_relax_time,
            Hamiltonian hamiltonian)
    : EnsemblerOrigin(num_particles, temperture, dt_relax, relax_time, total_accept),
      initial_relax_time_(initial_relax_time) {hamiltonian_ = hamiltonian;}

  template<class Rand>
  void set_initial_state(std::vector<double> & z, Rand & mt){
    int counter = 0;
    for(int step = 0; step < initial_relax_time_; ++step) EnsemblerOrigin::montecarlo(z, counter, hamiltonian_, mt);
  }

private:
  int initial_relax_time_;
  Hamiltonian hamiltonian_;
}; //end Ensembler definition

struct Settings : public SettingsCommon{
  int total_accept = 10;
  double initial_relax_time = 10;
  double relax_time = 10;
  double dt_relax = 0.1;
  double temperture = 1.0;

  double J = 1.0; //interaction constant;
  double alpha = 1.0; //interaction constant;

  Settings(int argc, char **argv, int & input_counter)
    : SettingsCommon(argc, argv, input_counter)
  {
    set(argc, argv, input_counter);
  }
  Settings() = default;

  inline void set(int argc, char **argv, int & input_counter){
    if (argc > input_counter) total_accept       = boost::lexical_cast<int>(argv[input_counter]);++input_counter;
    if (argc > input_counter) initial_relax_time = boost::lexical_cast<double>(argv[input_counter]);++input_counter;
    if (argc > input_counter) relax_time         = boost::lexical_cast<double>(argv[input_counter]);++input_counter;
    if (argc > input_counter) dt_relax           = boost::lexical_cast<double>(argv[input_counter]);++input_counter;
    if (argc > input_counter) temperture         = boost::lexical_cast<double>(argv[input_counter]);++input_counter;

    if (argc > input_counter) J                  = boost::lexical_cast<double>(argv[input_counter]);++input_counter;
    if (argc > input_counter) alpha              = boost::lexical_cast<double>(argv[input_counter]);++input_counter;
  }

  template <class Dataput>
  inline void declare(Dataput & dataput){
    SettingsCommon::declare(dataput);

    dataput <<  "<<System Depend Settings>> " << std::endl
            <<  "  " << Hamiltonian::name() << std::endl
            <<  "  " << EnsemblerOrigin::name() << std::endl
            <<  "  " << Lattice::name() << std::endl

            << "  Number of step for fianally accept : total_accept = " << total_accept << std::endl
            << "  Relaxtion time for update : relax_time = " << relax_time << std::endl
            << "  Interbal of time development : dt_relax  = " << dt_relax << std::endl
            << "  Temperture : temperture = " << temperture << std::endl

            << "  Coupling constant : J = " << J << std::endl
            << "  Coupling constant : alpha = " << alpha << std::endl;
  }

  Hamiltonian hamiltonian(){
    Lattice lattice_t(Ns);

    std::vector<std::vector<int> > pair_table(
                                              num_particles,
                                              std::vector<int>(N_adj)
                                              );
    lattice_t.create_table(pair_table);

    Hamiltonian hamiltonian_t(
                              num_particles,
                              J,
                              alpha,
                              pair_table,
                              N_adj
                              );
    return hamiltonian_t;
  }

  Ensembler ensembler(){
    Ensembler ensembler_t(
                          num_particles,
                          temperture,
                          dt_relax,
                          relax_time,
                          total_accept,
                          initial_relax_time,
                          hamiltonian()
                          );
    return ensembler_t;
  }

}; //end Settings definition

#endif


#ifndef OCV_CBIR_BOVW_HIST_CREATOR_HPP
#define OCV_CBIR_BOVW_HIST_CREATOR_HPP

#include "bovw.hpp"
#include "features_indexer.hpp"
#include "../arma/type_traits.hpp"

#include <opencv2/core.hpp>

#include <armadillo>

#include <omp.h>
#include <mutex>

/*!
 *  \addtogroup ocv
 *  @{
 */
namespace ocv{

/*!
 *  \addtogroup cbir
 *  @{
 */
namespace cbir{

/**
 *Create histogram of the images associate with the codebook
 *@tparam CodeBook type of the codebook
 *@tparam Feature type of the feature
 *@tparam HistType value type of the histogram
 *@tparam Hist Container type of the histogram
 *@code
 *features_indexer fi("ukbench.h5");
 *arma::Mat<float> code_book;
 *code_book.load("ukbench_code_book", arma::arma_ascii);
 *
 *bovw_hist_creator<float, float> bh(fi);
 *auto const hist = bh.create(code_book);
 *hist.save("ukbench_hist");
 *@endcode
 */
template<typename CodeBook, typename Feature,
         typename HistType = arma::uword,
         template <typename> class Hist = arma::SpMat>
class bovw_hist_creator
{
public:
    explicit bovw_hist_creator(features_indexer const &fi) :
        fi_(fi)
    {
        static_assert(std::is_arithmetic<CodeBook>::value,
                      "CodeBook should be arithmetic type");
        static_assert(std::is_arithmetic<Feature>::value,
                      "Feature should be arithmetic type");

        enum {is_spmat = armd::is_mat<Hist<HistType>>::value};
        enum {is_mat = armd::is_spmat<Hist<HistType>>::value};

        static_assert(is_spmat || is_mat,
                      "Hist should be arma::Mat<CodeBook> or "
                      "arma::SpMat<CodeBook>");
    }

    /**
     * Create the histogram of each image associated with
     * the vocab(code book)
     * @param vocab the code book(vocab)
     * @return histogram of each image(one col per image)
     */
    Hist<HistType> create(arma::Mat<CodeBook> const &vocab) const
    {
        auto const img_size =
                (fi_.get_names_dimension()[0]);
        Hist<HistType> hist(vocab.n_cols, img_size);
        bovw<CodeBook, Hist<HistType>> bv;

#pragma omp parallel for
        for(int i = 0; i < img_size; ++i){
            cv::Mat features;
            {
                std::lock_guard<std::mutex> guard(mutex_);
                fi_.read_image_features(features, i);
            }
            arma::Mat<CodeBook> const vocab_features =
                    to_arma(features,
                            typename std::is_same<CodeBook, Feature>::type());
            auto const shist = bv.describe(vocab_features, vocab);
            {
                std::lock_guard<std::mutex> guard(mutex_);
                hist.col(i) = shist;
            }
        }

        return hist;
    }

private:
    arma::Mat<CodeBook>
    to_arma(cv::Mat &features, std::true_type) const
    {
        return arma::Mat<CodeBook>(features.ptr<Feature>(0),
                                   features.cols,
                                   features.rows,
                                   false);
    }

    arma::Mat<CodeBook>
    to_arma(cv::Mat const &features, std::false_type) const
    {
        arma::Mat<CodeBook> result(features.cols, features.rows);
        //do not use std::copy because it may generate warning
        auto *ptr = features.ptr<Feature>(0);
        arma::uword const total = features.cols * features.rows;
        for(arma::uword i = 0; i != total; ++i){
            result(i) = static_cast<CodeBook>(ptr[i]);
        }

        return result;
    }

    features_indexer const &fi_;
    mutable std::mutex mutex_;
};

} /*! @} End of Doxygen Groups*/

} /*! @} End of Doxygen Groups*/

#endif // OCV_CBIR_BOVW_HIST_CREATOR_HPP


// Data structures
#define BOOST_TEST_MODULE
#include <boost/test/unit_test.hpp>
#include <util/test_macros.hpp>
#include <unity/lib/unity_sarray.hpp>
#include <unity/lib/unity_sframe.hpp>
#include <sframe/sframe.hpp>

#include <vector>
#include <string>
#include <random/random.hpp>

#include <sframe/testing_utils.hpp>
#include <util/testing_utils.hpp>
#include <unity/toolkits/recsys/models.hpp>
#include <unity/toolkits/evaluation/metrics.hpp>
#include <sframe/testing_utils.hpp>
#include <util/testing_utils.hpp>

using namespace turi;

struct evaluation_test {
 public:
  void test_rmse_and_max_error() {

    size_t num_observations = 5000;
    random::seed(0);

    auto predictions = std::vector<flexible_type>(num_observations);
    auto targets = std::vector<flexible_type>(num_observations);
    double total = 0.0;
    double true_max_error = 0;
    for (size_t i = 0; i < num_observations; ++i) {
      predictions[i] = random::fast_uniform<double>(0, 1);
      targets[i] = random::fast_uniform<double>(0, 1);
      double err = std::abs((double)predictions[i] - (double)targets[i]);
      total += std::pow(err, 2);
      true_max_error = std::max(true_max_error, err);
    }
    double true_rmse = std::pow(total / (double) num_observations, .5);

    std::shared_ptr<sarray<flexible_type> > predictions_sa = make_testing_sarray(
          flex_type_enum::FLOAT, predictions);
    std::shared_ptr<sarray<flexible_type> > targets_sa = make_testing_sarray(
          flex_type_enum::FLOAT, targets);
    std::shared_ptr<unity_sarray> unity_targets_sa = std::make_shared<unity_sarray>();
    unity_targets_sa->construct_from_sarray(targets_sa);
    std::shared_ptr<unity_sarray> unity_predictions_sa= std::make_shared<unity_sarray>();
    unity_predictions_sa->construct_from_sarray(predictions_sa);

    variant_type rmse = evaluation::_supervised_streaming_evaluator(
                        unity_targets_sa, unity_predictions_sa, "rmse");
    variant_type max_error = evaluation::_supervised_streaming_evaluator(
                        unity_targets_sa, unity_predictions_sa, "max_error");
    TS_ASSERT(std::abs(variant_get_value<double>(rmse) - true_rmse) < 1e-15);
    TS_ASSERT(std::abs(variant_get_value<double>(max_error) - true_max_error) < 1e-15);
  }

  void test_roc_curve() {

    // Arrange
    size_t num_observations = 20;
    random::seed(0);
    size_t true_positive = 0;
    size_t true_negative = 0;
    size_t false_positive = 0;
    size_t false_negative = 0;

    auto predictions = std::vector<flexible_type>(num_observations);
    auto targets = std::vector<flexible_type>(num_observations);

    for (size_t i = 0; i < num_observations; ++i) {
      predictions[i] = float(i) / num_observations;
      // random::fast_uniform<double>(0, 1);
      targets[i] = random::fast_uniform<size_t>(0, 1);
      if (targets[i] == 1) {
        if (predictions[i] == 1) {
          true_positive += 1;
        } else {
          false_negative += 1;
        }
      } else {
        if (predictions[i] == 1) {
          false_positive += 1;
        } else {
          true_negative += 1;
        }
      }
      size_t n_bins = 100;
      size_t bin = std::floor((double) predictions[i] * (double) n_bins);
      if (bin == n_bins) bin -= 1;
      //logprogress_stream << predictions[i]
      //                   << " "
      //                   << bin
      //                   << " "
      //                   << targets[i]
      //                   << " "
      //                   << std::endl;
    }

    std::shared_ptr<sarray<flexible_type> > predictions_sa = make_testing_sarray(
          flex_type_enum::FLOAT, predictions);
    std::shared_ptr<sarray<flexible_type> > targets_sa = make_testing_sarray(
          flex_type_enum::INTEGER, targets);
    std::shared_ptr<unity_sarray> unity_targets_sa = std::make_shared<unity_sarray>();
    unity_targets_sa->construct_from_sarray(targets_sa);
    std::shared_ptr<unity_sarray> unity_predictions_sa= std::make_shared<unity_sarray>();
    unity_predictions_sa->construct_from_sarray(predictions_sa);

    // Act
    std::map<std::string, flexible_type> kwargs {
               {"average", FLEX_UNDEFINED},
               {"binary", true}};
    variant_type result = evaluation::_supervised_streaming_evaluator(
             unity_targets_sa, unity_predictions_sa, "roc_curve", kwargs);

    // Assert
    //logprogress_stream << "Now printing." << std::endl;
    //auto sf = *(variant_get_value<std::shared_ptr<unity_sframe>>(result))->get_underlying_sframe();
    //sf.debug_print();

  }

  void test_accuracy() {

    size_t num_observations = 5000;
    random::seed(0);
    size_t true_positive = 0;
    size_t true_negative = 0;
    size_t false_positive = 0;
    size_t false_negative = 0;

    auto predictions = std::vector<flexible_type>(num_observations);
    auto targets = std::vector<flexible_type>(num_observations);

    for (size_t i = 0; i < num_observations; ++i) {
      predictions[i] = random::fast_uniform<size_t>(0, 1);
      targets[i] = random::fast_uniform<size_t>(0, 1);
      if (targets[i] == 1) {
        if (predictions[i] == 1) {
          true_positive += 1;
        } else {
          false_negative += 1;
        }
      } else {
        if (predictions[i] == 1) {
          false_positive += 1;
        } else {
          true_negative += 1;
        }
      }
    }

    std::shared_ptr<sarray<flexible_type> > predictions_sa = make_testing_sarray(
          flex_type_enum::FLOAT, predictions);
    std::shared_ptr<sarray<flexible_type> > targets_sa = make_testing_sarray(
          flex_type_enum::INTEGER, targets);
    std::shared_ptr<unity_sarray> unity_targets_sa = std::make_shared<unity_sarray>();
    unity_targets_sa->construct_from_sarray(targets_sa);
    std::shared_ptr<unity_sarray> unity_predictions_sa= std::make_shared<unity_sarray>();
    unity_predictions_sa->construct_from_sarray(predictions_sa);

    std::map<std::string, flexible_type> kwargs {
               {"average", "micro"},
               {"binary", true}};
    variant_type result = evaluation::_supervised_streaming_evaluator(
             unity_targets_sa, unity_predictions_sa, "roc_curve", kwargs);
    variant_type accuracy = evaluation::_supervised_streaming_evaluator(
                        unity_targets_sa, unity_predictions_sa, "accuracy");
    double true_accuray = (double)(true_positive + true_negative) / num_observations;
    TS_ASSERT(std::abs(variant_get_value<double>(accuracy) - true_accuray) < 1e-15);

  }

};

BOOST_FIXTURE_TEST_SUITE(_evaluation_test, evaluation_test)
BOOST_AUTO_TEST_CASE(test_rmse_and_max_error) {
  evaluation_test::test_rmse_and_max_error();
}
BOOST_AUTO_TEST_CASE(test_roc_curve) {
  evaluation_test::test_roc_curve();
}
BOOST_AUTO_TEST_CASE(test_accuracy) {
  evaluation_test::test_accuracy();
}
BOOST_AUTO_TEST_SUITE_END()


#pragma once

#include <Eigen/Core>
#include <Eigen/Sparse>

#include <opt/optimization_problem.hpp>

namespace ccd {
namespace opt {

    /**
     * We handle the LCP(A, b), which seeks vectors s and x which satisfy the
     * following constraints
     * \f{align*}
     *     s = A x + b \\
     *     0 \leq x \perp s \geq 0
     * \f}
     */
    enum LCPSolver {
        /// Solve the LCP using the Guass-Seidel method
        LCP_GAUSS_SEIDEL,
        /// Solve the LCP as a QP using Mosek
        LCP_MOSEK,
        /// Sove the LCP using minimum-map Newton's method
        LCP_NEWTON
    };

    /**
     * @brief Solve the LCP using a given solver.
     */
    bool lcp_solve(const Eigen::VectorXd& gxi,
        const Eigen::MatrixXd& jac_gxi,
        const Eigen::MatrixXd& tilde_jac_gxi,
        const Eigen::VectorXd& tilde_b,
        const LCPSolver solver,
        Eigen::VectorXd& alpha);

    /**
     * @brief Solve the LCP using Gauss-Seidel
     *
     * To use our gauss-seidel implementation we require to express
     * the problem as
     *      s = q + N (Mx + p)
     *
     * @param gxi \f$q\f$
     * @param jac_gxi \f$N\f$
     * @param tilde_jac_gxi \f$M\f$
     * @param tilde_b \f$p\f$
     * @param alpha \f$x\f$
     */
    bool lcp_gauss_seidel(const Eigen::VectorXd& gxi,
        const Eigen::MatrixXd& jac_gxi,
        const Eigen::MatrixXd& tilde_jac_gxi,
        const Eigen::VectorXd& tilde_b,
        Eigen::VectorXd& alpha);

#if BUILD_WITH_MOSEK
    bool lcp_mosek(
        const Eigen::MatrixXd& A, const Eigen::VectorXd& b, Eigen::VectorXd& x);
#endif

    bool lcp_newton(const Eigen::MatrixXd& A_dense,
        const Eigen::VectorXd& b,
        Eigen::VectorXd& x);

} // namespace opt
} // namespace ccd