#include <boost/math/special_functions/next.hpp>

#include <limits>

#include "caffe/common.hpp"
#include "caffe/util/math_functions.hpp"
#include "caffe/util/rng.hpp"

namespace caffe {

// SET
template<typename Dtype>
void caffe_set(const int_tp n, const Dtype alpha, Dtype* y) {
  if (alpha == 0) {
    memset(y, 0, sizeof(Dtype) * n);  // NOLINT(caffe/alt_fn)
    return;
  }
  for (int_tp i = 0; i < n; ++i) {
    y[i] = alpha;
  }
}

template void caffe_set<int8_t>(const int_tp n, const int8_t alpha,
                                 int8_t* y);
template void caffe_set<uint8_t>(const int_tp n, const uint8_t alpha,
                                uint8_t* y);
template void caffe_set<int16_t>(const int_tp n, const int16_t alpha,
                                 int16_t* y);
template void caffe_set<uint16_t>(const int_tp n, const uint16_t alpha,
                                  uint16_t* y);
template void caffe_set<int32_t>(const int_tp n, const int32_t alpha,
                                 int32_t* y);
template void caffe_set<uint32_t>(const int_tp n, const uint32_t alpha,
                                  uint32_t* y);
template void caffe_set<int64_t>(const int_tp n, const int64_t alpha,
                                 int64_t* y);
template void caffe_set<uint64_t>(const int_tp n, const uint64_t alpha,
                                  uint64_t* y);
template void caffe_set<half_fp>(const int_tp n,
                             const half_fp alpha, half_fp* y);
template void caffe_set<float>(const int_tp n, const float alpha, float* y);
template void caffe_set<double>(const int_tp n, const double alpha, double* y);

// ADD SCALAR
template<typename Dtype>
void caffe_add_scalar(const int_tp n, const Dtype alpha, Dtype* y) {
  for (size_t i = 0; i < n; ++i) {
    y[i] += alpha;
  }
}

template void caffe_add_scalar(const int_tp n, const half_fp alpha, half_fp* y);
template void caffe_add_scalar(const int_tp n, const float alpha, float* y);
template void caffe_add_scalar(const int_tp n, const double alpha, double* y);
template void caffe_add_scalar(const int_tp n, const uint8_t alpha, uint8_t* y);
template void caffe_add_scalar(const int_tp n, const uint16_t alpha, uint16_t* y);
template void caffe_add_scalar(const int_tp n, const uint32_t alpha, uint32_t* y);
template void caffe_add_scalar(const int_tp n, const uint64_t alpha, uint64_t* y);

// COPy
template<typename Dtype>
void caffe_copy(const int_tp n, const Dtype* x, Dtype* y) {
  if (x != y) {
    memcpy(y, x, sizeof(Dtype) * n);  // NOLINT(caffe/alt_fn)
  }
}

template void caffe_copy<int8_t>(const int_tp n, const int8_t* x,
                                     int8_t* y);
template void caffe_copy<int16_t>(const int_tp n, const int16_t* x,
                                     int16_t* y);
template void caffe_copy<int32_t>(const int_tp n, const int32_t* x,
                                     int32_t* y);
template void caffe_copy<int64_t>(const int_tp n, const int64_t* x,
                                     int64_t* y);
template void caffe_copy<uint8_t>(const int_tp n, const uint8_t* x,
                                     uint8_t* y);
template void caffe_copy<uint16_t>(const int_tp n, const uint16_t* x,
                                     uint16_t* y);
template void caffe_copy<uint32_t>(const int_tp n, const uint32_t* x,
                                     uint32_t* y);
template void caffe_copy<uint64_t>(const int_tp n, const uint64_t* x,
                                     uint64_t* y);
template void caffe_copy<half_fp>(const int_tp n,
                                      const half_fp* x, half_fp* y);
template void caffe_copy<float>(const int_tp n, const float* x,
                                    float* y);
template void caffe_copy<double>(const int_tp n, const double* x,
                                     double* y);

// ADD
template<typename Dtype>
void caffe_add(const int_tp n, const Dtype* a,
               const Dtype* b, Dtype* y) {
  for (int_tp i = 0; i < n; ++i) {
    y[i] = a[i] + b[i];
  }
}

template void caffe_add<half_fp>(const int_tp n, const half_fp* a,
                                 const half_fp* b, half_fp* y);
template void caffe_add<uint8_t>(const int_tp n, const uint8_t* a,
                                 const uint8_t* b, uint8_t* y);
template void caffe_add<uint16_t>(const int_tp n, const uint16_t* a,
                                 const uint16_t* b, uint16_t* y);
template void caffe_add<uint32_t>(const int_tp n, const uint32_t* a,
                                 const uint32_t* b, uint32_t* y);
template void caffe_add<uint64_t>(const int_tp n, const uint64_t* a,
                                 const uint64_t* b, uint64_t* y);

template<>
void caffe_add<float>(const int_tp n, const float* a, const float* b,
                      float* y) {
  vsAdd(n, a, b, y);
}

template<>
void caffe_add<double>(const int_tp n, const double* a, const double* b,
                       double* y) {
  vdAdd(n, a, b, y);
}

// SUB
template<typename Dtype>
void caffe_sub(const int_tp n, const Dtype* a,
               const Dtype* b, Dtype* y) {
  for (int_tp i = 0; i < n; ++i) {
    y[i] = a[i] - b[i];
  }
}

template void caffe_sub<half_fp>(const int_tp n, const half_fp* a,
                                 const half_fp* b, half_fp* y);
template void caffe_sub<uint8_t>(const int_tp n, const uint8_t* a,
                                 const uint8_t* b, uint8_t* y);
template void caffe_sub<uint16_t>(const int_tp n, const uint16_t* a,
                                 const uint16_t* b, uint16_t* y);
template void caffe_sub<uint32_t>(const int_tp n, const uint32_t* a,
                                 const uint32_t* b, uint32_t* y);
template void caffe_sub<uint64_t>(const int_tp n, const uint64_t* a,
                                 const uint64_t* b, uint64_t* y);

template<>
void caffe_sub<float>(const int_tp n, const float* a, const float* b,
                      float* y) {
  vsSub(n, a, b, y);
}
template<>
void caffe_sub<double>(const int_tp n, const double* a, const double* b,
                       double* y) {
  vdSub(n, a, b, y);
}

// MUL
template<typename Dtype>
void caffe_mul(const int_tp n, const Dtype* a, const Dtype* b, Dtype* y) {
  for (int_tp i = 0; i < n; ++i) {
    y[i] = a[i] * b[i];
  }
}

template void caffe_mul<half_fp>(const int_tp n, const half_fp* a,
                                 const half_fp* b, half_fp* y);
template void caffe_mul<uint8_t>(const int_tp n, const uint8_t* a,
                                 const uint8_t* b, uint8_t* y);
template void caffe_mul<uint16_t>(const int_tp n, const uint16_t* a,
                                 const uint16_t* b, uint16_t* y);
template void caffe_mul<uint32_t>(const int_tp n, const uint32_t* a,
                                 const uint32_t* b, uint32_t* y);
template void caffe_mul<uint64_t>(const int_tp n, const uint64_t* a,
                                 const uint64_t* b, uint64_t* y);

template<>
void caffe_mul<float>(const int_tp n, const float* a, const float* b,
                      float* y) {
  vsMul(n, a, b, y);
}
template<>
void caffe_mul<double>(const int_tp n, const double* a, const double* b,
                       double* y) {
  vdMul(n, a, b, y);
}

// DIV
template<typename Dtype>
void caffe_div(const int_tp n, const Dtype* a, const Dtype* b, Dtype* y) {
  for (int_tp i = 0; i < n; ++i) {
    y[i] = a[i] / b[i];
  }
}

template void caffe_div<half_fp>(const int_tp n, const half_fp* a,
                                 const half_fp* b, half_fp* y);
template void caffe_div<uint8_t>(const int_tp n, const uint8_t* a,
                                 const uint8_t* b, uint8_t* y);
template void caffe_div<uint16_t>(const int_tp n, const uint16_t* a,
                                 const uint16_t* b, uint16_t* y);
template void caffe_div<uint32_t>(const int_tp n, const uint32_t* a,
                                 const uint32_t* b, uint32_t* y);
template void caffe_div<uint64_t>(const int_tp n, const uint64_t* a,
                                 const uint64_t* b, uint64_t* y);

template<>
void caffe_div<float>(const int_tp n, const float* a, const float* b,
                      float* y) {
  vsDiv(n, a, b, y);
}
template<>
void caffe_div<double>(const int_tp n, const double* a, const double* b,
                       double* y) {
  vdDiv(n, a, b, y);
}

// POWX
template<typename Dtype>
void caffe_powx(const int_tp n, const Dtype* a, const Dtype b, Dtype* y) {
  for (int_tp i = 0; i < n; ++i) {
    y[i] = pow(a[i], b);
  }
}

template void caffe_powx<half_fp>(const int_tp n, const half_fp* a,
                                const half_fp b, half_fp* y);
template void caffe_powx<uint8_t>(const int_tp n, const uint8_t* a,
                                const uint8_t b, uint8_t* y);
template void caffe_powx<uint16_t>(const int_tp n, const uint16_t* a,
                                const uint16_t b, uint16_t* y);
template void caffe_powx<uint32_t>(const int_tp n, const uint32_t* a,
                                const uint32_t b, uint32_t* y);
template void caffe_powx<uint64_t>(const int_tp n, const uint64_t* a,
                                const uint64_t b, uint64_t* y);

template<>
void caffe_powx<float>(const int_tp n, const float* a, const float b,
                       float* y) {
  vsPowx(n, a, b, y);
}
template<>
void caffe_powx<double>(const int_tp n, const double* a, const double b,
                        double* y) {
  vdPowx(n, a, b, y);
}

// SQR
template<typename Dtype>
void caffe_sqr(const int_tp n, const Dtype* a, Dtype* y) {
  for (int_tp i = 0; i < n; ++i) {
    y[i] = a[i] * a[i];
  }
}

template void caffe_sqr<half_fp>(const int_tp n, const half_fp* a, half_fp* y);
template void caffe_sqr<uint8_t>(const int_tp n, const uint8_t* a, uint8_t* y);
template void caffe_sqr<uint16_t>(const int_tp n, const uint16_t* a, uint16_t* y);
template void caffe_sqr<uint32_t>(const int_tp n, const uint32_t* a, uint32_t* y);
template void caffe_sqr<uint64_t>(const int_tp n, const uint64_t* a, uint64_t* y);

template<>
void caffe_sqr<float>(const int_tp n, const float* a, float* y) {
  vsSqr(n, a, y);
}
template<>
void caffe_sqr<double>(const int_tp n, const double* a, double* y) {
  vdSqr(n, a, y);
}

// SQRT
template<typename Dtype>
void caffe_sqrt(const int_tp n, const Dtype* a, Dtype* y) {
  for (int_tp i = 0; i < n; ++i) {
    y[i] = std::sqrt(a[i]);
  }
}

template void caffe_sqrt(const int_tp n, const half_fp* a, half_fp* y);
template void caffe_sqrt(const int_tp n, const uint8_t* a, uint8_t* y);
template void caffe_sqrt(const int_tp n, const uint16_t* a, uint16_t* y);
template void caffe_sqrt(const int_tp n, const uint32_t* a, uint32_t* y);
template void caffe_sqrt(const int_tp n, const uint64_t* a, uint64_t* y);

template<>
void caffe_sqrt<float>(const int_tp n, const float* a, float* y) {
  vsSqrt(n, a, y);
}
template<>
void caffe_sqrt<double>(const int_tp n, const double* a, double* y) {
  vdSqrt(n, a, y);
}

// EXP
template<typename Dtype>
void caffe_exp(const int_tp n, const Dtype* a, Dtype* y) {
  for (int_tp i = 0; i < n; ++i) {
    y[i] = std::exp(a[i]);
  }
}

template void caffe_exp<half_fp>(const int_tp n, const half_fp* a, half_fp* y);
template void caffe_exp<uint8_t>(const int_tp n, const uint8_t* a, uint8_t* y);
template void caffe_exp<uint16_t>(const int_tp n, const uint16_t* a, uint16_t* y);
template void caffe_exp<uint32_t>(const int_tp n, const uint32_t* a, uint32_t* y);
template void caffe_exp<uint64_t>(const int_tp n, const uint64_t* a, uint64_t* y);

template<>
void caffe_exp<float>(const int_tp n, const float* a, float* y) {
  vsExp(n, a, y);
}
template<>
void caffe_exp<double>(const int_tp n, const double* a, double* y) {
  vdExp(n, a, y);
}

// LOG
template<typename Dtype>
void caffe_log(const int_tp n, const Dtype* a, Dtype* y) {
  for (int_tp i = 0; i < n; ++i) {
    y[i] = log(a[i]);
  }
}

template void caffe_log<half_fp>(const int_tp n, const half_fp* a, half_fp* y);
template void caffe_log<uint8_t>(const int_tp n, const uint8_t* a, uint8_t* y);
template void caffe_log<uint16_t>(const int_tp n, const uint16_t* a, uint16_t* y);
template void caffe_log<uint32_t>(const int_tp n, const uint32_t* a, uint32_t* y);
template void caffe_log<uint64_t>(const int_tp n, const uint64_t* a, uint64_t* y);

template<>
void caffe_log<float>(const int_tp n, const float* a, float* y) {
  vsLn(n, a, y);
}
template<>
void caffe_log<double>(const int_tp n, const double* a, double* y) {
  vdLn(n, a, y);
}

// ABS
template<typename Dtype>
void caffe_abs(const int_tp n, const Dtype* a, Dtype* y) {
  for (int_tp i = 0; i < n; ++i) {
    y[i] = fabs(a[i]);
  }
}

template void caffe_abs<uint8_t>(const int_tp n, const uint8_t* a,
                                 uint8_t* y);
template void caffe_abs<uint16_t>(const int_tp n, const uint16_t* a,
                                  uint16_t* y);
template void caffe_abs<uint32_t>(const int_tp n, const uint32_t* a,
                                  uint32_t* y);
template void caffe_abs<uint64_t>(const int_tp n, const uint64_t* a,
                                  uint64_t* y);

template<>
void caffe_abs<half_fp>(const int_tp n, const half_fp* a,
                                 half_fp* y) {
  for (int_tp i = 0; i < n; ++i) {
    y[i] = fabs(a[i]);
  }
}
template<>
void caffe_abs<float>(const int_tp n, const float* a, float* y) {
  vsAbs(n, a, y);
}
template<>
void caffe_abs<double>(const int_tp n, const double* a, double* y) {
  vdAbs(n, a, y);
}

}  // namespace caffe


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

// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands.
// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_GEOMETRY_TEST_SIMPLIFY_HPP
#define BOOST_GEOMETRY_TEST_SIMPLIFY_HPP

// Test-functionality, shared between single and multi tests

#include <geometry_test_common.hpp>
#include <boost/geometry/algorithms/simplify.hpp>
#include <boost/geometry/algorithms/distance.hpp>
#include <boost/geometry/strategies/strategies.hpp>

#include <boost/geometry/domains/gis/io/wkt/wkt.hpp>

template <typename Tag, typename Geometry>
struct test_inserter
{
    static void apply(Geometry& , std::string const& , double )
    {}
};

template <typename Geometry>
struct test_inserter<bg::linestring_tag, Geometry>
{
    static void apply(Geometry& geometry, std::string const& expected, double distance)
    {
        Geometry simplified;
        bg::detail::simplify::simplify_insert(geometry,
            std::back_inserter(simplified), distance);

        std::ostringstream out;
        out << bg::wkt(simplified);
        BOOST_CHECK_EQUAL(out.str(), expected);
    }
};

template <typename Geometry>
void test_geometry(std::string const& wkt, std::string const& expected, double distance)
{
    Geometry geometry, simplified;

    // Generate polygon using only integer coordinates and obvious results
    // Polygon is a hexagon, having one extra point (2,1) on a line which should be filtered out.
    bg::read_wkt(wkt, geometry);
    bg::simplify(geometry, simplified, distance);

    {
        std::ostringstream out;
        out << bg::wkt(simplified);

        BOOST_CHECK_MESSAGE(out.str() == expected,
            "simplify: " << bg::wkt(geometry)
            << " expected " << expected
            << " got " << bg::wkt(simplified));
    }

    // Check using user-specified strategy
    typedef typename bg::point_type<Geometry>::type point_type;
    typedef typename bg::cs_tag<point_type>::type tag;
    typedef bg::strategy::distance::projected_point
        <
            point_type,
            point_type
        > strategy;
    typedef bg::strategy::simplify::douglas_peucker
        <
            point_type,
            strategy
        > simplify_strategy_type;

    BOOST_CONCEPT_ASSERT( (bg::concept::SimplifyStrategy<simplify_strategy_type>) );
    bg::simplify(geometry, simplified, distance, simplify_strategy_type());

    {
        std::ostringstream out;
        out << bg::wkt(simplified);
        BOOST_CHECK_EQUAL(out.str(), expected);
    }

    // Check inserter (if applicable)
    test_inserter
        <
            typename bg::tag<Geometry>::type,
            Geometry
        >::apply(geometry, expected, distance);
}

#endif


/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\
|  Phycas: Python software for phylogenetic analysis                          |
|  Copyright (C) 2006 Mark T. Holder, Paul O. Lewis and David L. Swofford     |
|                                                                             |
|  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, write to the Free Software Foundation, Inc.,    |
|  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.                |
\~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

#if 0
#ifndef BRENT_PRAXIS_HPP
#define BRENT_PRAXIS_HPP

#include <cstdlib>
#include <climits>
#include <cfloat>
#include "basic_lot.hpp"
//#include "probability_distribution.hpp"
#include "xprobdist.hpp"
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#if defined(PYTHON_ONLY)
#	include <boost/python/call_method.hpp>
#endif
//typedef std::pair<double, double> ParamAndLnProb;
//typedef std::pair<double, double> SliceInterval;

namespace phycas
{

struct FuncToMinimize
	{
	virtual ~FuncToMinimize()
		{
		//std::cerr << "\n>>>>> FuncToMinimize dying..." << std::endl;
		}
    virtual double operator()(double) = 0;
	};

typedef boost::shared_ptr<FuncToMinimize> FuncToMinimizeShPtr;

/*----------------------------------------------------------------------------------------------------------------------
|	Implements the multivariate minimizer described by Brent, Richard P. 1973. Algorithms for Minimization Without
|   Derivatives. Prentice-Hall. ISBN: 0-13-022335-2. This implementation adapted from Karl Gegenfurtner's C translation,
|   available from http://archives.math.utk.edu/software/msdos/numerical.analysis/praxis/
*/
class BrentPraxis
	{
	public:
								BrentPraxis();
								BrentPraxis(LotShPtr rnd, FuncToMinimizeShPtr f);
		virtual					~BrentPraxis();

		double                  Minimize(const std::vector<double> starting_point);

		void					AttachFunc(FuncToMinimizeShPtr f);
		void					AttachRandomNumberGenerator(LotShPtr rnd);

    protected:
		FuncToSampleShPtr		_func;          /**< is a functor representing the function to be minimized */
		LotShPtr				_r;             /**< is the random number generator */
        std::vector<double>     _minimum_point; /**< is the final point representing the minimum */
        bool                    _illc;          /**< is false by default, but can be set to true during minimization if problem is determined to be ill-conditioned */
        unsigned                _prin;          /**< controls the printed output from the routine: 0 -> no output; 1 -> print only starting and final values; 2 -> detailed map of the minimization process; 3 -> print also eigenvalues and vectors of the search directions. The default value is 1. */
        double                  _tol;           /**< is the tolerance allowed for the precision of the solution. Minimize returns if the criterion 2 * ||x[k]-x[k-1]|| <= sqrt(macheps) * ||x[k]|| + _tol is fulfilled more than ktm times. The default value depends on the machine precision. */
        unsigned                _ktm;           /**< is the number of times the tolerance criterion can be met before Minimize returns. The default value is 1, and a value of 4 leads to a very(!) cautious stopping criterion. */
        unsigned                _maxfun;        /**< is the maximum number of calls to _func allowed. Minimize will return after _maxfun calls to _func even when the minimum is not yet found. The default value of 0 indicates no limit on the number of calls. This return condition is only checked every n iterations. */
        double                  _scbd;          /**< is a scaling parameter. 1.0 is the default and indicates no scaling. If the scales for the different parameters are very different, _scbd should be set to a value of about 10.0. */

