#include "cnn/model.h"
#include "cnn/tensor.h"
#include "cnn/aligned-mem-pool.h"
#include "cnn/cnn.h"
#include "cnn/macros.h"

#include <unordered_set>
#include <iostream>
#include <fstream>
#include <sstream>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>

#if HAVE_CUDA
#include <thrust/version.h>
#include "cnn/gpu-ops.h"
#include "cnn/cuda.h"
#include <thrust/device_ptr.h>
#include <thrust/transform.h>
#include <thrust/transform_reduce.h>
#include <cnn/functors.h>
#endif

using namespace std;

namespace cnn {

    extern AlignedMemoryPool<ALIGN>* glb_temp_lookup_gradient_value_mem;

    ParametersBase::~ParametersBase() {}

    Parameters::Parameters(const Dim& d, cnn::real scale, std::string nodename) : dim(d), name(nodename) {
        values.d = g.d = d;
        values.v = (cnn::real*)cnn_mm_malloc(d.size() * sizeof(cnn::real), CNN_ALIGN);
        values.m_device_id = device_id;
        if (scale == 1.0)
            /// fix scale to sqrt(6) / sqrt(d.d.sum_dims())
            TensorTools::Randomize(values);
        else
            TensorTools::Randomize(values, scale);
        g.v = (cnn::real*)cnn_mm_malloc(d.size() * sizeof(cnn::real), CNN_ALIGN);
        g.m_device_id = device_id;

        TensorTools::Zero(g);
    }

    size_t Parameters::size() const { return dim.size(); }

    void Parameters::reset_to_zero()
    {
#if HAVE_CUDA
        gpu::set_to_value_of(values.d.size(), values.v, 0.0);
#else
        (*values) *= 0.0;
#endif
    }

    void Parameters::scale_parameters(cnn::real a) {
        (*g) *= a;
    }

    void Parameters::squared_l2norm(cnn::real* sqnorm) const {
#if HAVE_CUDA
        gpu::l2_norm_reducer(values.d.size(), values.v, sqnorm, true, false);
#else
        *sqnorm = (*values).squaredNorm();
#endif
    }

    void Parameters::g_squared_l2norm(cnn::real* sqnorm) const {
#if HAVE_CUDA
        gpu::l2_norm_reducer(g.d.size(), g.v, sqnorm, true, false);
#else
        *sqnorm = (*g).squaredNorm();
#endif
    }

    /// use abs value to clip for each element
    /// compared to using gradient norm, this is simpler and cheaper
    void Parameters::g_simple_clipping(cnn::real threshold)  {
        cnn::real thr = threshold / g.d.size();
#if HAVE_CUDA
        gpu::simple_clipping(g.d.size(), g.v, g.v, thr);
#else
        for (int k = 0; k < g.d.size(); k++)
        {
            if (fabs(g.v[k]) > thr){
                if (g.v[k] > 0)
                    g.v[k] = thr;
                else
                    g.v[k] = -thr;
            }
        }
#endif
}

void Parameters::copy(const Parameters & param) {
  assert(dim == param.dim);
  TensorTools::CopyElements(values, param.values);
  this->name = param.name;
}

void Parameters::accumulate_grad(const Tensor& d) {
#if HAVE_CUDA
    if (sizeof(cnn::real) == sizeof(float))
        CUBLAS_CHECK(cublasSaxpy(cublas_handle, g.d.size(), reinterpret_cast<float*>(kSCALAR_ONE), reinterpret_cast<float*>(d.v), 1, reinterpret_cast<float*>(g.v), 1));
    else if (sizeof(cnn::real) == sizeof(double))
        CUBLAS_CHECK(cublasDaxpy(cublas_handle, g.d.size(), reinterpret_cast<double*>(kSCALAR_ONE), reinterpret_cast<double*>(d.v), 1, reinterpret_cast<double*>(g.v), 1));
#else
  *g += *d;
#endif
}

void Parameters::clear() {
  TensorTools::Zero(g);
}

LookupParameters::~LookupParameters()
{
    for (unsigned i = 0; i < values.size(); ++i) {
        auto& v = values[i];
#ifdef USE_CPU_FOR_LOOKUP_PARAM
        cnn_mm_free_host(v.v);
#else
        cnn_mm_free(v.v);
#endif
    }

    clear();
}

void LookupParameters::clear()
{
    /// the working memory is at GPU
    glb_temp_lookup_gradient_value_mem->free();

    values_for_non_zero_grads.clear();

    grads.clear();
}

LookupParameters::LookupParameters(unsigned n, const Dim& d, cnn::real scale, std::string nodename) : dim(d), values(n), grads(n), name(nodename) {
#ifdef USE_CPU_FOR_LOOKUP_PARAM
  bool b_cpu = true;
#else
  bool b_cpu = false;
#endif

  for (unsigned i = 0; i < n; ++i) {
    auto& v = values[i];
    v.d = d;
#ifdef USE_CPU_FOR_LOOKUP_PARAM
    v.v = (cnn::real*)cnn_mm_malloc_host(d.size() * sizeof(cnn::real), CNN_ALIGN);
    v.m_device_id= CPUDEVICE; /// for cpu
#else
    v.v = (cnn::real*)cnn_mm_malloc(d.size() * sizeof(cnn::real), CNN_ALIGN);
    v.m_device_id = device_id;
#endif
	if (scale == 1.0)
		/// fix scale to sqrt(6) / sqrt(d.d.sum_dims())
		TensorTools::Randomize(v);
	else
		TensorTools::Randomize(v, scale);
  }
}

void LookupParameters::scale_parameters(cnn::real a) {
  for (auto& p : values)
    (*p) *= a;
}

void LookupParameters::Initialize(unsigned index, const vector<cnn::real>& val) {
  assert(int(val.size()) == int(dim.size()));
#if HAVE_CUDA
  cerr << "implement LookupParameters::Initialize\n";
  throw cuda_not_implemented("LookupParameters::Initialize");
#else
  memcpy(values[index].v, &val[0], val.size() * sizeof(cnn::real));
#endif
}

size_t LookupParameters::size() const {
  return values.size() * dim.size();
}

void LookupParameters::g_squared_l2norm(cnn::real* sqnorm) const {
#ifdef HAVE_CUDA
    bool acc = false;
    for (auto g : grads) {
        gpu::l2_norm_reducer(g.second.d.size(), g.second.v, sqnorm, true, acc);
        acc = true;
    }
#else
    real a = 0;
    for (auto g : grads)
    {
        a += (*g.second).squaredNorm();
    }
    *sqnorm = a;
#endif
}

void LookupParameters::squared_l2norm(cnn::real* sqnorm) const {
    cnn::real a = 0;
    for (unsigned i = 0; i < values.size(); ++i)
        a += (*values[i]).squaredNorm();
#if HAVE_CUDA
    CUDA_CHECK(cudaMemcpy(sqnorm, &a, sizeof(cnn::real), cudaMemcpyHostToDevice));
#else
    *sqnorm = a;
#endif
    /*
#if HAVE_CUDA
    bool acc = false;
    for (unsigned i = 0; i < values.size(); ++i) {
        gpu::l2_norm_reducer(values[i].d.size(), values[i].v, sqnorm, true, acc);
        acc = true;
    }
#else
    cnn::real a = 0;
    for (unsigned i = 0; i < values.size(); ++i)
        a += (*values[i]).squaredNorm();
    *sqnorm = a;
#endif
    */
}

void LookupParameters::g_simple_clipping(cnn::real thr)  {
#if HAVE_CUDA
    for (auto g : grads) {
        cnn::real threshold = thr / g.second.d.size();
        gpu::simple_clipping(g.second.d.size(), g.second.v, g.second.v, threshold);
    }
#else
    for (auto g: grads)
    {
        cnn::real threshold = thr / g.second.d.size();
        for (int k = 0; k < g.second.d.size(); k++)
        {
            if (fabs(g.second.v[k]) > threshold){
                if (g.second.v[k] > 0)
                    g.second.v[k] = threshold;
                else
                    g.second.v[k] = -threshold;
            }
        }
    }
#endif
}

void LookupParameters::copy(const LookupParameters & param) {
    assert(dim == param.dim);
    for (size_t i = 0; i < param.values.size(); ++i)
        TensorTools::CopyElements(values[i], param.values[i]);
    this->name = param.name;
}

void LookupParameters::copy(const std::map<int, std::vector<cnn::real>> & param) {
    for (size_t i = 0; i < values.size(); ++i)
    {
        if (param.find(i) != param.end())
            TensorTools::SetElements(values[i], param.find(i)->second);
        else
            break;
    }
}

void LookupParameters::accumulate_grad(unsigned index, const Tensor& d) {
  if (grads.find(index) == grads.end())
  {
      cnn::real *g = (cnn::real*)glb_temp_lookup_gradient_value_mem->allocate(d.d.size() * sizeof(cnn::real));
      Tensor vv(d.d, g, device_id);
      vv.m_device_id = device_id; /// for cpu
      TensorTools::Zero(vv);  // gradient needs to be zero in the begining
      grads[index] = vv;
  }

#if HAVE_CUDA
  if (sizeof(cnn::real) == sizeof(float))
      CUBLAS_CHECK(cublasSaxpy(cublas_handle, d.d.size(), reinterpret_cast<float*>(kSCALAR_ONE), reinterpret_cast<float*>(d.v), 1, reinterpret_cast<float*>(grads[index].v), 1));
  else if (sizeof(cnn::real) == sizeof(double))
      CUBLAS_CHECK(cublasDaxpy(cublas_handle, d.d.size(), reinterpret_cast<double*>(kSCALAR_ONE), reinterpret_cast<double*>(d.v), 1, reinterpret_cast<double*>(grads[index].v), 1));
#else
  *grads[index] += *d;
#endif
}

Model::~Model() {
  for (auto p : all_params) delete p;
  if (gradient_norm_scratch)
      cnn_mm_free(gradient_norm_scratch);
  if (gscale)
      cnn_mm_free_host(gscale);
}

void Model::project_weights(cnn::real radius) {
  static cnn::real* project_scratch = 0;
  if (!project_scratch)
    project_scratch = (cnn::real*)cnn_mm_malloc(all_params.size() * sizeof(cnn::real), CNN_ALIGN);
  int pi = 0;
  for (auto p : all_params) {
    p->squared_l2norm(&project_scratch[pi]);
    ++pi;
  }
  cnn::real gg = 0;
  for (int i = 0; i < pi; ++i)
    gg += project_scratch[i];
  cerr << "NORM: " << sqrt(gg) << endl;
}

cnn::real Model::gradient_l2_norm() const {
  if (!gradient_norm_scratch)
  {
      gradient_norm_scratch = (cnn::real*)cnn_mm_malloc(all_params.size() * sizeof(cnn::real), CNN_ALIGN);
  }
  if (gscale == nullptr)
      gscale = (cnn::real*)cnn_mm_malloc_host(sizeof(cnn::real) * all_params.size(), CNN_ALIGN);
  int pi = 0;
  for (auto p : all_params) {
    p->g_squared_l2norm(&gradient_norm_scratch[pi]);
    ++pi;
  }
#if HAVE_CUDA
//  gpu::l2_norm_reducer(all_params.size(), gradient_norm_scratch, gradient_norm_scratch, false, false);
//  cudaMemcpy(gscale, gradient_norm_scratch, sizeof(cnn::real),  cudaMemcpyDeviceToHost);
  // *gscale = sqrt(*gscale);
  // return *gscale;

  // do reduction in CPU as one whole block of memcpy can be fast
  CUDA_CHECK(cudaMemcpy(gscale, gradient_norm_scratch, sizeof(cnn::real) * all_params.size(), cudaMemcpyDeviceToHost));
  cnn::real gg = 0;
  for (int k = 0; k < all_params.size(); k++)
      gg += gscale[k] * gscale[k];
  return sqrt(gg);
#else
  cnn::real gg = 0;
  for (int i = 0; i < pi; ++i)
    gg += gradient_norm_scratch[i];
  return sqrt(gg);
#endif
}

void Model::simple_gradient_clipping(cnn::real threshold)  {
    for (auto p : all_params) {
        p->g_simple_clipping(threshold);
    }
}

Parameters* Model::add_parameters(const Dim& d, cnn::real scale, std::string nodename) {
  Parameters* p = new Parameters(d, scale, nodename);
  all_params.push_back(p);
  params.push_back(p);
  return p;
}

LookupParameters* Model::add_lookup_parameters(unsigned n, const Dim& d, cnn::real scale, ::string nodename) {
  LookupParameters* p = new LookupParameters(n,d, scale, nodename);
  if (nodename != "") p->name = nodename;
  all_params.push_back(p);
  lookup_params.push_back(p);
  return p;
}

void Model::reset_gradient() {
  for (auto p : params) { p->clear(); }
  for (auto p : lookup_params) { p->clear(); }
}

void save_cnn_model(std::string filename, Model* model) {
#ifdef BINARY_BOOST
    ofstream out(filename, ios::binary);
    boost::archive::binary_oarchive oa(out);
#else
    ofstream out(filename, ofstream::out);
    boost::archive::text_oarchive oa(out);
#endif
    oa << *model;
    out.close();
};

void load_cnn_model(std::string filename, Model* model) {
#ifdef BINARY_BOOST
    ifstream in(filename, ios::binary);
    if (in.is_open())
    {
        boost::archive::binary_iarchive ia(in);
#else
    ifstream in(filename, ifstream::in);
    if (in.is_open())
    {
        boost::archive::text_iarchive ia(in);
#endif
        ia >> *model;
    }
};

} // namespace cnn


#include <boost/test/unit_test.hpp>
#include <k52/common/floating_point.h>

using ::k52::common::FloatingPoint;

BOOST_AUTO_TEST_SUITE(floating_point_tests);

BOOST_AUTO_TEST_CASE(are_equal_true)
{
    //Prepare
    double x = -123.000000000004;
    double y = -123.000000000001;

    //Test
    bool result = FloatingPoint::AreEqual(x, y);

    //Check
    BOOST_REQUIRE_EQUAL(result, true);
}

BOOST_AUTO_TEST_CASE(are_equal_false)
{
    //Prepare
    double x = -123.0000004;
    double y = -123.0000001;

    //Test
    bool result = FloatingPoint::AreEqual(x, y);

    //Check
    BOOST_REQUIRE_EQUAL(result, false);
}

BOOST_AUTO_TEST_CASE(is_zero_true)
{
    //Prepare
    double x = -0.000000000001;

    //Test
    bool result = FloatingPoint::IsZero(x);

    //Check
    BOOST_REQUIRE_EQUAL(result, true);
}

BOOST_AUTO_TEST_CASE(is_zero_false)
{
    //Prepare
    double x = -0.000001;
    //Test
    bool result = FloatingPoint::IsZero(x);

    //Check
    BOOST_REQUIRE_EQUAL(result, false);
}

BOOST_AUTO_TEST_SUITE_END();


// g2o - General Graph Optimization
// Copyright (C) 2012 R. Kümmerle
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright notice,
//   this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
//   notice, this list of conditions and the following disclaimer in the
//   documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

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

#include "g2o/stuff/command_args.h"
#include "g2o/core/batch_stats.h"
#include "g2o/core/sparse_optimizer.h"
#include "g2o/core/block_solver.h"
#include "g2o/core/solver.h"
#include "g2o/core/optimization_algorithm_levenberg.h"
#include "g2o/core/base_vertex.h"
#include "g2o/core/base_binary_edge.h"
#include "g2o/solvers/dense/linear_solver_dense.h"
#include "g2o/solvers/structure_only/structure_only_solver.h"
#include "g2o/solvers/pcg/linear_solver_pcg.h"

#include "EXTERNAL/ceres/autodiff.h"

#if defined G2O_HAVE_CHOLMOD
#include "g2o/solvers/cholmod/linear_solver_cholmod.h"
#elif defined G2O_HAVE_CSPARSE
#include "g2o/solvers/csparse/linear_solver_csparse.h"
#endif

using namespace g2o;
using namespace std;
using namespace Eigen;

/**
 * \brief camera vertex which stores the parameters for a pinhole camera
 *
 * The parameters of the camera are
 * - rx,ry,rz representing the rotation axis, whereas the angle is given by ||(rx,ry,rz)||
 * - tx,ty,tz the translation of the camera
 * - f the focal length of the camera
 * - k1, k2 two radial distortion parameters
 */
class VertexCameraBAL : public BaseVertex<9, Eigen::VectorXd>
{
  public:
    EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
    VertexCameraBAL()
    {
    }

