#pragma once

#include <fftw3.h>
#include <Eigen/Core>
#include <Eigen/Dense>
#include <boost/assert.hpp>
#include <complex>
#include <type_traits>

#include "base/array_buffer.hpp"
#include "planner.hpp"
#include "shift.hpp"

namespace local_ {
template <typename D1, typename D2>
struct enable_if_cc
    : public std::enable_if<std::is_same<typename D1::Scalar, std::complex<double>>::value &&
                            std::is_same<typename D2::Scalar, std::complex<double>>::value>
{
};

template <typename D1, typename D2>
struct enable_if_cr
    : public std::enable_if<std::is_same<typename D1::Scalar, std::complex<double>>::value &&
                            std::is_same<typename D2::Scalar, double>::value>
{
};

template <typename D1, typename D2>
struct enable_if_rc
    : public std::enable_if<std::is_same<typename D1::Scalar, double>::value &&
                            std::is_same<typename D2::Scalar, std::complex<double>>::value>
{
};
}  // local_

template <typename PLAN_HANDLER>
class FFTr2c
{
 public:
  typedef Eigen::Array<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> array_t;
  typedef Eigen::Array<std::complex<double>, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>
      complex_array_t;

  typedef std::complex<double> cdouble;

 public:
  //@{
  /// real valued transforms

  /**
   * @brief forward transform (real-valued)
   *
   * @param[out] dst
   * @param[in]  src
   * @param[in]  scale if true: scales output by 1/numel(src)
   *
   */
  template <typename DERIVED1, typename DERIVED2>
  typename local_::enable_if_cr<DERIVED1, DERIVED2>::type fft2(
      Eigen::DenseBase<DERIVED1> &dst,
      const Eigen::DenseBase<DERIVED2> &src,
      bool scale = true) const;

  /**
   * @brief inverse transform (does not preserve input, real-valued)
   *
   * @param[out] dst
   * @param[in]  src   full spectrum
   */
  template <typename DERIVED1, typename DERIVED2>
  typename local_::enable_if_rc<DERIVED1, DERIVED2>::type ifft2(
      Eigen::DenseBase<DERIVED1> &dst, Eigen::DenseBase<DERIVED2> &src) const;

  /**
   * @brief 2-dim fft (including fftshift, real-valued)
   *
   * @param[out] dst  complex array (centered zero-frequency convention)
   * @param[in] src   real array
   * @param[in] scale if true: scales output by 1/numel(src)
   *
   * This is the inverse of ift for \var scale set to false!
   *
   */
  template <typename DERIVED1, typename DERIVED2>
  typename local_::enable_if_cr<DERIVED1, DERIVED2>::type ft(Eigen::DenseBase<DERIVED1> &dst,
                                                             const Eigen::DenseBase<DERIVED2> &src,
                                                             bool scale = true) const;

  /**
   * @brief ifft2 (including ifftshift, real-valued)
   *
   * @param[out] dst   real array
   * @param[in]  src   complex array
   */
  template <typename DERIVED1, typename DERIVED2>
  typename local_::enable_if_rc<DERIVED1, DERIVED2>::type ift(
      Eigen::DenseBase<DERIVED1> &dst, const Eigen::DenseBase<DERIVED2> &src) const;
  //@}

  //@{
  /// complex transforms
  /**
   * complex, complex
   * @brief fft2 (including fftshift)
   *
   * @param[out] dst (in centered zero-frequency convention)
   * @param[int] src
   * @param bool  if true: scales output by 1/numel(src)
   *
   * This is the inverse of ift for \var scale set to false.
   *
   */
  template <typename DERIVED1, typename DERIVED2>
  typename local_::enable_if_cc<DERIVED1, DERIVED2>::type ft(Eigen::DenseBase<DERIVED1> &dst,
                                                             const Eigen::DenseBase<DERIVED2> &src,
                                                             bool scale = true) const;

  /**
   * complex, complex
   * @brief complex ifft2 (including ifftshift)
   *
   * @param[out] dst
   * @param[in] src
   */
  template <typename DERIVED1, typename DERIVED2>
  typename local_::enable_if_cc<DERIVED1, DERIVED2>::type ift(
      Eigen::DenseBase<DERIVED1> &dst, const Eigen::DenseBase<DERIVED2> &src) const;

  /**
   *
   */
  template <typename DERIVED1, typename DERIVED2>
  typename local_::enable_if_cc<DERIVED1, DERIVED2>::type fft2(
      Eigen::DenseBase<DERIVED1> &dst,
      const Eigen::DenseBase<DERIVED2> &src,
      bool scale = true) const;

  /**
   * @brief ifft2 (c -> c)
   *
   * @param[out] dst  destination
   * @param[in] src  will be overwritten by FFTW!
   */
  template <typename DERIVED1, typename DERIVED2>
  typename local_::enable_if_cc<DERIVED1, DERIVED2>::type ifft2(
      Eigen::DenseBase<DERIVED1> &dst, Eigen::DenseBase<DERIVED2> &src) const;
  //@}

  PLAN_HANDLER &get_plan() { return plan_h_; }

 private:
  static PLAN_HANDLER plan_h_;
  thread_local static ArrayBuffer<> buf_;
};

template <typename PLAN_HANDLER>
thread_local ArrayBuffer<> FFTr2c<PLAN_HANDLER>::buf_;

template <typename PLAN_HANDLER>
PLAN_HANDLER FFTr2c<PLAN_HANDLER>::plan_h_;

// --------------------------------------------------------------------------------
// --------------------------------------------------------------------------------
// -------------------------- REAL-COMPLEX TRANSFORMS
// -----------------------------
// --------------------------------------------------------------------------------
// --------------------------------------------------------------------------------
template <typename PLAN_HANDLER>
template <typename DERIVED1, typename DERIVED2>
typename local_::enable_if_cr<DERIVED1, DERIVED2>::type
FFTr2c<PLAN_HANDLER>::fft2(Eigen::DenseBase<DERIVED1> &dst,
                           const Eigen::DenseBase<DERIVED2> &src,
                           bool scale) const
{
  static_assert(DERIVED1::IsRowMajor, "requires row-major storage");
  static_assert(DERIVED2::IsRowMajor, "requires row-major storage");
  static_assert(sizeof(fftw_complex) == sizeof(cdouble), "type mismatch");
  // assert(dst.rows() == src.rows());
  // assert(dst.cols() == src.cols());

  dst.derived().resize(src.rows(), src.cols());

  // typedef double fftw_cdouble[2];
  typedef fftw_complex fftw_cdouble;
  const int n0 = src.rows();
  const int n1 = src.cols();
  int n[2] = {n0, n1};

  fftw_cdouble *out = reinterpret_cast<fftw_cdouble *>(dst.derived().data());
  double *in = const_cast<double *>(src.derived().data());

  fftw_plan fwd_plan = plan_h_.get_plan(n, PLAN_HANDLER::FWD, ft_type::R2C);
  BOOST_ASSERT_MSG(fwd_plan != NULL, "fftw plan not found!");
  fftw_execute_dft_r2c(fwd_plan, in, out);

  // mirror coefficients
  for (int i = 0; i < n0; ++i) {
    int idest = (n0 - i) % n0;
    for (int j = 1; j < n1 / 2 + n1 % 2; ++j) {
      dst(idest, n1 - j) = std::conj(dst(i, j));
    }
  }
  if (scale) {
    double f = 1. / (n0 * n1);
    dst *= f;
  }
}

// --------------------------------------------------------------------------------
template <typename PLAN_HANDLER>
template <typename DERIVED1, typename DERIVED2>
typename local_::enable_if_rc<DERIVED1, DERIVED2>::type
FFTr2c<PLAN_HANDLER>::ifft2(Eigen::DenseBase<DERIVED1> &dst, Eigen::DenseBase<DERIVED2> &src) const
{
  static_assert(std::is_same<typename DERIVED2::Scalar, std::complex<double>>::value,
                "type mismatch");
  static_assert(std::is_same<typename DERIVED1::Scalar, double>::value, "type mismatch");
  static_assert(sizeof(fftw_complex) == sizeof(cdouble), "type mismatch");
  static_assert(DERIVED1::IsRowMajor, "requires row-major storage");
  static_assert(DERIVED2::IsRowMajor, "requires row-major storage");

  dst.derived().resize(src.rows(), src.cols());
  // typedef double fftw_cdouble[2];
  typedef fftw_complex fftw_cdouble;
  const int n0 = src.rows();
  const int n1 = src.cols();
  int n[2] = {n0, n1};

  fftw_cdouble *in = reinterpret_cast<fftw_cdouble *>(src.derived().data());
  double *out = dst.derived().data();

  fftw_plan inv_plan = plan_h_.get_plan(n, PLAN_HANDLER::INV, ft_type::R2C);
  BOOST_ASSERT_MSG(inv_plan != NULL, "fftw plan not found!");
  // fftw_execute(inv_plan);
  fftw_execute_dft_c2r(inv_plan, in, out);

  double f = 1. / (n0 * n1);
  dst *= f;
}