        unsigned                _n;             /**< number of variables (i.e. length of _minimum_point) */
        std::vector<double>     _d;             /**< eigenvalues */
        double * *              _v;             /**< matrix of eigenvectors */
	};

typedef boost::shared_ptr<BrentPraxis> BrentPraxisShPtr;

} // namespace phycas

#endif
#endif


// Copyright Matt Overby 2021.
// Distributed under the MIT License.

#ifndef GINI_MESHTXT_HPP
#define GINI_MESHTXT_HPP 1

#include <iostream>
#include <fstream>
#include <sstream>
#include <Eigen/Geometry>
#include <vector>
#include <iomanip>

namespace mcl
{

// Simple, slow, plain text
//
// X is n x DIM vertices (DIM = 2 or 3)
// P is m x PDIM primitives (PDIM = 3 for tris, 4 for tets)
// Returns true on success
// TODO: some error checking on read

static inline bool write_mesh_txt(
    const std::string& filename,
    const Eigen::MatrixXd &X,
    const Eigen::MatrixXi &P);

static inline bool read_mesh_txt(
    const std::string& filename,
    const Eigen::MatrixXd &X,
    const Eigen::MatrixXi &P);

//
// Implementation
//

bool write_mesh_txt(
	const std::string& filename,
	const Eigen::MatrixXd &X,
	const Eigen::MatrixXi &P)
{
    char delim = ' ';

    if (X.cols() < 2 || X.cols() > 3)
    {
        printf("Cannot write mesh txt, bad dim X\n");
        return false;
    }

    if (P.cols() < 3 || P.cols() > 4)
    {
        printf("Cannot write mesh txt, bad dim P\n");
        return false;
    }

    std::ofstream fi(filename);
    fi << std::fixed << std::setprecision(16);

    int nx = X.rows();
    int xc = X.cols();
    for (int i=0; i<nx; ++i)
    {
        fi << 'v';
        for (int j=0; j<xc; ++j)
        {
            fi << delim << X(i,j);
        }
        fi << std::endl;
    }

    int np = P.rows();
    int pc = P.cols();
    char prim_tag = pc == 3 ? 'f' : 't'; // face, tet
    for (int i=0; i<np; ++i)
    {
        fi << prim_tag;
        for (int j=0; j<pc; ++j)
        {
            fi << delim << P(i,j);
        }
        fi << std::endl;
    }

    fi.close();
    return true;
}

bool read_mesh_txt(
    const std::string& filename,
    Eigen::MatrixXd &X,
    Eigen::MatrixXi &P)
{
    int x_dim = 0;
    int p_dim = 0;
    using namespace Eigen;
    std::vector<Vector3d> x;
    std::vector<Vector4i> p;

    std::ifstream fi(filename);
    if (fi.is_open())
    {
        std::string line;
        while (std::getline(fi,line) )
        {
            std::stringstream ss(line);
            char tag = 'X';
            ss >> tag;
            switch (tag)
            {
                default: {
                    printf("Bad line: %s\n", line.c_str());
                } break;
                case 'v': {
                    x.emplace_back(Vector3d::Zero());
                    for (int i=0; i<3 && ss.good(); ++i)
                    {
                        x_dim = std::max(x_dim, i+1);
                        ss >> x.back()[i];
                    }
                } break;
                case 'f': {
                    p.emplace_back(Vector4i::Zero());
                    for (int i=0; i<3 && ss.good(); ++i)
                    {
                        p_dim = std::max(p_dim, i+1);
                        ss >> p.back()[i];
                    }
                } break;
                case 't': {
                    p.emplace_back(Vector4i::Zero());
                    for (int i=0; i<4 && ss.good(); ++i)
                    {
                        p_dim = std::max(p_dim, i+1);
                        ss >> p.back()[i];
                    }
                } break;
            }
        }
        fi.close();
    }
    else
    {
        printf("Could not open %s\n", filename.c_str());
        return false;
    }

    // Make X
    int nx = x.size();
    X.resize(nx, x_dim);
    for (int i=0; i<nx; ++i)
    {
        for (int j=0; j<x_dim; ++j)
        {
            X(i,j) = x[i][j];
        }
    }

    // Make P
    int np = p.size();
    P.resize(np, p_dim);
    for (int i=0; i<np; ++i)
    {
        for (int j=0; j<p_dim; ++j)
        {
            P(i,j) = p[i][j];
        }
    }

    return true;
}

} // namespace mcl

#endif


#include "matmult.h"
#include "mailman.h"
#include "helper.h"

#include <iostream>
#include <thread>

#include <Eigen/Dense>
#include <Eigen/Core>

MatMult::MatMult(Genotype &xg,
			MatrixXdr &xgeno_matrix,
			bool xdebug,
			bool xvar_normalize,
			bool xmemory_efficient,
			bool xmissing,
			bool xfast_mode,
			int xnthreads,
			int xk) {
	g = xg;
	geno_matrix = xgeno_matrix;

	debug = xdebug;
	var_normalize = xvar_normalize;
	memory_efficient = xmemory_efficient;
	missing = xmissing;
	fast_mode = xfast_mode;
	nthreads = xnthreads;

	blocksize = xk;

	hsegsize = g.segment_size_hori;  // = log_3(n)
	hsize = pow(3, hsegsize);
	vsegsize = g.segment_size_ver;  // = log_3(p)
	vsize = pow(3, vsegsize);

	partialsums = new double*[nthreads];
	yint_m = new double*[nthreads];
	for (int t = 0; t < nthreads; t++) {
		partialsums[t] = new double[blocksize];
		yint_m[t] = new double[hsize*blocksize];
		memset (yint_m[t], 0, hsize*blocksize * sizeof(double));
	}

	sum_op = new double[blocksize];

	yint_e = new double* [nthreads];
	for (int t = 0; t < nthreads; t++) {
		yint_e[t] = new double[hsize*blocksize];
		memset (yint_e[t], 0, hsize*blocksize * sizeof(double));
	}

	y_e  = new double**[nthreads];
	for (int t = 0 ; t < nthreads ; t++) {
		y_e[t]  = new double*[g.Nindv];
		for (int i = 0 ; i < g.Nindv ; i++) {
			y_e[t][i] = new double[blocksize];
			memset (y_e[t][i], 0, blocksize * sizeof(double));
		}
	}

	y_m = new double**[nthreads];
	for (int t = 0; t < nthreads; t++) {
		y_m[t] = new double*[hsegsize];
		for (int i = 0; i < hsegsize; i++) {
			y_m[t][i] = new double[blocksize];
		}
	}
}

void MatMult::multiply_y_pre_fast_thread(int begin, int end, MatrixXdr &op, int Ncol_op, double *yint_m, double **y_m, double *partialsums, MatrixXdr &res) {
	for (int seg_iter = begin; seg_iter < end; seg_iter++) {
		mailman::fastmultiply(g.segment_size_hori, g.Nindv, Ncol_op, g.p[seg_iter], op, yint_m, partialsums, y_m);
		int p_base = seg_iter * g.segment_size_hori;
		for (int p_iter = p_base; (p_iter < p_base + g.segment_size_hori) && (p_iter < g.Nsnp); p_iter++) {
			for (int k_iter = 0; k_iter < Ncol_op; k_iter++) {
				res(p_iter, k_iter) = y_m[p_iter - p_base][k_iter];
			}
		}
	}
}

void MatMult::multiply_y_post_fast_thread(int begin, int end, MatrixXdr &op, int Ncol_op, double *yint_e, double **y_e, double *partialsums) {
	for (int i = 0; i < g.Nindv; i++) {
		memset (y_e[i], 0, blocksize * sizeof(double));
	}

	for (int seg_iter = begin; seg_iter < end; seg_iter++) {
		mailman::fastmultiply_pre(g.segment_size_hori, g.Nindv, Ncol_op, seg_iter * g.segment_size_hori, g.p[seg_iter], op, yint_e, partialsums, y_e);
	}
}

void MatMult::multiply_y_pre_fast(MatrixXdr &op, int Ncol_op, MatrixXdr &res, bool subtract_means) {
	for (int k_iter = 0; k_iter < Ncol_op; k_iter++) {
		sum_op[k_iter] = op.col(k_iter).sum();
	}

	#if DEBUG == 1
		if (debug) {
			print_time();
			std::cout << "Starting mailman on premultiply" << std::endl;
			std::cout << "Nops = " << Ncol_op << "\t" << g.Nsegments_hori << std::endl;
			std::cout << "Segment size = " << g.segment_size_hori << std::endl;
			std::cout << "Matrix size = " << g.segment_size_hori << "\t" << g.Nindv << std::endl;
			std::cout << "op = " <<  op.rows() << "\t" << op.cols() << std::endl;
		}
	#endif

	// TODO: Memory Effecient SSE FastMultipy

	nthreads = (nthreads > g.Nsegments_hori) ? g.Nsegments_hori: nthreads;

	std::thread th[nthreads];
	int perthread = g.Nsegments_hori / nthreads;
	// std::cout << g.Nsegments_hori << "\t" << nthreads << "\t" << perthread << std::endl;
	int t = 0;
	for (; t < nthreads - 1; t++) {
	// std::cout << "Launching thread " << t << std::endl;
		th[t] = std::thread(&MatMult::multiply_y_pre_fast_thread, this, t * perthread , (t+1)*perthread, std::ref(op), Ncol_op, yint_m[t], y_m[t], partialsums[t], std::ref(res));
	}

	th[t] = std::thread(&MatMult::multiply_y_pre_fast_thread, this, t * perthread , g.Nsegments_hori  - 1, std::ref(op), Ncol_op, yint_m[t], y_m[t], partialsums[t], std::ref(res));

	for (int t = 0; t < nthreads; t++) {
		th[t].join();
	}

	// for(int seg_iter = 0; seg_iter < g.Nsegments_hori - 1; seg_iter++){
	// mailman::fastmultiply ( g.segment_size_hori, g.Nindv, Ncol_op, g.p[seg_iter], op, yint_m, partialsums, y_m);
	// int p_base = seg_iter * g.segment_size_hori;
	// for(int p_iter=p_base; (p_iter < p_base + g.segment_size_hori) && (p_iter < g.Nsnp) ; p_iter++ ){
	//   for(int k_iter = 0; k_iter < Ncol_op; k_iter++)
	//     res(p_iter, k_iter) = y_m [p_iter - p_base][k_iter];
	//   }
	// }

	int last_seg_size = (g.Nsnp % g.segment_size_hori != 0) ? g.Nsnp % g.segment_size_hori : g.segment_size_hori;
	mailman::fastmultiply(last_seg_size, g.Nindv, Ncol_op, g.p[g.Nsegments_hori-1], op, yint_m[0], partialsums[0], y_m[0]);
	int p_base = (g.Nsegments_hori - 1) * g.segment_size_hori;
	for (int p_iter = p_base; (p_iter < p_base + g.segment_size_hori) && (p_iter < g.Nsnp); p_iter++) {
		for (int k_iter = 0; k_iter < Ncol_op; k_iter++) {
			res(p_iter, k_iter) = y_m[0][p_iter - p_base][k_iter];
		}
	}

	#if DEBUG == 1
		if (debug) {
			print_time();
			std::cout << "Ending mailman on premultiply" << std::endl;
		}
	#endif

	if (!subtract_means) {
		return;
	}

	for (int p_iter = 0; p_iter < g.Nsnp; p_iter++) {
		for (int k_iter = 0; k_iter < Ncol_op; k_iter++) {
			res(p_iter, k_iter) = res(p_iter, k_iter) - (g.get_col_mean(p_iter) * sum_op[k_iter]);
			if (var_normalize) {
				res(p_iter, k_iter) = res(p_iter, k_iter) / (g.get_col_std(p_iter));
			}
		}
	}
}