    virtual bool read(std::istream& /*is*/)
    {
      cerr << __PRETTY_FUNCTION__ << " not implemented yet" << endl;
      return false;
    }

    virtual bool write(std::ostream& /*os*/) const
    {
      cerr << __PRETTY_FUNCTION__ << " not implemented yet" << endl;
      return false;
    }

    virtual void setToOriginImpl()
    {
      cerr << __PRETTY_FUNCTION__ << " not implemented yet" << endl;
    }

    virtual void oplusImpl(const double* update)
    {
      Eigen::VectorXd::ConstMapType v(update, VertexCameraBAL::Dimension);
      _estimate += v;
    }
};

/**
 * \brief 3D world feature
 *
 * A 3D point feature in the world
 */
class VertexPointBAL : public BaseVertex<3, Eigen::Vector3d>
{
  public:
    EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
    VertexPointBAL()
    {
    }

    virtual bool read(std::istream& /*is*/)
    {
      cerr << __PRETTY_FUNCTION__ << " not implemented yet" << endl;
      return false;
    }

    virtual bool write(std::ostream& /*os*/) const
    {
      cerr << __PRETTY_FUNCTION__ << " not implemented yet" << endl;
      return false;
    }

    virtual void setToOriginImpl()
    {
      cerr << __PRETTY_FUNCTION__ << " not implemented yet" << endl;
    }

    virtual void oplusImpl(const double* update)
    {
      Eigen::Vector3d::ConstMapType v(update);
      _estimate += v;
    }
};

/**
 * \brief edge representing the observation of a world feature by a camera
 *
 * see: http://grail.cs.washington.edu/projects/bal/
 * We use a pinhole camera model; the parameters we estimate for each camera
 * area rotation R, a translation t, a focal length f and two radial distortion
 * parameters k1 and k2. The formula for projecting a 3D point X into a camera
 * R,t,f,k1,k2 is:
 * P  =  R * X + t       (conversion from world to camera coordinates)
 * p  = -P / P.z         (perspective division)
 * p' =  f * r(p) * p    (conversion to pixel coordinates) where P.z is the third (z) coordinate of P.
 *
 * In the last equation, r(p) is a function that computes a scaling factor to undo the radial
 * distortion:
 * r(p) = 1.0 + k1 * ||p||^2 + k2 * ||p||^4.
 *
 * This gives a projection in pixels, where the origin of the image is the
 * center of the image, the positive x-axis points right, and the positive
 * y-axis points up (in addition, in the camera coordinate system, the positive
 * z-axis points backwards, so the camera is looking down the negative z-axis,
 * as in OpenGL).
 */
class EdgeObservationBAL : public BaseBinaryEdge<2, Vector2d, VertexCameraBAL, VertexPointBAL>
{
  public:
    EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
    EdgeObservationBAL()
    {
    }
    virtual bool read(std::istream& /*is*/)
    {
      cerr << __PRETTY_FUNCTION__ << " not implemented yet" << endl;
      return false;
    }
    virtual bool write(std::ostream& /*os*/) const
    {
      cerr << __PRETTY_FUNCTION__ << " not implemented yet" << endl;
      return false;
    }

    template<typename T>
    inline void cross(const T x[3], const T y[3], T result[3]) const
    {
      result[0] = x[1] * y[2] - x[2] * y[1];
      result[1] = x[2] * y[0] - x[0] * y[2];
      result[2] = x[0] * y[1] - x[1] * y[0];
    }

    template<typename T>
    inline T dot(const T x[3], const T y[3]) const { return (x[0] * y[0] + x[1] * y[1] + x[2] * y[2]);}

    template<typename T>
    inline T squaredNorm(const T x[3]) const { return dot<T>(x, x);}

    /**
     * templatized function to compute the error as described in the comment above
     */
    template<typename T>
    bool operator()(const T* camera, const T* point, T* error) const
    {
      // Rodrigues' formula for the rotation
      T p[3];
      T theta = sqrt(squaredNorm(camera));
      if (theta > T(0)) {
        T v[3];
        v[0] = camera[0] / theta;
        v[1] = camera[1] / theta;
        v[2] = camera[2] / theta;
        T cth = cos(theta);
        T sth = sin(theta);

        T vXp[3];
        cross(v, point, vXp);
        T vDotp = dot(v, point);
        T oneMinusCth = T(1) - cth;

        for (int i = 0; i < 3; ++i)
          p[i] = point[i] * cth + vXp[i] * sth + v[i] * vDotp * oneMinusCth;
      } else {
        // taylor expansion for theta close to zero
        T aux[3];
        cross(camera, point, aux);
        for (int i = 0; i < 3; ++i)
          p[i] = point[i] + aux[i];
      }

      // translation of the camera
      p[0] += camera[3];
      p[1] += camera[4];
      p[2] += camera[5];

      // perspective division
      T projectedPoint[2];
      projectedPoint[0] = - p[0] / p[2];
      projectedPoint[1] = - p[1] / p[2];

      // conversion to pixel coordinates
      T radiusSqr = projectedPoint[0]*projectedPoint[0] + projectedPoint[1]*projectedPoint[1];
      T f         = T(camera[6]);
      T k1        = T(camera[7]);
      T k2        = T(camera[8]);
      T r_p       = T(1) + k1 * radiusSqr + k2 * radiusSqr * radiusSqr;
      T prediction[2];
      prediction[0] = f * r_p * projectedPoint[0];
      prediction[1] = f * r_p * projectedPoint[1];

      error[0] = prediction[0] - T(measurement()(0));
      error[1] = prediction[1] - T(measurement()(1));

      return true;
    }

    void computeError()
    {
      const VertexCameraBAL* cam = static_cast<const VertexCameraBAL*>(vertex(0));
      const VertexPointBAL* point = static_cast<const VertexPointBAL*>(vertex(1));

      (*this)(cam->estimate().data(), point->estimate().data(), _error.data());
    }