// --------------------------------------------------------------------------------
template <typename PLAN_HANDLER>
template <typename DERIVED1, typename DERIVED2>
typename local_::enable_if_cr<DERIVED1, DERIVED2>::type
FFTr2c<PLAN_HANDLER>::ft(Eigen::DenseBase<DERIVED1> &dst,
                         const Eigen::DenseBase<DERIVED2> &src,
                         bool scale) const
{
  static_assert(std::is_same<typename DERIVED1::Scalar, std::complex<double>>::value,
                "type mismatch");
  static_assert(std::is_same<typename DERIVED2::Scalar, double>::value, "type mismatch");

  auto tmp = buf_.get<complex_array_t>(src.rows(), src.cols());
  this->fft2(tmp, src, scale);
  dst.resize(tmp.rows(), tmp.cols());
  fftshift(dst, tmp);
}

// --------------------------------------------------------------------------------
template <typename PLAN_HANDLER>
template <typename DERIVED1, typename DERIVED2>
typename local_::enable_if_rc<DERIVED1, DERIVED2>::type
FFTr2c<PLAN_HANDLER>::ift(Eigen::DenseBase<DERIVED1> &dst,
                          const Eigen::DenseBase<DERIVED2> &src) const
{
  static_assert(std::is_same<typename DERIVED1::Scalar, double>::value, "type mismatch");
  static_assert(std::is_same<typename DERIVED2::Scalar, std::complex<double>>::value,
                "type mismatch");

  auto tmp = buf_.get<complex_array_t>(src.rows(), src.cols());
  ifftshift(tmp, src);
  this->ifft2(dst, tmp);
}

// --------------------------------------------------------------------------------
// --------------------------------------------------------------------------------
// ------------------------ COMPLEX-COMPLEX TRANSFORMS ----------------------------
// --------------------------------------------------------------------------------
// --------------------------------------------------------------------------------
template <typename PLAN_HANDLER>
template <typename DERIVED1, typename DERIVED2>
typename local_::enable_if_cc<DERIVED1, DERIVED2>::type
FFTr2c<PLAN_HANDLER>::fft2(Eigen::DenseBase<DERIVED1> &dst,
                           const Eigen::DenseBase<DERIVED2> &src,
                           bool scale) const
{
  static_assert(DERIVED1::IsRowMajor, "requires row-major storage");
  static_assert(DERIVED2::IsRowMajor, "requires row-major storage");
  static_assert(sizeof(fftw_complex) == sizeof(cdouble), "type mismatch");

  dst.derived().resize(src.rows(), src.cols());

  typedef fftw_complex fftw_cdouble;
  const int n0 = src.rows();
  const int n1 = src.cols();
  int n[2] = {n0, n1};

  fftw_cdouble *in =
      const_cast<fftw_cdouble *>(reinterpret_cast<const fftw_cdouble *>(src.derived().data()));
  fftw_cdouble *out = reinterpret_cast<fftw_cdouble *>(dst.derived().data());

  fftw_plan fwd_plan = plan_h_.get_plan(n, PLAN_HANDLER::FWD, ft_type::C2C);
  BOOST_ASSERT_MSG(fwd_plan != NULL, "fftw plan not found!");
  fftw_execute_dft(fwd_plan, in, out);

  if (scale) {
    double f = 1. / (n0 * n1);
    dst *= f;
  }
}

// --------------------------------------------------------------------------------
template <typename PLAN_HANDLER>
template <typename DERIVED1, typename DERIVED2>
typename local_::enable_if_cc<DERIVED1, DERIVED2>::type
FFTr2c<PLAN_HANDLER>::ifft2(Eigen::DenseBase<DERIVED1> &dst, Eigen::DenseBase<DERIVED2> &src) const
{
  static_assert(sizeof(fftw_complex) == sizeof(cdouble), "type mismatch");
  static_assert(DERIVED1::IsRowMajor, "requires row-major storage");
  static_assert(DERIVED2::IsRowMajor, "requires row-major storage");

  dst.derived().resize(src.rows(), src.cols());
  // typedef double fftw_cdouble[2];
  typedef fftw_complex fftw_cdouble;
  const int n0 = src.rows();
  const int n1 = src.cols();
  int n[2] = {n0, n1};

  fftw_cdouble *in =
      const_cast<fftw_cdouble *>(reinterpret_cast<const fftw_cdouble *>(src.derived().data()));
  fftw_cdouble *out = reinterpret_cast<fftw_cdouble *>(dst.derived().data());

  fftw_plan inv_plan = plan_h_.get_plan(n, PLAN_HANDLER::INV, ft_type::C2C);
  BOOST_ASSERT_MSG(inv_plan != NULL, "fftw plan not found!");
  // fftw_execute(inv_plan);
  fftw_execute_dft(inv_plan, in, out);

  double f = 1. / (n0 * n1);
  dst *= f;
}

// --------------------------------------------------------------------------------
template <typename PLAN_HANDLER>
template <typename DERIVED1, typename DERIVED2>
typename local_::enable_if_cc<DERIVED1, DERIVED2>::type
FFTr2c<PLAN_HANDLER>::ft(Eigen::DenseBase<DERIVED1> &dst,
                         const Eigen::DenseBase<DERIVED2> &src,
                         bool scale) const
{
  auto tmp = buf_.get<complex_array_t>(src.rows(), src.cols());
  this->fft2(tmp, src, scale);
  dst.resize(tmp.rows(), tmp.cols());
  fftshift(dst, tmp);
}

// --------------------------------------------------------------------------------
template <typename PLAN_HANDLER>
template <typename DERIVED1, typename DERIVED2>
typename local_::enable_if_cc<DERIVED1, DERIVED2>::type
FFTr2c<PLAN_HANDLER>::ift(Eigen::DenseBase<DERIVED1> &dst,
                          const Eigen::DenseBase<DERIVED2> &src) const
{
  auto tmp = buf_.get<complex_array_t>(src.rows(), src.cols());
  ifftshift(tmp, src);
  this->ifft2(dst, tmp);
}