void MatMult::multiply_y_post_fast(MatrixXdr &op_orig, int Nrows_op, MatrixXdr &res, bool subtract_means) {
	MatrixXdr op;
	op = op_orig.transpose();

	if (var_normalize && subtract_means) {
		for (int p_iter = 0; p_iter < g.Nsnp; p_iter++) {
			for (int k_iter = 0; k_iter < Nrows_op; k_iter++) {
				op(p_iter, k_iter) = op(p_iter, k_iter) / (g.get_col_std(p_iter));
			}
		}
	}

	#if DEBUG == 1
		if (debug) {
			print_time();
			std::cout << "Starting mailman on postmultiply" << std::endl;
		}
	#endif

	int Ncol_op = Nrows_op;

	nthreads = (nthreads > g.Nsegments_hori) ? g.Nsegments_hori: nthreads;

	std::thread th[nthreads];
	int perthread = g.Nsegments_hori / nthreads;
	// std::cout << "post: " << g.segment_size_hori << "\t" << g.Nsegments_hori << "\t" << nthreads << "\t" << perthread << std::endl;
	int t = 0;
	for (; t < nthreads - 1; t++) {
	// std::cout << "Launching " << t << std::endl;
		th[t] = std::thread(&MatMult::multiply_y_post_fast_thread, this, t * perthread, (t+1) * perthread, std::ref(op), Ncol_op, yint_e[t], y_e[t], partialsums[t]);
	}
	// std::cout << "Launching " << t << std::endl;
	th[t] = std::thread(&MatMult::multiply_y_post_fast_thread, this, t * perthread, g.Nsegments_hori - 1, std::ref(op), Ncol_op, yint_e[t], y_e[t], partialsums[t]);
	for (int t = 0; t < nthreads; t++) {
		th[t].join();
	}
	// std::cout << "Joined "<< std::endl;

	// int seg_iter;
	// for(seg_iter = 0; seg_iter < g.Nsegments_hori-1; seg_iter++){
	// 	mailman::fastmultiply_pre (g.segment_size_hori, g.Nindv, Ncol_op, seg_iter * g.segment_size_hori, g.p[seg_iter], op, yint_e, partialsums[0], y_e);
	// }

	for (int t = 1; t < nthreads; t++) {
		for (int n_iter = 0; n_iter < g.Nindv; n_iter++) {
			for (int k_iter = 0; k_iter < Ncol_op; k_iter++) {
				y_e[0][n_iter][k_iter] += y_e[t][n_iter][k_iter];
			}
		}
	}

	int last_seg_size = (g.Nsnp % g.segment_size_hori != 0) ? g.Nsnp % g.segment_size_hori : g.segment_size_hori;
	mailman::fastmultiply_pre(last_seg_size, g.Nindv, Ncol_op, (g.Nsegments_hori-1) * g.segment_size_hori,
							  g.p[g.Nsegments_hori-1], op, yint_e[0], partialsums[0], y_e[0]);

	for (int n_iter = 0; n_iter < g.Nindv; n_iter++)  {
		for (int k_iter = 0; k_iter < Ncol_op; k_iter++) {
			res(k_iter, n_iter) = y_e[0][n_iter][k_iter];
			y_e[0][n_iter][k_iter] = 0;
		}
	}

	#if DEBUG == 1
		if (debug) {
			print_time();
			std::cout << "Ending mailman on postmultiply" << std::endl;
		}
	#endif

	if (!subtract_means) {
		return;
	}

	double *sums_elements = new double[Ncol_op];
	memset (sums_elements, 0, Nrows_op * sizeof(int));

	for (int k_iter = 0; k_iter < Ncol_op; k_iter++) {
		double sum_to_calc = 0.0;
		for (int p_iter = 0; p_iter < g.Nsnp; p_iter++) {
			sum_to_calc += g.get_col_mean(p_iter) * op(p_iter, k_iter);
		}
		sums_elements[k_iter] = sum_to_calc;
	}
	for (int k_iter = 0; k_iter < Ncol_op; k_iter++) {
		for (int n_iter = 0; n_iter < g.Nindv; n_iter++) {
			res(k_iter, n_iter) = res(k_iter, n_iter) - sums_elements[k_iter];
		}
	}
}

void MatMult::multiply_y_pre_naive_mem(MatrixXdr &op, int Ncol_op, MatrixXdr &res) {
	for (int p_iter = 0; p_iter < g.Nsnp; p_iter++) {
		for (int k_iter = 0; k_iter < Ncol_op; k_iter++) {
			double temp = 0;
			for (int n_iter = 0; n_iter < g.Nindv; n_iter++) {
				temp+= g.get_geno(p_iter, n_iter, var_normalize) * op(n_iter, k_iter);
			}
			res(p_iter, k_iter) = temp;
		}
	}
}

void MatMult::multiply_y_post_naive_mem(MatrixXdr &op, int Nrows_op, MatrixXdr &res) {
	for (int n_iter = 0; n_iter < g.Nindv; n_iter++) {
		for (int k_iter = 0; k_iter < Nrows_op; k_iter++) {
			double temp = 0;
			for(int p_iter = 0; p_iter < g.Nsnp; p_iter++)
				temp += op(k_iter, p_iter) * (g.get_geno(p_iter, n_iter, var_normalize));
			res(k_iter, n_iter) = temp;
		}
	}
}

void MatMult::multiply_y_post(MatrixXdr &op, int Nrows_op, MatrixXdr &res, bool subtract_means) {
	if (fast_mode) {
		multiply_y_post_fast(op, Nrows_op, res, subtract_means);
	} else {
		if (memory_efficient)
			multiply_y_post_naive_mem(op, Nrows_op, res);
		else
			res = op * geno_matrix;
	}
}

void MatMult::multiply_y_pre(MatrixXdr &op, int Ncol_op, MatrixXdr &res, bool subtract_means) {
	if (fast_mode) {
		multiply_y_pre_fast(op, Ncol_op, res, subtract_means);
	} else {
		if (memory_efficient) {
			multiply_y_pre_naive_mem(op, Ncol_op, res);
		} else {
			res = geno_matrix * op;
		}
	}
}

void MatMult::clean_up() {
	delete[] sum_op;

	for (int t = 0; t < nthreads; t++) {
		delete[] yint_e[t];
	}
	delete[] yint_e;

	for (int t = 0; t < nthreads; t++) {
		delete[] yint_m[t];
		delete[] partialsums[t];
	}
	delete[] yint_m;
	delete[] partialsums;

	for (int t = 0; t < nthreads; t++) {
		for (int i  = 0; i < hsegsize; i++) {
			delete[] y_m[t][i];
		}
		delete[] y_m[t];
	}
	delete[] y_m;

	for (int t = 0; t < nthreads; t++) {
		for (int i  = 0; i < g.Nindv; i++) {
			delete[] y_e[t][i];
		}
		delete[] y_e[t];
	}
	delete[] y_e;
}


#pragma once
#include <boost/serialization/access.hpp>
#include <boost/serialization/base_object.hpp>
#include "Neuron.hpp"

namespace snn::internal
{
    class RecurrentNeuron final : public Neuron
    {
    private:
        friend class GatedRecurrentUnit;
        friend class StochasticGradientDescent;
        friend class boost::serialization::access;
        template <class Archive>
        void serialize(Archive& ar, unsigned version);

        float lastOutput = 0;
        float previousOutput = 0;
        float recurrentError = 0;
        float previousSum = 0;

        void reset();

    public:
        RecurrentNeuron() = default; // use restricted to Boost library only
        RecurrentNeuron(NeuronModel model, std::shared_ptr<NeuralNetworkOptimizer> optimizer);
        RecurrentNeuron(const RecurrentNeuron& recurrentNeuron) = default;
        ~RecurrentNeuron() = default;

        [[nodiscard]] float output(const std::vector<float>& inputs, bool reset);
        [[nodiscard]] std::vector<float>& backOutput(float error);
        void train(float error);

        [[nodiscard]] int isValid() const;

        bool operator==(const RecurrentNeuron& neuron) const;
        bool operator!=(const RecurrentNeuron& neuron) const;
    };

    template <class Archive>
    void RecurrentNeuron::serialize(Archive& ar, [[maybe_unused]] const unsigned version)
    {
        boost::serialization::void_cast_register<RecurrentNeuron, Neuron>();
        ar & boost::serialization::base_object<Neuron>(*this);
        ar & this->lastOutput;
        ar & this->previousOutput;
        ar & this->recurrentError;
        ar & this->previousSum;
    }
}