    void linearizeOplus()
    {
      // use numeric Jacobians
      //BaseBinaryEdge<2, Vector2d, VertexCameraBAL, VertexPointBAL>::linearizeOplus();
      //return;

      const VertexCameraBAL* cam = static_cast<const VertexCameraBAL*>(vertex(0));
      const VertexPointBAL* point = static_cast<const VertexPointBAL*>(vertex(1));
      typedef ceres::internal::AutoDiff<EdgeObservationBAL, double, VertexCameraBAL::Dimension, VertexPointBAL::Dimension> BalAutoDiff;

      Matrix<double, Dimension, VertexCameraBAL::Dimension, Eigen::RowMajor> dError_dCamera;
      Matrix<double, Dimension, VertexPointBAL::Dimension, Eigen::RowMajor> dError_dPoint;
      double *parameters[] = { const_cast<double*>(cam->estimate().data()), const_cast<double*>(point->estimate().data()) };
      double *jacobians[] = { dError_dCamera.data(), dError_dPoint.data() };
      double value[Dimension];
      bool diffState = BalAutoDiff::Differentiate(*this, parameters, Dimension, value, jacobians);

      // copy over the Jacobians (convert row-major -> column-major)
      if (diffState) {
        _jacobianOplusXi = dError_dCamera;
        _jacobianOplusXj = dError_dPoint;
      } else {
        assert(0 && "Error while differentiating");
        _jacobianOplusXi.setZero();
        _jacobianOplusXi.setZero();
      }
    }
};

int main(int argc, char** argv)
{
  int maxIterations;
  bool verbose;
  bool usePCG;
  string outputFilename;
  string inputFilename;
  string statsFilename;
  CommandArgs arg;
  arg.param("i", maxIterations, 5, "perform n iterations");
  arg.param("o", outputFilename, "", "write points into a vrml file");
  arg.param("pcg", usePCG, false, "use PCG instead of the Cholesky");
  arg.param("v", verbose, false, "verbose output of the optimization process");
  arg.param("stats", statsFilename, "", "specify a file for the statistics");
  arg.paramLeftOver("graph-input", inputFilename, "", "file which will be processed");

  arg.parseArgs(argc, argv);

  typedef g2o::BlockSolver< g2o::BlockSolverTraits<9, 3> >  BalBlockSolver;
#ifdef G2O_HAVE_CHOLMOD
  string choleskySolverName = "CHOLMOD";
  typedef g2o::LinearSolverCholmod<BalBlockSolver::PoseMatrixType> BalLinearSolver;
#elif defined G2O_HAVE_CSPARSE
  string choleskySolverName = "CSparse";
  typedef g2o::LinearSolverCSparse<BalBlockSolver::PoseMatrixType> BalLinearSolver;
#else
#error neither CSparse nor CHOLMOD are available
#endif
  typedef g2o::LinearSolverPCG<BalBlockSolver::PoseMatrixType> BalLinearSolverPCG;

  g2o::SparseOptimizer optimizer;
  std::unique_ptr<g2o::LinearSolver<BalBlockSolver::PoseMatrixType>> linearSolver;
  if (usePCG) {
    cout << "Using PCG" << endl;
    linearSolver = g2o::make_unique<BalLinearSolverPCG>();
  } else {
    cout << "Using Cholesky: " << choleskySolverName << endl;
    auto cholesky = g2o::make_unique<BalLinearSolver>();
    cholesky->setBlockOrdering(true);
    linearSolver = std::move(cholesky);
  }
  g2o::OptimizationAlgorithmLevenberg* solver = new g2o::OptimizationAlgorithmLevenberg(
    g2o::make_unique<BalBlockSolver>(std::move(linearSolver)));

  //solver->setUserLambdaInit(1);
  optimizer.setAlgorithm(solver);
  if (statsFilename.size() > 0){
    optimizer.setComputeBatchStatistics(true);
  }

  vector<VertexPointBAL*> points;
  vector<VertexCameraBAL*> cameras;

  // parse BAL dataset
  cout << "Loading BAL dataset " << inputFilename << endl;
  {
    ifstream ifs(inputFilename.c_str());
    int numCameras, numPoints, numObservations;
    ifs >> numCameras >> numPoints >> numObservations;

    cerr << PVAR(numCameras) << " " << PVAR(numPoints) << " " << PVAR(numObservations) << endl;

    int id = 0;
    cameras.reserve(numCameras);
    for (int i = 0; i < numCameras; ++i, ++id) {
      VertexCameraBAL* cam = new VertexCameraBAL;
      cam->setId(id);
      optimizer.addVertex(cam);
      cameras.push_back(cam);
    }

    points.reserve(numPoints);
    for (int i = 0; i < numPoints; ++i, ++id) {
      VertexPointBAL* p = new VertexPointBAL;
      p->setId(id);
      p->setMarginalized(true);
      bool addedVertex = optimizer.addVertex(p);
      if (! addedVertex) {
        cerr << "failing adding vertex" << endl;
      }
      points.push_back(p);
    }

    // read in the observation
    for (int i = 0; i < numObservations; ++i) {
      int camIndex, pointIndex;
      double obsX, obsY;
      ifs >> camIndex >> pointIndex >> obsX >> obsY;

      assert(camIndex >= 0 && (size_t)camIndex < cameras.size() && "Index out of bounds");
      VertexCameraBAL* cam = cameras[camIndex];
      assert(pointIndex >= 0 && (size_t)pointIndex < points.size() && "Index out of bounds");
      VertexPointBAL* point = points[pointIndex];

      EdgeObservationBAL* e = new EdgeObservationBAL;
      e->setVertex(0, cam);
      e->setVertex(1, point);
      e->setInformation(Eigen::Matrix2d::Identity());
      e->setMeasurement(Eigen::Vector2d(obsX, obsY));
      bool addedEdge = optimizer.addEdge(e);
      if (! addedEdge) {
        cerr << "error adding edge" << endl;
      }
    }

    // read in the camera params
    Eigen::VectorXd cameraParameter(9);
    for (int i = 0; i < numCameras; ++i) {
      for (int j = 0; j < 9; ++j)
        ifs >> cameraParameter(j);
      VertexCameraBAL* cam = cameras[i];
      cam->setEstimate(cameraParameter);
    }

    // read in the points
    Eigen::Vector3d p;
    for (int i = 0; i < numPoints; ++i) {
      ifs >> p(0) >> p(1) >> p(2);

      VertexPointBAL* point = points[i];
      point->setEstimate(p);
    }

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

  cout << "Initializing ... " << flush;
  optimizer.initializeOptimization();
  cout << "done." << endl;
  optimizer.setVerbose(verbose);
  cout << "Start to optimize" << endl;
  optimizer.optimize(maxIterations);

  if (statsFilename!=""){
    cerr << "writing stats to file \"" << statsFilename << "\" ... ";
    ofstream fout(statsFilename.c_str());
    const BatchStatisticsContainer& bsc = optimizer.batchStatistics();
    for (size_t i=0; i<bsc.size(); i++)
      fout << bsc[i] << endl;
    cerr << "done." << endl;
  }

  // dump the points
  if (outputFilename.size() > 0) {
    ofstream fout(outputFilename.c_str()); // loadable with meshlab
    fout
      << "#VRML V2.0 utf8\n"
      << "Shape {\n"
      << "  appearance Appearance {\n"
      << "    material Material {\n"
      << "      diffuseColor " << 1 << " " << 0 << " " << 0 << "\n"
      << "      ambientIntensity 0.2\n"
      << "      emissiveColor 0.0 0.0 0.0\n"
      << "      specularColor 0.0 0.0 0.0\n"
      << "      shininess 0.2\n"
      << "      transparency 0.0\n"
      << "    }\n"
      << "  }\n"
      << "  geometry PointSet {\n"
      << "    coord Coordinate {\n"
      << "      point [\n";
    for (vector<VertexPointBAL*>::const_iterator it = points.begin(); it != points.end(); ++it) {
      fout << (*it)->estimate().transpose() << endl;
    }
    fout << "    ]\n" << "  }\n" << "}\n" << "  }\n";
  }

  return 0;
}


//==================================================================================================
/*!
  @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)
*/
//==================================================================================================
#ifndef BOOST_SIMD_ARCH_COMMON_SIMD_FUNCTION_IS_EQUAL_WITH_EQUAL_NANS_HPP_INCLUDED
#define BOOST_SIMD_ARCH_COMMON_SIMD_FUNCTION_IS_EQUAL_WITH_EQUAL_NANS_HPP_INCLUDED
#include <boost/simd/detail/overload.hpp>

#include <boost/simd/meta/hierarchy/simd.hpp>
#include <boost/simd/meta/as_logical.hpp>
#include <boost/simd/function/simd/is_equal.hpp>
#include <boost/simd/function/simd/is_nan.hpp>
#include <boost/simd/function/simd/logical_and.hpp>
#include <boost/simd/function/simd/logical_or.hpp>

namespace boost { namespace simd { namespace ext
{
   namespace bd = boost::dispatch;
   namespace bs = boost::simd;
   BOOST_DISPATCH_OVERLOAD(is_equal_with_equal_nans_
                          , (typename A0, typename X)
                          , bd::cpu_
                          , bs::pack_<bd::floating_<A0>, X>
                          , bs::pack_<bd::floating_<A0>, X>
                          )
   {
      BOOST_FORCEINLINE bs::as_logical_t<A0>  operator()( const A0& a0, const A0& a1) const BOOST_NOEXCEPT
      {
        return logical_or(is_equal(a0,a1), logical_and(bs::is_nan(a0), bs::is_nan(a1)));
      }
   };

   BOOST_DISPATCH_OVERLOAD(is_equal_with_equal_nans_
                          , (typename A0, typename X)
                          , bd::cpu_
                          , bs::pack_<bd::fundamental_<A0>, X>
                          , bs::pack_<bd::fundamental_<A0>, X>
                          )
   {
      BOOST_FORCEINLINE bs::as_logical_t<A0>   operator()( const A0& a0, const A0& a1) const BOOST_NOEXCEPT
      {
        return is_equal(a0,a1);
      }
   };

} } }

#endif


/*
@copyright Louis Dionne 2014
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/hana/map.hpp>

#include <boost/hana/detail/assert.hpp>
#include <boost/hana/detail/constexpr.hpp>
#include <boost/hana/detail/minimal/list.hpp>
#include <boost/hana/detail/minimal/product.hpp>
#include <boost/hana/integral.hpp>
using namespace boost::hana;

template <int i>
constexpr auto key = int_<i>;

template <int i>
constexpr auto value = int_<i + 100>;

template <int i, int j>
BOOST_HANA_CONSTEXPR_LAMBDA auto p = detail::minimal::product<>(key<i>, value<j>);

int main() {
    constexpr auto list = detail::minimal::list<>;

    BOOST_HANA_CONSTANT_ASSERT(to<Map>(list()) == map());
    BOOST_HANA_CONSTANT_ASSERT(to<Map>(list(p<1, 1>)) == map(p<1, 1>));
    BOOST_HANA_CONSTANT_ASSERT(to<Map>(list(p<1, 1>, p<2, 2>)) == map(p<1, 1>, p<2, 2>));
    BOOST_HANA_CONSTANT_ASSERT(to<Map>(list(p<1, 1>, p<2, 2>, p<3, 3>)) == map(p<1, 1>, p<2, 2>, p<3, 3>));
}


//==================================================================================================
/*!
  @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)
*/
//==================================================================================================
#ifndef BOOST_SIMD_FUNCTION_RAW_REC_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_RAW_REC_HPP_INCLUDED

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

 /*!

    @ingroup group-arithmetic
    Function object implementing raw_rec capabilities

    Computes an rough approximation of the inverse of its parameter.

    @par semantic:
    For any given value @c x of type @c T:

    @code
    T r = raw_rec(x);
    @endcode

    is similar to:

    @code
    T r = T(1)/x;
    @endcode

    @par Note:

    Raw means that the computation is possibly done with the most speed available
    on current hardware but with the least precision.

    @see raw_rec, rec

  **/
  const boost::dispatch::functor<tag::raw_rec_> raw_rec = {};
} }
#endif

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

#endif


#include <iostream>
#include <boost/filesystem.hpp>
#include <argparse/argparse.hpp>
#include <json/reader.h>
#include <glog/logging.h>

#include "common_util.h"
#include "metrics.h"
#include "viz.h"
#include "trajectory.h"

#include <CGAL/Point_set_3/IO.h>
#include <CGAL/Search_traits_adapter.h>

int main(int argc, char** argv){
    std::string log_root("../../../log/DensitySampling/");

	std::cout << "Read config " << argv[2] << std::endl;
	Json::Value args;
	argparse::ArgumentParser program("Analyze");
	{
		program.add_argument("--model").required();
		program.add_argument("--density_important").required();
		program.add_argument("--density_unimportant").required();
		program.add_argument("--filter_z").default_value(0);
		try {
			program.parse_args(argc, argv);
		}
		catch (const std::runtime_error& err) {
			std::cout << err.what() << std::endl;
			std::cout << program;
			exit(0);
		}
	}
	google::InitGoogleLogging(argv[0]);
	google::SetStderrLogging(google::GLOG_INFO);

	LOG(INFO) << "Loading model";
	const std::string model_file = program.get<std::string>("--model");
	const double density_important = std::atof(program.get<std::string>("--density_important").c_str());
	const double density_unimportant = std::atof(program.get<std::string>("--density_unimportant").c_str());
	const double filter_z = std::atof(program.get<std::string>("--filter_z").c_str());

	SurfaceMesh model_important,model_unimportant;

	model_important = modeltools::read_model(model_file);
	CGAL::IO::write_PLY("test.ply",model_important);
	LOG(INFO) << boost::format("%d vertices, %d faces") % model_important.num_vertices() % model_important.num_faces();
	model_unimportant = model_important;

	for (auto face : model_important.faces())
	{
		bool remove = true;
		for (auto vertex : model_important.vertices_around_face(model_important.halfedge(face)))
		{
			if (model_important.point(vertex).z() > filter_z)
				remove = false;
		}
		if (remove)
			CGAL::remove_face(face, model_important);
	}
	for (auto face : model_unimportant.faces())
	{
		bool remove = true;
		for (auto vertex : model_unimportant.vertices_around_face(model_unimportant.halfedge(face)))
		{
			if (model_unimportant.point(vertex).z() > filter_z)
				remove = false;
		}
		if (!remove)
			CGAL::remove_face(face, model_unimportant);
	}

	CGAL::collect_garbage(model_important);
	CGAL::collect_garbage(model_unimportant);
	CGAL::IO::write_PLY(log_root + "model_important.ply", model_important);
	CGAL::IO::write_PLY(log_root + "model_unimportant.ply", model_unimportant);

	double important_area = CGAL::Polygon_mesh_processing::area(model_important);
	double unimportant_area = CGAL::Polygon_mesh_processing::area(model_unimportant);
	double total_area = important_area + unimportant_area;

	int num_important_sample = important_area * density_important;
	int num_unimportant_sample = unimportant_area * density_unimportant;
	PointSet3 important_sample_points = modeltools::sample_points(model_important, num_important_sample);
	PointSet3 unimportant_sample_points = modeltools::sample_points(model_unimportant, num_unimportant_sample);
	CGAL::IO::write_point_set(log_root + "points_important.ply", important_sample_points);
	CGAL::IO::write_point_set(log_root + "points_unimportant.ply", unimportant_sample_points);

	PointSet3 whole_points(important_sample_points);
	for (auto point : unimportant_sample_points)
		whole_points.insert(unimportant_sample_points.point(point), unimportant_sample_points.normal(point));
	CGAL::IO::write_point_set(log_root + "points_whole.ply", whole_points);

	LOG(INFO) << boost::format("Important part: %d points; Unimportant part: %d points") % important_sample_points.size() % unimportant_sample_points.size();

	My_Visualizer viz(model_file);
	viz.lock();
	viz.m_important_points = important_sample_points;
	viz.m_unimportant_points = unimportant_sample_points;
	viz.unlock();

    comutil::debug_img();
}