/*******************************************************************************
 * 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 ExpectedInPlaceGenMatGenRowVec : public Expected {
    public:
      ExpectedInPlaceGenMatGenRowVec() {
        cout << "Compute ExpectedInPlaceGenMatGenRowVec(): " << endl;

          vector<vector<pair<string, void*>>> inputs = Input::getTestParameters({
            InputClass::GenMat,
            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;
                  _genRowVec = *static_cast<Row<double>*>(value.second);
                  break;
              }
              ++n;
            }

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

            _copyOfGenMat = _genMat;
            _copyOfGenRowVec = _genRowVec;

            expectedMatInPlaceEqual();

            _genMat = _copyOfGenMat;
            _genRowVec = _copyOfGenRowVec;
            expectedMatInPlacePlus();

            _genMat = _copyOfGenMat;
            _genRowVec = _copyOfGenRowVec;
            expectedMatInPlaceMinus();

            _genMat = _copyOfGenMat;
            _genRowVec = _copyOfGenRowVec;
            expectedMatInPlaceTimes();

            _genMat = _copyOfGenMat;
            _genRowVec = _copyOfGenRowVec;
            expectedMatInPlaceElemTimes();

            _genMat = _copyOfGenMat;
            _genRowVec = _copyOfGenRowVec;
            expectedMatInPlaceElemDivide();

            _genMat = _copyOfGenMat;
            _genRowVec = _copyOfGenRowVec;
            expectedMatSwap();

            _genMat = _copyOfGenMat;
            _genRowVec = _copyOfGenRowVec;
            expectedMatEach_colEqual();

            _genMat = _copyOfGenMat;
            _genRowVec = _copyOfGenRowVec;
            expectedMatEach_colPlus();

            _genMat = _copyOfGenMat;
            _genRowVec = _copyOfGenRowVec;
            expectedMatEach_colMinus();

            _genMat = _copyOfGenMat;
            _genRowVec = _copyOfGenRowVec;
            expectedMatEach_colElemTimes();

            _genMat = _copyOfGenMat;
            _genRowVec = _copyOfGenRowVec;
            expectedMatEach_colElemDivide();

            _genMat = _copyOfGenMat;
            _genRowVec = _copyOfGenRowVec;
            expectedMatEach_rowEqual();

            _genMat = _copyOfGenMat;
            _genRowVec = _copyOfGenRowVec;
            expectedMatEach_rowPlus();

            _genMat = _copyOfGenMat;
            _genRowVec = _copyOfGenRowVec;
            expectedMatEach_rowMinus();

            _genMat = _copyOfGenMat;
            _genRowVec = _copyOfGenRowVec;
            expectedMatEach_rowElemTimes();

            _genMat = _copyOfGenMat;
            _genRowVec = _copyOfGenRowVec;
            expectedMatEach_rowElemDivide();

            _genMat = _copyOfGenMat;
            _genRowVec = _copyOfGenRowVec;
            expectedMatDiagEqual();

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

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

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

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

            _genMat = _copyOfGenMat;
            _genRowVec = _copyOfGenRowVec;
            expectedMatCopy_size();
          }

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

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

      Mat<double> _genRowVec;
      Mat<double> _copyOfGenRowVec;

      void expectedMatInPlaceEqual() {
        cout << "- Compute expectedMatInPlaceEqual() ... ";

        _genMat = _genRowVec;
        save<double>("Mat.inPlaceEqual", _genMat);

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

      void expectedMatInPlacePlus() {
        if(_genMat.n_rows != _genRowVec.n_rows || _genMat.n_cols != _genRowVec.n_cols) {
          return;
        }

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

        _genMat += _genRowVec;
        save<double>("Mat.inPlacePlus", _genMat);

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

      void expectedMatInPlaceMinus() {
        if(_genMat.n_rows != _genRowVec.n_rows || _genMat.n_cols != _genRowVec.n_cols) {
          return;
        }

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

        _genMat -= _genRowVec;
        save<double>("Mat.inPlaceMinus", _genMat);

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

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

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

        _genMat *= _genRowVec;
        save<double>("Mat.inPlaceTimes", _genMat);

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

      void expectedMatInPlaceElemTimes() {
        if(_genMat.n_rows != _genRowVec.n_rows || _genMat.n_cols != _genRowVec.n_cols) {
          return;
        }

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

        _genMat %= _genRowVec;
        save<double>("Mat.inPlaceElemTimes", _genMat);

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

      void expectedMatInPlaceElemDivide() {
        if(_genMat.n_rows != _genRowVec.n_rows || _genMat.n_cols != _genRowVec.n_cols) {
          return;
        }

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

        _genMat /= _genRowVec;
        save<double>("Mat.inPlaceElemDivide", _genMat);

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

      void expectedMatSwap() {
        if(!_genMat.is_rowvec()) {
          return;
        }

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

        _genMat.swap(_genRowVec);
        save<double>("Mat.swap", _genMat);

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

      void expectedMatEach_colEqual() {
        if(!_genRowVec.is_colvec() || _genMat.n_rows != _genRowVec.n_rows) {
          return;
        }

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

        _genMat.each_col() = _genRowVec;
        save<double>("Mat.each_colEqual", _genMat);

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

      void expectedMatEach_colPlus() {
        if(!_genRowVec.is_colvec() || _genMat.n_rows != _genRowVec.n_rows) {
          return;
        }

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

        _genMat.each_col() += _genRowVec;
        save<double>("Mat.each_colPlus", _genMat);

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

      void expectedMatEach_colMinus() {
        if(!_genRowVec.is_colvec() || _genMat.n_rows != _genRowVec.n_rows) {
          return;
        }

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

        _genMat.each_col() -= _genRowVec;
        save<double>("Mat.each_colMinus", _genMat);

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

      void expectedMatEach_colElemTimes() {
        if(!_genRowVec.is_colvec() || _genMat.n_rows != _genRowVec.n_rows) {
          return;
        }

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

        _genMat.each_col() %= _genRowVec;
        save<double>("Mat.each_colElemTimes", _genMat);

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

      void expectedMatEach_colElemDivide() {
        if(!_genRowVec.is_colvec() || _genMat.n_rows != _genRowVec.n_rows) {
          return;
        }

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

        _genMat.each_col() /= _genRowVec;
        save<double>("Mat.each_colElemDivide", _genMat);

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

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

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

        _genMat.each_row() = _genRowVec;
        save<double>("Mat.each_rowEqual", _genMat);

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

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

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

        _genMat.each_row() += _genRowVec;
        save<double>("Mat.each_rowPlus", _genMat);

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

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

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

        _genMat.each_row() -= _genRowVec;
        save<double>("Mat.each_rowMinus", _genMat);

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

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

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

        _genMat.each_row() %= _genRowVec;
        save<double>("Mat.each_rowElemTimes", _genMat);

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

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

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

        _genMat.each_row() /= _genRowVec;
        save<double>("Mat.each_rowElemDivide", _genMat);

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

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

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

        _genMat.diag() = _genRowVec;
        save<double>("Mat.diagEqual", _genMat);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

      void expectedMatCopy_size() {
        cout << "- Compute expectedMatCopy_size() ... ";

        _genMat.copy_size(_genRowVec);
        save<double>("Mat.copy_size", _genMat);

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


#include <boost/simd/include/functions/shuffle.hpp>
#include <boost/simd/sdk/simd/pack.hpp>
#include <boost/simd/sdk/simd/io.hpp>
#include <iostream>

using boost::simd::pack;
using boost::simd::shuffle;

int main()
{
  pack<float,4> f(1,2,3,4);

  // Broadcast the 3rd element: [3 3 3 3]
  std::cout << shuffle<2,2,2,2>(f) << "\n";
}


// Copyright 2002 Rensselaer Polytechnic Institute

// 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)

//  Authors: Lauren Foutz
//           Scott Hill
#include <boost/graph/floyd_warshall_shortest.hpp>
#include <map>
#include <algorithm>
#include <iostream>
#include <boost/random/linear_congruential.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/graph/properties.hpp>
#include <boost/graph/bellman_ford_shortest_paths.hpp>
#include <boost/graph/random.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/adjacency_matrix.hpp>
#include <boost/test/minimal.hpp>
#include <algorithm>
using namespace boost;

template <typename T>
struct inf_plus{
  T operator()(const T& a, const T& b) const {
    T inf = std::numeric_limits<T>::max BOOST_PREVENT_MACRO_SUBSTITUTION();
    if (a == inf || b == inf){
      return inf;
    }
    return a + b;
  }
};

template<typename T>
inline const T& my_min(const T& x, const T& y)
{ return x < y? x : y; }

template<typename Graph>
bool acceptance_test(Graph& g, int vec, int e)
{
  boost::minstd_rand ran(vec);

  {
    typename boost::property_map<Graph, boost::vertex_name_t>::type index =
      boost::get(boost::vertex_name, g);
    typename boost::graph_traits<Graph>::vertex_iterator firstv, lastv,
      firstv2, lastv2;
    int x = 0;
    for(boost::tie(firstv, lastv) = boost::vertices(g); firstv != lastv;
        firstv++){
      boost::put(index, *firstv, x);
      x++;
    }

    for(int i = 0; i < e; i++){
      boost::add_edge(index[ran() % vec], index[ran() % vec], g);
    }

    typename boost::graph_traits<Graph>::edge_iterator first, last;
    typename boost::property_map<Graph, boost::edge_weight_t>::type
      local_edge_map = boost::get(boost::edge_weight, g);
    for(boost::tie(first, last) = boost::edges(g); first != last; first++){
      if (ran() % vec != 0){
        boost::put(local_edge_map, *first, ran() % 100);
      } else {
        boost::put(local_edge_map, *first, 0 - (ran() % 100));
      }
    }

    int int_inf =
      std::numeric_limits<int>::max BOOST_PREVENT_MACRO_SUBSTITUTION();
    typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex_des;
    std::map<vertex_des,int> matrixRow;
    std::map<vertex_des, std::map<vertex_des ,int> > matrix;
    typedef typename boost::property_map<Graph, boost::vertex_distance_t>::type
      distance_type;
    distance_type distance_row = boost::get(boost::vertex_distance, g);
    for(boost::tie(firstv, lastv) = boost::vertices(g); firstv != lastv;
        firstv++){
      boost::put(distance_row, *firstv, int_inf);
      matrixRow[*firstv] = int_inf;
    }
    for(boost::tie(firstv, lastv) = boost::vertices(g); firstv != lastv;
        firstv++){
      matrix[*firstv] = matrixRow;
    }
    for(boost::tie(firstv, lastv) = boost::vertices(g); firstv != lastv;
        firstv++){
      matrix[*firstv][*firstv] = 0;
    }
    std::map<vertex_des, std::map<vertex_des, int> > matrix3(matrix);
    std::map<vertex_des, std::map<vertex_des, int> > matrix4(matrix);
    for(boost::tie(first, last) = boost::edges(g); first != last; first++){
      if (matrix[boost::source(*first, g)][boost::target(*first, g)] != int_inf)
      {
        matrix[boost::source(*first, g)][boost::target(*first, g)] =
          my_min
            (boost::get(local_edge_map, *first),
             matrix[boost::source(*first, g)][boost::target(*first, g)]);
      } else {
        matrix[boost::source(*first, g)][boost::target(*first, g)] =
          boost::get(local_edge_map, *first);
      }
    }
    bool is_undirected =
      boost::is_same<typename boost::graph_traits<Graph>::directed_category,
      boost::undirected_tag>::value;
    if (is_undirected){
      for(boost::tie(first, last) = boost::edges(g); first != last; first++){
        if (matrix[boost::target(*first, g)][boost::source(*first, g)] != int_inf)
        {
          matrix[boost::target(*first, g)][boost::source(*first, g)] =
            my_min
              (boost::get(local_edge_map, *first),
               matrix[boost::target(*first, g)][boost::source(*first, g)]);
        } else {
          matrix[boost::target(*first, g)][boost::source(*first, g)] =
            boost::get(local_edge_map, *first);
        }
      }
    }

    bool bellman, floyd1, floyd2, floyd3;
    std::less<int> compare;
    inf_plus<int> combine;
    floyd1 =
      boost::floyd_warshall_initialized_all_pairs_shortest_paths
        (g,
         matrix, weight_map(boost::get(boost::edge_weight, g)).
         distance_compare(compare). distance_combine(combine).
         distance_inf(int_inf). distance_zero(0));

    floyd2 =
      boost::floyd_warshall_all_pairs_shortest_paths
        (g, matrix3,
         weight_map(local_edge_map).  distance_compare(compare).
         distance_combine(combine).
         distance_inf(int_inf). distance_zero(0));

    floyd3 = boost::floyd_warshall_all_pairs_shortest_paths(g, matrix4);

    boost::dummy_property_map dummy_map;
    std::map<vertex_des, std::map<vertex_des, int> > matrix2;
    for(boost::tie(firstv, lastv) = vertices(g); firstv != lastv; firstv++){
      boost::put(distance_row, *firstv, 0);
      bellman =
        boost::bellman_ford_shortest_paths
          (g, vec,
           weight_map(boost::get(boost::edge_weight, g)).
           distance_map(boost::get(boost::vertex_distance, g)).
           predecessor_map(dummy_map).distance_compare(compare).
           distance_combine(combine));
      distance_row = boost::get(boost::vertex_distance, g);
      for(boost::tie(firstv2, lastv2) = vertices(g); firstv2 != lastv2;
          firstv2++){
        matrix2[*firstv][*firstv2] = boost::get(distance_row, *firstv2);
        boost::put(distance_row, *firstv2, int_inf);
      }
      if(bellman == false){
        break;
      }
    }

    if (bellman != floyd1 || bellman != floyd2 || bellman != floyd3){
      std::cout <<
        "A negative cycle was detected in one algorithm but not the others. "
                << std::endl;
      return false;
    }
    else if (bellman == false && floyd1 == false && floyd2 == false &&
             floyd3 == false){
      return true;
    }
    else {
      typename boost::graph_traits<Graph>::vertex_iterator first1, first2,
        last1, last2;
      for (boost::tie(first1, last1) = boost::vertices(g); first1 != last1;
           first1++){
        for (boost::tie(first2, last2) = boost::vertices(g); first2 != last2;
             first2++){
          if (matrix2[*first1][*first2] != matrix[*first1][*first2]){
            std::cout << "Algorithms do not match at matrix point "
                      << index[*first1] << " " << index[*first2]
                      << " Bellman results: " << matrix2[*first1][*first2]
                      << " floyd 1 results " << matrix[*first1][*first2]
                      << std::endl;
            return false;
          }
          if (matrix2[*first1][*first2] != matrix3[*first1][*first2]){
            std::cout << "Algorithms do not match at matrix point "
                      << index[*first1] << " " << index[*first2]
                      << " Bellman results: " << matrix2[*first1][*first2]
                      << " floyd 2 results " << matrix3[*first1][*first2]
                      << std::endl;
            return false;
          }
          if (matrix2[*first1][*first2] != matrix4[*first1][*first2]){
            std::cout << "Algorithms do not match at matrix point "
                      << index[*first1] << " " << index[*first2]
                      << " Bellman results: " << matrix2[*first1][*first2]
                      << " floyd 3 results " << matrix4[*first1][*first2]
                      << std::endl;
            return false;
          }
        }
      }
    }

  }
  return true;
}

template<typename Graph>
bool acceptance_test2(Graph& g, int vec, int e)
{
  boost::minstd_rand ran(vec);

  {

    typename boost::property_map<Graph, boost::vertex_name_t>::type index =
      boost::get(boost::vertex_name, g);
    typename boost::graph_traits<Graph>::vertex_iterator firstv, lastv,
      firstv2, lastv2;
    int x = 0;
    for(boost::tie(firstv, lastv) = boost::vertices(g); firstv != lastv;
        firstv++){
      boost::put(index, *firstv, x);
      x++;
    }

    boost::generate_random_graph(g, vec, e, ran, true);

    typename boost::graph_traits<Graph>::edge_iterator first, last;
    typename boost::property_map<Graph, boost::edge_weight_t>::type
      local_edge_map = boost::get(boost::edge_weight, g);
    for(boost::tie(first, last) = boost::edges(g); first != last; first++){
      if (ran() % vec != 0){
        boost::put(local_edge_map, *first, ran() % 100);
      } else {
        boost::put(local_edge_map, *first, 0 - (ran() % 100));
      }
    }

    int int_inf =
      std::numeric_limits<int>::max BOOST_PREVENT_MACRO_SUBSTITUTION();
    typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex_des;
    std::map<vertex_des,int> matrixRow;
    std::map<vertex_des, std::map<vertex_des ,int> > matrix;
    typedef typename boost::property_map<Graph, boost::vertex_distance_t>::type
      distance_type;
    distance_type distance_row = boost::get(boost::vertex_distance, g);
    for(boost::tie(firstv, lastv) = boost::vertices(g); firstv != lastv;
        firstv++){
      boost::put(distance_row, *firstv, int_inf);
      matrixRow[*firstv] = int_inf;
    }
    for(boost::tie(firstv, lastv) = boost::vertices(g); firstv != lastv;
        firstv++){
      matrix[*firstv] = matrixRow;
    }
    for(boost::tie(firstv, lastv) = boost::vertices(g); firstv != lastv;
        firstv++){
      matrix[*firstv][*firstv] = 0;
    }
    std::map<vertex_des, std::map<vertex_des, int> > matrix3(matrix);
    std::map<vertex_des, std::map<vertex_des, int> > matrix4(matrix);
    for(boost::tie(first, last) = boost::edges(g); first != last; first++){
      if (matrix[boost::source(*first, g)][boost::target(*first, g)] != int_inf)
      {
        matrix[boost::source(*first, g)][boost::target(*first, g)] =
          my_min
            (boost::get(local_edge_map, *first),
             matrix[boost::source(*first, g)][boost::target(*first, g)]);
      } else {
        matrix[boost::source(*first, g)][boost::target(*first, g)] =
          boost::get(local_edge_map, *first);
      }
    }
    bool is_undirected =
      boost::is_same<typename boost::graph_traits<Graph>::directed_category,
      boost::undirected_tag>::value;
    if (is_undirected){
      for(boost::tie(first, last) = boost::edges(g); first != last; first++){
        if (matrix[boost::target(*first, g)][boost::source(*first, g)]
            != int_inf){
          matrix[boost::target(*first, g)][boost::source(*first, g)] =
            my_min
              (boost::get(local_edge_map, *first),
               matrix[boost::target(*first, g)][boost::source(*first, g)]);
        } else {
          matrix[boost::target(*first, g)][boost::source(*first, g)] =
            boost::get(local_edge_map, *first);
        }
      }
    }

    bool bellman, floyd1, floyd2, floyd3;
    std::less<int> compare;
    inf_plus<int> combine;
    floyd1 =
      boost::floyd_warshall_initialized_all_pairs_shortest_paths
        (g,
         matrix, weight_map(boost::get(boost::edge_weight, g)).
         distance_compare(compare). distance_combine(combine).
         distance_inf(int_inf). distance_zero(0));

    floyd2 =
      boost::floyd_warshall_all_pairs_shortest_paths
        (g, matrix3,
         weight_map(local_edge_map).  distance_compare(compare).
         distance_combine(combine).
         distance_inf(int_inf). distance_zero(0));

    floyd3 = boost::floyd_warshall_all_pairs_shortest_paths(g, matrix4);

    boost::dummy_property_map dummy_map;
    std::map<vertex_des, std::map<vertex_des, int> > matrix2;
    for(boost::tie(firstv, lastv) = vertices(g); firstv != lastv; firstv++){
      boost::put(distance_row, *firstv, 0);
      bellman =
        boost::bellman_ford_shortest_paths
          (g, vec,
           weight_map(boost::get(boost::edge_weight, g)).
           distance_map(boost::get(boost::vertex_distance, g)).
           predecessor_map(dummy_map).distance_compare(compare).
           distance_combine(combine));
      distance_row = boost::get(boost::vertex_distance, g);
      for(boost::tie(firstv2, lastv2) = vertices(g); firstv2 != lastv2;
          firstv2++){
        matrix2[*firstv][*firstv2] = boost::get(distance_row, *firstv2);
        boost::put(distance_row, *firstv2, int_inf);
      }
      if(bellman == false){
        break;
      }
    }

    if (bellman != floyd1 || bellman != floyd2 || bellman != floyd3){
      std::cout <<
        "A negative cycle was detected in one algorithm but not the others. "
                << std::endl;
      return false;
    }
    else if (bellman == false && floyd1 == false && floyd2 == false &&
             floyd3 == false){
      return true;
    }
    else {
      typename boost::graph_traits<Graph>::vertex_iterator first1, first2,
        last1, last2;
      for (boost::tie(first1, last1) = boost::vertices(g); first1 != last1;
           first1++){
        for (boost::tie(first2, last2) = boost::vertices(g); first2 != last2;
             first2++){
          if (matrix2[*first1][*first2] != matrix[*first1][*first2]){
            std::cout << "Algorithms do not match at matrix point "
                      << index[*first1] << " " << index[*first2]
                      << " Bellman results: " << matrix2[*first1][*first2]
                      << " floyd 1 results " << matrix[*first1][*first2]
                      << std::endl;
            return false;
          }
          if (matrix2[*first1][*first2] != matrix3[*first1][*first2]){
            std::cout << "Algorithms do not match at matrix point "
                      << index[*first1] << " " << index[*first2]
                      << " Bellman results: " << matrix2[*first1][*first2]
                      << " floyd 2 results " << matrix3[*first1][*first2]
                      << std::endl;
            return false;
          }
          if (matrix2[*first1][*first2] != matrix4[*first1][*first2]){
            std::cout << "Algorithms do not match at matrix point "
                      << index[*first1] << " " << index[*first2]
                      << " Bellman results: " << matrix2[*first1][*first2]
                      << " floyd 3 results " << matrix4[*first1][*first2]
                      << std::endl;
            return false;
          }
        }
      }
    }

  }
  return true;
}

int test_main(int, char*[])
{
  typedef boost::adjacency_list<boost::listS, boost::listS, boost::directedS,
            boost::property<boost::vertex_distance_t, int,
            boost::property<boost::vertex_name_t, int> > ,
            boost::property<boost::edge_weight_t, int> > Digraph;
  Digraph adjlist_digraph;
  BOOST_CHECK(acceptance_test2(adjlist_digraph, 100, 2000));

  typedef boost::adjacency_matrix<boost::undirectedS,
            boost::property<boost::vertex_distance_t, int,
            boost::property<boost::vertex_name_t, int> > ,
            boost::property<boost::edge_weight_t, int> > Graph;
  Graph matrix_graph(100);
  BOOST_CHECK(acceptance_test(matrix_graph, 100, 2000));

  return 0;
}


/*
 * Copyright 2015 David A. Boyuka II
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * quantize.cpp
 *
 *  Created on: May 22, 2014
 *      Author: David A. Boyuka II
 */