//==============================================================================
//
//   (c) Copyright, 2010 University Corporation for Atmospheric Research (UCAR).
//       All rights reserved.
//       Do not copy or distribute without authorization.
//
//       File: $RCSfile: lambertGridLatLong.cc,v $
//       Version: $Revision: 1.1 $  Dated: $Date: 2012-01-04 00:33:41 $
//
//==============================================================================

/**
 * @file lambertGridLatLong.cc
 *
 * Convert lat-longs cloud grid coordinates
 *
 * @date 3/25/10
 */

// Include files
#include <stdio.h>
#include <string>
#include <vector>
#include <projects.h>
#include <boost/format.hpp>
#include "LambertConfigReader.hh"
#include "Proj4Wrap.hh"
#include "writeBinary.hh"

using boost::format;
using std::string;
using std::vector;

// Functions

void usage(char *programName)
{
  fprintf(stderr, "Usage: %s configFile latFile lonFile\n", programName);
}

int main(int argc, char **argv)
{
  if (argc != 4)
    {
      usage(argv[0]);
      exit(2);
    }

  LambertConfigReader cfg(argv[1]);
  if (cfg.error != string(""))
    {
      printf("Error: configuration file error %s\n", cfg.error.c_str());
      return 1;
    }

  // Uses latitude where lambert conformal projection is true and
  // median aligned with cartesian y-axis
  string paramString = str(format("+proj=lcc +R=6371200 +lon_0=%1% +lat_0=%2% +lat_1=%3% +lat_2=%4%") % cfg.lov % cfg.latin1 % cfg.latin1 % cfg.latin2);

  p4w::Proj4Wrap lambertProj(paramString, p4w::Proj4Wrap::LON_LAT_TYPE, cfg.lo1, cfg.la1, cfg.dx, cfg.dy);
  double xc;
  double yc;
  double lon;
  double lat;

  vector<float> xvec;
  vector<float> yvec;

  for (int i=0; i<cfg.ny; i++)
    {
      for (int j=0; j<cfg.nx; j++)
	{
	  xc = j;
	  yc = i;
	  lambertProj.xy2ll(xc, yc, &lon, &lat);
	  xvec.push_back(lon);
	  yvec.push_back(lat);
	}
    }

  string error;
  writeBinary(yvec, argv[2], error);
  writeBinary(xvec, argv[3], error);
}


// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2014 Daniele Panozzo <daniele.panozzo@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.

#include "nrosy.h"

#include <igl/copyleft/comiso/nrosy.h>
#include <igl/triangle_triangle_adjacency.h>
#include <igl/edge_topology.h>
#include <igl/per_face_normals.h>

#include <stdexcept>
#include "../../PI.h"

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

#include <gmm/gmm.h>
#include <CoMISo/Solver/ConstrainedSolver.hh>
#include <CoMISo/Solver/MISolver.hh>
#include <CoMISo/Solver/GMM_Tools.hh>

namespace igl
{
namespace copyleft
{

namespace comiso
{
class NRosyField
{
public:
  // Init
  IGL_INLINE NRosyField(const Eigen::MatrixXd& _V, const Eigen::MatrixXi& _F);

  // Generate the N-rosy field
  // N degree of the rosy field
  // round separately: round the integer variables one at a time, slower but higher quality
  IGL_INLINE void solve(int N = 4);

  // Set a hard constraint on fid
  // fid: face id
  // v: direction to fix (in 3d)
  IGL_INLINE void setConstraintHard(int fid, const Eigen::Vector3d& v);

  // Set a soft constraint on fid
  // fid: face id
  // w: weight of the soft constraint, clipped between 0 and 1
  // v: direction to fix (in 3d)
  IGL_INLINE void setConstraintSoft(int fid, double w, const Eigen::Vector3d& v);

  // Set the ratio between smoothness and soft constraints (0 -> smoothness only, 1 -> soft constr only)
  IGL_INLINE void setSoftAlpha(double alpha);

  // Reset constraints (at least one constraint must be present or solve will fail)
  IGL_INLINE void resetConstraints();

  // Return the current field
  IGL_INLINE Eigen::MatrixXd getFieldPerFace();

  // Compute singularity indexes
  IGL_INLINE void findCones(int N);

  // Return the singularities
  IGL_INLINE Eigen::VectorXd getSingularityIndexPerVertex();

private:
  // Compute angle differences between reference frames
  IGL_INLINE void computek();

  // Remove useless matchings
  IGL_INLINE void reduceSpace();

  // Prepare the system matrix
  IGL_INLINE void prepareSystemMatrix(int N);

  // Solve with roundings using CoMIso
  IGL_INLINE void solveRoundings();

  // Convert a vector in 3d to an angle wrt the local reference system
  IGL_INLINE double convert3DtoLocal(unsigned fid, const Eigen::Vector3d& v);

  // Convert an angle wrt the local reference system to a 3d vector
  IGL_INLINE Eigen::Vector3d convertLocalto3D(unsigned fid, double a);

  // Compute the per vertex angle defect
  IGL_INLINE Eigen::VectorXd angleDefect();

  // Temporary variable for the field
  Eigen::VectorXd angles;

  // Hard constraints
  Eigen::VectorXd hard;
  std::vector<bool> isHard;

  // Soft constraints
  Eigen::VectorXd soft;
  Eigen::VectorXd wSoft;
  double softAlpha;

  // Face Topology
  Eigen::MatrixXi TT, TTi;

  // Edge Topology
  Eigen::MatrixXi EV, FE, EF;
  std::vector<bool> isBorderEdge;

  // Per Edge information
  // Angle between two reference frames
  Eigen::VectorXd k;

  // Jumps
  Eigen::VectorXi p;
  std::vector<bool> pFixed;

  // Mesh
  Eigen::MatrixXd V;
  Eigen::MatrixXi F;

  // Normals per face
  Eigen::MatrixXd N;

  // Singularity index
  Eigen::VectorXd singularityIndex;

  // Reference frame per triangle
  std::vector<Eigen::MatrixXd> TPs;

  // System stuff
  Eigen::SparseMatrix<double> A;
  Eigen::VectorXd b;
  Eigen::VectorXi tag_t;
  Eigen::VectorXi tag_p;

};

} // NAMESPACE COMISO
} // NAMESPACE COPYLEFT
} // NAMESPACE IGL

igl::copyleft::comiso::NRosyField::NRosyField(const Eigen::MatrixXd& _V, const Eigen::MatrixXi& _F)
{
  V = _V;
  F = _F;

  assert(V.rows() > 0);
  assert(F.rows() > 0);

  // Generate topological relations
  igl::triangle_triangle_adjacency(F,TT,TTi);
  igl::edge_topology(V,F, EV, FE, EF);

  // Flag border edges
  isBorderEdge.resize(EV.rows());
  for(unsigned i=0; i<EV.rows(); ++i)
    isBorderEdge[i] = (EF(i,0) == -1) || ((EF(i,1) == -1));

  // Generate normals per face
  igl::per_face_normals(V, F, N);

  // Generate reference frames
  for(unsigned fid=0; fid<F.rows(); ++fid)
  {
    // First edge
    Eigen::Vector3d e1 = V.row(F(fid,1)) - V.row(F(fid,0));
    e1.normalize();
    Eigen::Vector3d e2 = N.row(fid);
    e2 = e2.cross(e1);
    e2.normalize();

    Eigen::MatrixXd TP(2,3);
    TP << e1.transpose(), e2.transpose();
    TPs.push_back(TP);
  }

  // Alloc internal variables
  angles = Eigen::VectorXd::Zero(F.rows());
  p = Eigen::VectorXi::Zero(EV.rows());
  pFixed.resize(EV.rows());
  k = Eigen::VectorXd::Zero(EV.rows());
  singularityIndex = Eigen::VectorXd::Zero(V.rows());

  // Reset the constraints
  resetConstraints();

  // Compute k, differences between reference frames
  computek();
  softAlpha = 0.5;
}

void igl::copyleft::comiso::NRosyField::setSoftAlpha(double alpha)
{
  assert(alpha >= 0 && alpha < 1);
  softAlpha = alpha;
}