#include <boost/accumulators/statistics/moment.hpp>


// Distributed under the MIT License.
// See LICENSE.txt for details.

#pragma once

#include <array>
#include <boost/functional/hash.hpp>
#include <cstddef>
#include <string>
#include <unordered_map>
#include <utility>

#include "DataStructures/SliceVariables.hpp"
#include "DataStructures/Tags.hpp"
#include "DataStructures/Tensor/EagerMath/DotProduct.hpp"
#include "DataStructures/Tensor/EagerMath/Magnitude.hpp"
#include "DataStructures/Tensor/Slice.hpp"
#include "DataStructures/Tensor/Tensor.hpp"
#include "DataStructures/Variables.hpp"
#include "Domain/SizeOfElement.hpp"
#include "Domain/Structure/Direction.hpp"
#include "Domain/Structure/Element.hpp"
#include "Domain/Tags.hpp"
#include "Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp"
#include "Evolution/Systems/NewtonianEuler/Tags.hpp"
#include "NumericalAlgorithms/LinearOperators/DefiniteIntegral.hpp"
#include "NumericalAlgorithms/LinearOperators/MeanValue.hpp"
#include "NumericalAlgorithms/Spectral/Mesh.hpp"
#include "Utilities/ErrorHandling/Assert.hpp"

/// \cond
template <size_t VolumeDim>
class ElementId;
/// \endcond

namespace NewtonianEuler {
namespace Limiters {
namespace Tci {

/// \ingroup LimitersGroup
/// \brief Implements the troubled-cell indicator from Krivodonova et al, 2004.
///
/// The KXRCF (these are the author initials) TCI is described in
/// \cite Krivodonova2004.
///
/// In summary, this TCI uses the size of discontinuities between neighboring DG
/// elements to determine the smoothness of the solution. This works because the
/// discontinuities converge rapidly for smooth solutions, therefore a large
/// discontinuity suggests a lack of smoothness and the need to apply a limiter.
///
/// The reference sets the constant we call `kxrcf_constant` to 1. This should
/// generally be a good threshold to use, though it might not be the optimal
/// value (in balancing robustness vs accuracy) for any particular problem.
///
/// This implementation
/// - does not support h- or p-refinement; this is checked by assertion.
/// - chooses not to check external boundaries, because this adds complexity.
///   However, by not checking external boundaries, the implementation may not
///   be robust for problems that feed in shocks through boundary conditions.
template <size_t VolumeDim, typename PackagedData>
bool kxrcf_indicator(
    const double kxrcf_constant, const Scalar<DataVector>& cons_mass_density,
    const tnsr::I<DataVector, VolumeDim>& cons_momentum_density,
    const Scalar<DataVector>& cons_energy_density, const Mesh<VolumeDim>& mesh,
    const Element<VolumeDim>& element,
    const std::array<double, VolumeDim>& element_size,
    const Scalar<DataVector>& det_logical_to_inertial_jacobian,
    const typename evolution::dg::Tags::NormalCovectorAndMagnitude<
        VolumeDim>::type& normals_and_magnitudes,
    const std::unordered_map<
        std::pair<Direction<VolumeDim>, ElementId<VolumeDim>>, PackagedData,
        boost::hash<std::pair<Direction<VolumeDim>, ElementId<VolumeDim>>>>&
        neighbor_data) noexcept {
  // Enforce restrictions on h-refinement, p-refinement
  if (UNLIKELY(alg::any_of(element.neighbors(),
                           [](const auto& direction_neighbors) noexcept {
                             return direction_neighbors.second.size() != 1;
                           }))) {
    ERROR("The Kxrcf TCI does not yet support h-refinement");
    // Removing this limitation will require adapting the surface integrals to
    // correctly acount for,
    // - multiple (smaller) neighbors contributing to the integral
    // - only a portion of a (larger) neighbor contributing to the integral
  }
  alg::for_each(neighbor_data, [&mesh](const auto& neighbor_and_data) noexcept {
    if (UNLIKELY(neighbor_and_data.second.mesh != mesh)) {
      ERROR("The Kxrcf TCI does not yet support p-refinement");
      // Removing this limitation will require generalizing the surface
      // integrals to make sure the meshes are consistent.
    }
  });
  // Check the mesh matches expectations:
  // - the extents must be uniform, because the TCI expects a unique "order" for
  //   the DG scheme. This shows up when computing the threshold parameter,
  //   h^(degree+1)/2
  // - the quadrature must be GL, because the current implementation doesn't
  //   handle extrapolation to the boundary, though this could be changed.
  // - the basis in principle could be changed, but until we need to change it
  //   we just check it's the expected Legendre for simplicity
  ASSERT(mesh == Mesh<VolumeDim>(mesh.extents(0), Spectral::Basis::Legendre,
                                 Spectral::Quadrature::GaussLobatto),
         "The Kxrcf TCI expects a uniform LGL mesh, but got mesh = " << mesh);

  bool inflow_boundaries_present = false;
  double inflow_area = 0.;
  double inflow_delta_density = 0.;
  double inflow_delta_energy = 0.;

  // Skip boundary integrations on external boundaries. This choice might be
  // problematic for evolutions (likely only simple test cases) that feed in
  // shocks through the boundary condition: the limiter might fail to activate
  // in the cell that touches the boundary.
  //
  // To properly compute the limiter at external boundaries we would need the
  // limiter to know about the boundary condition, which may be difficult to
  // do in a general way.
  for (const auto& [neighbor, data] : neighbor_data) {
    const auto& dir = neighbor.first;

    // Check consistency of neighbor_data with element and normals
    ASSERT(element.neighbors().contains(dir),
           "Received neighbor data from dir = "
               << dir << ", but element has no neighbor in this dir");
    ASSERT(normals_and_magnitudes.contains(dir),
           "Received neighbor data from dir = "
               << dir
               << ", but normals_and_magnitudes has no normal in this dir");
    ASSERT(normals_and_magnitudes.at(dir).has_value(),
           "The normals_and_magnitudes are not up-to-date in dir = " << dir);
    const auto& normal = get<evolution::dg::Tags::NormalCovector<VolumeDim>>(
        normals_and_magnitudes.at(dir).value());
    const auto& magnitude_of_normal =
        get<evolution::dg::Tags::MagnitudeOfNormal>(
            normals_and_magnitudes.at(dir).value());

    const size_t sliced_dim = dir.dimension();
    const size_t index_of_slice =
        (dir.side() == Side::Lower ? 0 : mesh.extents()[sliced_dim] - 1);
    const auto momentum_on_slice = data_on_slice(
        cons_momentum_density, mesh.extents(), sliced_dim, index_of_slice);
    const auto momentum_dot_normal = dot_product(momentum_on_slice, normal);

    // Skip boundaries with no significant inflow
    // Note: the cutoff value here is small but arbitrarily chosen.
    if (min(get(momentum_dot_normal)) > -1e-12) {
      continue;
    }
    inflow_boundaries_present = true;

    // This mask has value 1. for momentum_dot_normal < 0.
    //                     0. for momentum_dot_normal >= 0.
    const DataVector inflow_mask = 1. - step_function(get(momentum_dot_normal));
    // Mask is then weighted pointwise by the Jacobian determinant giving
    // surface integrals in inertial coordinates. This Jacobian determinant is
    // given by the product of the volume Jacobian determinant with the
    // magnitude of the unnormalized normal covectors.
    const DataVector weighted_inflow_mask =
        inflow_mask *
        get(data_on_slice(det_logical_to_inertial_jacobian, mesh.extents(),
                          sliced_dim, index_of_slice)) *
        get(magnitude_of_normal);

    inflow_area +=
        definite_integral(weighted_inflow_mask, mesh.slice_away(sliced_dim));

    // This is the step that is incompatible with h/p refinement. For use with
    // h/p refinement, would need to correctly obtain the neighbor solution on
    // the local grid points.
    const auto neighbor_vars_on_slice = data_on_slice(
        data.volume_data, mesh.extents(), sliced_dim, index_of_slice);

    const auto density_on_slice = data_on_slice(
        cons_mass_density, mesh.extents(), sliced_dim, index_of_slice);
    const auto& neighbor_density_on_slice =
        get<NewtonianEuler::Tags::MassDensityCons>(neighbor_vars_on_slice);
    inflow_delta_density += definite_integral(
        (get(density_on_slice) - get(neighbor_density_on_slice)) *
            weighted_inflow_mask,
        mesh.slice_away(sliced_dim));

    const auto energy_on_slice = data_on_slice(
        cons_energy_density, mesh.extents(), sliced_dim, index_of_slice);
    const auto& neighbor_energy_on_slice =
        get<NewtonianEuler::Tags::EnergyDensity>(neighbor_vars_on_slice);
    inflow_delta_energy += definite_integral(
        (get(energy_on_slice) - get(neighbor_energy_on_slice)) *
            weighted_inflow_mask,
        mesh.slice_away(sliced_dim));
  }

  if (not inflow_boundaries_present) {
    // No boundaries had inflow, so not a troubled cell
    return false;
  }

  // KXRCF take h to be the radius of the circumscribed circle
  const double h = 0.5 * magnitude(element_size);
  const double h_pow = pow(h, 0.5 * mesh.extents(0));

  ASSERT(inflow_area > 0.,
         "Sanity check failed: negative area of inflow boundaries");

  const double norm_squared_density = mean_value(
      get(det_logical_to_inertial_jacobian) * square(get(cons_mass_density)),
      mesh);
  ASSERT(norm_squared_density > 0.,
         "Sanity check failed: negative density norm over element");
  const double norm_density = sqrt(norm_squared_density);
  const double ratio_for_density =
      abs(inflow_delta_density) / (h_pow * inflow_area * norm_density);

  const double norm_squared_energy = mean_value(
      get(det_logical_to_inertial_jacobian) * square(get(cons_energy_density)),
      mesh);
  ASSERT(norm_squared_energy > 0.,
         "Sanity check failed: negative energy norm over element");
  const double norm_energy = sqrt(norm_squared_energy);
  const double ratio_for_energy =
      abs(inflow_delta_energy) / (h_pow * inflow_area * norm_energy);

  return (ratio_for_density > kxrcf_constant or
          ratio_for_energy > kxrcf_constant);
}

}  // namespace Tci
}  // namespace Limiters
}  // namespace NewtonianEuler


/*
 Copyright (C) 2016 Quaternion Risk Management Ltd
 All rights reserved.

 This file is part of ORE, a free-software/open-source library
 for transparent pricing and risk analysis - http://opensourcerisk.org

 ORE is free software: you can redistribute it and/or modify it
 under the terms of the Modified BSD License.  You should have received a
 copy of the license along with this program.
 The license is also available online at <http://opensourcerisk.org>

 This program is distributed on the basis that it will form a useful
 contribution to risk analytics and model standardisation, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
*/

#include <ql/instruments/makecapfloor.hpp>
#include <ql/option.hpp>
#include <ql/pricingengines/blackformula.hpp>
#include <ql/pricingengines/capfloor/bacheliercapfloorengine.hpp>
#include <ql/pricingengines/capfloor/blackcapfloorengine.hpp>
#include <ql/utilities/dataformatters.hpp>
#include <qle/termstructures/optionletstripper1.hpp>

#include <boost/make_shared.hpp>

using boost::shared_ptr;