extern "C" {
#include <getopt.h>
#ifndef _Bool
#define _Bool bool
#endif
#include <pique/util/myopts.h>
}

#include <iostream>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <cassert>
#include <set>
#include <map>

#include <boost/algorithm/string.hpp>

using namespace std;

enum struct QuantizeMode { TRUNCATE, CONV_1C_TO_2C, ZERO_INSIGBITS};

struct cmd_args_t {
	char *in_filename_str;
	char *out_filename_str;
	int datatype_size;
	int outdatatype_size;
	int sigbits;
	char *quantize_mode;
	bool as_bids;
};

struct cmd_config_t {
	string in_filename;
	string out_filename;
	int datatype_size;
	int outdatatype_size;
	int sigbits;
	QuantizeMode mode;
	bool as_bids;
};

template<typename WordT, typename OutWordT>
static void quantize_binids(const cmd_config_t &conf) {
	const int wordbits = (sizeof(WordT) << 3);
	const int insigbits = wordbits - conf.sigbits;

	FILE *fin = std::fopen(conf.in_filename.c_str(), "r");
	FILE *fout = std::fopen(conf.out_filename.c_str(), "w");
	assert(fin && fout);

	std::set< WordT > keys;

	const uint64_t BUFFER_SIZE = 1048576;
	std::vector<WordT> buf(BUFFER_SIZE);

	while (!feof(fin)) {
		const size_t nwords = fread(&buf.front(), sizeof(WordT), buf.size(), fin);
		switch (conf.mode) {
		case QuantizeMode::TRUNCATE:
			for (size_t i = 0; i < nwords; i++)
				keys.insert(buf[i] >> insigbits);
			break;
		case QuantizeMode::CONV_1C_TO_2C:
		{
			const WordT signbitmask = (WordT)1 << (wordbits - 1);
			for (size_t i = 0; i < nwords; i++) {
				// 0XXXXXXXX -> 1XXXXXXXX
				// 1XXXXXXXX -> 0~(XXXXXXXX)
				if (buf[i] & signbitmask) {
					keys.insert((WordT)(~buf[i]) >> insigbits);
				} else {
					keys.insert((buf[i] | signbitmask) >> insigbits);
				}
			}
			break;
		}
		case QuantizeMode::ZERO_INSIGBITS:
		{
			const WordT keepmask = (~(WordT)0) << insigbits;
			for (size_t i = 0; i < nwords; i++)
				keys.insert(buf[i] & keepmask);
		}
		}
	}

	fseek(fin, 0, SEEK_SET);

	OutWordT nextbid = 0;
	std::map< WordT, OutWordT > bin_id_map;
	for (auto it = keys.begin(); it != keys.end(); it++)
		bin_id_map.insert(std::make_pair(*it, nextbid++));

	std::vector<OutWordT> outbuf(BUFFER_SIZE);

	while (!feof(fin)) {
		const size_t nwords = fread(&buf.front(), sizeof(WordT), buf.size(), fin);
		switch (conf.mode) {
		case QuantizeMode::TRUNCATE:
			for (size_t i = 0; i < nwords; i++)
				outbuf[i] = bin_id_map[buf[i] >> insigbits];
			break;
		case QuantizeMode::CONV_1C_TO_2C:
		{
			const WordT signbitmask = (WordT)1 << (wordbits - 1);
			for (size_t i = 0; i < nwords; i++) {
				// 0XXXXXXXX -> 1XXXXXXXX
				// 1XXXXXXXX -> 0~(XXXXXXXX)
				if (buf[i] & signbitmask) {
					outbuf[i] = bin_id_map[(WordT)(~buf[i]) >> insigbits];
				} else {
					outbuf[i] = bin_id_map[(buf[i] | signbitmask) >> insigbits];
				}
			}
			break;
		}
		case QuantizeMode::ZERO_INSIGBITS:
		{
			const WordT keepmask = (~(WordT)0) << insigbits;
			for (size_t i = 0; i < nwords; i++)
				outbuf[i] = bin_id_map[buf[i] & keepmask];
		}
		}
		fwrite(&outbuf.front(), sizeof(OutWordT), nwords, fout);
	}

	fclose(fin);
	fclose(fout);
}