void igl::copyleft::comiso::NRosyField::prepareSystemMatrix(const int N)
{
  double Nd = N;

  // Minimize the MIQ energy
  // Energy on edge ij is
  //     (t_i - t_j + kij + pij*(2*pi/N))^2
  // Partial derivatives:
  //   t_i: 2     ( t_i - t_j + kij + pij*(2*pi/N)) = 0
  //   t_j: 2     (-t_i + t_j - kij - pij*(2*pi/N)) = 0
  //   pij: 4pi/N ( t_i - t_j + kij + pij*(2*pi/N)) = 0
  //
  //          t_i      t_j         pij       kij
  // t_i [     2       -2           4pi/N      2    ]
  // t_j [    -2        2          -4pi/N     -2    ]
  // pij [   4pi/N   -4pi/N    2*(2pi/N)^2   4pi/N  ]

  // Count and tag the variables
  tag_t = Eigen::VectorXi::Constant(F.rows(),-1);
  std::vector<int> id_t;
  size_t count = 0;
  for(unsigned i=0; i<F.rows(); ++i)
    if (!isHard[i])
    {
      tag_t(i) = count++;
      id_t.push_back(i);
    }

  size_t count_t = id_t.size();

  tag_p = Eigen::VectorXi::Constant(EF.rows(),-1);
  std::vector<int> id_p;
  for(unsigned i=0; i<EF.rows(); ++i)
  {
    if (!pFixed[i])
    {
      // if it is not fixed then it is a variable
      tag_p(i) = count++;
    }

    // if it is not a border edge,
    if (!isBorderEdge[i])
    {
      // and it is not between two fixed faces
      if (!(isHard[EF(i,0)] && isHard[EF(i,1)]))
      {
          // then it participates in the energy!
          id_p.push_back(i);
      }
    }
  }

  size_t count_p = count - count_t;
  // System sizes: A (count_t + count_p) x (count_t + count_p)
  //               b (count_t + count_p)

  b.resize(count_t + count_p);
  b.setZero();

  std::vector<Eigen::Triplet<double> > T;
  T.reserve(3 * 4 * count_p);

  for(auto eid : id_p)
  {
    int i = EF(eid, 0);
    int j = EF(eid, 1);
    bool isFixed_i = isHard[i];
    bool isFixed_j = isHard[j];
    bool isFixed_p = pFixed[eid];
    int row;
    // (i)-th row: t_i [     2       -2           4pi/N      2    ]
    if (!isFixed_i)
    {
      row = tag_t[i];
      T.emplace_back(row, tag_t[i], 2);
      if (isFixed_j)
        b(row) +=  2 * hard[j];
      else
        T.emplace_back(row, tag_t[j], -2);
      if (isFixed_p)
        b(row) += -((4. * igl::PI) / Nd) * p[eid];
      else
        T.emplace_back(row, tag_p[eid], ((4. * igl::PI) / Nd));
      b(row) += -2 * k[eid];
      assert(hard[i] == hard[i]);
      assert(hard[j] == hard[j]);
      assert(p[eid] == p[eid]);
      assert(k[eid] == k[eid]);
      assert(b(row) == b(row));
    }
    // (j)+1 -th row: t_j [    -2        2          -4pi/N     -2    ]
    if (!isFixed_j)
    {
      row = tag_t[j];
      T.emplace_back(row, tag_t[j], 2);
      if (isFixed_i)
        b(row) += 2 * hard[i];
      else
        T.emplace_back(row, tag_t[i], -2);
      if (isFixed_p)
        b(row) += ((4. * igl::PI) / Nd) * p[eid];
      else
        T.emplace_back(row, tag_p[eid], -((4. * igl::PI) / Nd));
      b(row) += 2 * k[eid];
      assert(k[eid] == k[eid]);
      assert(b(row) == b(row));
    }
    // (r*3)+2 -th row: pij [   4pi/N   -4pi/N    2*(2pi/N)^2   4pi/N  ]
    if (!isFixed_p)
    {
      row = tag_p[eid];
      T.emplace_back(row, tag_p[eid], (2. * pow(((2. * igl::PI) / Nd), 2)));
      if (isFixed_i)
        b(row) += -(4. * igl::PI) / Nd * hard[i];
      else
        T.emplace_back(row, tag_t[i], (4. * igl::PI) / Nd);
      if (isFixed_j)
        b(row) += (4. * igl::PI) / Nd * hard[j];
      else
        T.emplace_back(row,tag_t[j], -(4. * igl::PI) / Nd);
      b(row) += - (4 * igl::PI)/Nd * k[eid];
      assert(k[eid] == k[eid]);
      assert(b(row) == b(row));
    }
  }

  A.resize(count_t + count_p, count_t + count_p);
  A.setFromTriplets(T.begin(), T.end());

  // Soft constraints
  bool addSoft = false;

  for(unsigned i=0; i<wSoft.size();++i)
    if (wSoft[i] != 0)
      addSoft = true;

  if (addSoft)
  {
    Eigen::VectorXd bSoft = Eigen::VectorXd::Zero(count_t + count_p);
    std::vector<Eigen::Triplet<double> > TSoft;
    TSoft.reserve(2 * count_p);

    for(unsigned i=0; i<F.rows(); ++i)
    {
      int varid = tag_t[i];
      if (varid != -1) // if it is a variable in the system
      {
        TSoft.emplace_back(varid, varid, wSoft[i]);
        bSoft[varid] += wSoft[i] * soft[i];
      }
    }
    Eigen::SparseMatrix<double> ASoft(count_t + count_p, count_t + count_p);
    ASoft.setFromTriplets(TSoft.begin(), TSoft.end());

    A = (1.0 - softAlpha) * A + softAlpha * ASoft;
    b = b * (1.0 - softAlpha) + bSoft * softAlpha;
  }
}

void igl::copyleft::comiso::NRosyField::solveRoundings()
{
  unsigned n = A.rows();

  gmm::col_matrix< gmm::wsvector< double > > gmm_A(n, n);
  std::vector<double> gmm_b(n);
  std::vector<int> ids_to_round;
  std::vector<double> x(n);

  // Copy A
  for (int k=0; k<A.outerSize(); ++k)
    for (Eigen::SparseMatrix<double>::InnerIterator it(A, k); it; ++it)
    {
      gmm_A(it.row(),it.col()) += it.value();
    }

  // Copy b
  for(unsigned int i = 0; i < n;++i)
    gmm_b[i] = b[i];

  // Set variables to round
  ids_to_round.clear();
  for(unsigned i=0; i<tag_p.size();++i)
    if(tag_p[i] != -1)
      ids_to_round.push_back(tag_p[i]);

  // Empty constraints
  gmm::row_matrix< gmm::wsvector< double > > gmm_C(0, n);

  COMISO::ConstrainedSolver cs;
  cs.solve(gmm_C, gmm_A, x, gmm_b, ids_to_round, 0.0, false, true);

  // Copy the result back
  for(unsigned i=0; i<F.rows(); ++i)
    if (tag_t[i] != -1)
      angles[i] = x[tag_t[i]];
    else
      angles[i] = hard[i];

  for(unsigned i=0; i<EF.rows(); ++i)
    if(tag_p[i]  != -1)
      p[i] = (int)std::round(x[tag_p[i]]);
}

void igl::copyleft::comiso::NRosyField::solve(const int N)
{
  // Reduce the search space by fixing matchings
  reduceSpace();

  // Build the system
  prepareSystemMatrix(N);

  // Solve with integer roundings
  solveRoundings();

  // Find the cones
  findCones(N);
}

void igl::copyleft::comiso::NRosyField::setConstraintHard(const int fid, const Eigen::Vector3d& v)
{
  isHard[fid] = true;
  hard(fid) = convert3DtoLocal(fid, v);
}

void igl::copyleft::comiso::NRosyField::setConstraintSoft(const int fid, const double w, const Eigen::Vector3d& v)
{
  wSoft(fid) = w;
  soft(fid) = convert3DtoLocal(fid, v);
}

void igl::copyleft::comiso::NRosyField::resetConstraints()
{
  isHard.resize(F.rows());
  for(unsigned i = 0; i < F.rows(); ++i)
    isHard[i] = false;
  hard   = Eigen::VectorXd::Zero(F.rows());
  wSoft = Eigen::VectorXd::Zero(F.rows());
  soft = Eigen::VectorXd::Zero(F.rows());
}

Eigen::MatrixXd igl::copyleft::comiso::NRosyField::getFieldPerFace()
{
  Eigen::MatrixXd result(F.rows(),3);
  for(unsigned int i = 0; i < F.rows(); ++i)
    result.row(i) = convertLocalto3D(i, angles(i));
  return result;
}

void igl::copyleft::comiso::NRosyField::computek()
{
  // For every non-border edge
  for (unsigned eid = 0; eid < EF.rows(); ++eid)
  {
    if (!isBorderEdge[eid])
    {
      int fid0 = EF(eid,0);
      int fid1 = EF(eid,1);

      Eigen::Vector3d N0 = N.row(fid0);
      Eigen::Vector3d N1 = N.row(fid1);

      // find common edge on triangle 0 and 1
      int fid0_vc = -1;
      int fid1_vc = -1;
      for (unsigned i=0;i<3;++i)
      {
        if (EV(eid,0) == F(fid0,i))
          fid0_vc = i;
        if (EV(eid,1) == F(fid1,i))
          fid1_vc = i;
      }
      assert(fid0_vc != -1);
      assert(fid1_vc != -1);

      Eigen::Vector3d common_edge = V.row(F(fid0,(fid0_vc+1)%3)) - V.row(F(fid0,fid0_vc));
      common_edge.normalize();

      // Map the two triangles in a new space where the common edge is the x axis and the N0 the z axis
      Eigen::MatrixXd P(3,3);
      Eigen::VectorXd o = V.row(F(fid0,fid0_vc));
      Eigen::VectorXd tmp = -N0.cross(common_edge);
      P << common_edge, tmp, N0;
      P.transposeInPlace();

      Eigen::MatrixXd V0(3,3);
      V0.row(0) = V.row(F(fid0,0)).transpose() -o;
      V0.row(1) = V.row(F(fid0,1)).transpose() -o;
      V0.row(2) = V.row(F(fid0,2)).transpose() -o;

      V0 = (P*V0.transpose()).transpose();

      assert(V0(0,2) < 10e-10);
      assert(V0(1,2) < 10e-10);
      assert(V0(2,2) < 10e-10);

      Eigen::MatrixXd V1(3,3);
      V1.row(0) = V.row(F(fid1,0)).transpose() -o;
      V1.row(1) = V.row(F(fid1,1)).transpose() -o;
      V1.row(2) = V.row(F(fid1,2)).transpose() -o;
      V1 = (P*V1.transpose()).transpose();

      assert(V1(fid1_vc,2) < 10e-10);
      assert(V1((fid1_vc+1)%3,2) < 10e-10);

      // compute rotation R such that R * N1 = N0
      // i.e. map both triangles to the same plane
      double alpha = -std::atan2(V1((fid1_vc + 2) % 3, 2), V1((fid1_vc + 2) % 3, 1));

      Eigen::MatrixXd R(3,3);
      R << 1,          0,            0,
           0, std::cos(alpha), -std::sin(alpha) ,
           0, std::sin(alpha),  std::cos(alpha);
      V1 = (R*V1.transpose()).transpose();

      assert(V1(0,2) < 10e-10);
      assert(V1(1,2) < 10e-10);
      assert(V1(2,2) < 10e-10);

      // measure the angle between the reference frames
      // k_ij is the angle between the triangle on the left and the one on the right
      Eigen::VectorXd ref0 = V0.row(1) - V0.row(0);
      Eigen::VectorXd ref1 = V1.row(1) - V1.row(0);

      ref0.normalize();
      ref1.normalize();

      double ktemp = std::atan2(ref1(1), ref1(0)) - std::atan2(ref0(1), ref0(0));

      // just to be sure, rotate ref0 using angle ktemp...
      Eigen::MatrixXd R2(2,2);
      R2 << std::cos(ktemp), -std::sin(ktemp), std::sin(ktemp), std::cos(ktemp);

      tmp = R2*ref0.head<2>();

      assert(tmp(0) - ref1(0) < 10^10);
      assert(tmp(1) - ref1(1) < 10^10);

      k[eid] = ktemp;
    }
  }

}