namespace QuantExt {

OptionletStripper1::OptionletStripper1(const shared_ptr<QuantExt::CapFloorTermVolSurface>& termVolSurface,
                                       const shared_ptr<IborIndex>& index, Rate switchStrike, Real accuracy,
                                       Natural maxIter, const Handle<YieldTermStructure>& discount,
                                       const VolatilityType type, const Real displacement, bool dontThrow,
                                       const optional<VolatilityType> targetVolatilityType,
                                       const optional<Real> targetDisplacement, Real dontThrowMinVol)
    : OptionletStripper(termVolSurface, index, discount, targetVolatilityType ? *targetVolatilityType : type,
                        targetDisplacement ? *targetDisplacement : displacement),
      volQuotes_(nOptionletTenors_, std::vector<shared_ptr<SimpleQuote> >(nStrikes_)),
      floatingSwitchStrike_(switchStrike == Null<Rate>() ? true : false), capFlooMatrixNotInitialized_(true),
      switchStrike_(switchStrike), accuracy_(accuracy), maxIter_(maxIter), dontThrow_(dontThrow),
      dontThrowMinVol_(dontThrowMinVol), inputVolatilityType_(type), inputDisplacement_(displacement) {

    capFloorPrices_ = Matrix(nOptionletTenors_, nStrikes_);
    optionletPrices_ = Matrix(nOptionletTenors_, nStrikes_);
    capletVols_ = Matrix(nOptionletTenors_, nStrikes_);
    capFloorVols_ = Matrix(nOptionletTenors_, nStrikes_);

    Real firstGuess = 0.14; // guess is only used for shifted lognormal vols
    optionletStDevs_ = Matrix(nOptionletTenors_, nStrikes_, firstGuess);

    capFloors_ = CapFloorMatrix(nOptionletTenors_);
    capFloorEngines_ = std::vector<std::vector<boost::shared_ptr<PricingEngine> > >(nOptionletTenors_);
}

void OptionletStripper1::performCalculations() const {

    // update dates
    const Date& referenceDate = termVolSurface_->referenceDate();
    const DayCounter& dc = termVolSurface_->dayCounter();
    shared_ptr<BlackCapFloorEngine> dummy(new BlackCapFloorEngine( // discounting does not matter here
        iborIndex_->forwardingTermStructure(), 0.20, dc));
    for (Size i = 0; i < nOptionletTenors_; ++i) {
        CapFloor temp = MakeCapFloor(CapFloor::Cap, capFloorLengths_[i], iborIndex_,
                                     0.04, // dummy strike
                                     0 * Days)
                            .withPricingEngine(dummy);
        shared_ptr<FloatingRateCoupon> lFRC = temp.lastFloatingRateCoupon();
        optionletDates_[i] = lFRC->fixingDate();
        optionletPaymentDates_[i] = lFRC->date();
        optionletAccrualPeriods_[i] = lFRC->accrualPeriod();
        optionletTimes_[i] = dc.yearFraction(referenceDate, optionletDates_[i]);
        atmOptionletRate_[i] = lFRC->indexFixing();
    }

    if (floatingSwitchStrike_) {
        Rate averageAtmOptionletRate = 0.0;
        for (Size i = 0; i < nOptionletTenors_; ++i) {
            averageAtmOptionletRate += atmOptionletRate_[i];
        }
        switchStrike_ = averageAtmOptionletRate / nOptionletTenors_;
    }

    const Handle<YieldTermStructure>& discountCurve =
        discount_.empty() ? iborIndex_->forwardingTermStructure() : discount_;

    const std::vector<Rate>& strikes = termVolSurface_->strikes();
    // initialize CapFloorMatrix
    if (capFlooMatrixNotInitialized_) {
        for (Size i = 0; i < nOptionletTenors_; ++i) {
            capFloors_[i].resize(nStrikes_);
            capFloorEngines_[i].resize(nStrikes_);
        }
        // construction might go here
        for (Size j = 0; j < nStrikes_; ++j) {
            for (Size i = 0; i < nOptionletTenors_; ++i) {
                volQuotes_[i][j] = shared_ptr<SimpleQuote>(new SimpleQuote());
                if (inputVolatilityType_ == ShiftedLognormal) {
                    capFloorEngines_[i][j] = boost::make_shared<BlackCapFloorEngine>(
                        discountCurve, Handle<Quote>(volQuotes_[i][j]), dc, inputDisplacement_);
                } else if (inputVolatilityType_ == Normal) {
                    capFloorEngines_[i][j] =
                        boost::make_shared<BachelierCapFloorEngine>(discountCurve, Handle<Quote>(volQuotes_[i][j]), dc);
                } else {
                    QL_FAIL("unknown volatility type: " << volatilityType_);
                }
            }
        }
        capFlooMatrixNotInitialized_ = false;
    }

    for (Size j = 0; j < nStrikes_; ++j) {
        // using out-of-the-money options
        CapFloor::Type capFloorType = strikes[j] < switchStrike_ ? CapFloor::Floor : CapFloor::Cap;
        Option::Type optionletType = strikes[j] < switchStrike_ ? Option::Put : Option::Call;

        Real previousCapFloorPrice = 0.0;
        for (Size i = 0; i < nOptionletTenors_; ++i) {

            capFloorVols_[i][j] = termVolSurface_->volatility(capFloorLengths_[i], strikes[j], true);
            volQuotes_[i][j]->setValue(capFloorVols_[i][j]);
            capFloors_[i][j] = MakeCapFloor(capFloorType, capFloorLengths_[i], iborIndex_, strikes[j], -0 * Days)
                                   .withPricingEngine(capFloorEngines_[i][j]);
            capFloorPrices_[i][j] = capFloors_[i][j]->NPV();
            optionletPrices_[i][j] = capFloorPrices_[i][j] - previousCapFloorPrice;
            previousCapFloorPrice = capFloorPrices_[i][j];
            DiscountFactor d = discountCurve->discount(optionletPaymentDates_[i]);
            DiscountFactor optionletAnnuity = optionletAccrualPeriods_[i] * d;
            try {
                if (volatilityType_ == ShiftedLognormal) {
                    optionletStDevs_[i][j] = blackFormulaImpliedStdDev(
                        optionletType, strikes[j], atmOptionletRate_[i], optionletPrices_[i][j], optionletAnnuity,
                        displacement_, optionletStDevs_[i][j], accuracy_, maxIter_);
                } else if (volatilityType_ == Normal) {
                    optionletStDevs_[i][j] =
                        std::sqrt(optionletTimes_[i]) *
                        bachelierBlackFormulaImpliedVol(optionletType, strikes[j], atmOptionletRate_[i],
                                                        optionletTimes_[i], optionletPrices_[i][j], optionletAnnuity);
                } else {
                    QL_FAIL("Unknown target volatility type: " << volatilityType_);
                }
            } catch (std::exception& e) {
                if (dontThrow_)
                    optionletStDevs_[i][j] = dontThrowMinVol_; // really need a way to log this
                else
                    QL_FAIL("could not bootstrap optionlet:"
                            "\n type:    "
                            << optionletType << "\n strike:  " << io::rate(strikes[j])
                            << "\n atm:     " << io::rate(atmOptionletRate_[i])
                            << "\n price:   " << optionletPrices_[i][j] << "\n annuity: " << optionletAnnuity
                            << "\n expiry:  " << optionletDates_[i] << "\n error:   " << e.what());
            }
            optionletVolatilities_[i][j] = optionletStDevs_[i][j] / std::sqrt(optionletTimes_[i]);
        }
    }
}

const Matrix& OptionletStripper1::capletVols() const {
    calculate();
    return capletVols_;
}

const Matrix& OptionletStripper1::capFloorPrices() const {
    calculate();
    return capFloorPrices_;
}

const Matrix& OptionletStripper1::capFloorVolatilities() const {
    calculate();
    return capFloorVols_;
}

const Matrix& OptionletStripper1::optionletPrices() const {
    calculate();
    return optionletPrices_;
}

Rate OptionletStripper1::switchStrike() const {
    if (floatingSwitchStrike_)
        calculate();
    return switchStrike_;
}
} // namespace QuantExt


//============================================================================
// Name         : dnaplatemotionmodels.hpp
// Author       : Roger Fraser
// Contributors :
// Version      : 1.00
// Copyright    : Copyright 2018 Geoscience Australia
//
//                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.
//
// Description  : Declaration/definition of plate motion model parameter in form of
//				  Helmert rotations
//============================================================================

#ifndef DNAPMM_PARAM_H_
#define DNAPMM_PARAM_H_

#if defined(_MSC_VER)
	#if defined(LIST_INCLUDES_ON_BUILD)
		#pragma message("  " __FILE__)
	#endif
#endif

#include <string>
#include <iomanip>
#include <sstream>
#include <boost/exception_ptr.hpp>

#include <include/parameters/dnaepsg.hpp>

using namespace std;
using namespace boost;

using namespace dynadjust::epsg;

namespace dynadjust {
namespace pmm_parameters {

// AU
template <class T, class S>
struct _au_plate_model_ {
	static const S plate_id;			// two-character plate id
	static const T plate_rotations[3];	// milli-arc-seconds p/yr
};

template <class T, class S>
const S _au_plate_model_<T, S>::plate_id = "AU";

template <class T, class S>
const T _au_plate_model_<T, S>::plate_rotations[3] = {
	// Euler parameters:
	//  - pole latitude:  32.2447
	// 	- pole longitude: 38.2022
	//  - rotation rate:   0.6285
	1.50379,		// x rotation rate
	1.18346,		// y rotation rate
	1.20716			// z rotation rate
};

template <class T, class S>
class AU_PLATE_MOTION_MODEL : public _au_plate_model_<T, S> {
public:
};

////////////////////////////////////////////////////////////////////////////////////

}	// namespace datum_parameters
}	// namespace dynadjust

#endif /* DNAPMM_PARAM_H_ */


/*
-------------------------------------------------------------------------
   This file is part of BayesOpt, an efficient C++ library for
   Bayesian optimization.

   Copyright (C) 2011-2015 Ruben Martinez-Cantin <rmcantin@unizar.es>

   BayesOpt is free software: you can redistribute it and/or modify it
   under the terms of the GNU Affero General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   BayesOpt is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with BayesOpt.  If not, see <http://www.gnu.org/licenses/>.
------------------------------------------------------------------------
*/

#define _USE_MATH_DEFINES
#include <cmath>
#include <algorithm>
#include <boost/numeric/ublas/assignment.hpp>
#include "bayesopt/bayesopt.hpp"
#include "param_loader.hpp"
#include "fileparser.hpp"

#ifndef M_PI
/* It shouldn't be necessary, but Windows is completely nuts about
   math constants and I HATE when include order matters. */
    #define M_PI       3.14159265358979323846
#endif

class SystemCallsBranin: public bayesopt::ContinuousModel
{
public:
  SystemCallsBranin(bayesopt::Parameters par):
    ContinuousModel(2,par) {}

  double evaluateSample( const vectord& xin)
  {
     if (xin.size() != 2)
      {
	std::cout << "WARNING: This only works for 2D inputs." << std::endl
		  << "WARNING: Using only first two components." << std::endl;
      }

    float y = -1;
    double x1 = xin(0);
    double x2 = xin(1);

    bayesopt::utils::FileParser fp("results.txt");
    std::string call = "python ../examples/standalone_calls/eval_branin.py " +
      fp.to_string(x1) + " " + fp.to_string(x2);
    int ret = system(call.c_str());

    fp.openInput();
    fp.read("y",y);
    fp.close();

    return y;
  }

  bool checkReachability(const vectord &query)
  {return true;};

  inline double sqr( double x ){ return x*x; };

  void printOptimal()
  {
    vectord sv(2);
    sv(0) = 0.1238938; sv(1) = 0.818333;
    std::cout << "Solutions: " << sv << "->"
	      << evaluateSample(sv) << std::endl;
    sv(0) = 0.5427728; sv(1) = 0.151667;
    std::cout << "Solutions: " << sv << "->"
	      << evaluateSample(sv) << std::endl;
    sv(0) = 0.961652; sv(1) = 0.1650;
    std::cout << "Solutions: " << sv << "->"
	      << evaluateSample(sv) << std::endl;
  }

};

int main(int nargs, char *args[])
{
  bayesopt::Parameters par;
  if(nargs > 1){
    if(!bayesopt::utils::ParamLoader::load(args[1], par)){
        std::cout << "ERROR: provided file \"" << args[1] << "\" does not exist" << std::endl;
        return -1;
    }
  }
  else{
    par = initialize_parameters_to_default();
    par.n_iterations = 190;
    par.random_seed = 0;
    par.verbose_level = 1;
    par.noise = 1e-10;
    //bayesopt::utils::ParamLoader::save("system_opt.txt", par);
  }

  SystemCallsBranin branin(par);
  vectord result(2);

  branin.optimize(result);
  std::cout << "Result: " << result << "->"
	    << branin.evaluateSample(result) << std::endl;
  branin.printOptimal();

  // Remove results.txt file
  std::string filename("results.txt");
  if( remove( filename.c_str() ) == 0 ){
    std::cout << "File \"" << filename << "\" successfully removed" << std::endl;
  }
  else{
    std::cout << "Error: cannot remove \"" << filename << "\" file" << std::endl;
  }

  return 0;
}


#define PY_ARRAY_UNIQUE_SYMBOL vigranumpygraphs_PyArray_API
#define NO_IMPORT_ARRAY

/*boost python before anything else*/
#include <boost/python.hpp>
#include <vigra/numpy_array.hxx>
#include <vigra/numpy_array_converters.hxx>
#include <vigra/eccentricitytransform.hxx>

namespace python = boost::python;

/*
 BROKEN! REFACTOR ME
*/