template<typename WordT, typename OutWordT>
static void quantize(const cmd_config_t &conf) {
	if (conf.as_bids) {
		quantize_binids<WordT, OutWordT>(conf);
		return;
	}

	const int wordbits = (sizeof(WordT) << 3);
	const int insigbits = wordbits - conf.sigbits;

	FILE *fin = std::fopen(conf.in_filename.c_str(), "r");
	FILE *fout = std::fopen(conf.out_filename.c_str(), "w");
	assert(fin && fout);

	const uint64_t BUFFER_SIZE = 1048576;
	std::vector<WordT> buf(BUFFER_SIZE);
	std::vector<OutWordT> outbuf(BUFFER_SIZE);

	while (!feof(fin)) {
		const size_t nwords = fread(&buf.front(), sizeof(WordT), buf.size(), fin);
		switch (conf.mode) {
		case QuantizeMode::TRUNCATE:
			std::cerr << "TRUNCATE MODE" << std::endl;
			for (size_t i = 0; i < nwords; i++)
				outbuf[i] = (OutWordT)(buf[i] >> insigbits);
			break;
		case QuantizeMode::CONV_1C_TO_2C:
		{
			std::cerr << "1's TO 2's COMPLEMENT CONVERT MODE" << std::endl;
			const WordT signbitmask = (WordT)1 << (wordbits - 1);
			for (size_t i = 0; i < nwords; i++) {
				if (buf[i] & signbitmask) {
					const WordT rmsignbit = buf[i] ^ signbitmask;
					outbuf[i] = ~(OutWordT)(rmsignbit >> insigbits) + 1;
				} else {
					outbuf[i] = (OutWordT)(buf[i] >> insigbits);
				}
			}
			break;
		}
		case QuantizeMode::ZERO_INSIGBITS:
		{
			std::cerr << "ZERO INSIGBITS MODE" << std::endl;
			assert(typeid(WordT) == typeid(OutWordT));
			const WordT keepmask = (~(WordT)0) << insigbits;
			for (size_t i = 0; i < nwords; i++)
				outbuf[i] = (OutWordT)(buf[i] & keepmask);
		}
		}
		fwrite(&outbuf.front(), sizeof(OutWordT), nwords, fout);
	}

	fclose(fin);
	fclose(fout);
}

static void validate_and_fully_parse_args(cmd_config_t &conf, cmd_args_t &args) {
	conf.in_filename = string(args.in_filename_str);
	conf.out_filename = string(args.out_filename_str);
	conf.datatype_size = args.datatype_size;
	conf.outdatatype_size = args.outdatatype_size;
	conf.sigbits = args.sigbits;
	conf.as_bids = args.as_bids;

	if (strcasecmp(args.quantize_mode, "truncate") == 0)
		conf.mode = QuantizeMode::TRUNCATE;
	else if (strcasecmp(args.quantize_mode, "conv2c") == 0)
		conf.mode = QuantizeMode::CONV_1C_TO_2C;
	else if (strcasecmp(args.quantize_mode, "zero") == 0)
		conf.mode = QuantizeMode::ZERO_INSIGBITS;
	else
		abort();
}

static myoption addopt(const char *flagname, int hasarg, OPTION_VALUE_TYPE type, void *output, void *fixedval = NULL) {
	myoption opt;
	opt.flagname = flagname;
	opt.hasarg = hasarg;
	opt.type = type;
	opt.output = output;
	opt.fixedval = fixedval;
	return opt;
}

template<typename WordT>
static void quantize_outword_switch(const cmd_config_t &conf) {
    switch (conf.outdatatype_size) {
    case 1: quantize<WordT, uint8_t>(conf); break;
    case 2: quantize<WordT, uint16_t>(conf); break;
    case 4: quantize<WordT, uint32_t>(conf); break;
    case 8: quantize<WordT, uint64_t>(conf); break;
    default:
    	std::cout << "Invalid out datatype size " << conf.outdatatype_size << std::endl;
    	abort();
    }
}

static void quantize_inword_switch(const cmd_config_t &conf) {
    switch (conf.datatype_size) {
    case 1: quantize_outword_switch<uint8_t>(conf); break;
    case 2: quantize_outword_switch<uint16_t>(conf); break;
    case 4: quantize_outword_switch<uint32_t>(conf); break;
    case 8: quantize_outword_switch<uint64_t>(conf); break;
    default:
    	std::cout << "Invalid datatype size " << conf.datatype_size << std::endl;
    	abort();
    }
}