void igl::copyleft::comiso::NRosyField::reduceSpace()
{
  // All variables are free in the beginning
  for(unsigned int i = 0; i < EV.rows(); ++i)
    pFixed[i] = false;

  std::vector<bool> visited(EV.rows(), false);
  std::vector<bool> starting(EV.rows(), false);

  std::queue<int> q;
  for(unsigned int i = 0; i < F.rows(); ++i)
    if (isHard[i] || wSoft[i] != 0)
    {
      q.push(i);
      starting[i] = true;
    }

  // Reduce the search space (see MI paper)
  while (!q.empty())
  {
    int c = q.front();
    q.pop();

    visited[c] = true;
    for(int i=0; i<3; ++i)
    {
      int eid = FE(c,i);
      int fid = TT(c,i);

      // skip borders
      if (fid != -1)
      {
        assert((EF(eid,0) == c && EF(eid,1) == fid) || (EF(eid,1) == c && EF(eid,0) == fid));
        // for every neighbouring face
        if (!visited[fid] && !starting[fid])
        {
          pFixed[eid] = true;
          p[eid] = 0;
          visited[fid] = true;
          q.push(fid);
        }
      }
      else
      {
        // fix borders
        pFixed[eid] = true;
        p[eid] = 0;
      }
    }
  }

  // Force matchings between fixed faces
  for(unsigned int i = 0; i < F.rows();++i)
  {
    if (isHard[i])
    {
      for(unsigned int j = 0; j < 3; ++j)
      {
        int fid = TT(i,j);
        if ((fid!=-1) && (isHard[fid]))
        {
          // i and fid are adjacent and fixed
          int eid = FE(i,j);
          int fid0 = EF(eid,0);
          int fid1 = EF(eid,1);

          pFixed[eid] = true;
          p[eid] = (int)std::round(2.0 / igl::PI * (hard(fid1) - hard(fid0) - k(eid)));
        }
      }
    }
  }
}

double igl::copyleft::comiso::NRosyField::convert3DtoLocal(unsigned fid, const Eigen::Vector3d& v)
{
  // Project onto the tangent plane
  Eigen::Vector2d vp = TPs[fid] * v;

  // Convert to angle
  return std::atan2(vp(1), vp(0));
}

Eigen::Vector3d igl::copyleft::comiso::NRosyField::convertLocalto3D(unsigned fid, double a)
{
  Eigen::Vector2d vp(std::cos(a), std::sin(a));
  return vp.transpose() * TPs[fid];
}

Eigen::VectorXd igl::copyleft::comiso::NRosyField::angleDefect()
{
  Eigen::VectorXd A = Eigen::VectorXd::Constant(V.rows(),-2*igl::PI);

  for (unsigned int i = 0; i < F.rows(); ++i)
  {
    for (int j = 0; j < 3; ++j)
    {
      Eigen::VectorXd a = V.row(F(i,(j+1)%3)) - V.row(F(i,j));
      Eigen::VectorXd b = V.row(F(i,(j+2)%3)) - V.row(F(i,j));
      double t = a.transpose() * b;
      if(a.norm() > 0. && b.norm() > 0.)
        t /= (a.norm() * b.norm());
      else
        throw std::runtime_error("igl::copyleft::comiso::NRosyField::angleDefect: Division by zero!");
      A(F(i, j)) += std::acos(std::max(std::min(t, 1.), -1.));
    }
  }

  return A;
}

void igl::copyleft::comiso::NRosyField::findCones(int N)
{
  // Compute I0, see http://www.graphics.rwth-aachen.de/media/papers/bommes_zimmer_2009_siggraph_011.pdf for details

  singularityIndex = Eigen::VectorXd::Zero(V.rows());

  // first the k
  for (unsigned i = 0; i < EV.rows(); ++i)
  {
    if (!isBorderEdge[i])
    {
      singularityIndex(EV(i, 0)) -= k(i);
      singularityIndex(EV(i, 1)) += k(i);
    }
  }

  // then the A
  Eigen::VectorXd A = angleDefect();
  singularityIndex += A;
  // normalize
  singularityIndex /= (2 * igl::PI);

  // round to integer (remove numerical noise)
  for (unsigned i = 0; i < singularityIndex.size(); ++i)
    singularityIndex(i) = round(singularityIndex(i));

  for (unsigned i = 0; i < EV.rows(); ++i)
  {
    if (!isBorderEdge[i])
    {
      singularityIndex(EV(i, 0)) -= double(p(i)) / double(N);
      singularityIndex(EV(i, 1)) += double(p(i)) / double(N);
    }
  }

  // Clear the vertices on the edges
  for (unsigned i = 0; i < EV.rows(); ++i)
  {
    if (isBorderEdge[i])
    {
      singularityIndex(EV(i,0)) = 0;
      singularityIndex(EV(i,1)) = 0;
    }
  }
}

Eigen::VectorXd igl::copyleft::comiso::NRosyField::getSingularityIndexPerVertex()
{
  return singularityIndex;
}

IGL_INLINE void igl::copyleft::comiso::nrosy(
  const Eigen::MatrixXd& V,
  const Eigen::MatrixXi& F,
  const Eigen::VectorXi& b,
  const Eigen::MatrixXd& bc,
  const Eigen::VectorXi& b_soft,
  const Eigen::VectorXd& w_soft,
  const Eigen::MatrixXd& bc_soft,
  const int N,
  const double soft,
  Eigen::MatrixXd& R,
  Eigen::VectorXd& S
  )
{
  // Init solver
  igl::copyleft::comiso::NRosyField solver(V, F);

  // Add hard constraints
  for (unsigned i = 0; i < b.size(); ++i)
    solver.setConstraintHard(b(i), bc.row(i));

  // Add soft constraints
  for (unsigned i = 0; i < b_soft.size(); ++i)
    solver.setConstraintSoft(b_soft(i), w_soft(i), bc_soft.row(i));

  // Set the soft constraints global weight
  solver.setSoftAlpha(soft);

  // Interpolate
  solver.solve(N);

  // Copy the result back
  R = solver.getFieldPerFace();

  // Extract singularity indices
  S = solver.getSingularityIndexPerVertex();
}

IGL_INLINE void igl::copyleft::comiso::nrosy(
                           const Eigen::MatrixXd& V,
                           const Eigen::MatrixXi& F,
                           const Eigen::VectorXi& b,
                           const Eigen::MatrixXd& bc,
                           const int N,
                           Eigen::MatrixXd& R,
                           Eigen::VectorXd& S
                           )
{
  // Init solver
  igl::copyleft::comiso::NRosyField solver(V, F);

  // Add hard constraints
  for (unsigned i= 0; i < b.size(); ++i)
    solver.setConstraintHard(b(i), bc.row(i));

  // Interpolate
  solver.solve(N);

  // Copy the result back
  R = solver.getFieldPerFace();

  // Extract singularity indices
  S = solver.getSingularityIndexPerVertex();
}


#include <Eigen/Core>

#include <numpy_eigen/boost_python_headers.hpp>
Eigen::Matrix<double, 6, Eigen::Dynamic> test_double_6_D(const Eigen::Matrix<double, 6, Eigen::Dynamic> & M)
{
	return M;
}
void export_double_6_D()
{
	boost::python::def("test_double_6_D",test_double_6_D);
}


// Copyright (c) 2019 Graphcore Ltd. All rights reserved.
#define BOOST_TEST_MODULE RemoveUnusedInputTest0

#include <boost/test/unit_test.hpp>
#include <filereader.hpp>
#include <onnx/onnx_pb.h>
#include <popart/builder.hpp>
#include <popart/graphtransformer.hpp>
#include <popart/tensordata.hpp>

using namespace popart;

// confirm that when prepareNodesFroTraining,
// batch-normalization has 5 outputs
BOOST_AUTO_TEST_CASE(Transformation_PrepareTrain0) {

  auto test = [](bool prepareTraining) {
    auto builder = Builder::create();
    auto aiOnnx  = builder->aiOnnxOpset9();

    TensorInfo info_d{"FLOAT", std::vector<int64_t>{4, 3, 32, 32}};
    TensorInfo info_c{"FLOAT", std::vector<int64_t>{3}};

    auto in0 = builder->addInputTensor(info_d);

    auto a0 = builder->addInputTensor(info_c);
    auto a1 = builder->addInputTensor(info_c);
    auto a2 = builder->addInputTensor(info_c);
    auto a3 = builder->addInputTensor(info_c);

    // in both cases, we set the number of outputs to 1
    unsigned num_outputs = 1;
    float eps            = 0.2;
    float momen          = 0.3;
    auto outs            = aiOnnx.batchnormalization(
        {in0, a0, a1, a2, a3}, num_outputs, eps, momen);
    builder->addOutputTensor(outs[0]);

    std::string proto = builder->getModelProto();

    GraphTransformer gt(proto);

    if (prepareTraining) {
      gt.prepareNodesForTraining();
    }
    auto postProto = io::getModelFromString(gt.getModelProto());

    // cofirm that the batch normalization has 5
    // outputs when the transformation has been run
    int visited = 0;
    for (auto node : postProto.graph().node()) {
      if (node.op_type() == "BatchNormalization") {
        BOOST_CHECK(node.output_size() == prepareTraining ? 5 : 1);
        ++visited;
      }
    }
    BOOST_CHECK(visited == 1);
  };

  test(true);
  test(false);
}


#include "ModelClass.h"
#include <dlib/image_processing/render_face_detections.h>

using namespace dlib;

ModelClass::ModelClass(const char *s) {
	deserialize(s) >> sp;
}

full_object_detection ModelClass::getsp(array2d<rgb_pixel> &img, dlib::rectangle &d) {
	return sp(img, d);
}

full_object_detection ModelClass::getsp(dlib::cv_image<uchar> &img, dlib::rectangle &d) {
	return sp(img, d);
}


// Copyright 2004 The Trustees of Indiana University.

// Use, modification and distribution is subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

//  Authors: Douglas Gregor
//           Andrew Lumsdaine