namespace vigra
{

    template< int N, class T, class S >
    NumpyAnyArray
    pythonEccentricityTransformOnLabels(
            const NumpyArray< N, T > & src,
            NumpyArray< N, S > dest
    ){
        dest.reshapeIfEmpty(src.taggedShape(),
                           "eccentricityTransformOnLabels(): Output image has wrong dimensions");
        eccentricityTransformOnLabels(src, dest);
        return dest;
    }

    template< int N, class T >
    NumpyAnyArray
    pythonEccentricityCenters(
            const NumpyArray< N, T > & src,
            NumpyArray< 2, MultiArrayIndex > centers
    ){
        T maxLabel = *std::max_element(src.begin(), src.end());
        centers.reshapeIfEmpty(Shape2(maxLabel, N),
                               "eccentricityCenters(): Output has wrong dimensions");
        eccentricityCenters(src, centers);
        return centers;
    }

    template< int N, class T, class S >
    python::tuple
    pythonEccentricityTransformWithCenters(
            const NumpyArray< N, T > & src,
            NumpyArray< N, S > dest,
            NumpyArray< 2, MultiArrayIndex > centers
    ){
        dest.reshapeIfEmpty(src.taggedShape(),
                            "eccentricityTransformWithCenters(): Output (dest) has wrong dimensions");
        T maxLabel = *std::max_element(src.begin(), src.end());
        centers.reshapeIfEmpty(Shape2(maxLabel, N),
                               "eccentricityTransformWithCenters(): Output (centers) has wrong dimensions");
        eccentricityTransformOnLabels(src, dest, centers);
        return python::make_tuple(dest, centers);
    }

    template< int N, class T, class S >
    void defineEcc()
    {
        python::def("_eccentricityTransform",registerConverters(&pythonEccentricityTransformOnLabels< N, T, S >),
                    (
                        python::arg("labels"),
                        python::arg("out")=python::object()
                    )
        );
    }

    template< int N, class T >
    void defineCenters()
    {
        python::def("_eccentricityCenters",registerConverters(&pythonEccentricityCenters< N, T >),
                    (
                        python::arg("labels"),
                        python::arg("out")=python::object()
                    )
        );
    }

    template< int N, class T, class S >
    void defineEccWithCenters()
    {
        python::def("_eccentricityTransformWithCenters",registerConverters(&pythonEccentricityTransformWithCenters<N, T, S >),
                    (
                        python::arg("labels"),
                        python::arg("ecc")=python::object(),
                        python::arg("centers")=python::object()
                    )
        );
    }

    void defineEccentricity()
    {
        defineEcc< 2, UInt32, float >();
        defineEcc< 3, UInt32, float >();
        defineCenters< 2, UInt32 >();
        defineCenters< 3, UInt32 >();
        defineEccWithCenters< 2, UInt32, float >();
        defineEccWithCenters< 3, UInt32, float >();
    }

}


/**
 * @file cf_model.hpp
 * @author Wenhao Huang
 * @author Khizir Siddiqui
 *
 * A serializable CF model, used by the main program.
 *
 * mlpack is free software; you may redistribute it and/or modify it under the
 * terms of the 3-clause BSD license.  You should have received a copy of the
 * 3-clause BSD license along with mlpack.  If not, see
 * http://www.opensource.org/licenses/BSD-3-Clause for more information.
 */
#ifndef MLPACK_METHODS_CF_CF_MODEL_HPP
#define MLPACK_METHODS_CF_CF_MODEL_HPP

#include <mlpack/core.hpp>
#include <boost/variant.hpp>
#include "cf.hpp"

#include <mlpack/methods/cf/decomposition_policies/batch_svd_method.hpp>
#include <mlpack/methods/cf/decomposition_policies/randomized_svd_method.hpp>
#include <mlpack/methods/cf/decomposition_policies/regularized_svd_method.hpp>
#include <mlpack/methods/cf/decomposition_policies/svd_complete_method.hpp>
#include <mlpack/methods/cf/decomposition_policies/svd_incomplete_method.hpp>
#include <mlpack/methods/cf/decomposition_policies/bias_svd_method.hpp>
#include <mlpack/methods/cf/decomposition_policies/svdplusplus_method.hpp>

#include <mlpack/methods/cf/normalization/no_normalization.hpp>
#include <mlpack/methods/cf/normalization/overall_mean_normalization.hpp>
#include <mlpack/methods/cf/normalization/user_mean_normalization.hpp>
#include <mlpack/methods/cf/normalization/item_mean_normalization.hpp>
#include <mlpack/methods/cf/normalization/z_score_normalization.hpp>

namespace mlpack {
namespace cf {

/**
 * DeleteVisitor deletes the CFType<> object which is pointed to by the
 * variable cf in class CFModel.
 */
class DeleteVisitor : public boost::static_visitor<void>
{
 public:
  //! Delete CFType object.
  template <typename DecompositionPolicy,
            typename NormalizationType = NoNormalization>
  void operator()(CFType<DecompositionPolicy, NormalizationType>* c) const;
};

/**
 * GetValueVisitor returns the pointer which points to the CFType object.
 */
class GetValueVisitor : public boost::static_visitor<void*>
{
 public:
  //! Return stored pointer as void* type.
  template <typename DecompositionPolicy,
            typename NormalizationType = NoNormalization>
  void* operator()(CFType<DecompositionPolicy, NormalizationType>* c) const;
};

/**
 * PredictVisitor uses the CFType object to make predictions on the given
 * combinations of users and items.
 */
template <typename NeighborSearchPolicy,
          typename InterpolationPolicy>
class PredictVisitor : public boost::static_visitor<void>
{
 private:
  //! User/item combinations to predict.
  const arma::Mat<size_t>& combinations;
  //! Predicted ratings for each user/item combination.
  arma::vec& predictions;

 public:
  //! Predict ratings for each user-item combination.
  template <typename DecompositionPolicy,
            typename NormalizationType = NoNormalization>
  void operator()(CFType<DecompositionPolicy, NormalizationType>* c) const;

  //! Visitor constructor.
  PredictVisitor(const arma::Mat<size_t>& combinations,
                 arma::vec& predictions);
};

/**
 * RecommendationVisitor uses the CFType object to get recommendations for the
 * given users.
 */
template <typename NeighborSearchPolicy,
          typename InterpolationPolicy>
class RecommendationVisitor : public boost::static_visitor<void>
{
 private:
  //! Number of Recommendations.
  const size_t numRecs;
  //! Recommendations matrix to save recommendations.
  arma::Mat<size_t>& recommendations;
  //! Users for which recommendations are to be generated.
  const arma::Col<size_t>& users;
  //! Whether users are given.
  const bool usersGiven;

 public:
  //! Visitor constructor.
  RecommendationVisitor(const size_t numRecs,
                        arma::Mat<size_t>& recommendations,
                        const arma::Col<size_t>& users,
                        const bool usersGiven);

  //! Generates the given number of recommendations.
  template <typename DecompositionPolicy,
            typename NormalizationType = NoNormalization>
  void operator()(CFType<DecompositionPolicy, NormalizationType>* c) const;
};

/**
 * The model to save to disk.
 */
class CFModel
{
 private:
  /**
   * cf holds an instance of the CFType class for the current
   * decompositionPolicy and normalizationType. It is initialized every time
   * Train() is executed. We access to the contained value through the visitor
   * classes defined above.
   */
  boost::variant<CFType<NMFPolicy, NoNormalization>*,
                 CFType<BatchSVDPolicy, NoNormalization>*,
                 CFType<RandomizedSVDPolicy, NoNormalization>*,
                 CFType<RegSVDPolicy, NoNormalization>*,
                 CFType<SVDCompletePolicy, NoNormalization>*,
                 CFType<SVDIncompletePolicy, NoNormalization>*,
                 CFType<BiasSVDPolicy, NoNormalization>*,
                 CFType<SVDPlusPlusPolicy, NoNormalization>*,

                 CFType<NMFPolicy, ItemMeanNormalization>*,
                 CFType<BatchSVDPolicy, ItemMeanNormalization>*,
                 CFType<RandomizedSVDPolicy, ItemMeanNormalization>*,
                 CFType<RegSVDPolicy, ItemMeanNormalization>*,
                 CFType<SVDCompletePolicy, ItemMeanNormalization>*,
                 CFType<SVDIncompletePolicy, ItemMeanNormalization>*,
                 CFType<BiasSVDPolicy, ItemMeanNormalization>*,
                 CFType<SVDPlusPlusPolicy, ItemMeanNormalization>*,

                 CFType<NMFPolicy, UserMeanNormalization>*,
                 CFType<BatchSVDPolicy, UserMeanNormalization>*,
                 CFType<RandomizedSVDPolicy, UserMeanNormalization>*,
                 CFType<RegSVDPolicy, UserMeanNormalization>*,
                 CFType<SVDCompletePolicy, UserMeanNormalization>*,
                 CFType<SVDIncompletePolicy, UserMeanNormalization>*,
                 CFType<BiasSVDPolicy, UserMeanNormalization>*,
                 CFType<SVDPlusPlusPolicy, UserMeanNormalization>*,

                 CFType<NMFPolicy, OverallMeanNormalization>*,
                 CFType<BatchSVDPolicy, OverallMeanNormalization>*,
                 CFType<RandomizedSVDPolicy, OverallMeanNormalization>*,
                 CFType<RegSVDPolicy, OverallMeanNormalization>*,
                 CFType<SVDCompletePolicy, OverallMeanNormalization>*,
                 CFType<SVDIncompletePolicy, OverallMeanNormalization>*,
                 CFType<BiasSVDPolicy, OverallMeanNormalization>*,
                 CFType<SVDPlusPlusPolicy, OverallMeanNormalization>*,

                 CFType<NMFPolicy, ZScoreNormalization>*,
                 CFType<BatchSVDPolicy, ZScoreNormalization>*,
                 CFType<RandomizedSVDPolicy, ZScoreNormalization>*,
                 CFType<RegSVDPolicy, ZScoreNormalization>*,
                 CFType<SVDCompletePolicy, ZScoreNormalization>*,
                 CFType<SVDIncompletePolicy, ZScoreNormalization>*,
                 CFType<BiasSVDPolicy, ZScoreNormalization>*,
                 CFType<SVDPlusPlusPolicy, ZScoreNormalization>*> cf;

 public:
  //! Create an empty CF model.
  CFModel() { }

  //! Clean up memory.
  ~CFModel();

  //! Get the pointer to CFType<> object.
  template <typename DecompositionPolicy,
            typename NormalizationType = NoNormalization>
  const CFType<DecompositionPolicy, NormalizationType>* CFPtr() const;

  //! Train the model.
  template<typename DecompositionPolicy,
           typename MatType>
  void Train(const MatType& data,
             const size_t numUsersForSimilarity,
             const size_t rank,
             const size_t maxIterations,
             const double minResidue,
             const bool mit,
             const std::string& normalizationType = "none");

  //! Make predictions.
  template <typename NeighborSearchPolicy,
            typename InterpolationPolicy>
  void Predict(const arma::Mat<size_t>& combinations,
               arma::vec& predictions);

  //! Compute recommendations for query users.
  template<typename NeighborSearchPolicy,
           typename InterpolationPolicy>
  void GetRecommendations(const size_t numRecs,
                          arma::Mat<size_t>& recommendations,
                          const arma::Col<size_t>& users);

  //! Compute recommendations for all users.
  template<typename NeighborSearchPolicy,
           typename InterpolationPolicy>
  void GetRecommendations(const size_t numRecs,
                          arma::Mat<size_t>& recommendations);

  //! Serialize the model.
  template<typename Archive>
  void serialize(Archive& ar, const unsigned int /* version */);
};

} // namespace cf
} // namespace mlpack

// Include implementation.
#include "cf_model_impl.hpp"

#endif


#define BOOST_TEST_MODULE "test_global_pair_interaction"

#ifdef BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#else
#include <boost/test/included/unit_test.hpp>
#endif

#include <test/util/utility.hpp>

#include <mjolnir/core/SimulatorTraits.hpp>
#include <mjolnir/core/BoundaryCondition.hpp>
#include <mjolnir/core/NaivePairCalculation.hpp>
#include <mjolnir/forcefield/global/GlobalPairInteraction.hpp>
#include <mjolnir/forcefield/global/DebyeHuckelPotential.hpp>
#include <mjolnir/util/make_unique.hpp>
#include <random>