int main(int argc, char **argv) {
    cmd_args_t args;
    cmd_config_t conf;

    static bool FALSEVAL = false; // , TRUEVAL = true;
    static char DEF_QUANTIZE_MODE[] = "truncate";

    myoption opts[] = {
        addopt("fin", required_argument, OPTION_TYPE_STRING, &args.in_filename_str),
        addopt("fout", required_argument, OPTION_TYPE_STRING, &args.out_filename_str),
        addopt("dtsize", required_argument, OPTION_TYPE_INT, &args.datatype_size),
        addopt("odtsize", required_argument, OPTION_TYPE_INT, &args.outdatatype_size),
        addopt("sigbits", required_argument, OPTION_TYPE_INT, &args.sigbits),
        addopt("quantmode", required_argument, OPTION_TYPE_STRING, &args.quantize_mode, DEF_QUANTIZE_MODE),
        addopt("as_bids", required_argument, OPTION_TYPE_BOOLEAN, &args.as_bids, &FALSEVAL),
        addopt(NULL, required_argument, OPTION_TYPE_BOOLEAN, NULL),
    };
    parse_args(&argc, &argv, opts);
    validate_and_fully_parse_args(conf, args);

    assert(conf.sigbits >= 1 && conf.sigbits <= (conf.datatype_size << 3));

    quantize_inword_switch(conf);
}


//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2012   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#include <boost/simd/reduction/include/functions/any.hpp>
#include <boost/simd/sdk/simd/native.hpp>
#include <boost/simd/sdk/simd/logical.hpp>
#include <boost/simd/sdk/simd/io.hpp>
#include <boost/simd/include/constants/true.hpp>
#include <boost/simd/include/constants/false.hpp>
#include <boost/simd/include/constants/inf.hpp>
#include <boost/simd/include/constants/nan.hpp>
#include <boost/simd/include/constants/minf.hpp>
#include <boost/simd/include/constants/mzero.hpp>

#include <nt2/sdk/unit/tests.hpp>
#include <nt2/sdk/unit/module.hpp>

NT2_TEST_CASE_TPL ( any, BOOST_SIMD_SIMD_TYPES)
{
  using boost::simd::True;
  using boost::simd::False;
  using boost::simd::tag::any_;
  using boost::simd::native;
  typedef BOOST_SIMD_DEFAULT_EXTENSION            ext_t;
  typedef native<T,ext_t>                         vT;
  typedef boost::simd::logical<T>                 lT;

  vT all_ok, none_ok, some_ok;
  for(std::size_t i=0;i<vT::static_size;++i)
  {
    none_ok[i] = T(0);
    some_ok[i] = all_ok[i] =T(1+i);
  }

  some_ok[vT::static_size/2] = T(0);

  NT2_TEST_EQUAL( boost::simd::any(all_ok) , True<lT>()  );
  NT2_TEST_EQUAL( boost::simd::any(some_ok), True<lT>()  );
  NT2_TEST_EQUAL( boost::simd::any(none_ok), False<lT>() );
}

NT2_TEST_CASE_TPL ( any_real, BOOST_SIMD_SIMD_REAL_TYPES)
{
  using boost::simd::True;
  using boost::simd::Minf;
  using boost::simd::Inf;
  using boost::simd::Nan;
  using boost::simd::False;
  using boost::simd::Mzero;
  using boost::simd::tag::any_;
  using boost::simd::native;
  typedef BOOST_SIMD_DEFAULT_EXTENSION            ext_t;
  typedef native<T,ext_t>                         vT;
  typedef boost::simd::logical<T>                 lT;

  NT2_TEST_EQUAL( boost::simd::any(Inf<vT>()) , True<lT>()   );
  NT2_TEST_EQUAL( boost::simd::any(Minf<vT>()), True<lT>()   );
  NT2_TEST_EQUAL( boost::simd::any(Nan<vT>()) , True<lT>()   );
  NT2_TEST_EQUAL( boost::simd::any(Mzero<vT>()), False<lT>() );
}


#pragma once

#include <boost/random/random_device.hpp>
#include <boost/random/uniform_int_distribution.hpp>

#include "calotypes/DataSelector.hpp"

namespace calotypes
{

/*! \brief Greedily selects data to minimize the dataset function up to the subset size. */
template <class Data>
class GreedyDataSelector
: public DataSelector<Data>
{
public:

	typedef std::shared_ptr<GreedyDataSelector> Ptr;
	typedef typename DataSelector<Data>::Dataset Dataset;

	GreedyDataSelector( const typename DatasetFunction<Data>::Ptr& m )
	: metric( m ) {}

	virtual void SelectData( const Dataset& data, unsigned int subsetSize, Dataset& subset )
	{
		subset.clear();
		subset.reserve( subsetSize );

		boost::random::random_device rng;

		Dataset available( data ); // Remaining data

		double current;
		std::vector<double> bestDelta;
		std::vector<unsigned int> bestInds;
		while( subset.size() < subsetSize && !available.empty() )
		{

// 			std::cout << "Subset is currently: " << std::endl;
// 			for( unsigned int i = 0; i < subset.size(); i++ )
// 			{
// 				std::cout << subset[i].name << " " << subset[i].pose.ToVector().transpose() << std::endl;
// 			}

			bestDelta.clear();
			bestInds.clear();

			current = metric->delta( subset, available[0] );
			bestDelta.push_back( current );
			bestInds.push_back( 0 );

			for( unsigned int i = 1; i < available.size(); i++ )
			{
				current = metric->delta( subset, available[i] );

// 				std::cout << "delta for " << available[i].name << " is " << current << std::endl;

				if( current > bestDelta[0] ) { continue; }
				// if current == bestDelta[0], we accumulate indices
				if( current < bestDelta[0] )
				{
					bestDelta.clear();
					bestInds.clear();
// 					std::cout << "New best delta for ind " << i << std::endl;
				}

				bestDelta.push_back( current );
				bestInds.push_back( i );
			}

			// Tie-break randomly
			if( bestDelta.size() > 1 )
			{
				boost::random::uniform_int_distribution<> dist( 0, bestDelta.size() - 1 );
				unsigned int tiebreak = dist( rng );
				bestInds[0] = bestInds[ tiebreak ];
				bestDelta[0] = bestDelta[ tiebreak ];
			}

			// Want to minimize - if greedy increases the score, we're done
			if( bestDelta[0] >= 0 ) { return; }

			subset.push_back( available[ bestInds[0] ] );
			available.erase( available.begin() + bestInds[0] );
		}
	}
private:

	typename DatasetFunction<Data>::Ptr metric;
};

template <class Data>
class RepeatedGreedyDataSelector
: public DataSelector<Data>
{
public:

	typedef std::shared_ptr<RepeatedGreedyDataSelector> Ptr;
	typedef typename DataSelector<Data>::Dataset Dataset;

	RepeatedGreedyDataSelector( const typename DatasetFunction<Data>::Ptr& m, unsigned int n = 1 )
	: metric( m ), greedy( m ), numRetries( n ) {}

	virtual void SelectData( const Dataset& data, unsigned int subsetSize, Dataset& bestSubset )
	{
		Dataset subset;
		double cost, bestCost = std::numeric_limits<double>::infinity();
		for( unsigned int i = 0; i < numRetries; i++ )
		{
			greedy.SelectData( data, subsetSize, subset );
			cost = (*metric)( subset );
			if( cost < bestCost )
			{
				bestSubset = subset;
				bestCost = cost;
			}
		}
	}

private:

	typename DatasetFunction<Data>::Ptr metric;
	GreedyDataSelector<Data> greedy;
	unsigned int numRetries;

};

} // end namespace calotypes


#ifndef _maps_Utils_hpp_
#define _maps_Utils_hpp_

#include <Eigen/Geometry>
#include <vector>

namespace maps {

class Utils {

public:
  static bool isOrthographic(const Eigen::Matrix4f& iMatrix);
  static bool composeViewMatrix(Eigen::Projective3f& oMatrix,
                                const Eigen::Matrix3f& iCalib,
                                const Eigen::Isometry3f& iPose,
                                const bool iIsOrthographic);
  static bool factorViewMatrix(const Eigen::Projective3f& iMatrix,
                               Eigen::Matrix3f& oCalib,
                               Eigen::Isometry3f& oPose,
                               bool& oIsOrthographic);

  static uint64_t rand64();
};

}

#endif


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

  @copyright 2016 NumScale SAS

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

#include <boost/simd/detail/overload.hpp>
#include <boost/simd/function/if_else_zero.hpp>
#include <boost/simd/function/rsqrt.hpp>

#if !defined(BOOST_SIMD_NO_INVALIDS)
#include <boost/simd/constant/inf.hpp>
#include <boost/simd/function/if_else.hpp>
#endif

namespace boost { namespace simd { namespace ext
{
   namespace bd = boost::dispatch;
   namespace bs = boost::simd;

   BOOST_DISPATCH_OVERLOAD( sqrt_
                          , (typename A0)
                          , bs::vmx_
                          , bs::pack_< bd::single_<A0>, bs::vmx_>
                          )
   {
      BOOST_FORCEINLINE A0 operator()(const A0& a0) const BOOST_NOEXCEPT
      {
        A0 that = if_else_zero(a0, a0 * rsqrt(a0));
        #if !defined(BOOST_SIMD_NO_INVALIDS)
        return if_else( a0 == Inf<A0>(), a0, that);
        #else
        return that;
        #endif
      }
   };