#include <boost/graph/use_mpi.hpp>
#include <boost/config.hpp>
#include <boost/throw_exception.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/graph/distributed/dehne_gotz_min_spanning_tree.hpp>
#include <boost/graph/distributed/mpi_process_group.hpp>
#include <boost/graph/distributed/vertex_list_adaptor.hpp>
#include <boost/graph/parallel/distribution.hpp>
#include <boost/test/minimal.hpp>
#include <boost/graph/distributed/adjacency_list.hpp>
#include <iostream>
#include <cstdlib>

#ifdef BOOST_NO_EXCEPTIONS
void
boost::throw_exception(std::exception const& ex)
{
    std::cout << ex.what() << std::endl;
    abort();
}
#endif

using namespace boost;
using boost::graph::distributed::mpi_process_group;

template<typename Graph, typename WeightMap, typename InputIterator>
int
total_weight(const Graph& g, WeightMap weight_map,
             InputIterator first, InputIterator last)
{
  typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;

  int total_weight = 0;
  while (first != last) {
    total_weight += get(weight_map, *first);
    if (process_id(g.process_group()) == 0) {
      vertex_descriptor u = source(*first, g);
      vertex_descriptor v = target(*first, g);
      std::cout << "(" << g.distribution().global(owner(u), local(u))
                << ", " << g.distribution().global(owner(v), local(v))
                << ")\n";
    }
    ++first;
  }

  return total_weight;
}

void
test_distributed_dense_boruvka()
{
  typedef adjacency_list<listS,
                         distributedS<mpi_process_group, vecS>,
                         undirectedS,
                         // Vertex properties
                         no_property,
                         // Edge properties
                         property<edge_weight_t, int> > Graph;

  typedef graph_traits<Graph>::edge_descriptor edge_descriptor;

  typedef std::pair<int, int> E;

  const int num_nodes = 5;
  E edge_array[] = { E(0, 2), E(1, 3), E(1, 4), E(2, 1), E(2, 3),
    E(3, 4), E(4, 0), E(4, 1)
  };
  int weights[] = { 1, 1, 2, 7, 3, 1, 1, 1 };
  std::size_t num_edges = sizeof(edge_array) / sizeof(E);

  Graph g(edge_array, edge_array + num_edges, weights, num_nodes);

  {
    if (process_id(g.process_group()) == 0)
      std::cerr << "--Dense Boruvka--\n";
    typedef property_map<Graph, edge_weight_t>::type WeightMap;
    WeightMap weight_map = get(edge_weight, g);

    std::vector<edge_descriptor> mst_edges;
    dense_boruvka_minimum_spanning_tree(make_vertex_list_adaptor(g),
                                        weight_map,
                                        std::back_inserter(mst_edges));
    int w = total_weight(g, weight_map, mst_edges.begin(), mst_edges.end());
    BOOST_CHECK(w == 4);
    BOOST_CHECK(mst_edges.size() == 4);
  }

  {
    if (process_id(g.process_group()) == 0)
      std::cerr << "--Merge local MSTs--\n";
    typedef property_map<Graph, edge_weight_t>::type WeightMap;
    WeightMap weight_map = get(edge_weight, g);

    std::vector<edge_descriptor> mst_edges;
    merge_local_minimum_spanning_trees(make_vertex_list_adaptor(g), weight_map,
                                       std::back_inserter(mst_edges));
    if (process_id(g.process_group()) == 0) {
      int w = total_weight(g, weight_map, mst_edges.begin(), mst_edges.end());
      BOOST_CHECK(w == 4);
      BOOST_CHECK(mst_edges.size() == 4);
    }
  }

  {
    if (process_id(g.process_group()) == 0)
      std::cerr << "--Boruvka then Merge--\n";
    typedef property_map<Graph, edge_weight_t>::type WeightMap;
    WeightMap weight_map = get(edge_weight, g);

    std::vector<edge_descriptor> mst_edges;
    boruvka_then_merge(make_vertex_list_adaptor(g), weight_map,
                        std::back_inserter(mst_edges));
    if (process_id(g.process_group()) == 0) {
      int w = total_weight(g, weight_map, mst_edges.begin(), mst_edges.end());
      BOOST_CHECK(w == 4);
      BOOST_CHECK(mst_edges.size() == 4);
    }
  }

  {
    if (process_id(g.process_group()) == 0)
      std::cerr << "--Boruvka mixed Merge--\n";
    typedef property_map<Graph, edge_weight_t>::type WeightMap;
    WeightMap weight_map = get(edge_weight, g);

    std::vector<edge_descriptor> mst_edges;
    boruvka_mixed_merge(make_vertex_list_adaptor(g), weight_map,
                        std::back_inserter(mst_edges));
    if (process_id(g.process_group()) == 0) {
      int w = total_weight(g, weight_map, mst_edges.begin(), mst_edges.end());
      BOOST_CHECK(w == 4);
      BOOST_CHECK(mst_edges.size() == 4);
    }
  }
}

int test_main(int argc, char** argv)
{
  boost::mpi::environment env(argc, argv);
  test_distributed_dense_boruvka();
  return 0;
}


#include <facter/facts/linux/memory_resolver.hpp>
#include <facter/util/file.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>

using namespace std;
using namespace facter::util;

using boost::lexical_cast;
using boost::bad_lexical_cast;

namespace facter { namespace facts { namespace linux {

    memory_resolver::data memory_resolver::collect_data(collection& facts)
    {
        data result;
        file::each_line("/proc/meminfo", [&](string& line) {
            uint64_t* variable = nullptr;
            if (boost::starts_with(line, "MemTotal:")) {
                variable = &result.mem_total;
            } else if (boost::starts_with(line, "MemFree:") ||
                       boost::starts_with(line, "Buffers:") ||
                       boost::starts_with(line, "Cached:")) {
                variable = &result.mem_free;
            } else if (boost::starts_with(line, "SwapTotal:")) {
                variable = &result.swap_total;
            } else if (boost::starts_with(line, "SwapFree:")) {
                variable = &result.swap_free;
            }
            if (!variable) {
                return true;
            }

            vector<boost::iterator_range<string::iterator>> parts;
            boost::split(parts, line, boost::is_space(), boost::token_compress_on);
            if (parts.size() < 2) {
                return true;
            }

            try {
                *variable += lexical_cast<uint64_t>(parts[1]) * 1024;
            } catch (bad_lexical_cast&) {
            }
            return true;
        });
        return result;
    }

}}}  // namespace facter::facts::linux


#include <boost/numeric/interval/checking.hpp>


// Copyright (C) 2011 by the BEM++ Authors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#include "../check_arrays_are_close.hpp"
#include "../type_template.hpp"

#include "assembly/assembly_options.hpp"
#include "assembly/discrete_boundary_operator.hpp"
#include "assembly/boundary_operator.hpp"
#include "assembly/context.hpp"
#include "assembly/helmholtz_3d_adjoint_double_layer_boundary_operator.hpp"
#include "assembly/helmholtz_3d_double_layer_boundary_operator.hpp"
#include "assembly/numerical_quadrature_strategy.hpp"

#include "common/boost_make_shared_fwd.hpp"

#include "grid/grid_factory.hpp"
#include "grid/grid.hpp"

#include "space/piecewise_linear_continuous_scalar_space.hpp"
#include "space/piecewise_constant_scalar_space.hpp"

#include <algorithm>
#include "common/armadillo_fwd.hpp"
#include <boost/test/unit_test.hpp>
#include <boost/test/floating_point_comparison.hpp>
#include <boost/version.hpp>
#include <complex>

// Tests

using namespace Bempp;

BOOST_AUTO_TEST_SUITE(AdjointAbstractBoundaryOperator)

BOOST_AUTO_TEST_CASE_TEMPLATE(adjoint_dlp_agrees_with_adlp, ResultType, complex_result_types)
{
    typedef ResultType RT;
    typedef typename Fiber::ScalarTraits<RT>::RealType BFT;
    typedef typename Fiber::ScalarTraits<RT>::RealType CT;

    GridParameters params;
    params.topology = GridParameters::TRIANGULAR;
    shared_ptr<Grid> grid = GridFactory::importGmshGrid(
                params, "meshes/cube-12-reoriented.msh",
                false /* verbose */);

    PiecewiseLinearContinuousScalarSpace<BFT> pwiseLinears(grid);
    PiecewiseConstantScalarSpace<BFT> pwiseConstants(grid);

    AssemblyOptions assemblyOptions;
    assemblyOptions.setVerbosityLevel(VerbosityLevel::LOW);
    AccuracyOptions accuracyOptions;
    accuracyOptions.doubleSingular.setRelativeQuadratureOrder(4);
    NumericalQuadratureStrategy<BFT, RT> quadStrategy(accuracyOptions);

    Context<BFT, RT> context(make_shared_from_ref(quadStrategy), assemblyOptions);

    const ResultType waveNumber = 2.;
    BoundaryOperator<BFT, RT> dlp =
        helmholtz3dDoubleLayerBoundaryOperator<BFT>(
        make_shared_from_ref(context),
        make_shared_from_ref(pwiseLinears),
        make_shared_from_ref(pwiseLinears),
        make_shared_from_ref(pwiseConstants),
        waveNumber);
    BoundaryOperator<BFT, RT> op = adjoint(dlp);
    BOOST_CHECK_EQUAL(op.domain().get(), &pwiseConstants); // -> dualToRange
    BOOST_CHECK_EQUAL(op.range().get(), &pwiseConstants);  // -> dualToDomain
    BOOST_CHECK_EQUAL(op.dualToRange().get(), &pwiseLinears); // -> domain

    BoundaryOperator<BFT, RT> adlp =
        helmholtz3dAdjointDoubleLayerBoundaryOperator<BFT>(
        make_shared_from_ref(context),
        make_shared_from_ref(pwiseConstants),
        make_shared_from_ref(pwiseConstants),
        make_shared_from_ref(pwiseLinears),
        waveNumber);

    arma::Mat<RT> matOp = op.weakForm()->asMatrix();
    arma::Mat<RT> expected = adlp.weakForm()->asMatrix();

    // we won't get full-precision agreement because singular quadrature rules
    // are not symmetric
    CT tol = 10e-6;
    BOOST_CHECK(check_arrays_are_close<RT>(matOp, expected, tol));
}

BOOST_AUTO_TEST_SUITE_END()