BOOST_AUTO_TEST_CASE(GlobalPairInteraction_double)
{
    namespace test = mjolnir::test;
    mjolnir::LoggerManager::set_default_logger("test_global_pair_interaction.log");

    using traits_type = mjolnir::SimulatorTraits<double, mjolnir::UnlimitedBoundary>;
    using real_type           = traits_type::real_type;
    using coordinate_type     = traits_type::coordinate_type;
    using boundary_type       = traits_type::boundary_type;
    using system_type         = mjolnir::System<traits_type>;
    using topology_type       = mjolnir::Topology;
    using potential_type      = mjolnir::DebyeHuckelPotential<real_type>;
    using parameter_list_type = mjolnir::DebyeHuckelParameterList<traits_type>;
    using parameter_type      = typename parameter_list_type::parameter_type;
    using partition_type      = mjolnir::NaivePairCalculation<traits_type, potential_type>;
    using interaction_type    = mjolnir::GlobalPairInteraction<traits_type, potential_type>;

    {
        // to make the value realistic, we need to modify the unit system.
        using phys_type = mjolnir::physics::constants<real_type>;
        using unit_type = mjolnir::unit::constants<real_type>;

        phys_type::set_kB(unit_type::boltzmann_constant() *
                          1e-3 * unit_type::J_to_cal() * unit_type::avogadro_constant());
        phys_type::set_NA(unit_type::avogadro_constant());
        phys_type::set_eps0((unit_type::vacuum_permittivity() /
            unit_type::elementary_charge()) / unit_type::elementary_charge() *
            (1e+3 / unit_type::J_to_cal() / unit_type::avogadro_constant()) *
            (1.0 / unit_type::m_to_angstrom()));

        phys_type::set_m_to_length(1e-10);
        phys_type::set_length_to_m(1e+10);
        phys_type::set_L_to_volume(1e+27);
        phys_type::set_volume_to_L(1e-27);
    }

    parameter_list_type parameter_list(
        std::vector<std::pair<std::size_t, parameter_type>>{
            {0, {/* charge = */1.0}},
            {1, {/* charge = */1.0}}
        }, {}, typename parameter_list_type::ignore_molecule_type("Nothing"),
               typename parameter_list_type::ignore_group_type   ({})
        );

    interaction_type interaction(potential_type{5.5},
        mjolnir::ParameterList<traits_type, potential_type>(
            mjolnir::make_unique<parameter_list_type>(std::move(parameter_list))),
        mjolnir::SpatialPartition<traits_type, potential_type>(
            mjolnir::make_unique<partition_type>()));

    system_type sys(2, boundary_type{});
    test::clear_everything(sys);
    sys.attribute("temperature") = 300.0;
    sys.attribute("ionic_strength") = 0.2;

    topology_type topol(2);
    topol.construct_molecules();

    interaction.initialize(sys, topol);
    // check if the pair of particles are within the cutoff range.
    BOOST_REQUIRE(interaction.calc_energy(sys) != 0.0);

    std::mt19937 rng(123456789);

    potential_type potential(5.5);
    potential.initialize(sys);

    const real_type r_min = 0.5;
    const real_type r_max = potential.debye_length() * 5.5;
    const real_type dr    = 1e-3;
    const int max_count = (r_max - r_min) / dr;

    for(int i = 0; i < max_count-1; ++i)
    {
        const real_type dist = r_min + i * dr;

        sys.position(0) = coordinate_type(0,0,0);
        sys.position(1) = coordinate_type(dist,0,0);
        sys.force(0)    = coordinate_type(0,0,0);
        sys.force(1)    = coordinate_type(0,0,0);

        test::apply_random_perturbation(sys, rng, 0.1);
        test::apply_random_rotation(sys, rng);

        constexpr real_type tol = 1e-4;
        constexpr real_type dr  = 1e-5;

        test::check_force(sys, interaction, tol, dr);
        test::check_virial(sys, interaction, tol);
        test::check_force_and_virial(sys, interaction, tol);
        test::check_force_and_energy(sys, interaction, tol);
        test::check_force_energy_virial(sys, interaction, tol);
    }
}


// Copyright (c) 2015
// Author: Chrono Law
#include <utility>
#include <std.hpp>
using namespace std;

#include <boost/operators.hpp>
using namespace boost;

//////////////////////////////////////////
class demo_class
{
public:
    demo_class(int n):x(n){}
    int x;
    friend bool operator<(const demo_class& l, const demo_class&r)
    {   return l.x < r.x;   }
};

void case1()
{
    demo_class a(10), b(20);
    using namespace std::rel_ops;

    assert(a<b) ;
    assert(b>=a) ;

}

//////////////////////////////////////////

class point : //boost::less_than_comparable<point,
              //boost::equality_comparable<point> >
              totally_ordered<point,
              additive<point> >
{
    int x, y, z;
public:
    explicit point(int a=0, int b=0, int c=0):x(a),y(b),z(c){}
    void print()const
    {   cout << x <<","<< y <<","<< z << endl;  }

    friend bool operator<(const point& l, const point& r)
    {
        return (l.x*l.x + l.y*l.y +l.z*l.z <
                r.x*r.x + r.y*r.y +r.z*r.z);
    }

    friend bool operator==(const point& l, const point& r)
    {   return r.x == l.x && r.y == l.y && r.z == l.z;  }

    point& operator+=(const point& r)       //支持addable概念
    {
        x += r.x;
        y += r.y;
        z += r.z;
        return *this;
    }

    point& operator-=(const point& r)   //支持subtractable概念
    {
        x -= r.x;
        y -= r.y;
        z -= r.z;
        return *this;
    }

};

void case2()
{
    point p0, p1(1,2,3), p2(3,0,5), p3(3,2,1);

    assert(p0 < p1 && p1 < p2);
    assert(p2 > p0);
    assert(p1 <= p3);
    assert(!(p1<p3)&&!(p1>p3) );

    {
        point p0, p1(1,2,3), p2(p1), p3(3,2,1);
        assert(p1 == p2);
        assert(p1 != p3);
    }
}

//////////////////////////////////////////
#include <boost/assign.hpp>
void case3()
{
    point p0, p1(1,2,3), p2(5,6,7), p3(3,2,1);

    using namespace boost::assign;
    vector<point> v = (list_of(p0), p1, p2, p3);

    auto pos = std::find(v.begin(), v.end(), point(1,2,3));
    pos->print();

    (p1 + p2).print();
    (p3 - p1).print();

    assert((p2 - p2) == p0);

}

//////////////////////////////////////////
void case4()
{
    point p0, p1(1,2,3), p2(5,6,7), p3(3,2,1);

    using namespace boost::assign;
    vector<point> v = (list_of(p0), p1, p2, p3);

    auto pos = std::find(v.begin(), v.end(), point(1,2,3));
    for (; pos != v.end();
            pos = std::find(pos + 1, v.end(), point(1,2,3)))
    {   pos->print();}

    pos = std::find(v.begin(), v.end(), point(2,1,3));
    assert(pos == v.end());

}

//////////////////////////////////////////
template<typename T>
class my_smart_ptr:
    public dereferenceable<my_smart_ptr<T>, T* >
{
    T *p;
public:
    my_smart_ptr(T *x):p(x){}
    ~my_smart_ptr(){delete p;}
    T& operator*() const
    {   return *p;  }
};

void case5()
{
    my_smart_ptr<string > p(new string("123"));
    assert(p->size() == 3);
}

//////////////////////////////////////////
template<typename T>
class my_smart_array:
    public indexable<my_smart_array<T>, int, T& >
{
    T *p;
    public:
    typedef my_smart_array<T> this_type;
    typedef T*      iter_type;

    my_smart_array(T *x):p(x){}
    ~my_smart_array(){delete[] p;}

    friend iter_type operator+(const this_type& a, int n)
    {
        return a.p + n;
    }
};

void case6()
{
    my_smart_array<double> ma(new double[10]);
    ma[0] = 1.0;
    *(ma + 1) = 2.0;
    cout << ma[1] << endl;

}

//////////////////////////////////////////
#include <boost/core/explicit_operator_bool.hpp>

struct demo_a
{
    BOOST_EXPLICIT_OPERATOR_BOOL()
    bool operator!() const
    {   return false;}
    //explicit operator bool() const {
    //    return true;
    //}
};

struct demo_b
{
    BOOST_EXPLICIT_OPERATOR_BOOL_NOEXCEPT()
    bool operator!() const
    {   return true;}
    //explicit operator bool() const {
    //    return false;
    //}
};

void case7()
{
    demo_a a;
    demo_b b;
    assert(a && !b);
    //assert(a != b);
    //assert(a > b);
}

//////////////////////////////////////////

int main()
{
    case1();
    case2();
    case3();
    case4();
    case5();
    case6();
    case7();
}