   BOOST_DISPATCH_OVERLOAD( sqrt_
                          , (typename A0)
                          , bs::vmx_
                          , bs::fast_tag
                          , bs::pack_< bd::single_<A0>, bs::vmx_>
                          )
   {
      BOOST_FORCEINLINE A0 operator()(bs::fast_tag const&, const A0& a0) const BOOST_NOEXCEPT
      {
        return if_else_zero(a0, a0 * fast_(rsqrt)(a0));
      }
   };
} } }

#endif


#include "initialPlace.h"
#include "placerBase.h"
#include "logger.h"
#include <iostream>

#include <Eigen/IterativeLinearSolvers>

#include "plot.h"

namespace replace {
using namespace std;

using Eigen::BiCGSTAB;
using Eigen::IdentityPreconditioner;

typedef Eigen::Triplet< float > T;

InitialPlaceVars::InitialPlaceVars()
  : maxIter(20),
  minDiffLength(1500),
  maxSolverIter(100),
  maxFanout(200),
  netWeightScale(800.0),
  incrementalPlaceMode(false) {}

void InitialPlaceVars::reset() {
  maxIter = 20;
  minDiffLength = 1500;
  maxSolverIter = 100;
  maxFanout = 200;
  netWeightScale = 800.0;
  incrementalPlaceMode = false;
}

InitialPlace::InitialPlace()
: ipVars_(), pb_(nullptr), log_(nullptr) {}

InitialPlace::InitialPlace(InitialPlaceVars ipVars,
    std::shared_ptr<PlacerBase> pb,
    std::shared_ptr<Logger> log)
: ipVars_(ipVars), pb_(pb), log_(log) {}

InitialPlace::~InitialPlace() {
  reset();
}

void InitialPlace::reset() {
  pb_ = nullptr;
  ipVars_.reset();
}

#ifdef ENABLE_CIMG_LIB
static PlotEnv pe;
#endif

void InitialPlace::doBicgstabPlace() {
  log_->procBegin("InitialPlace", 3);

  float errorX = 0.0f, errorY = 0.0f;

#ifdef ENABLE_CIMG_LIB
  pe.setPlacerBase(pb_);
  pe.Init();
#endif

  // normally, initial place will place all cells in the centers.
  if( !ipVars_.incrementalPlaceMode ) {
    placeInstsCenter();
  }

  // set ExtId for idx reference // easy recovery
  setPlaceInstExtId();
  for(int i=1; i<=ipVars_.maxIter; i++) {
    updatePinInfo();
    createSparseMatrix();

    // BiCGSTAB solver for initial place
    BiCGSTAB< SMatrix, IdentityPreconditioner > solver;
    solver.setMaxIterations(ipVars_.maxSolverIter);
    solver.compute(placeInstForceMatrixX_);
    instLocVecX_ = solver.solveWithGuess(fixedInstForceVecX_, instLocVecX_);
    errorX = solver.error();

    solver.compute(placeInstForceMatrixY_);
    instLocVecY_ = solver.solveWithGuess(fixedInstForceVecY_, instLocVecY_);
    errorY = solver.error();

    cout << "[InitialPlace]  Iter: " << i
      << " CG Error: " << max(errorX, errorY)
      << " HPWL: " << pb_->hpwl() << endl;
    updateCoordi();

#ifdef ENABLE_CIMG_LIB
    pe.SaveCellPlotAsJPEG(
        string("InitPlace ") + to_string(i), false,
        string("./plot/cell/ip_") + to_string(i));
#endif

    if( max(errorX, errorY) <= 1e-5 && i >= 5 ) {
      break;
    }
  }

  log_->procEnd("InitialPlace", 3);
}

// starting point of initial place is center.
void InitialPlace::placeInstsCenter() {
  const int centerX = pb_->die().coreCx();
  const int centerY = pb_->die().coreCy();

  for(auto& inst: pb_->placeInsts()) {
    inst->setCenterLocation(centerX, centerY);
  }
}

void InitialPlace::setPlaceInstExtId() {
  // reset ExtId for all instances
  for(auto& inst : pb_->insts()) {
    inst->setExtId(INT_MAX);
  }
  // set index only with place-able instances
  for(auto& inst : pb_->placeInsts()) {
    inst->setExtId(&inst - &(pb_->placeInsts()[0]));
  }
}

void InitialPlace::updatePinInfo() {
  // reset all MinMax attributes
  for(auto& pin : pb_->pins()) {
    pin->unsetMinPinX();
    pin->unsetMinPinY();
    pin->unsetMaxPinX();
    pin->unsetMaxPinY();
  }

  for(auto& net : pb_->nets()) {
    Pin* pinMinX = nullptr, *pinMinY = nullptr;
    Pin* pinMaxX = nullptr, *pinMaxY = nullptr;
    int lx = INT_MAX, ly = INT_MAX;
    int ux = INT_MIN, uy = INT_MIN;

    // Mark B2B info on Pin structures
    for(auto& pin : net->pins()) {
      if( lx > pin->cx() ) {
        if( pinMinX ) {
          pinMinX->unsetMinPinX();
        }
        lx = pin->cx();
        pinMinX = pin;
        pinMinX->setMinPinX();
      }

      if( ux < pin->cx() ) {
        if( pinMaxX ) {
          pinMaxX->unsetMaxPinX();
        }
        ux = pin->cx();
        pinMaxX = pin;
        pinMaxX->setMaxPinX();
      }

      if( ly > pin->cy() ) {
        if( pinMinY ) {
          pinMinY->unsetMinPinY();
        }
        ly = pin->cy();
        pinMinY = pin;
        pinMinY->setMinPinY();
      }

      if( uy < pin->cy() ) {
        if( pinMaxY ) {
          pinMaxY->unsetMaxPinY();
        }
        uy = pin->cy();
        pinMaxY = pin;
        pinMaxY->setMaxPinY();
      }
    }
  }
}

// solve placeInstForceMatrixX_ * xcg_x_ = xcg_b_ and placeInstForceMatrixY_ * ycg_x_ = ycg_b_ eq.
void InitialPlace::createSparseMatrix() {
  const int placeCnt = pb_->placeInsts().size();
  instLocVecX_.resize( placeCnt );
  fixedInstForceVecX_.resize( placeCnt );
  instLocVecY_.resize( placeCnt );
  fixedInstForceVecY_.resize( placeCnt );

  placeInstForceMatrixX_.resize( placeCnt, placeCnt );
  placeInstForceMatrixY_.resize( placeCnt, placeCnt );

  //
  // listX and listY is a temporary vector that have tuples, (idx1, idx2, val)
  //
  // listX finally becomes placeInstForceMatrixX_
  // listY finally becomes placeInstForceMatrixY_
  //
  // The triplet vector is recommended usages
  // to fill in SparseMatrix from Eigen docs.
  //

  vector< T > listX, listY;
  listX.reserve(1000000);
  listY.reserve(1000000);

  // initialize vector
  for(auto& inst : pb_->placeInsts()) {
    int idx = inst->extId();

    instLocVecX_(idx) = inst->cx();
    instLocVecY_(idx) = inst->cy();

    fixedInstForceVecX_(idx) = fixedInstForceVecY_(idx) = 0;
  }

  // for each net
  for(auto& net : pb_->nets()) {

    // skip for small nets.
    if( net->pins().size() <= 1 ) {
      continue;
    }

    // escape long time cals on huge fanout.
    //
    if( net->pins().size() >= ipVars_.maxFanout) {
      continue;
    }

    float netWeight = ipVars_.netWeightScale
      / (net->pins().size() - 1);
    //cout << "net: " << net.net()->getConstName() << endl;

    // foreach two pins in single nets.
    for(auto& pin1 : net->pins()) {
      int pinIdx1 = &pin1 - &(net->pins()[0]);
      for(auto& pin2 : net->pins()) {
        int pinIdx2 = &pin2 - &(net->pins()[0]);

        //
        // will compare two pins "only once."
        //
        if( pinIdx1 < pinIdx2 ) {
          break;
        }

        // no need to fill in when instance is same
        if( pin1->instance() == pin2->instance() ) {
          continue;
        }

        // B2B modeling on min/maxX pins.
        if( pin1->isMinPinX() || pin1->isMaxPinX() ||
            pin2->isMinPinX() || pin2->isMaxPinX() ) {
          int diffX = abs(pin1->cx() - pin2->cx());
          float weightX = 0;
          if( diffX > ipVars_.minDiffLength ) {
            weightX = netWeight / diffX;
          }
          else {
            weightX = netWeight
              / ipVars_.minDiffLength;
          }
          //cout << weightX << endl;

          // both pin cames from instance
          if( pin1->isPlaceInstConnected()
              && pin2->isPlaceInstConnected() ) {
            const int inst1 = pin1->instance()->extId();
            const int inst2 = pin2->instance()->extId();
            //cout << "inst: " << inst1 << " " << inst2 << endl;

            listX.push_back( T(inst1, inst1, weightX) );
            listX.push_back( T(inst2, inst2, weightX) );

            listX.push_back( T(inst1, inst2, -weightX) );
            listX.push_back( T(inst2, inst1, -weightX) );

            //cout << pin1->cx() << " "
            //  << pin1->instance()->cx() << endl;
            fixedInstForceVecX_(inst1) +=
              -weightX * (
              (pin1->cx() - pin1->instance()->cx()) -
              (pin2->cx() - pin2->instance()->cx()));

            fixedInstForceVecX_(inst2) +=
              -weightX * (
              (pin2->cx() - pin2->instance()->cx()) -
              (pin1->cx() - pin1->instance()->cx()));
          }
          // pin1 from IO port / pin2 from Instance
          else if( !pin1->isPlaceInstConnected()
              && pin2->isPlaceInstConnected() ) {
            const int inst2 = pin2->instance()->extId();
            //cout << "inst2: " << inst2 << endl;
            listX.push_back( T(inst2, inst2, weightX) );
            fixedInstForceVecX_(inst2) += weightX *
              ( pin1->cx() -
                ( pin2->cx() - pin2->instance()->cx()) );
          }
          // pin1 from Instance / pin2 from IO port
          else if( pin1->isPlaceInstConnected()
              && !pin2->isPlaceInstConnected() ) {
            const int inst1 = pin1->instance()->extId();
            //cout << "inst1: " << inst1 << endl;
            listX.push_back( T(inst1, inst1, weightX) );
            fixedInstForceVecX_(inst1) += weightX *
              ( pin2->cx() -
                ( pin1->cx() - pin1->instance()->cx()) );
          }
        }

        // B2B modeling on min/maxY pins.
        if( pin1->isMinPinY() || pin1->isMaxPinY() ||
            pin2->isMinPinY() || pin2->isMaxPinY() ) {

          int diffY = abs(pin1->cy() - pin2->cy());
          float weightY = 0;
          if( diffY > ipVars_.minDiffLength ) {
            weightY = netWeight / diffY;
          }
          else {
            weightY = netWeight
              / ipVars_.minDiffLength;
          }

          // both pin cames from instance
          if( pin1->isPlaceInstConnected()
              && pin2->isPlaceInstConnected() ) {
            const int inst1 = pin1->instance()->extId();
            const int inst2 = pin2->instance()->extId();

            listY.push_back( T(inst1, inst1, weightY) );
            listY.push_back( T(inst2, inst2, weightY) );

            listY.push_back( T(inst1, inst2, -weightY) );
            listY.push_back( T(inst2, inst1, -weightY) );

            fixedInstForceVecY_(inst1) +=
              -weightY * (
              (pin1->cy() - pin1->instance()->cy()) -
              (pin2->cy() - pin2->instance()->cy()));

            fixedInstForceVecY_(inst2) +=
              -weightY * (
              (pin2->cy() - pin2->instance()->cy()) -
              (pin1->cy() - pin1->instance()->cy()));
          }
          // pin1 from IO port / pin2 from Instance
          else if( !pin1->isPlaceInstConnected()
              && pin2->isPlaceInstConnected() ) {
            const int inst2 = pin2->instance()->extId();
            listY.push_back( T(inst2, inst2, weightY) );
            fixedInstForceVecY_(inst2) += weightY *
              ( pin1->cy() -
                ( pin2->cy() - pin2->instance()->cy()) );
          }
          // pin1 from Instance / pin2 from IO port
          else if( pin1->isPlaceInstConnected()
              && !pin2->isPlaceInstConnected() ) {
            const int inst1 = pin1->instance()->extId();
            listY.push_back( T(inst1, inst1, weightY) );
            fixedInstForceVecY_(inst1) += weightY *
              ( pin2->cy() -
                ( pin1->cy() - pin1->instance()->cy()) );
          }
        }
      }
    }
  }

  placeInstForceMatrixX_.setFromTriplets(listX.begin(), listX.end());
  placeInstForceMatrixY_.setFromTriplets(listY.begin(), listY.end());
}

void InitialPlace::updateCoordi() {
  for(auto& inst : pb_->placeInsts()) {
    int idx = inst->extId();
    inst->dbSetCenterLocation( instLocVecX_(idx), instLocVecY_(idx) );
  }
}

}


/* Copyright (c) 2017 - 2020, the adamantine authors.
 *
 * This file is subject to the Modified BSD License and may not be distributed
 * without copyright and license information. Please refer to the file LICENSE
 * for the text and further information on this license.
 */

#define BOOST_TEST_MODULE ImplicitOperator

#include <Geometry.hh>
#include <ImplicitOperator.hh>
#include <ThermalOperator.hh>

#include <deal.II/dofs/dof_tools.h>
#include <deal.II/fe/fe_q.h>
#include <deal.II/lac/dynamic_sparsity_pattern.h>
#include <deal.II/lac/sparse_matrix.h>
#include <deal.II/numerics/matrix_tools.h>

#include <boost/property_tree/ptree.hpp>

#include "main.cc"

BOOST_AUTO_TEST_CASE(implicit_operator)
{
  MPI_Comm communicator = MPI_COMM_WORLD;

  // Create the Geometry
  boost::property_tree::ptree geometry_database;
  geometry_database.put("length", 12);
  geometry_database.put("length_divisions", 4);
  geometry_database.put("height", 6);
  geometry_database.put("height_divisions", 5);
  adamantine::Geometry<2> geometry(communicator, geometry_database);
  // Create the DoFHandler
  dealii::FE_Q<2> fe(2);
  dealii::DoFHandler<2> dof_handler(geometry.get_triangulation());
  dof_handler.distribute_dofs(fe);
  dealii::AffineConstraints<double> affine_constraints;
  affine_constraints.close();
  dealii::QGauss<1> quad(3);

  // Create the MaterialProperty
  boost::property_tree::ptree mat_prop_database;
  mat_prop_database.put("property_format", "polynomial");
  mat_prop_database.put("n_materials", 1);
  mat_prop_database.put("material_0.solid.density", 1.);
  mat_prop_database.put("material_0.powder.density", 1.);
  mat_prop_database.put("material_0.liquid.density", 1.);
  mat_prop_database.put("material_0.solid.specific_heat", 1.);
  mat_prop_database.put("material_0.powder.specific_heat", 1.);
  mat_prop_database.put("material_0.liquid.specific_heat", 1.);
  mat_prop_database.put("material_0.solid.thermal_conductivity", 10.);
  mat_prop_database.put("material_0.powder.thermal_conductivity", 10.);
  mat_prop_database.put("material_0.liquid.thermal_conductivity", 10.);
  std::shared_ptr<adamantine::MaterialProperty<2>> mat_properties(
      new adamantine::MaterialProperty<2>(
          communicator, geometry.get_triangulation(), mat_prop_database));

  // Initialize the ThermalOperator
  std::shared_ptr<adamantine::ThermalOperator<2, 2, dealii::MemorySpace::Host>>
      thermal_operator = std::make_shared<
          adamantine::ThermalOperator<2, 2, dealii::MemorySpace::Host>>(
          communicator, mat_properties);
  thermal_operator->reinit(dof_handler, affine_constraints, quad);
  thermal_operator->compute_inverse_mass_matrix(dof_handler,
                                                affine_constraints);
  dealii::LA::distributed::Vector<double, dealii::MemorySpace::Host> dummy(
      thermal_operator->m());
  thermal_operator->evaluate_material_properties(dummy);

  // Initialize the ImplicitOperator
  adamantine::ImplicitOperator<dealii::MemorySpace::Host> implicit_operator(
      thermal_operator, false);
  BOOST_CHECK(implicit_operator.m() == 99);
  BOOST_CHECK(implicit_operator.n() == 99);
  BOOST_CHECK_THROW(implicit_operator.Tvmult(dummy, dummy),
                    adamantine::NotImplementedExc);
  BOOST_CHECK_THROW(implicit_operator.vmult_add(dummy, dummy),
                    adamantine::NotImplementedExc);
  BOOST_CHECK_THROW(implicit_operator.Tvmult_add(dummy, dummy),
                    adamantine::NotImplementedExc);

  // Initialize the ImplicitOperator with JFNK
  adamantine::ImplicitOperator<dealii::MemorySpace::Host>
      implicit_operator_jfnk(thermal_operator, true);

  // Check that ImplicitOperator with and without JFNK give the same results.
  unsigned int const size = thermal_operator->m();
  dealii::LA::distributed::Vector<double, dealii::MemorySpace::Host> source(
      size);
  std::shared_ptr<
      dealii::LA::distributed::Vector<double, dealii::MemorySpace::Host>>
  inverse_mass_matrix(
      new dealii::LA::distributed::Vector<double, dealii::MemorySpace::Host>(
          size));
  dealii::LA::distributed::Vector<double, dealii::MemorySpace::Host> dst(size);
  dealii::LA::distributed::Vector<double, dealii::MemorySpace::Host> dst_jfnk(
      size);
  for (unsigned int i = 0; i < size; ++i)
  {
    source[i] = 1.;
    (*inverse_mass_matrix)[i] = 1.;
  }

  implicit_operator.set_tau(1.);
  implicit_operator_jfnk.set_tau(1.);

  implicit_operator.set_inverse_mass_matrix(inverse_mass_matrix);
  implicit_operator_jfnk.set_inverse_mass_matrix(inverse_mass_matrix);

  implicit_operator.vmult(dst, source);
  implicit_operator_jfnk.vmult(dst_jfnk, source);

  double const tolerance = 1e-7;
  BOOST_CHECK_CLOSE(dst.l2_norm(), dst_jfnk.l2_norm(), tolerance);
}