//==================================================================================================
/*!

  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/bitwise_andnot.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>

STF_CASE_TPL (" bitwise_andnot real",  STF_IEEE_TYPES)
{
  namespace bs = boost::simd;

  using bs::bitwise_andnot;
  using r_t = decltype(bitwise_andnot(T(), T()));
  typedef T wished_r_t;

  // return type conformity test
  STF_TYPE_IS(r_t, wished_r_t);

  // specific values tests
#ifndef BOOST_SIMD_NO_INVALIDS
  STF_EQUAL(bitwise_andnot(bs::Inf<T>(), bs::Inf<T>()), bs::Zero<r_t>());
  STF_EQUAL(bitwise_andnot(bs::Minf<T>(), bs::Minf<T>()), bs::Zero<r_t>());
  STF_EQUAL(bitwise_andnot(bs::Nan<T>(), bs::Nan<T>()), bs::Zero<r_t>());
#endif
  STF_EQUAL(bitwise_andnot(bs::One<T>(),bs::Zero<T>()), bs::One<r_t>());
  STF_EQUAL(bitwise_andnot(bs::Zero<T>(), bs::Zero<T>()), bs::Zero<r_t>());
} // end of test for floating_

STF_CASE_TPL (" bitwise_andnot integer",  STF_INTEGRAL_TYPES)
{
  namespace bs = boost::simd;

  using bs::bitwise_andnot;
  using r_t = decltype(bitwise_andnot(T(), T()));

  // return type conformity test
  STF_TYPE_IS(r_t, T);

  STF_EQUAL(bitwise_andnot(bs::One<T>(), bs::One<T>()), bs::Zero<r_t>());
  STF_EQUAL(bitwise_andnot(bs::One<T>(),bs::Zero<T>()), bs::One<r_t>());
  STF_EQUAL(bitwise_andnot(bs::Zero<T>(), bs::Zero<T>()), bs::Zero<r_t>());
} // end of test for integer_

STF_CASE_TPL("bitwise_andnot mix", STF_IEEE_TYPES)
{
  namespace bs = boost::simd;
  namespace bd = boost::dispatch;
  using bs::bitwise_andnot;
  using siT = bd::as_integer_t<T>;
  using uiT = bd::as_integer_t<T, unsigned>;

  // return type conformity test
  STF_EXPR_IS(bitwise_andnot(T(),uiT()), T);
  STF_EXPR_IS(bitwise_andnot(T(),siT()), T);
  STF_EXPR_IS(bitwise_andnot(uiT(), T()), uiT);
  STF_EXPR_IS(bitwise_andnot(siT(), T()), siT);

  // specific values tests
  STF_IEEE_EQUAL(bitwise_andnot(bs::Nan<T>(),bs::Zero<uiT>()), bs::Nan<T>());
  STF_IEEE_EQUAL(bitwise_andnot(bs::Nan<T>(), bs::Zero<siT>()), bs::Nan<T>());
  STF_EQUAL(bitwise_andnot(bs::Mone<siT>(),bs::Zero<T>()), bs::Mone<siT>());
  STF_EQUAL(bitwise_andnot(bs::Valmax<uiT>(), bs::Zero<T>()), bs::Valmax<uiT>());
}


/*
	Copyright (C) 2003-2013 by David White <davewx7@gmail.com>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once

#include <boost/intrusive_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include "reference_counted_object.hpp"

namespace graphics
{
	template <typename T1, typename T2> class octree;
	template <typename T1, typename T2> typedef boost::intrusive_ptr<octree<T1,T2> > octree_ptr<T1,T2>;

	template <typename T1, typename T2>
	class octree : public reference_counted_object
	{
	public:
		explicit octree(const glm::vec3& origin, float radius)
			: origin_(origin), radius_(radius)
		{
		}

		virtual ~octree()
		{}

		int octant_from_point(const glm::vec3& pt)
		{
			int octant = 0;
			if(pt.x >= origin_.x) {
				octant |= 4;
			}
			if(pt.y >= origin_.y) {
				octant |= 2;
			}
			if(pt.z >= origin_.z) {
				octant |= 1;
			}
			return octant;
		}

		bool is_leaf const()
		{
			return children_.empty();
		}

		void insert(const glm::vec3& pt, const T2& data)
		{
			if(is_leaf()) {
				is(data_ == NULL) {
					data_.reset(new std::make_pair(pt, data));
				} else {
					T2 old_pt = data_->first;
					T2 old_data = data_->second;
					data_.reset();
					for(int n = 0; n != 8; ++n) {
						glm::vec3 child_origin(origin_.x+radius/(i&4 ? 2.0f : -2.0f),
							origin_.y+radius/(i&2 ? 2.0f : -2.0f),
							origin_.z+radius/(i&1 ? 2.0f : -2.0f));
						children_.push_back(new octree(child_origin, radius/2.0f));
					}
					children_[octant_from_point(old_pt)].insert(old_pt, old_data);
					children_[octant_from_point(pt)].insert(pt, data);
				}
			} else {
				children_[octant_from_point(pt)].insert(pt, data);
			}
		}

		void points_in_box(const glm::vec3& b1, const glm::vec3& b2, std::vector<T2>& results)
		{
			if(is_leaf()) {
				if(data_ != NULL) {
					if(data_->first.x > b1.x || data_->first.y > b1.y || data_->first.z > b1.z
						|| data_->first.x < b2.x || data_->first.y < b2.y || data_->first.z < b2.z) {
						return;
					}
					results.push_back(data_->second);
				}
			} else {
				for(auto child : children_) {
					glm::vec3 c1 = child.origin_ - child.radius_;
					glm::vec3 c2 = child.origin_ + child.radius_;

					if(c1.x < b1.x || c1.y < b1.y || c1.z < b1.z
						|| c2.x > b2.x || c2.y > b2.y || c2.z > b2.z) {
						continue;
					}
					child.points_in_box(b1, b2, results);
				}
			}
		}

	private:
		glm::vec3 origin_;
		float radius_;

		std::vector<octree_ptr<T1> > children_;
		boost::shared_ptr<std::pair<glm::vec3, T2> > data_;

		octree();
		octree(const octree&);
	};
}


// (C) 2014 Arek Olek

#pragma once

#include <vector>

#include <boost/graph/adjacency_list.hpp>

template <class Graph, class Tree>
void visit(Graph const & G, unsigned v, std::vector<bool>& discovered, Tree& T) {
  discovered[v] = true;
  for(auto w : range(adjacent_vertices(v, G))) {
    if(!discovered[w]) {
      add_edge(v, w, T);
      visit(G, w, discovered, T);
    }
  }
}

template <class Graph, class Tree>
Tree dfs_tree(Graph const & G) {
  std::vector<bool> visited(num_vertices(G));
  Tree tree(num_vertices(G));
  visit(G, 0, visited, tree);
  return tree;
}


#ifndef COMB_MULTI_ARRAY_SERIALIZATION_HPP__
#define COMB_MULTI_ARRAY_SERIALIZATION_HPP__

/* This code is based off of the example:
   https://svn.boost.org/svn/boost/sandbox/statistics/detail/multi_array/boost/statistics/detail/multi_array/serialization/serialize.hpp

   (C) Copyright 2009 Erwann Rogard
   Use, modification and distribution are 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 <boost/multi_array.hpp>
#include <boost/serialization/array.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/split_free.hpp>

namespace boost{ namespace serialization{

    template<class Archive,class T>
    inline void load( Archive & ar, boost::multi_array<T,5> & t, const unsigned int file_version)
    {
        typedef boost::multi_array<T,5> multi_array_;
        typedef typename multi_array_::size_type size_;

        size_ n0;
        ar >> BOOST_SERIALIZATION_NVP(n0);
        size_ n1;
        ar >> BOOST_SERIALIZATION_NVP(n1);
        size_ n2;
        ar >> BOOST_SERIALIZATION_NVP(n2);
        size_ n3;
        ar >> BOOST_SERIALIZATION_NVP(n3);
        size_ n4;
        ar >> BOOST_SERIALIZATION_NVP(n4);

        t.resize(boost::extents[n0][n1][n2][n3][n4]);
        ar >> make_array(t.data(), t.num_elements());
    }

    template<typename Archive,typename T>
    inline void save( Archive & ar, const boost::multi_array<T,5> & t, const unsigned int file_version)
    {
        typedef boost::multi_array<T,5> multi_array_;
        typedef typename multi_array_::size_type size_;

        size_ n0 = (t.shape()[0]);
        ar << BOOST_SERIALIZATION_NVP(n0);
        size_ n1 = (t.shape()[1]);
        ar << BOOST_SERIALIZATION_NVP(n1);
        size_ n2 = (t.shape()[2]);
        ar << BOOST_SERIALIZATION_NVP(n2);
        size_ n3 = (t.shape()[3]);
        ar << BOOST_SERIALIZATION_NVP(n3);
        size_ n4 = (t.shape()[4]);
        ar << BOOST_SERIALIZATION_NVP(n4);
        ar << boost::serialization::make_array(t.data(), t.num_elements());
    }

    template<class Archive,typename T>
    inline void serialize( Archive & ar, boost::multi_array<T,5>& t, const unsigned int file_version)
    {
        split_free(ar, t, file_version);
    }

    template<class Archive,class T>
    inline void load( Archive & ar, boost::multi_array<T,4> & t, const unsigned int file_version)
    {
        typedef boost::multi_array<T,4> multi_array_;
        typedef typename multi_array_::size_type size_;

        size_ n0;
        ar >> BOOST_SERIALIZATION_NVP(n0);
        size_ n1;
        ar >> BOOST_SERIALIZATION_NVP(n1);
        size_ n2;
        ar >> BOOST_SERIALIZATION_NVP(n2);
        size_ n3;
        ar >> BOOST_SERIALIZATION_NVP(n3);

        t.resize(boost::extents[n0][n1][n2][n3]);
        ar >> make_array(t.data(), t.num_elements());
    }

    template<typename Archive,typename T>
    inline void save( Archive & ar, const boost::multi_array<T,4> & t, const unsigned int file_version)
    {
        typedef boost::multi_array<T,4> multi_array_;
        typedef typename multi_array_::size_type size_;

        size_ n0 = (t.shape()[0]);
        ar << BOOST_SERIALIZATION_NVP(n0);
        size_ n1 = (t.shape()[1]);
        ar << BOOST_SERIALIZATION_NVP(n1);
        size_ n2 = (t.shape()[2]);
        ar << BOOST_SERIALIZATION_NVP(n2);
        size_ n3 = (t.shape()[3]);
        ar << BOOST_SERIALIZATION_NVP(n3);
        ar << boost::serialization::make_array(t.data(), t.num_elements());
    }

    template<class Archive,typename T>
    inline void serialize( Archive & ar, boost::multi_array<T,4>& t, const unsigned int file_version)
    {
        split_free(ar, t, file_version);
    }

    template<class Archive,class T>
    inline void load( Archive & ar, boost::multi_array<T,3> & t, const unsigned int file_version)
    {
        typedef boost::multi_array<T,3> multi_array_;
        typedef typename multi_array_::size_type size_;

        size_ n0;
        ar >> BOOST_SERIALIZATION_NVP(n0);
        size_ n1;
        ar >> BOOST_SERIALIZATION_NVP(n1);
        size_ n2;
        ar >> BOOST_SERIALIZATION_NVP(n2);

        t.resize(boost::extents[n0][n1][n2]);
        ar >> make_array(t.data(), t.num_elements());
    }

    template<typename Archive,typename T>
    inline void save( Archive & ar, const boost::multi_array<T,3> & t, const unsigned int file_version)
    {
        typedef boost::multi_array<T,3> multi_array_;
        typedef typename multi_array_::size_type size_;

        size_ n0 = (t.shape()[0]);
        ar << BOOST_SERIALIZATION_NVP(n0);
        size_ n1 = (t.shape()[1]);
        ar << BOOST_SERIALIZATION_NVP(n1);
        size_ n2 = (t.shape()[2]);
        ar << BOOST_SERIALIZATION_NVP(n2);

        ar << boost::serialization::make_array(t.data(), t.num_elements());
    }

    template<class Archive,typename T>
    inline void serialize( Archive & ar, boost::multi_array<T,3>& t, const unsigned int file_version)
    {
        split_free(ar, t, file_version);
    }

    template<class Archive,class T>
    inline void load( Archive & ar, boost::multi_array<T,2> & t, const unsigned int file_version)
    {
        typedef boost::multi_array<T,2> multi_array_;
        typedef typename multi_array_::size_type size_;

        size_ n0;
        ar >> BOOST_SERIALIZATION_NVP(n0);
        size_ n1;
        ar >> BOOST_SERIALIZATION_NVP(n1);

        t.resize(boost::extents[n0][n1]);
        ar >> make_array(t.data(), t.num_elements());
    }

    template<typename Archive,typename T>
    inline void save( Archive & ar, const boost::multi_array<T,2> & t, const unsigned int file_version)
    {
        typedef boost::multi_array<T,2> multi_array_;
        typedef typename multi_array_::size_type size_;

        size_ n0 = (t.shape()[0]);
        ar << BOOST_SERIALIZATION_NVP(n0);
        size_ n1 = (t.shape()[1]);
        ar << BOOST_SERIALIZATION_NVP(n1);

        ar << boost::serialization::make_array(t.data(), t.num_elements());
    }

    template<class Archive,typename T>
    inline void serialize( Archive & ar, boost::multi_array<T,2>& t, const unsigned int file_version)
    {
        split_free(ar, t, file_version);
    }

}}

#endif


#ifndef YASMIC_SIMPLE_CSR_MATRIX_HPP
#define YASMIC_SIMPLE_CSR_MATRIX_HPP

/**
 * @file simple_csr_matrix.hpp
 * This file implements a simple compressed sparse row matrix wrapper
 * that assumes that the underlying datastore is a series of pointers.
 */

/*
 * David Gleich
 * Copyright, Stanford University, 2007
 */

/*
 * 9 November 2006
 * Initial version
 *
 * 8 July 2007
 * Commented out non-working code
 * Updated to work with simple_csr_matrix_as_graph.hpp
 *
 * 29 August 2007
 * Removed big commented section
 */

#include <yasmic/smatrix_traits.hpp>
#include <boost/iterator/counting_iterator.hpp>

namespace yasmic
{

/**
 * csr_matrix is a more user manageable compressed sparse row matrix type.
 * It is based on the same ideas as the compressed_row_matrix, but designed
 * to be less general and more specific.
 *
 * The idea is that an application will use the csr_matrix structure to
 * manage a sparse matrix and design algorithms that are NOT more
 * generally applicable.
 */
template <class IndexType, class ValueType, class NzSizeType=IndexType>
struct simple_csr_matrix
{
    IndexType nrows;
    IndexType ncols;
    NzSizeType nnz;

    NzSizeType* ai;
    IndexType* aj;
    ValueType* a;

    // an extra indextype to serve as the default value of ai for
    // an empty matrix
    IndexType empty;

    simple_csr_matrix()
        : nrows(0), ncols(0), nnz(0), empty(0), ai(&empty), aj(NULL), a(NULL) {}

    simple_csr_matrix(IndexType nrows, IndexType ncols, NzSizeType nnz,
         NzSizeType *ai, IndexType *aj, ValueType *a)
         : nrows(nrows), ncols(ncols), nnz(nnz), ai(ai), aj(aj), a(a) {}
};

template <class IndexType, class ValueType, class NzSizeType>
struct smatrix_traits< simple_csr_matrix<IndexType, ValueType, NzSizeType> >
{
    typedef IndexType index_type;
    typedef ValueType value_type;

    typedef NzSizeType nz_size_type;
    typedef NzSizeType nz_index_type;

    //typedef typename impl::csr_nonzero<IndexType, ValueType, NzSizeType> nonzero_iterator;
    //typedef impl::csr_nonzero<IndexType, ValueType, NzSizeType> nonzero_descriptor;

    //typedef typename compressed_row_matrix_type::row_nonzero_iterator row_nonzero_iterator;
    //typedef impl::csr_nonzero<IndexType, ValueType, NzSizeType> row_nonzero_descriptor;

    typedef boost::counting_iterator<IndexType> row_iterator;

    struct properties
        : public row_access_tag, public nonzero_index_tag
    {};
};

template <class IndexType, class ValueType, class NzSizeType>
IndexType nrows(const simple_csr_matrix<IndexType, ValueType, NzSizeType>& m)
{ return m.nrows; }

template <class IndexType, class ValueType, class NzSizeType>
IndexType ncols(const simple_csr_matrix<IndexType, ValueType, NzSizeType>& m)
{ return m.ncols; }

template <class IndexType, class ValueType, class NzSizeType>
NzSizeType nnz(const simple_csr_matrix<IndexType, ValueType, NzSizeType>& m)
{ return m.nnz; }

} // namespace yasmic

#endif /* YASMIC_SIMPLE_CSR_MATRIX_HPP */