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

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

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

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

#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_NORMALIZE_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_NORMALIZE_HPP

#include <cstddef>

#include <boost/numeric/conversion/cast.hpp>

#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/coordinate_system.hpp>
#include <boost/geometry/core/coordinate_type.hpp>
#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/core/tag.hpp>
#include <boost/geometry/core/tags.hpp>

#include <boost/geometry/util/normalize_spheroidal_coordinates.hpp>
#include <boost/geometry/util/normalize_spheroidal_box_coordinates.hpp>

#include <boost/geometry/views/detail/indexed_point_view.hpp>

namespace boost { namespace geometry
{

#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace normalization
{

struct do_nothing
{
    template <typename GeometryIn, typename GeometryOut>
    static inline void apply(GeometryIn const&, GeometryOut&)
    {
    }
};

template <std::size_t Dimension, std::size_t DimensionCount>
struct assign_loop
{
    template <typename CoordinateType, typename PointIn, typename PointOut>
    static inline void apply(CoordinateType const& longitude,
                             CoordinateType const& latitude,
                             PointIn const& point_in,
                             PointOut& point_out)
    {
        geometry::set<Dimension>(point_out, boost::numeric_cast
            <
                typename coordinate_type<PointOut>::type
            >(geometry::get<Dimension>(point_in)));

        assign_loop
            <
                Dimension + 1, DimensionCount
            >::apply(longitude, latitude, point_in, point_out);
    }
};

template <std::size_t DimensionCount>
struct assign_loop<DimensionCount, DimensionCount>
{
    template <typename CoordinateType, typename PointIn, typename PointOut>
    static inline void apply(CoordinateType const&,
                             CoordinateType const&,
                             PointIn const&,
                             PointOut&)
    {
    }
};

template <std::size_t DimensionCount>
struct assign_loop<0, DimensionCount>
{
    template <typename CoordinateType, typename PointIn, typename PointOut>
    static inline void apply(CoordinateType const& longitude,
                             CoordinateType const& latitude,
                             PointIn const& point_in,
                             PointOut& point_out)
    {
        geometry::set<0>(point_out, boost::numeric_cast
            <
                typename coordinate_type<PointOut>::type
            >(longitude));

        assign_loop
            <
                1, DimensionCount
            >::apply(longitude, latitude, point_in, point_out);
    }
};

template <std::size_t DimensionCount>
struct assign_loop<1, DimensionCount>
{
    template <typename CoordinateType, typename PointIn, typename PointOut>
    static inline void apply(CoordinateType const& longitude,
                             CoordinateType const& latitude,
                             PointIn const& point_in,
                             PointOut& point_out)
    {
        geometry::set<1>(point_out, boost::numeric_cast
            <
                typename coordinate_type<PointOut>::type
            >(latitude));

        assign_loop
            <
                2, DimensionCount
            >::apply(longitude, latitude, point_in, point_out);
    }
};

template <typename PointIn, typename PointOut, bool IsEquatorial = true>
struct normalize_point
{
    static inline void apply(PointIn const& point_in, PointOut& point_out)
    {
        typedef typename coordinate_type<PointIn>::type in_coordinate_type;

        in_coordinate_type longitude = geometry::get<0>(point_in);
        in_coordinate_type latitude = geometry::get<1>(point_in);

        math::normalize_spheroidal_coordinates
            <
                typename coordinate_system<PointIn>::type::units,
                IsEquatorial,
                in_coordinate_type
            >(longitude, latitude);

        assign_loop
            <
                0, dimension<PointIn>::value
            >::apply(longitude, latitude, point_in, point_out);
    }
};

template <typename BoxIn, typename BoxOut, bool IsEquatorial = true>
class normalize_box
{
    template <typename UnitsIn, typename UnitsOut, typename CoordinateInType>
    static inline void apply_to_coordinates(CoordinateInType& lon_min,
                                            CoordinateInType& lat_min,
                                            CoordinateInType& lon_max,
                                            CoordinateInType& lat_max,
                                            BoxIn const& box_in,
                                            BoxOut& box_out)
    {
        detail::indexed_point_view<BoxOut, min_corner> p_min_out(box_out);
        assign_loop
            <
                0, dimension<BoxIn>::value
            >::apply(lon_min,
                     lat_min,
                     detail::indexed_point_view
                         <
                             BoxIn const, min_corner
                         >(box_in),
                     p_min_out);

        detail::indexed_point_view<BoxOut, max_corner> p_max_out(box_out);
        assign_loop
            <
                0, dimension<BoxIn>::value
            >::apply(lon_max,
                     lat_max,
                     detail::indexed_point_view
                         <
                             BoxIn const, max_corner
                         >(box_in),
                     p_max_out);
    }

public:
    static inline void apply(BoxIn const& box_in, BoxOut& box_out)
    {
        typedef typename coordinate_type<BoxIn>::type in_coordinate_type;

        in_coordinate_type lon_min = geometry::get<min_corner, 0>(box_in);
        in_coordinate_type lat_min = geometry::get<min_corner, 1>(box_in);
        in_coordinate_type lon_max = geometry::get<max_corner, 0>(box_in);
        in_coordinate_type lat_max = geometry::get<max_corner, 1>(box_in);

        math::normalize_spheroidal_box_coordinates
            <
                typename coordinate_system<BoxIn>::type::units,
                IsEquatorial,
                in_coordinate_type
            >(lon_min, lat_min, lon_max, lat_max);

        apply_to_coordinates
            <
                typename coordinate_system<BoxIn>::type::units,
                typename coordinate_system<BoxOut>::type::units
            >(lon_min, lat_min, lon_max, lat_max, box_in, box_out);
    }
};

}} // namespace detail::normalization
#endif // DOXYGEN_NO_DETAIL

#ifndef DOXYGEN_NO_DISPATCH
namespace dispatch
{

template
<
    typename GeometryIn,
    typename GeometryOut,
    typename TagIn = typename tag<GeometryIn>::type,
    typename TagOut = typename tag<GeometryOut>::type,
    typename CSTagIn = typename cs_tag<GeometryIn>::type,
    typename CSTagOut = typename cs_tag<GeometryOut>::type
>
struct normalize : detail::normalization::do_nothing
{};

template <typename PointIn, typename PointOut>
struct normalize
    <
        PointIn, PointOut, point_tag, point_tag,
        spherical_equatorial_tag, spherical_equatorial_tag
    > : detail::normalization::normalize_point<PointIn, PointOut>
{};

template <typename PointIn, typename PointOut>
struct normalize
    <
        PointIn, PointOut, point_tag, point_tag,
        spherical_polar_tag, spherical_polar_tag
    > : detail::normalization::normalize_point<PointIn, PointOut, false>
{};

template <typename PointIn, typename PointOut>
struct normalize
    <
        PointIn, PointOut, point_tag, point_tag, geographic_tag, geographic_tag
    > : detail::normalization::normalize_point<PointIn, PointOut>
{};

template <typename BoxIn, typename BoxOut>
struct normalize
    <
        BoxIn, BoxOut, box_tag, box_tag,
        spherical_equatorial_tag, spherical_equatorial_tag
    > : detail::normalization::normalize_box<BoxIn, BoxOut>
{};

template <typename BoxIn, typename BoxOut>
struct normalize
    <
        BoxIn, BoxOut, box_tag, box_tag,
        spherical_polar_tag, spherical_polar_tag
    > : detail::normalization::normalize_box<BoxIn, BoxOut, false>
{};

template <typename BoxIn, typename BoxOut>
struct normalize
    <
        BoxIn, BoxOut, box_tag, box_tag, geographic_tag, geographic_tag
    > : detail::normalization::normalize_box<BoxIn, BoxOut>
{};

} // namespace dispatch
#endif // DOXYGEN_NO_DISPATCH

#ifndef DOXYGEN_NO_DETAIL
namespace detail
{

template <typename GeometryIn, typename GeometryOut>
inline void normalize(GeometryIn const& geometry_in, GeometryOut& geometry_out)
{
    dispatch::normalize
        <
            GeometryIn, GeometryOut
        >::apply(geometry_in, geometry_out);
}

template <typename GeometryOut, typename GeometryIn>
inline GeometryOut return_normalized(GeometryIn const& geometry_in)
{
    GeometryOut geometry_out;
    detail::normalize(geometry_in, geometry_out);
    return geometry_out;
}

} // namespace detail
#endif // DOXYGEN_NO_DETAIL

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_NORMALIZE_HPP


#define BOOST_TEST_MODULE lue core time duration
#include <boost/test/unit_test.hpp>
#include "lue/core/time.hpp"

BOOST_AUTO_TEST_CASE(seconds)
{
    using namespace lue::data_model::time;

    // Period of 60 seconds modelled as 6 * (10 seconds)
    std::size_t const nr_ticks = 6;

    using TickPeriod = TickPeriod<Second>;
    using Duration = Duration<TickPeriod>;

    Duration duration(nr_ticks);

    BOOST_CHECK_EQUAL(duration.nr_ticks(), nr_ticks);
}


#ifndef HEADER_ELEMENT
#define HEADER_ELEMENT

#include <armadillo>
#include <iostream>
#include <memory>
#include <set>

template <class PointType>
class ShapeModel;

class ControlPoint;
class Element {

public:

	/**
	Constructor
	*/
	Element(std::vector<int> & control_points,ShapeModel<ControlPoint> * owning_shape);

	/**
	Get neighbors
	@param if false, only return neighbors sharing an edge. Else, returns all neighbors
	@return Pointer to neighboring elements, plus the calling element
	*/
	virtual std::set < int > get_neighbors(bool all_neighbors) const = 0;

	/**
	Recomputes element-specific values
	*/
	void update() ;

	const arma::vec::fixed<3> & get_point_coordinates(int point_index) const;

	/**
	Get element normal.
	Definition varies depending upon Element type:
	- the facet normal if the element is a triangular facet
	- the normal evaluated at the center of the element if the element is a Bezier patch
	@return element normal.
	*/
	arma::vec::fixed<3> get_normal_coordinates() const;

	/**
	Get element center.
	Definition varies depending upon Element type:
	- vertices average position if the element is a triangular facet
	- Bezier patch evaluated at u == v == w == 1./3 if the element is a Bezier patch
	@return element center
	*/
	arma::vec::fixed<3> get_center() const;

	/**
	Return the control points owned by this element
	@return owned control points
	*/
	const std::vector<int > & get_points() const;

	void set_control_points(std::vector<int > & control_points);

	void set_owning_shape(ShapeModel<ControlPoint> * owning_shape);

	/**
	Returns number of control points in owning shape
	@return number of control points in owning shape
	*/

	int get_NControlPoints_in_owning_shape() const;

	/**
	Gets an eventual super element corresponding to the present element
	@return super element
	*/
	int get_super_element() const;

	/**
	Sets an eventual super element corresponding to the present element
	@param super_element super element to assign
	*/
	void set_super_element(int super_element);

	/**
	Return surface area of element
	@return surface area of element
	*/
	double get_area() const;

	void set_global_index(int i );

	int get_global_index() const;

protected:

	virtual void compute_center() = 0;
	virtual void compute_normal() = 0;
	virtual void compute_area() = 0;

	std::vector<int>  control_points;

	// Element * super_element = nullptr;
	int super_element = -1;

	arma::vec::fixed<3> normal;
	arma::vec::fixed<3> center;

	double area;
	int global_index;

	ShapeModel<ControlPoint> * owning_shape;
};

#endif


/* ---------------------------------------------------------------------
 *
 * Copyright (C) 2014 - 2020 by the deal.II authors
 *
 * This file is part of the deal.II library.
 *
 * The deal.II library is free software; you can use it, redistribute
 * it, and/or modify it under the terms of the GNU Lesser General
 * Public License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * The full text of the license can be found in the file LICENSE.md at
 * the top level directory of deal.II.
 *
 * ---------------------------------------------------------------------
 *
 * Authors: Wolfgang Bangerth, Texas A&M University, 2014
 *          Luca Heltai, SISSA, 2014
 *          D. Sarah Stamps, MIT, 2014
 */

// 让我们从这里需要的包含文件开始。显然，我们需要描述三角形的文件（  tria.h  ），以及允许我们创建和输出三角形的文件（  grid_generator.h  和  grid_out.h  ）。此外，我们需要声明Manifold和ChartManifold类的头文件，我们将需要这些类来描述几何体（ manifold.h ）。然后我们还需要以下头文件中的 GridTools::transform() 函数；这个函数的用途将在我们使用它时讨论。

#include <deal.II/grid/tria.h>
#include <deal.II/grid/grid_generator.h>
#include <deal.II/grid/grid_out.h>
#include <deal.II/grid/manifold.h>
#include <deal.II/grid/grid_tools.h>

// 其余的包含文件与读取地形数据有关。正如介绍中所解释的，我们将从一个文件中读取它，然后使用下面头文件中第一个声明的 Functions::InterpolatedUniformGridData  类。因为数据很大，所以我们读取的文件是以gzip压缩数据的形式存储的，我们利用BOOST提供的一些功能来直接读取gzipped数据。

#include <deal.II/base/function_lib.h>

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

#include <fstream>
#include <iostream>
#include <memory>

// 上事的最后部分是打开一个命名空间，把所有东西都放进去，然后把dealii命名空间导入其中。

namespace Step53
{
  using namespace dealii;
// @sect3{Describing topography: AfricaTopography}

// 这个程序的第一个重要部分是描述地形 $h(\hat phi,\hat \theta)$ 作为经度和纬度的函数的类。正如在介绍中所讨论的那样，我们在这里将使我们的生活更容易一些，不以最普遍的方式来写这个类，而是只为我们在这里感兴趣的特定目的来写：插值从一个非常具体的数据文件中获得的数据，该文件包含了关于世界上一个特定地区的信息，我们知道该地区的范围。

// 该类的总体布局已经在上面讨论过了。下面是它的声明，包括我们在初始化 topography_data 成员变量时需要的三个静态成员函数。

  class AfricaTopography
  {
  public:
    AfricaTopography();

    double value(const double lon, const double lat) const;

  private:
    const Functions::InterpolatedUniformGridData<2> topography_data;

    static std::vector<double> get_data();
  };

// 让我们来看看这个类的实现。该类的有趣部分是构造函数和 value() 函数。前者初始化了 Functions::InterpolatedUniformGridData 成员变量，我们将使用这个构造函数，它要求我们传入我们要插值的二维数据集的端点（这里由区间 $[-6.983333, 11.98333]$ 给出）。 ]，使用介绍中讨论的切换端点的技巧，和 $[25, 35.983333]$ ，都是以度数给出的），数据被分割成的区间数（纬度方向379，经度方向219，总共 $380\times 220$ 个数据点），和一个包含数据的表对象。然后，数据的大小当然是 $380\times 220$ ，我们通过提供一个迭代器给下面 std::vector 函数返回的 get_data() 对象的83,600个元素中的第一个来初始化它。注意，我们在这里调用的所有成员函数都是静态的，因为(i)它们不访问类的任何成员变量，(ii)因为它们是在对象没有完全初始化的时候调用的。

  AfricaTopography::AfricaTopography()
    : topography_data({{std::make_pair(-6.983333, 11.966667),
                        std::make_pair(25, 35.95)}},
                      {{379, 219}},
                      Table<2, double>(380, 220, get_data().begin()))
  {}

  double AfricaTopography::value(const double lon, const double lat) const
  {
    return topography_data.value(
      Point<2>(-lat * 180 / numbers::PI, lon * 180 / numbers::PI));
  }

// 唯一一个更有意义的函数是 get_data() 函数。它返回一个临时向量，其中包含描述高度的所有83600个数据点，并从文件 topography.txt.gz 中读取。因为文件被gzip压缩了，所以我们不能直接通过类型为 std::ifstream, 的对象来读取它，但在BOOST库中有一些方便的方法（见http:www.boost.org），允许我们从压缩的文件中读取，而不用先在磁盘上解压缩。其结果是，基本上，只是另一个输入流，就所有的实际目的而言，看起来就像我们一直使用的那些输入流。

// 当读取数据时，我们读取三列数据，但忽略了前两列。最后一列的数据被附加到一个数组中，我们返回的数组将被复制到 topography_data 的表中，并被初始化。由于BOOST.iostreams库在输入文件不存在、不可读或不包含正确的数据行数时没有提供非常有用的异常，我们捕捉它可能产生的所有异常并创建我们自己的异常。为此，在 catch 子句中，我们让程序运行到一个 AssertThrow(false, ...) 语句中。由于条件总是假的，这总是会触发一个异常。换句话说，这相当于写了 throw ExcMessage("...") ，但它也填补了异常对象中的某些字段，这些字段以后会被打印在屏幕上，识别出发生异常的函数、文件和行。

  std::vector<double> AfricaTopography::get_data()
  {
    std::vector<double> data;

// 创建一个流，我们从gzipped数据中读取

    boost::iostreams::filtering_istream in;
    in.push(boost::iostreams::basic_gzip_decompressor<>());
    in.push(boost::iostreams::file_source("topography.txt.gz"));

    for (unsigned int line = 0; line < 83600; ++line)
      {
        try
          {
            double lat, lon, elevation;
            in >> lat >> lon >> elevation;

            data.push_back(elevation);
          }
        catch (...)
          {
            AssertThrow(false,
                        ExcMessage("Could not read all 83,600 data points "
                                   "from the file <topography.txt.gz>!"));
          }
      }

    return data;
  }
// @sect3{Describing the geometry: AfricaGeometry}

// 下面的类是本程序的主类。它的结构已经在介绍中详细描述过了，不需要再多做介绍。

  class AfricaGeometry : public ChartManifold<3, 3>
  {
  public:
    virtual Point<3> pull_back(const Point<3> &space_point) const override;

    virtual Point<3> push_forward(const Point<3> &chart_point) const override;

    virtual std::unique_ptr<Manifold<3, 3>> clone() const override;

  private:
    static const double R;
    static const double ellipticity;

    const AfricaTopography topography;

    Point<3> push_forward_wgs84(const Point<3> &phi_theta_d) const;
    Point<3> pull_back_wgs84(const Point<3> &x) const;

    Point<3> push_forward_topo(const Point<3> &phi_theta_d_hat) const;
    Point<3> pull_back_topo(const Point<3> &phi_theta_d) const;
  };

  const double AfricaGeometry::R           = 6378137;
  const double AfricaGeometry::ellipticity = 8.1819190842622e-2;

// 如果你读过介绍，实现起来也是非常简单的。特别是，回拉和前推函数都只是WGS 84和地形图映射各自函数的串联。

  Point<3> AfricaGeometry::pull_back(const Point<3> &space_point) const
  {
    return pull_back_topo(pull_back_wgs84(space_point));
  }

  Point<3> AfricaGeometry::push_forward(const Point<3> &chart_point) const
  {
    return push_forward_wgs84(push_forward_topo(chart_point));
  }

// 下一个函数是Manifold基类的接口所要求的，它允许克隆AfricaGeometry类。注意，虽然该函数返回一个  `std::unique_ptr<Manifold<3,3>>`,  我们在内部创建了一个 `unique_ptr<AfricaGeometry>`。换句话说，这个库需要一个指向基类的指针，我们通过创建一个指向派生类的指针来提供这个指针。

  std::unique_ptr<Manifold<3, 3>> AfricaGeometry::clone() const
  {
    return std::make_unique<AfricaGeometry>();
  }

// 下面的两个函数就定义了对应于地球WGS84参考形状的正向和反向变换。正向变换遵循介绍中所示的公式。反变换要复杂得多，至少不是直观的。它还存在一个问题，即它返回一个角度，在函数结束时，如果它应该从那里逃出来，我们需要将其夹回区间 $[0,2\pi]$ 。

  Point<3> AfricaGeometry::push_forward_wgs84(const Point<3> &phi_theta_d) const
  {
    const double phi   = phi_theta_d[0];
    const double theta = phi_theta_d[1];
    const double d     = phi_theta_d[2];

    const double R_bar = R / std::sqrt(1 - (ellipticity * ellipticity *
                                            std::sin(theta) * std::sin(theta)));

    return {(R_bar + d) * std::cos(phi) * std::cos(theta),
            (R_bar + d) * std::sin(phi) * std::cos(theta),
            ((1 - ellipticity * ellipticity) * R_bar + d) * std::sin(theta)};
  }

  Point<3> AfricaGeometry::pull_back_wgs84(const Point<3> &x) const
  {
    const double b   = std::sqrt(R * R * (1 - ellipticity * ellipticity));
    const double ep  = std::sqrt((R * R - b * b) / (b * b));
    const double p   = std::sqrt(x(0) * x(0) + x(1) * x(1));
    const double th  = std::atan2(R * x(2), b * p);
    const double phi = std::atan2(x(1), x(0));
    const double theta =
      std::atan2(x(2) + ep * ep * b * std::pow(std::sin(th), 3),
                 (p -
                  (ellipticity * ellipticity * R * std::pow(std::cos(th), 3))));
    const double R_bar =
      R / (std::sqrt(1 - ellipticity * ellipticity * std::sin(theta) *
                           std::sin(theta)));
    const double R_plus_d = p / std::cos(theta);

    Point<3> phi_theta_d;
    if (phi < 0)
      phi_theta_d[0] = phi + 2 * numbers::PI;
    else if (phi > 2 * numbers::PI)
      phi_theta_d[0] = phi - 2 * numbers::PI;
    else
      phi_theta_d[0] = phi;
    phi_theta_d[1] = theta;
    phi_theta_d[2] = R_plus_d - R_bar;
    return phi_theta_d;
  }

// 与此相反，地形变换完全按照介绍中的描述进行。因此，没有什么可以补充的。

  Point<3>
  AfricaGeometry::push_forward_topo(const Point<3> &phi_theta_d_hat) const
  {
    const double d_hat = phi_theta_d_hat[2];
    const double h = topography.value(phi_theta_d_hat[0], phi_theta_d_hat[1]);
    const double d = d_hat + (d_hat + 500000) / 500000 * h;
    return {phi_theta_d_hat[0], phi_theta_d_hat[1], d};
  }

  Point<3> AfricaGeometry::pull_back_topo(const Point<3> &phi_theta_d) const
  {
    const double d     = phi_theta_d[2];
    const double h     = topography.value(phi_theta_d[0], phi_theta_d[1]);
    const double d_hat = 500000 * (d - h) / (500000 + h);
    return {phi_theta_d[0], phi_theta_d[1], d_hat};
  }
// @sect3{Creating the mesh}

// 在描述了几何体的属性之后，现在是处理用于离散它的网格的时候了。为此，我们为几何体和三角形创建对象，然后继续创建一个与参考域 $1\times 2\times 1$ 相对应的 $\hat U=[26,35]\times[-10,5]\times[-500000,0]$ 矩形网格。我们选择这个数目的细分，因为它导致了单元格大致上像立方体，而不是在某个方向上被拉伸。

// 当然，我们实际上对参考域的网格划分不感兴趣。我们感兴趣的是对真实域的网格划分。因此，我们将使用 GridTools::transform() 函数，它只是根据一个给定的变换来移动三角形的每个点。它想要的变换函数是一个将参考域中的一个点作为其单一参数的函数，并返回我们想要映射到的域中的相应位置。当然，这正是我们使用的几何学的前推函数。我们用一个lambda函数来包装它，以获得转换所需的那种函数对象。

  void run()
  {
    AfricaGeometry   geometry;
    Triangulation<3> triangulation;

    {
      const Point<3> corner_points[2] = {
        Point<3>(26 * numbers::PI / 180, -10 * numbers::PI / 180, -500000),
        Point<3>(35 * numbers::PI / 180, 5 * numbers::PI / 180, 0)};
      std::vector<unsigned int> subdivisions(3);
      subdivisions[0] = 1;
      subdivisions[1] = 2;
      subdivisions[2] = 1;
      GridGenerator::subdivided_hyper_rectangle(
        triangulation, subdivisions, corner_points[0], corner_points[1], true);

      GridTools::transform(
        [&geometry](const Point<3> &chart_point) {
          return geometry.push_forward(chart_point);
        },
        triangulation);
    }

// 下一步是向三角计算说明，在细化网格时，每当需要一个新的点时，都要使用我们的几何对象。我们通过告诉三角计算对所有流形指示器为零的物体使用我们的几何体，然后继续用流形指示器为零标记所有单元及其边界面和边。这确保了三角计算在每次需要新的顶点时都会参考我们的几何对象。由于流形指标是由母体继承给子体的，这也会在几个递归细化步骤之后发生。

    triangulation.set_manifold(0, geometry);
    for (const auto &cell : triangulation.active_cell_iterators())
      cell->set_all_manifold_ids(0);

// 最后一步是在最初的 $1\times 2\times 1$ 粗略网格之外细化该网格。我们可以在全局范围内细化若干次，但由于本教程程序的目的，我们实际上只对靠近表面的情况感兴趣，所以我们只是对所有在边界上有一个指标为5的面的单元进行6次细化。在我们上面使用的 GridGenerator::subdivided_hyper_rectangle() 函数的文档中查找，发现边界指标5对应于域的顶面（这就是上面调用 GridGenerator::subdivided_hyper_rectangle() 的最后一个 true 参数的含义：通过给每个边界分配一个独特的边界指标来给边界 "着色"）。

    for (unsigned int i = 0; i < 6; ++i)
      {
        for (const auto &cell : triangulation.active_cell_iterators())
          for (const auto &face : cell->face_iterators())
            if (face->boundary_id() == 5)
              {
                cell->set_refine_flag();
                break;
              }
        triangulation.execute_coarsening_and_refinement();

        std::cout << "Refinement step " << i + 1 << ": "
                  << triangulation.n_active_cells() << " cells, "
                  << GridTools::minimal_cell_diameter(triangulation) / 1000
                  << "km minimal cell diameter" << std::endl;
      }

// 做完这一切，我们现在可以将网格输出到一个自己的文件中。

    const std::string filename = "mesh.vtu";
    std::ofstream     out(filename);
    GridOut           grid_out;
    grid_out.write_vtu(triangulation, out);
  }
} // namespace Step53

//  @sect3{The main function}

// 最后是主函数，它采用了从  step-6  开始的所有教程程序中使用的相同方案。这里没有什么可做的，只需要调用单一的  run()  函数。

int main()
{
  try
    {
      Step53::run();
    }
  catch (std::exception &exc)
    {
      std::cerr << std::endl
                << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Exception on processing: " << std::endl
                << exc.what() << std::endl
                << "Aborting!" << std::endl
                << "----------------------------------------------------"
                << std::endl;

      return 1;
    }
  catch (...)
    {
      std::cerr << std::endl
                << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Unknown exception!" << std::endl
                << "Aborting!" << std::endl
                << "----------------------------------------------------"
                << std::endl;
      return 1;
    }
}


#include <string>
#include <sstream>
#include <iostream>
#include <map>
#include <regex>
#include <fstream>
#include <boost/algorithm/string.hpp>

using namespace std;

bool is_valid_1(map<string, string> passport) {
    // Part 1
    if (!(passport.size() == 8 || (passport.size() == 7 && passport.find("cid") == passport.end()))) {
        return false;
    }
    return true;
}

bool is_valid_2(map<string, string> passport) {
    if (!is_valid_1(passport)) {
        return false;
    }

    // Part 2
    int byr = stoi(passport["byr"]);
    if (byr < 1920 || byr > 2002) {
        return false;
    }

    int iyr = stoi(passport["iyr"]);
    if (iyr < 2010 || iyr > 2020) {
        return false;
    }

    int eyr = stoi(passport["eyr"]);
    if (eyr < 2020 || eyr > 2030) {
        return false;
    }

    smatch matches;
    regex e1 ("(\\d+)(in|cm)");
    regex_match (passport["hgt"], matches, e1);
    if (matches.size() != 3) {
        return false;
    }

    if (matches[2] == "cm") {
        int h = stoi(matches[1]);
        if (h < 150 || h > 193) {
            return false;
        }
    } else if (matches[2] == "in") {
        int h = stoi(matches[1]);
        if (h < 59 || h > 76) {
            return false;
        }
    } else {
        return false;
    }

    regex e2 ("#[0-9a-f]{6}");
    if (!regex_match (passport["hcl"], e2)) {
        return false;
    }

    string ecl = passport["ecl"];
    if (ecl != "amb" && ecl != "blu" && ecl != "brn" && ecl != "gry" && ecl != "grn" && ecl != "hzl" && ecl != "oth") {
        return false;
    }

    regex e3 ("\\d{9}");
    if (!regex_match (passport["pid"], e3)) {
        return false;
    }

    return true;
}

int main() {
    ifstream file ("2020/4.txt");
    if (!file.is_open()) {
        cout << "Failed to open file: " << strerror(errno) << endl;
        return -1;
    }

    int valid1 = 0;
    int valid2 = 0;
    map<string, string> passport;

    string line;
    while (getline(file, line, '\n')) {
        boost::trim(line);

        // We assume every time we get to a empty line, that a password has ended.
        // This also assume the file ends in a new line.
        if (line == "") {
            // Handle the passport
            if (is_valid_1(passport)) {
                valid1++;
            }
            if (is_valid_2(passport)) {
                valid2++;
            }
            passport.clear();
            continue;
        }

        // Split the line, and all the key/values on it
        vector<string> values;
        boost::split(values, line, boost::is_any_of("\t\n "));

        for (const string & value : values) {
            vector<string> kv;
            boost::split(kv, value, boost::is_any_of(":"));
            if (kv.size() != 2) {
                cout << "invalid value: " << value << endl;
                return -1;
            }
            passport[kv[0]] = kv[1];
        }
    }

    cout << "Answer 4.1: " << valid1 << endl;
    cout << "Answer 4.2: " << valid2 << endl;
    file.close();
}


#ifndef PYTHONIC_SCIPY_SPECIAL_IVP_HPP
#define PYTHONIC_SCIPY_SPECIAL_IVP_HPP

#include "pythonic/include/scipy/special/ivp.hpp"

#include "pythonic/types/ndarray.hpp"
#include "pythonic/utils/functor.hpp"
#include "pythonic/utils/numpy_traits.hpp"

#define BOOST_MATH_THREAD_LOCAL thread_local
#include <boost/math/special_functions/bessel_prime.hpp>

PYTHONIC_NS_BEGIN

namespace scipy
{
  namespace special
  {
    namespace details
    {
      template <class T0, class T1>
      double ivp(T0 x, T1 y)
      {
        using namespace boost::math::policies;
        return boost::math::cyl_bessel_i_prime(
            x, y, make_policy(promote_double<true>()));
      }
    }

#define NUMPY_NARY_FUNC_NAME ivp
#define NUMPY_NARY_FUNC_SYM details::ivp
#include "pythonic/types/numpy_nary_expr.hpp"
  }
}
PYTHONIC_NS_END

#endif


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

/*! \file portfolio/builders/swap.hpp
    \brief
    \ingroup builders
*/

#pragma once

#include <boost/make_shared.hpp>
#include <ored/portfolio/builders/cachingenginebuilder.hpp>
#include <ored/portfolio/enginefactory.hpp>
#include <ored/utilities/log.hpp>
#include <ql/pricingengines/swap/discountingswapengine.hpp>
#include <qle/pricingengines/discountingcurrencyswapengine.hpp>
#include <qle/pricingengines/discountingswapenginemulticurve.hpp>

namespace ore {
namespace data {

//! Engine Builder base class for Single Currency Swaps
/*! Pricing engines are cached by currency
    \ingroup builders
*/
class SwapEngineBuilderBase : public CachingPricingEngineBuilder<string, const Currency&> {
public:
    SwapEngineBuilderBase(const std::string& model, const std::string& engine)
        : CachingEngineBuilder(model, engine, {"Swap"}) {}

protected:
    virtual string keyImpl(const Currency& ccy) override { return ccy.code(); }
};

//! Engine Builder for Single Currency Swaps
/*! This builder uses QuantLib::DiscountingSwapEngine
    \ingroup builders
*/
class SwapEngineBuilder : public SwapEngineBuilderBase {
public:
    SwapEngineBuilder() : SwapEngineBuilderBase("DiscountedCashflows", "DiscountingSwapEngine") {}

protected:
    virtual boost::shared_ptr<PricingEngine> engineImpl(const Currency& ccy) override {

        Handle<YieldTermStructure> yts = market_->discountCurve(ccy.code(), configuration(MarketContext::pricing));
        return boost::make_shared<QuantLib::DiscountingSwapEngine>(yts);
    }
};

//! Engine Builder for Single Currency Swaps
/*! This builder uses QuantExt::DiscountingSwapEngineMultiCurve
    \ingroup builders
*/
class SwapEngineBuilderOptimised : public SwapEngineBuilderBase {
public:
    SwapEngineBuilderOptimised() : SwapEngineBuilderBase("DiscountedCashflows", "DiscountingSwapEngineOptimised") {}

protected:
    virtual boost::shared_ptr<PricingEngine> engineImpl(const Currency& ccy) override {

        Handle<YieldTermStructure> yts = market_->discountCurve(ccy.code(), configuration(MarketContext::pricing));
        return boost::make_shared<QuantExt::DiscountingSwapEngineMultiCurve>(yts);
    }
};

//! Engine Builder for Cross Currency Swaps
/*! Pricing engines are cached by currencies (represented as a string list)

    \ingroup builders
*/
class CrossCurrencySwapEngineBuilder
    : public CachingPricingEngineBuilder<string, const std::vector<Currency>&, const Currency&> {
public:
    CrossCurrencySwapEngineBuilder()
        : CachingEngineBuilder("DiscountedCashflows", "DiscountingCrossCurrencySwapEngine", {"CrossCurrencySwap"}) {}

protected:
    virtual string keyImpl(const std::vector<Currency>& ccys, const Currency& base) override {
        std::ostringstream ccyskey;
        ccyskey << base << "/";
        for (Size i = 0; i < ccys.size(); ++i)
            ccyskey << ccys[i] << ((i < ccys.size() - 1) ? "-" : "");
        return ccyskey.str();
    }

    virtual boost::shared_ptr<PricingEngine> engineImpl(const std::vector<Currency>& ccys,
                                                        const Currency& base) override {

        std::vector<Handle<YieldTermStructure>> discountCurves;
        std::vector<Handle<Quote>> fxQuotes;
        for (Size i = 0; i < ccys.size(); ++i) {
            discountCurves.push_back(market_->discountCurve(ccys[i].code(), configuration(MarketContext::pricing)));
            string pair = ccys[i].code() + base.code();
            fxQuotes.push_back(market_->fxSpot(pair, configuration(MarketContext::pricing)));
        }

        return boost::make_shared<QuantExt::DiscountingCurrencySwapEngine>(discountCurves, fxQuotes, ccys, base);
    }
};

} // namespace data
} // namespace ore


#include "AnalysisGraph.hpp"
#include <range/v3/all.hpp>
#include <unsupported/Eigen/MatrixFunctions>
#include <boost/range/adaptors.hpp>

using namespace std;
using Eigen::VectorXd, Eigen::MatrixXd;
using fmt::print, fmt::format;
namespace rs = ranges;
using boost::adaptors::transformed;

/*
 ============================================================================
 Private: Inference
 ============================================================================
*/

void AnalysisGraph::print_latent_state(const VectorXd& v) {
  for (int i=0; i < this->num_vertices(); i++){
    cout << (*this)[i].name << " " << v[2*i] << endl;
  }
}

void AnalysisGraph::sample_predicted_latent_state_sequences(
    int prediction_timesteps,
    int initial_prediction_step,
    int total_timesteps,
    bool project) {
  this->n_timesteps = prediction_timesteps;

  // Allocate memory for prediction_latent_state_sequences
  this->predicted_latent_state_sequences.clear();
  this->predicted_latent_state_sequences = vector<vector<VectorXd>>(
      this->res,
      vector<VectorXd>(this->n_timesteps, VectorXd(this->num_vertices() * 2)));

  for (int samp = 0; samp < this->res; samp++) {
    for (int t = 0; t < this->n_timesteps; t++) {
      const MatrixXd& A_d = this->transition_matrix_collection.at(samp);
      if (project) {
        // Perform projection based on the perturbed initial latent state s0
        // FIXME The matrix A_d here is the transition matrix for a discrete update.
        // In order to use the matrix exponential it must be the the matrix for
        // the continuous 'differential' update equation.
        this->predicted_latent_state_sequences[samp][t] = A_d.pow(t) * this->s0;
        if (samp == 0) {
          this->print_latent_state(this->predicted_latent_state_sequences[samp][t]);
        }
      }
      else {
        // Perform inference based on the sampled initial latent states
        const VectorXd& s0_samp = this->initial_latent_state_collection[samp];
        this->predicted_latent_state_sequences[samp][t] = A_d.pow(t) * s0_samp;
      }
    }
  }
}

void AnalysisGraph::
    generate_predicted_observed_state_sequences_from_predicted_latent_state_sequences() {
  using rs::to, rs::views::transform;

  // Allocate memory for observed_state_sequences
  this->predicted_observed_state_sequences.clear();
  this->predicted_observed_state_sequences =
      vector<PredictedObservedStateSequence>(
          this->res,
          PredictedObservedStateSequence(this->n_timesteps,
                                         vector<vector<double>>()));

  for (int samp = 0; samp < this->res; samp++) {
    vector<VectorXd>& sample = this->predicted_latent_state_sequences[samp];

    this->predicted_observed_state_sequences[samp] =
        sample | transform([this](VectorXd latent_state) {
          return this->sample_observed_state(latent_state);
        }) |
        to<vector>();
  }
}

FormattedPredictionResult AnalysisGraph::format_prediction_result() {
  // Access
  // [ sample ][ time_step ][ vertex_name ][ indicator_name ]
  auto result = FormattedPredictionResult(
      this->res,
      vector<unordered_map<string, unordered_map<string, double>>>(
          this->pred_timesteps));

  for (int samp = 0; samp < this->res; samp++) {
    for (int ts = 0; ts < this->pred_timesteps; ts++) {
      for (auto [vert_name, vert_id] : this->name_to_vertex) {
        for (auto [ind_name, ind_id] : (*this)[vert_id].nameToIndexMap) {
          result[samp][ts][vert_name][ind_name] =
              this->predicted_observed_state_sequences[samp][ts][vert_id]
                                                      [ind_id];
        }
      }
    }
  }

  return result;
}

FormattedProjectionResult AnalysisGraph::format_projection_result() {
  // Access
  // [ vertex_name ][ timestep ][ sample ]
  FormattedProjectionResult result;

  for (auto [vert_name, vert_id] : this->name_to_vertex) {
    result[vert_name] =
        vector<vector<double>>(this->pred_timesteps, vector<double>(this->res));
    for (int ts = 0; ts < this->pred_timesteps; ts++) {
      for (int samp = 0; samp < this->res; samp++) {
        result[vert_name][ts][samp] =
            // this->predicted_latent_state_sequences[samp][ts](2 * vert_id);
            this->predicted_observed_state_sequences[samp][ts][vert_id][0];
      }
      rs::sort(result[vert_name][ts]);
    }
  }

  return result;
}

void AnalysisGraph::run_model(int start_year,
                              int start_month,
                              int end_year,
                              int end_month,
                              bool project) {
  if (!this->trained) {
    print("Passed untrained Causal Analysis Graph (CAG) Model. \n",
          "Try calling <CAG>.train_model(...) first!");
    throw "Model not yet trained";
  }

  // Check for sensible ranges.
  if (start_year < this->training_range.first.first ||
      (start_year == this->training_range.first.first &&
       start_month < this->training_range.first.second)) {
    print("The initial prediction date can't be before the "
         "inital training date. Defaulting initial prediction date "
         "to initial training date.");
    start_year = this->training_range.first.first;
    start_month = this->training_range.first.second;
  }

  /*
   *              total_timesteps
   *   ____________________________________________
   *  |                                            |
   *  v                                            v
   * start training                          end prediction
   *  |--------------------------------------------|
   *  :           |--------------------------------|
   *  :         start prediction                   :
   *  ^           ^                                ^
   *  |___________|________________________________|
   *      diff              pred_timesteps
   */
  int total_timesteps =
      this->calculate_num_timesteps(this->training_range.first.first,
                                    this->training_range.first.second,
                                    end_year,
                                    end_month);

  this->pred_timesteps = this->calculate_num_timesteps(
      start_year, start_month, end_year, end_month);

  int pred_init_timestep = total_timesteps - pred_timesteps;

  int year = start_year;
  int month = start_month;

  this->pred_range.clear();
  this->pred_range = vector<string>(this->pred_timesteps);

  for (int t = 0; t < this->pred_timesteps; t++) {
    this->pred_range[t] = to_string(year) + "-" + to_string(month);

    if (month == 12) {
      year++;
      month = 1;
    }
    else {
      month++;
    }
  }

  this->sample_predicted_latent_state_sequences(
      this->pred_timesteps, 0, total_timesteps, project);
  this->generate_predicted_observed_state_sequences_from_predicted_latent_state_sequences();
}

/*
 ============================================================================
 Public: Inference
 ============================================================================
*/

Prediction AnalysisGraph::generate_prediction(int start_year,
                                              int start_month,
                                              int end_year,
                                              int end_month) {
  this->run_model(start_year, start_month, end_year, end_month);

  return make_tuple(
      this->training_range, this->pred_range, this->format_prediction_result());
}

vector<vector<double>> AnalysisGraph::prediction_to_array(string indicator) {
  int vert_id = -1;
  int ind_id = -1;

  auto result =
      vector<vector<double>>(this->res, vector<double>(this->pred_timesteps));

  // Find the vertex id the indicator is attached to and
  // the indicator id of it.
  // TODO: We can make this more efficient by making indicators_in_CAG
  // a map from indicator names to vertices they are attached to.
  // This is just a quick and dirty implementation
  for (auto [v_name, v_id] : this->name_to_vertex) {
    for (auto [i_name, i_id] : (*this)[v_id].nameToIndexMap) {
      if (indicator.compare(i_name) == 0) {
        vert_id = v_id;
        ind_id = i_id;
        goto indicator_found;
      }
    }
  }
  // Program will reach here only if the indicator is not found
  throw IndicatorNotFoundException(format(
      "AnalysisGraph::prediction_to_array - indicator \"{}\" not found!\n",
      indicator));

indicator_found:

  for (int samp = 0; samp < this->res; samp++) {
    for (int ts = 0; ts < this->pred_timesteps; ts++) {
      result[samp][ts] =
          this->predicted_observed_state_sequences[samp][ts][vert_id][ind_id];
    }
  }

  return result;
}


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

#include <boost/simd/arithmetic/functions/adds.hpp>
#include <boost/simd/include/functions/simd/shift_right.hpp>
#include <boost/simd/include/functions/simd/plus.hpp>
#include <boost/simd/include/functions/simd/bitwise_or.hpp>
#include <boost/simd/include/functions/simd/bitwise_xor.hpp>
#include <boost/simd/include/functions/simd/if_else.hpp>
#include <boost/simd/include/functions/simd/is_less.hpp>
#include <boost/simd/include/functions/simd/is_greater_equal.hpp>
#include <boost/simd/include/functions/simd/complement.hpp>
#include <boost/simd/include/functions/simd/genmask.hpp>
#include <boost/simd/include/functions/simd/bitwise_cast.hpp>
#include <boost/simd/include/constants/valmax.hpp>
#include <boost/simd/include/constants/zero.hpp>
#include <boost/simd/sdk/meta/scalar_of.hpp>
#include <boost/dispatch/attributes.hpp>

namespace boost { namespace simd { namespace ext
{
  BOOST_DISPATCH_IMPLEMENT          ( adds_, tag::cpu_
                                    , (A0)(X)
                                    , ((simd_<floating_<A0>,X>))
                                      ((simd_<floating_<A0>,X>))
                                    )
  {
    typedef A0 result_type;
    BOOST_FORCEINLINE BOOST_SIMD_FUNCTOR_CALL_REPEAT(2)
    {
      return a0+a1;
    }
  };

  BOOST_DISPATCH_IMPLEMENT          ( adds_, tag::cpu_
                                    , (A0)(X)
                                    , ((simd_<uint_<A0>,X>))
                                      ((simd_<uint_<A0>,X>))
                                    )
  {
    typedef A0 result_type;
    BOOST_FORCEINLINE BOOST_SIMD_FUNCTOR_CALL_REPEAT(2)
    {
      A0 a0pa1 = a0+a1;
      return a0pa1 | genmask(a0pa1 < a0);
    }
  };

  BOOST_DISPATCH_IMPLEMENT          ( adds_, tag::cpu_
                                    , (A0)(X)
                                    , ((simd_<int_<A0>,X>))
                                      ((simd_<int_<A0>,X>))
                                    )
  {
    typedef A0 result_type;
    BOOST_SIMD_FUNCTOR_CALL_REPEAT(2)
    {
      typedef typename dispatch::meta::as_unsigned<A0>::type utype;
      typedef typename meta::scalar_of<A0>::type stype;

      utype ux = bitwise_cast<utype>(a0);
      utype uy = bitwise_cast<utype>(a1);
      utype res = ux + uy;

      ux = shift_right(ux, sizeof(stype)*CHAR_BIT-1) + static_cast<typename meta::scalar_of<utype>::type>(Valmax<stype>());

      return bitwise_cast<A0>(if_else(bitwise_cast<A0>((ux ^ uy) | ~(uy ^ res)) >=  Zero<A0>(), ux, res));
    }
  };
} } }
#endif


// This file is part of KWIVER, and is distributed under the
// OSI-approved BSD 3-Clause License. See top-level LICENSE file or
// https://github.com/Kitware/kwiver/blob/master/LICENSE for details.

/// \file
/// Operations to calculate closest points and ray intersections
/// to triangles and meshes.

#include "mesh_intersect.h"

#include <limits>

#include <vital/logger/logger.h>

#include <Eigen/Geometry>

namespace kwiver {

namespace arrows {

namespace core {

using namespace kwiver::vital;

// ----------------------------------------------------------------------------
bool
mesh_intersect_triangle(
  const point_3d& p, const vector_3d& d, const point_3d& a, const point_3d& b,
  const point_3d& c, double& dist, double& u, double& v )
{
  vector_3d n( ( b.value() - a.value() ).cross( c.value() - a.value() ) );
  return mesh_intersect_triangle( p, d, a, b, c, n, dist, u, v );
}

// ----------------------------------------------------------------------------
bool
mesh_intersect_triangle(
  const point_3d& p, const vector_3d& d, const point_3d& a, const point_3d& b,
  const point_3d& c, const vector_3d& n, double& dist, double& u, double& v )
{
  double denom = -d.dot( n );

  if( denom <= 0 ) // back facing triangles
  {
    return false;
  }

  vector_3d ap( p.value() - a.value() );
  vector_3d t( d.cross( ap ) );
  v = ( b.value() - p.value() ).dot( t );
  if( v < 0.0 || v > denom )
  {
    return false;
  }

  u = -( c.value() - p.value() ).dot( t );
  if( u < 0.0 || u + v > denom )
  {
    return false;
  }

  dist = ap.dot( n );
  if( dist < 0.0 )
  {
    return false;
  }

  u /= denom;
  v /= denom;
  dist /= denom;

  return true;
}

// ----------------------------------------------------------------------------
bool
mesh_intersect_triangle_min_dist(
  const point_3d& p, const vector_3d& d, const point_3d& a, const point_3d& b,
  const point_3d& c, const vector_3d& n, double& dist, double& u, double& v )
{
  double denom = -d.dot( n );

  if( denom <= 0 ) // back facing triangles
  {
    return false;
  }

  vector_3d ap( p.value() - a.value() );
  double new_dist = ap.dot( n ) / denom;

  if( new_dist < 0.0 || new_dist > dist )
  {
    return false;
  }

  vector_3d t( d.cross( ap ) );
  v = ( b.value() - p.value() ).dot( t );
  if( v < 0.0 || v > denom )
  {
    return false;
  }

  u = -( c.value() - p.value() ).dot( t );
  if( u < 0.0 || u + v > denom )
  {
    return false;
  }

  dist = new_dist;
  u /= denom;
  v /= denom;

  return true;
}

// ----------------------------------------------------------------------------
unsigned char
mesh_triangle_closest_point(
  const point_3d& p, const point_3d& a, const point_3d& b, const point_3d& c,
  const vector_3d& n, double& dist, double& u, double& v )
{
  double denom = 1.0 / n.squaredNorm();

  vector_3d ap( p.value() - a.value() );
  vector_3d bp( p.value() - b.value() );
  vector_3d cp( p.value() - c.value() );

  vector_3d t( n.cross( ap ) );
  v = bp.dot( t ) * denom;
  u = -cp.dot( t ) * denom;

  vector_3d ab( b.value() - a.value() );
  vector_3d bc( c.value() - b.value() );
  vector_3d ca( a.value() - c.value() );

  double eps = std::numeric_limits< double >::epsilon();

  unsigned char state = 0;
  double uv;
  if( u <= eps )
  {
    double p_v = v - u * ab.dot( ca ) / ca.squaredNorm();

    if( p_v <= eps )
    {
      state = 1;
    }
    else if( p_v >= 1.0 )
    {
      state = 4;
    }
    else
    {
      u = 0.0;
      v = p_v;
      dist = ( ( 1 - v ) * ap + v * cp ).norm();
      return 5;
    }
  }
  if( v <= eps )
  {
    double p_u = u - v * ca.dot( ab ) / ab.squaredNorm();

    if( p_u <= eps )
    {
      state = 1;
    }
    else if( p_u >= 1.0 )
    {
      state = 2;
    }
    else
    {
      u = p_u;
      v = 0.0;
      dist = ( ( 1 - u ) * ap + u * bp ).norm();
      return 3;
    }
  }
  if( ( uv = 1.0 - u - v ) <= eps )
  {
    double s = -ca.dot( bc ) / bc.squaredNorm();
    double p_u = u + uv * s;
    double p_v = v + uv * ( 1.0 - s );
    if( p_v <= eps )
    {
      state = 2;
    }
    else if( p_u <= eps )
    {
      state = 4;
    }
    else
    {
      u = p_u;
      v = p_v;
      dist = ( u * bp + v * cp ).norm();
      return 6;
    }
  }

  switch( state )
  {
    case 1:
      u = 0.0;
      v = 0.0;
      dist = ap.norm();
      return 1;
    case 2:
      u = 1.0;
      v = 0.0;
      dist = bp.norm();
      return 2;
    case 4:
      u = 0.0;
      v = 1.0;
      dist = cp.norm();
      return 4;
    default:
      dist = std::abs( ap.dot( n ) * std::sqrt( denom ) );
      return 7;
  }

  return 0;
}

// ----------------------------------------------------------------------------
unsigned char
mesh_triangle_closest_point(
  const point_3d& p, const point_3d& a, const point_3d& b, const point_3d& c,
  double& dist, double& u, double& v )
{
  vector_3d n( ( b.value() - a.value() ).cross( c.value() - a.value() ) );
  return mesh_triangle_closest_point( p, a, b, c, n, dist, u, v );
}

// ----------------------------------------------------------------------------
vital::point_3d
mesh_triangle_closest_point(
  const point_3d& p, const point_3d& a, const point_3d& b, const point_3d& c,
  double& dist )
{
  double u, v;
  mesh_triangle_closest_point( p, a, b, c, dist, u, v );

  double t = 1 - u - v;
  return point_3d( t * a[ 0 ] + u * b[ 0 ] + v * c[ 0 ],
                   t * a[ 1 ] + u * b[ 1 ] + v * c[ 1 ],
                   t * a[ 2 ] + u * b[ 2 ] + v * c[ 2 ] );
}

// ----------------------------------------------------------------------------
int
mesh_closest_point(
  const point_3d& p, const mesh& mesh, point_3d& cp, double& u, double& v )
{
  // check for a triangular mesh
  if( mesh.faces().regularity() != 3 )
  {
    LOG_ERROR( vital::get_logger( "arrows.core.mesh_closest_point" ),
               "Closest point calculation requires triangular mesh." );
    return -1;
  }

  const mesh_vertex_array< 3 >& verts = mesh.vertices< 3 >();
  const mesh_regular_face_array< 3 >& faces =
    static_cast< const mesh_regular_face_array< 3 >& >( mesh.faces() );

  int isect = -1;
  double u1, v1;
  double shortest_dist = std::numeric_limits< double >::infinity();

  for( unsigned int i = 0; i < faces.size(); ++i )
  {
    const mesh_regular_face< 3 >& f = faces[ i ];
    vital::point_3d a( verts[ f[ 0 ] ] );
    vital::point_3d b( verts[ f[ 1 ] ] );
    vital::point_3d c( verts[ f[ 2 ] ] );
    double dist = shortest_dist;
    if( mesh_triangle_closest_point( p, a, b, c, dist, u1, v1 ) &&
        dist < shortest_dist )
    {
      u = u1;
      v = v1;
      isect = i;
      shortest_dist = dist;
    }
  }

  // Get the closest point in physical space from barycentric coordinates
  double t = 1 - u - v;
  const mesh_regular_face< 3 >& f = faces[ isect ];
  vital::point_3d a( verts[ f[ 0 ] ] );
  vital::point_3d b( verts[ f[ 1 ] ] );
  vital::point_3d c( verts[ f[ 2 ] ] );
  cp.set_value( vector_3d( t * a[ 0 ] + u * b[ 0 ] + v * c[ 0 ],
                           t * a[ 1 ] + u * b[ 1 ] + v * c[ 1 ],
                           t * a[ 2 ] + u * b[ 2 ] + v * c[ 2 ] ) );
  return isect;
}

// ----------------------------------------------------------------------------
int
mesh_intersect(
  const point_3d& p, const vector_3d& d, const mesh& mesh, double& dist,
  double& u, double& v )
{
  // check for a triangular mesh
  if( mesh.faces().regularity() != 3 )
  {
    LOG_ERROR( vital::get_logger( "arrows.core.mesh_closest_point" ),
               "Closest point calculation requires triangular mesh." );
    return -1;
  }

  // Calculate normals if needed
  if( !mesh.faces().has_normals() )
  {
    LOG_ERROR( vital::get_logger( "arrows.core.mesh_closest_point" ),
               "Closest point calculation requires faces normals." );
    return -1;
  }

  const mesh_vertex_array< 3 >& verts = mesh.vertices< 3 >();
  const mesh_regular_face_array< 3 >& faces =
    static_cast< const mesh_regular_face_array< 3 >& >( mesh.faces() );

  // Check that normal magnitude corresponds to face area
  if( ( ( verts[ faces[ 0 ][ 1 ] ] - verts[ faces[ 0 ][ 0 ] ] )
        .cross( verts[ faces[ 0 ][ 2 ] ] - verts[ faces[ 0 ][ 0 ] ] ) -
        0.5 * faces.normal( 0 ) ).norm() < 1e-14 )
  {
    LOG_ERROR( vital::get_logger(
                 "arrows.core.mesh_closest_point" ),
               "Closest point calculation requires faces normal lengths be set to face area." );
    return -1;
  }

  int isect = -1;
  dist = std::numeric_limits< double >::infinity();
  for( unsigned int i = 0; i < faces.size(); ++i )
  {
    const mesh_regular_face< 3 >& f = faces[ i ];
    vital::point_3d a( verts[ f[ 0 ] ] );
    vital::point_3d b( verts[ f[ 1 ] ] );
    vital::point_3d c( verts[ f[ 2 ] ] );
    if( mesh_intersect_triangle_min_dist( p, d, a, b, c, faces.normal( i ),
                                          dist, u, v ) )
    {
      isect = i;
    }
  }
  return isect;
}

} // namespace core

} // namespace arrows

} // namespace kwiver


#pragma once

#ifdef POLYFEM_WITH_HYPRE

////////////////////////////////////////////////////////////////////////////////
#include <polyfem/Common.hpp>
#include <polyfem/LinearSolver.hpp>
#include <Eigen/Core>
#include <Eigen/Sparse>
#include <vector>

#include <HYPRE_utilities.h>
#include <HYPRE.h>
#include <HYPRE_parcsr_ls.h>
#include <HYPRE_parcsr_mv.h>
#include <IJ_matrix.h>

////////////////////////////////////////////////////////////////////////////////
//
// https://computation.llnl.gov/sites/default/files/public/hypre-2.11.2_usr_manual.pdf
// https://github.com/LLNL/hypre/blob/v2.14.0/docs/HYPRE_usr_manual.pdf
//

namespace polyfem {

	class LinearSolverHypre : public LinearSolver {

	public:
		LinearSolverHypre();
		~LinearSolverHypre();

	private:
		POLYFEM_DELETE_MOVE_COPY(LinearSolverHypre)

	public:
		//////////////////////
		// Public interface //
		//////////////////////

		// Set solver parameters
		virtual void setParameters(const json &params) override;

		// Retrieve memory information from Pardiso
		virtual void getInfo(json &params) const override;

		// Analyze sparsity pattern
		virtual void analyzePattern(const StiffnessMatrix &A) override;

		// Factorize system matrix
		virtual void factorize(const StiffnessMatrix &) override { }

		// Solve the linear system Ax = b
		virtual void solve(const Ref<const VectorXd> b, Ref<VectorXd> x) override;

		// Name of the solver type (for debugging purposes)
		virtual std::string name() const override { return "Hypre"; }

	protected:
		int dimension_ = 1; // 1 = scalar (Laplace), 2 or 3 = vector (Elasticity)
		int max_iter_ = 1000;
		int pre_max_iter_ = 1;
		double conv_tol_ = 1e-10;

		HYPRE_Int num_iterations;
		HYPRE_Complex final_res_norm;
	private:
		bool has_matrix_ = false;

		HYPRE_IJMatrix A;
		HYPRE_ParCSRMatrix parcsr_A;

	};

} // namespace polyfem

#endif


// Copyright Ankit Daftery 2011-2012.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

/**
 *  @brief An example to demonstrate use of universal functions or ufuncs
 *
 *
 * @todo Calling the overloaded () operator is in a roundabout manner, find a simpler way
 *       None of the methods like np::add, np::multiply etc are supported as yet
 */

#include <boost/python/numpy.hpp>
#include <iostream>

namespace p = boost::python;
namespace np = boost::python::numpy;

// Create the structs necessary to implement the ufuncs
// The typedefs *must* be made

struct UnarySquare
{
  typedef double argument_type;
  typedef double result_type;

  double operator()(double r) const { return r * r;}
};

struct BinarySquare
{
  typedef double first_argument_type;
  typedef double second_argument_type;
  typedef double result_type;

  double operator()(double a,double b) const { return (a*a + b*b) ; }
};

int main(int argc, char **argv)
{
  // Initialize the Python runtime.
  Py_Initialize();
  // Initialize NumPy
  np::initialize();
  // Expose the struct UnarySquare to Python as a class, and let ud be the class object
  p::object ud = p::class_<UnarySquare, boost::shared_ptr<UnarySquare> >("UnarySquare")
    .def("__call__", np::unary_ufunc<UnarySquare>::make());
  // Let inst be an instance of the class ud
  p::object inst = ud();
  // Use the "__call__" method to call the overloaded () operator and print the value
  std::cout << "Square of unary scalar 1.0 is " << p::extract <char const * > (p::str(inst.attr("__call__")(1.0))) << std::endl ;
  // Create an array in C++
  int arr[] = {1,2,3,4} ;
  // ..and use it to create the ndarray in Python
  np::ndarray demo_array = np::from_data(arr, np::dtype::get_builtin<int>() , p::make_tuple(4), p::make_tuple(4), p::object());
  // Print out the demo array
  std::cout << "Demo array is " << p::extract <char const * > (p::str(demo_array)) << std::endl ;
  // Call the "__call__" method to perform the operation and assign the value to result_array
  p::object result_array = inst.attr("__call__")(demo_array) ;
  // Print the resultant array
  std::cout << "Square of demo array is " << p::extract <char const * > (p::str(result_array)) << std::endl ;
  // Lets try the same with a list
  p::list li ;
  li.append(3);
  li.append(7);
  // Print out the demo list
  std::cout << "Demo list is " << p::extract <char const * > (p::str(li)) << std::endl ;
  // Call the ufunc for the list
  result_array = inst.attr("__call__")(li) ;
  // And print the list out
  std::cout << "Square of demo list is " << p::extract <char const * > (p::str(result_array)) << std::endl ;
  // Now lets try Binary ufuncs
  // Expose the struct BinarySquare to Python as a class, and let ud be the class object
  ud = p::class_<BinarySquare, boost::shared_ptr<BinarySquare> >("BinarySquare")
    .def("__call__", np::binary_ufunc<BinarySquare>::make());
  // Again initialise inst as an instance of the class ud
  inst = ud();
  // Print the two input listsPrint the two input lists
  std::cout << "The two input list for binary ufunc are " << std::endl << p::extract <char const * > (p::str(demo_array)) << std::endl << p::extract <char const * > (p::str(demo_array)) << std::endl ;
  // Call the binary ufunc taking demo_array as both inputs
  result_array = inst.attr("__call__")(demo_array,demo_array) ;
  std::cout << "Square of list with binary ufunc is " << p::extract <char const * > (p::str(result_array)) << std::endl ;
}


#define BOOST_TEST_MODULE SolutionTest

#include "solution.hpp"

#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>

struct less_than_key {
    inline bool operator() (const vector<string>& vec1, const vector<string>& vec2) {
        for (int i = 0; i < vec1.size(); i++) {
            int rst = vec1[i].compare(vec2[i]);
            if (rst < 0) return true;
            else if (rst > 0) return false;
            if (i == vec2.size()-1) return false;
        }
        if (vec1.size() < vec2.size()) return true;
        else return false;
    }
};

BOOST_AUTO_TEST_SUITE(SolutionSuite)

BOOST_AUTO_TEST_CASE(PlainTest1)
{
    vector<string> strs{
        "eat", "tea", "tan", "ate", "nat", "bat"
    };
    vector<vector<string>> result = Solution().groupAnagrams(strs);

    vector<vector<string>> expected{
        {"ate","eat","tea"},
        {"nat","tan"},
        {"bat"}
    };

    vector<vector<string>> new_result, new_expected;
    set<string> heap;
    for (auto s:result) {
        vector<string> tmp;
        for (int i = 0; i < s.size(); ++i) {
            heap.emplace(s[i]);
        }
        for (int i = 0; i < s.size(); ++i) {
            auto itr = heap.begin();
            tmp.emplace_back(*itr);
            heap.erase(itr);
        }
        new_result.emplace_back(tmp);
    }
    for (auto s:expected) {
        vector<string> tmp;
        for (int i = 0; i < s.size(); ++i) {
            heap.emplace(s[i]);
        }
        for (int i = 0; i < s.size(); ++i) {
            auto itr = heap.begin();
            tmp.emplace_back(*itr);
            heap.erase(itr);
        }
        new_expected.emplace_back(tmp);
    }
    BOOST_CHECK_EQUAL(new_result.size(), new_expected.size());
    sort(new_result.begin(), new_result.end(), less_than_key());
    sort(new_expected.begin(), new_expected.end(), less_than_key());
    for (int i = 0; i < new_result.size(); i++)
        BOOST_CHECK_EQUAL_COLLECTIONS(new_result[i].begin(), new_result[i].end(), new_expected[i].begin(), new_expected[i].end());
}

BOOST_AUTO_TEST_SUITE_END()


/**
 * Copyright (c) 2017 Melown Technologies SE
 *
 * 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 "utility/openmp.hpp"

#include "geometry/meshop.hpp"
#include "geometry/mesh-voxelizer.hpp"
#include "math/geometry_core.hpp"
#include "imgproc/scanconversion.hpp"

//#include <opencv2/opencv.hpp>

#include <boost/lexical_cast.hpp>
#include <boost/numeric/ublas/vector.hpp>

#include <algorithm>
//#define RASTERIZE_MESH_DEBUG

namespace geometry{

MeshVoxelizer::MeshVoxelizer(const Parameters &params){
    params_ = params;
}

void MeshVoxelizer::add( geometry::Mesh & mesh ){
    meshes.push_back(&mesh);
}

void MeshVoxelizer::voxelize(){
    if(meshes.size()<=0){
        LOG( warn2 )<<"Zero meshes to voxelize. Skipping voxelization.";
        return;
    }

    //add floor to mimic closed mesh
    std::vector<geometry::Mesh> seals;
    if(params_.addSeal){
        for(auto & mesh: meshes){
            seals.push_back(sealOfMesh(*mesh));
        }
    }

    //compute united extents
    math::Extents3 extents(math::InvalidExtents{});
    for(auto & mesh: meshes){
        math::Extents3 meshExtents = math::computeExtents(mesh->vertices);
        extents = math::unite(extents, meshExtents);
    }
    for(auto & mesh: seals){
        math::Extents3 meshExtents = math::computeExtents(mesh.vertices);
        extents = math::unite(extents, meshExtents);
    }

    LOG( info2 )<<"Meshes extents: "<<extents;

    // volume size

    {
        math::Point3 extentsSize = extents.ur-extents.ll;
        math::Point3i volumeGridRes(
                    std::ceil(extentsSize(0)/params_.voxelSize)
                    ,std::ceil(extentsSize(1)/params_.voxelSize)
                    ,std::ceil(extentsSize(2)/params_.voxelSize));
        math::Point3 newExtentsSize = volumeGridRes*params_.voxelSize;
        math::Point3 extentsCenter = math::center(extents);

        extents = math::Extents3( extentsCenter-newExtentsSize/2
                                 , extentsCenter+newExtentsSize/2);

        if ( valid(params_.overrideExtents) ) {
            LOG(info2) << "Using supplied extents for voxelization.";

            extents = params_.overrideExtents;

            for (uint i(0); i < 3; ++i) {
                volumeGridRes(i)
                    = std::ceil(size(extents)(i)/params_.voxelSize);
            }
        }

        LOG(info2) << "Volumetric grid resolution: " << volumeGridRes;
        LOG(info2) << "Volume extents: " << extents;
    }

    //generate directions for 3 main axes + axes of icosahedron faces
    std::vector<math::Point3> directions;
    directions.push_back(-math::Point3(1,0,0));
    directions.push_back(-math::Point3(0,1,0));
    directions.push_back(-math::Point3(0,0,1));

    directions.push_back(-math::Point3(1,1,1));
    directions.push_back(-math::Point3(1,-1, 1));
    directions.push_back(-math::Point3(-1,1,1));
    directions.push_back(-math::Point3(-1,-1,1));
    directions.push_back(-math::Point3(1,0,1));
    directions.push_back(-math::Point3(-1,0,1));
    directions.push_back(-math::Point3(0,1,1));
    directions.push_back(-math::Point3(0,-1,1));
    directions.push_back(-math::Point3(1,1,0));
    directions.push_back(-math::Point3(1,-1,0));

    //generate projection matrices and layered zbuffers

    Projections projections;
    ProjectionResults results;
    for(const auto & dir: directions){
        Projection projection = orthoProj( dir, extents, params_.voxelSize);
        projections.push_back(projection);
    }

    LOG( info2 )<<"Rasterizing meshes";
    //rasterize mesh using generated projection matrices
    for(uint p=0; p<projections.size(); ++p){
        LayeredZBuffer buffer(projections[p].viewportSize);
        for(auto & mesh: meshes){
            rasterizeMesh(*mesh, projections[p].transformation, buffer);
        }
        for(auto & mesh: seals){
            rasterizeMesh(mesh, projections[p].transformation, buffer);
        }
        buffer.sortCells();
        results.push_back(ProjectionResult( projections[p].transformation
                                      , CompressedLayeredZBuffer(buffer)));
    }

#ifdef RASTERIZE_MESH_DEBUG
    uint i=0;
    for( auto &proj : results){
        fs::path path = fs::path(std::string("depth")
                                 +boost::lexical_cast<std::string>(i++)
                                 +std::string(".png"));
        visualizeDepthMap(proj, extents ,path);
    }
#endif

    //create new volume
    /**************
     * Ugly hack (shaveVolume)
     *
     * Voxelization has problems with edges - create volume 2*cells
     * smaller from both x and y directions
     *
     * Voxelization itself should be fixed instead
     **************************/
    {
        math::Size3i volSize( std::ceil( size(extents)(0)/params_.voxelSize )
                            , std::ceil( size(extents)(1)/params_.voxelSize )
                            , std::ceil( size(extents)(2)/params_.voxelSize ));

        math::Point3 ll( extents.ll(0)
                       , extents.ll(1)
                       , extents.ll(2));

        if (params_.shaveVolume) {
            for (uint i(0); i < 2; ++i) {
                // strip 2 from each side
                volSize(i) -= 4;
                // shift extents's ll by 2 voxel size because that is what we stripped
                ll(i) += 2*params_.voxelSize;
            }
        }

        // prepare volume for filtering and downsampling: reserve space so all
        // dimensions can be inflated to closest larger odd value
        // add few layers of cells on the ceiling of the volume as a margin
        // for subsampling.
        math::Size3i capacity( volSize(0) + !(volSize(0) % 2)
                             , volSize(1) + !(volSize(1) % 2)
                             , volSize(2) + !(volSize(2) % 2) + 4);

        LOG(info2) << "Creating volume of size " << volSize
                   << " and capacity " << capacity;

        volume_ = std::unique_ptr<Volume>( new Volume( ll
                                                     , params_.voxelSize
                                                     , volSize
                                                     , VoxelizerUnit::empty()
                                                     , capacity));
    }

    math::Size3i vSize = volume_->cSize();
    long long volMem = (long long)vSize.width * vSize.height
                    * vSize.depth * sizeof(unsigned short);
    LOG( info2 )<<"Memory consumption of volume: "
        << volMem/1024.0/1024.0/1024.0 << " GB.";

    long long mem = 0;
    for(auto res : results){
        mem += res.buffer.mem();
    }

    LOG( info2 )<<"Memory consumption of zbuffers: "
        << mem/1024.0/1024.0/1024.0 << " GB.";

    uint progress = 0;
    LOG( info2 )<<"Voxelization progress: "<<progress;
#ifdef _OPENMP
    #pragma omp parallel for schedule( dynamic, 10 )
#endif
    for(int x=0; x< vSize.width; ++x){
        for(int y=0; y< vSize.height; ++y){
            for(int z=0; z< vSize.depth; ++z){
                //voxel center position
                auto voxCenter
                        = volume_->grid2geo(math::Point3(x,y,z));
                if(isInside({voxCenter.x, voxCenter.y, voxCenter.z}, results)){
                        volume_->set( x, y, z,VoxelizerUnit::full());
                }

            }
        }
        //voxelization progress
        uint newProgress = std::ceil((float)x/vSize.width * 100);
        if(newProgress>progress){
            progress=newProgress;
            LOG( info2 )<<"Voxelization progress: "<<progress;
        }
    }

    if(params_.addSeal){
        LOG( info2 )<<"Filling volume from seal";
        fillVolumeFromSeal();
    }
}

std::shared_ptr<MeshVoxelizer::Volume> MeshVoxelizer::volume(){
    return volume_;
}

void MeshVoxelizer::reset(){
    std::vector<geometry::Mesh*>().swap(meshes);
}

MeshVoxelizer::Projection MeshVoxelizer::orthoProj(const math::Point3 &direction
                            , const math::Extents3 &extents
                            , const float &voxelSize
                            ){
    LOG( debug )<<"Projection direction: "<<direction;
    LOG( debug )<<"Mesh extents: "<<extents;

    math::Point3 normalizedDir = math::normalize(direction);

    math::Matrix4 projMat = ublas::identity_matrix<double>(4);
    //translate so the center of the extents is in the middle
    math::Point3 extentsCenter = math::center(extents);
    projMat(0,3) = -extentsCenter[0];
    projMat(1,3) = -extentsCenter[1];
    projMat(2,3) = -extentsCenter[2];

    //LOG( debug )<<"Projection matrix - after centering: "<<projMat;

    //rotate scene in specified direction
    math::Matrix4 rotation = ublas::identity_matrix<double>(4);

    math::Point3 up(0,0,-1);
    if(std::abs(ublas::inner_prod(normalizedDir,math::Point3(0,0,1)))==1){
        up = math::Point3(0,1,0);
    }
    math::Point3 right = math::normalize(math::crossProduct(up,direction));
    up = math::normalize(math::crossProduct(normalizedDir,right));

    auto c1 = ublas::row(rotation,0);
    auto c2 = ublas::row(rotation,1);
    auto c3 = ublas::row(rotation,2);

    ublas::subrange(c1,0,3) = right;
    ublas::subrange(c2,0,3) = up;
    ublas::subrange(c3,0,3) = normalizedDir;

    projMat = prod(rotation,projMat);

    //find out extents images
    math::Extents2 viewExtents(math::InvalidExtents{});
    math::Points3 extPoints;
    extPoints.push_back(math::Point3(extents.ll[0],extents.ll[1],extents.ll[2]));
    extPoints.push_back(math::Point3(extents.ll[0],extents.ll[1],extents.ur[2]));
    extPoints.push_back(math::Point3(extents.ll[0],extents.ur[1],extents.ll[2]));
    extPoints.push_back(math::Point3(extents.ll[0],extents.ur[1],extents.ur[2]));
    extPoints.push_back(math::Point3(extents.ur[0],extents.ll[1],extents.ll[2]));
    extPoints.push_back(math::Point3(extents.ur[0],extents.ll[1],extents.ur[2]));
    extPoints.push_back(math::Point3(extents.ur[0],extents.ur[1],extents.ll[2]));
    extPoints.push_back(math::Point3(extents.ur[0],extents.ur[1],extents.ur[2]));

    for(const auto &point : extPoints){
        math::Point3 pointImg = transform(projMat,point);
        viewExtents.ll[0] = std::min(pointImg[0],viewExtents.ll[0]);
        viewExtents.ll[1] = std::min(pointImg[1],viewExtents.ll[1]);
        viewExtents.ur[0] = std::max(pointImg[0],viewExtents.ur[0]);
        viewExtents.ur[1] = std::max(pointImg[1],viewExtents.ur[1]);
    }

    //scale scene so the extents fit to space < -1,1 > CC/NDC
    math::Matrix4 scaleMat = ublas::identity_matrix<double>(4);
    scaleMat(0,0) = 2./size(viewExtents).width;
    scaleMat(1,1) = 2./size(viewExtents).height;

    projMat = prod(scaleMat, projMat);

    //scale and transform scene to VC
    math::Matrix4 transformMat = ublas::identity_matrix<double>(4);
    transformMat(0,3)=1;
    transformMat(1,3)=1;

    //calculate viewport size
    LOG( debug ) << "View extents: "<< viewExtents;

    math::Size2 viewport( std::ceil(size(viewExtents).width/voxelSize)
                        , std::ceil(size(viewExtents).height/voxelSize));

    LOG( debug ) << "Viewport:" << viewport;

    scaleMat = ublas::identity_matrix<double>(4);
    scaleMat(0,0) = viewport.width/2;
    scaleMat(1,1) = viewport.height/2;

    projMat = prod(transformMat, projMat);
    projMat = prod(scaleMat, projMat);

    LOG( debug )<<"Projection matrix - final from WC to CC: "<<projMat;

    for(const auto &point : extPoints){
        math::Point3 pointImg = transform(projMat,point);
        LOG( debug ) << point <<" - "<< pointImg;
    }

    return Projection(projMat, viewport);
}

geometry::Mesh MeshVoxelizer::sealOfMesh(geometry::Mesh & mesh){

    math::Extents3 extents = math::computeExtents(mesh.vertices);

    float offset = params_.voxelSize * params_.sealFactor;

    geometry::Mesh seal;
    math::Point3 extSize = extents.ur-extents.ll;

    double cellWidth = offset;

    int cols=std::ceil(extSize(0)/cellWidth);
    int rows=std::ceil(extSize(1)/cellWidth);

    std::vector<std::vector<double>> minMap
        = std::vector<std::vector<double>>(cols);
    for(int x=0; x<cols; ++x){
        minMap[x] = std::vector<double>(rows,INFINITY);
    }

    for(const auto& vertex: mesh.vertices){
        int x = std::min((int)std::floor((vertex(0)-extents.ll(0))/cellWidth),cols-1);
        int y = std::min((int)std::floor((vertex(1)-extents.ll(1))/cellWidth),rows-1);
        minMap[x][y]=std::min(minMap[x][y],vertex(2));
    }

    for(int x=0; x< cols; ++x){
        for(int y=0; y< rows; ++y){
            if(x==0 || y==0 || x==cols-1 || y==rows-1){
                //fill unsetted values
                int searchOffset=1;
                while(!std::isfinite(minMap[x][y])){
                    for(int xoff=-searchOffset; xoff<searchOffset; ++xoff ){
                        for(int yoff=-searchOffset; yoff<searchOffset; ++yoff ){
                            if( x+xoff>0 && x+xoff<(int)cols
                               && y+yoff>0 && y+yoff<(int)rows){
                                minMap[x][y]=std::min( minMap[x][y]
                                                     , minMap[x+xoff][y+yoff]);
                            }
                        }
                    }
                    searchOffset++;
                    if(searchOffset>params_.sealFactor*2){
                        minMap[x][y] = extents.ll(2);
                    }
                }
            }
        }
    }

    for(int x=1; x< cols-1; ++x){
        for(int y=1; y< rows-1; ++y){
            minMap[x][y] = extents.ll(2);
        }
    }

    for(int y=0; y< rows; ++y){
        for(int x=0; x< cols; ++x){
            seal.vertices.push_back(
                math::Point3( x*cellWidth+extents.ll(0)
                            , y*cellWidth+extents.ll(1)
                            , minMap[x][y]-offset ));
        }
    }

    for(int x=0; x< cols-1; ++x){
        for(int y=0; y< rows-1; ++y){
            seal.addFace(x+(y*cols), x+1+((y+1)*cols), x+1+(y*cols));
            seal.addFace(x+(y*cols), x+((y+1)*cols), x+1+((y+1)*cols));
        }
    }

    return seal;
}

void MeshVoxelizer::fillVolumeFromSeal(){
    math::Size3i vSize = volume_->cSize();
    for(int x = 0;x<vSize.width; ++x){
        for(int y = 0;y<vSize.height; ++y){
            //find first full voxel from the direction of seal
            int full = -1;
            for(int z = 0;z<vSize.depth; ++z){
                if(volume_->get(x,y,z)!=VoxelizerUnit::empty()){
                        full = z;
                        break;
                }
            }
            //fill from bottom up
            for(int z = 0;z<full; ++z){
                volume_->set(x,y,z,VoxelizerUnit::full());
            }
        }
    }
}

void MeshVoxelizer::rasterizeMesh( const Mesh &mesh
                                 , const math::Matrix4 &projMat
                                 , LayeredZBuffer & lZBuffer){
    std::vector<imgproc::Scanline> scanlines;

    LOG(info2) << "rasterizing " << mesh.faces.size() << " triangles";

    // draw all faces into the zBuffer
    for (const auto &face : mesh.faces)
    {
        cv::Point3f tri[3];

        int i(0);
        for (auto it(mesh.begin(face)); it != mesh.end(face); ++it, ++i) {
            math::Point3d pt(transform(projMat, *it));
            tri[i] = {float(pt(0)), float(pt(1)), float(pt(2))};
        }

        scanlines.clear();
        imgproc::scanConvertTriangle(tri, 0, lZBuffer.size.height, scanlines);

        for (const auto& sl : scanlines)
        {
            imgproc::processScanline(sl, 0, lZBuffer.size.width,
                [&](int x, int y, float z) {
                    lZBuffer.data[x][y].push_back(z);
                } );
        }
    }

}

bool MeshVoxelizer::isInside( const math::Point3 & position
                      ,  ProjectionResults & projectionResults){
    uint inside=0;
    uint outside=0;

    uint projId = 0;
    for(auto & proj : projectionResults){
        math::Point3 projPos = math::transform(proj.transformation, position);
        projId++;
        if(params_.method==Method::PARITY_COUNT){
            uint parity=0;
            if(projPos[0]<proj.buffer.size.width
                    && projPos[1]<proj.buffer.size.height
                    && projPos[0]>0 && projPos[1]>0){
                //std::cout<<proj.buffer.data[projPos[0]][projPos[1]].size()<<std::endl;
                for(auto dit = proj.buffer.begin(projPos[0],projPos[1]);
                        dit != proj.buffer.end(projPos[0],projPos[1]); ++dit){

                    if(*dit<projPos[2]){
                        parity++;
                    }
                    else{
                        break;
                    }
                }
                if(parity%2==1){
                    inside++;
                    continue;
                }
                outside++;
            }
        }

        if(params_.method==Method::RAY_STABING){
            if( projPos[2]<*proj.buffer.begin(projPos[0],projPos[1])
               && projPos[2]>*proj.buffer.end(projPos[0],projPos[1])){
                inside++;
                continue;
            }
            outside++;
        }
    }

    if(params_.method==Method::PARITY_COUNT){
        if(inside>outside)
            return true;
    }
    if(params_.method==Method::RAY_STABING){
        if(outside==0)
            return true;
    }
    return false;
}

void MeshVoxelizer::visualizeDepthMap( const ProjectionResult &proj
                                      , const math::Extents3 & extents
                                      , const fs::path & path){
    cv::Mat depthMapImg( proj.buffer.size.height, proj.buffer.size.width
                     , CV_8UC3, cv::Scalar(0,0,0));

    float min = FLT_MAX;
    float max = -FLT_MAX;

    for(int x = 0; x < proj.buffer.size.width; ++x){
        for(int y = 0; y < proj.buffer.size.height; ++y){
            if(proj.buffer.begin(x,y) == proj.buffer.end(x,y)){
                continue;
            }
            min = std::min(*proj.buffer.begin(x,y),min);
            max = std::max(*std::prev(proj.buffer.end(x,y)),max);
        }
    }

    float range = max-min;

    LOG( debug )<<"Minimal/maximal depth: "<< min <<" / "<< max
                << " Range: "<<range;

    for(int x = 0; x < proj.buffer.size.width; ++x){
        for(int y = 0; y < proj.buffer.size.height; ++y){
            if(proj.buffer.begin(x,y) == proj.buffer.end(x,y)){
                depthMapImg.at<cv::Vec3b>(y,x)=
                        cv::Vec3b(0,0,255);
                continue;
            }
            float val  = (*proj.buffer.begin(x,y) - min) / range;
            depthMapImg.at<cv::Vec3b>(y,x)=
                    cv::Vec3b(255-val*255,255-val*255,255-val*255);
        }
    }

    math::Points3 extPoints;
    extPoints.push_back(math::Point3(extents.ll[0],extents.ll[1],extents.ll[2]));
    extPoints.push_back(math::Point3(extents.ll[0],extents.ll[1],extents.ur[2]));
    extPoints.push_back(math::Point3(extents.ll[0],extents.ur[1],extents.ll[2]));
    extPoints.push_back(math::Point3(extents.ll[0],extents.ur[1],extents.ur[2]));
    extPoints.push_back(math::Point3(extents.ur[0],extents.ll[1],extents.ll[2]));
    extPoints.push_back(math::Point3(extents.ur[0],extents.ll[1],extents.ur[2]));
    extPoints.push_back(math::Point3(extents.ur[0],extents.ur[1],extents.ll[2]));
    extPoints.push_back(math::Point3(extents.ur[0],extents.ur[1],extents.ur[2]));

    for(auto &point : extPoints){
        point = transform(proj.transformation,point);
    }

    cv::line(depthMapImg, cv::Point2f(extPoints[0][0],extPoints[0][1])
            , cv::Point2f(extPoints[1][0],extPoints[1][1]),cv::Scalar(255,0,0),2);
    cv::line(depthMapImg, cv::Point2f(extPoints[2][0],extPoints[2][1])
            , cv::Point2f(extPoints[3][0],extPoints[3][1]),cv::Scalar(255,0,0),2);

    cv::line(depthMapImg, cv::Point2f(extPoints[0][0],extPoints[0][1])
            , cv::Point2f(extPoints[2][0],extPoints[2][1]),cv::Scalar(255,0,0),2);
    cv::line(depthMapImg, cv::Point2f(extPoints[1][0],extPoints[1][1])
            , cv::Point2f(extPoints[3][0],extPoints[3][1]),cv::Scalar(255,0,0),2);

    cv::line(depthMapImg, cv::Point2f(extPoints[4][0],extPoints[4][1])
            , cv::Point2f(extPoints[5][0],extPoints[5][1]),cv::Scalar(255,0,0),2);
    cv::line(depthMapImg, cv::Point2f(extPoints[6][0],extPoints[6][1])
            , cv::Point2f(extPoints[7][0],extPoints[7][1]),cv::Scalar(255,0,0),2);

    cv::line(depthMapImg, cv::Point2f(extPoints[4][0],extPoints[4][1])
            , cv::Point2f(extPoints[6][0],extPoints[6][1]),cv::Scalar(255,0,0),2);
    cv::line(depthMapImg, cv::Point2f(extPoints[5][0],extPoints[5][1])
            , cv::Point2f(extPoints[7][0],extPoints[7][1]),cv::Scalar(255,0,0),2);

    cv::line(depthMapImg, cv::Point2f(extPoints[1][0],extPoints[1][1])
            , cv::Point2f(extPoints[5][0],extPoints[5][1]),cv::Scalar(255,0,0),2);
    cv::line(depthMapImg, cv::Point2f(extPoints[0][0],extPoints[0][1])
            , cv::Point2f(extPoints[4][0],extPoints[4][1]),cv::Scalar(255,0,0),2);

    cv::line(depthMapImg, cv::Point2f(extPoints[3][0],extPoints[3][1])
            , cv::Point2f(extPoints[7][0],extPoints[7][1]),cv::Scalar(255,0,0),2);
    cv::line(depthMapImg, cv::Point2f(extPoints[2][0],extPoints[2][1])
            , cv::Point2f(extPoints[6][0],extPoints[6][1]),cv::Scalar(255,0,0),2);

    cv::imwrite( path.string().c_str(), depthMapImg );
}

} //namespace geometry


/*
 * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
 *
 * This software is open source.
 *
 * 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.
 *
 * Neither the name of the NLNET LABS nor the names of its contributors may
 * be used to endorse or promote products derived from this software without
 * specific prior written permission.

 * 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 REGENTS 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 "TimeGM.h"
#include <boost/algorithm/string.hpp>
#include <cmath>
#include <ctime>

/* Number of days per month (except for February in leap years). */
// static const int monoff[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};

int TimeGM::is_leap_year(int year) {
  return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}

int TimeGM::leap_days(int y1, int y2) {
  --y1;
  --y2;
  return (y2 / 4 - y1 / 4) - (y2 / 100 - y1 / 100) + (y2 / 400 - y1 / 400);
}

time_t TimeGM::parse_fractional_seconds(uint64_t sfrac,
                                        const int ntotal,
                                        const SQLTypeInfo& ti) {
  int dimen = ti.get_dimension();
  int nfrac = log10(sfrac) + 1;
  if (ntotal - nfrac > dimen) {
    return 0;
  }
  if (ntotal >= 0 && ntotal < dimen) {
    sfrac *= pow(10, dimen - ntotal);
  } else if (ntotal > dimen) {
    sfrac /= pow(10, ntotal - dimen);
  }
  return sfrac;
}

time_t TimeGM::parse_meridians(const time_t& timeval,
                               const char* p,
                               const uint32_t& hour,
                               const SQLTypeInfo& ti) {
  char meridies[20];
  if (sscanf(p, "%*d %s", meridies) != 1) {
    if (sscanf(p, "%s", meridies) != 1) {
      return timeval;
    };
  }
  if (boost::iequals(std::string(meridies), "pm") ||
      boost::iequals(std::string(meridies), "p.m.") ||
      boost::iequals(std::string(meridies), "p.m")) {
    return hour == 12 ? timeval
                      : timeval + kSecsPerHalfDay *
                                      static_cast<int64_t>(pow(10, ti.get_dimension()));
  } else if (boost::iequals(std::string(meridies), "am") ||
             boost::iequals(std::string(meridies), "a.m.") ||
             boost::iequals(std::string(meridies), "a.m")) {
    return hour == 12 ? timeval - kSecsPerHalfDay *
                                      static_cast<int64_t>(pow(10, ti.get_dimension()))
                      : timeval;
  } else {
    return timeval;
  }
}

/*
 * Code adapted from Python 2.4.1 sources (Lib/calendar.py).
 */
time_t TimeGM::my_timegm_days(const struct tm* tm) {
  int32_t year;
  time_t days;
  year = 1900 + tm->tm_year;
  days = 365 * (year - 1970) + leap_days(1970, year);
  days += monoff[tm->tm_mon];
  if (tm->tm_mon > 1 && is_leap_year(year)) {
    ++days;
  }
  days += tm->tm_mday - 1;
  return days;
}

time_t TimeGM::my_timegm(const struct tm* tm) {
  time_t days;
  time_t hours;
  time_t minutes;
  time_t seconds;

  days = my_timegm_days(tm);
  hours = days * 24 + tm->tm_hour;
  minutes = hours * 60 + tm->tm_min;
  seconds = minutes * 60 + tm->tm_sec;

  seconds -= tm->tm_gmtoff;

  return seconds;
}

time_t TimeGM::my_timegm(const struct tm* tm, const time_t& fsc, const SQLTypeInfo& ti) {
  time_t sec;

  sec = my_timegm(tm) * static_cast<int64_t>(pow(10, ti.get_dimension()));
  sec += fsc;

  return sec;
}


// Copyright (C) 2017 Vicente J. Botet Escriba
//
//  Distributed under the Boost Software License, Version 1.0. (See accompanying
//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

// <experimental/strong_type.hpp>

/// This example tries to see how close we can define money using strong_type
/// money should be close to strong_counter as it is close to duration.
/// However, there are some issues.
///     There is not a common currency (until we decide to have it)
///         Adding money with different currencies needs the currency result, and so needs to be lazy
///     Having a common currency means to do two operations to convert from two currencies.
///         The compiler could optimize it if the currency is know at compile time, but in general this is not the case.
///     Should we have implicit or explicit conversions between currencies?

#include <iostream>
#include <functional>
#include <type_traits>
#include "currency.hpp"
#include <experimental/strong_counter.hpp>
#include <experimental/fundamental/v2/config.hpp>

namespace stdex = std::experimental;

template <class M>
using currency_t = typename M::currency_type;

template <class Currency, class Rep = double>
struct money;

namespace std
{
template <class Currency, class UT1, class UT2>
struct common_type<money<Currency, UT1>, money<Currency, UT2>>
{
 using type = money<Currency,common_type_t< UT1, UT2>>;
};
namespace experimental
{
inline  namespace fundamental_v3
{
namespace mixin
{
template <class Currency, class UT1, class UT2>
struct is_compatible_with<money<Currency, UT1>, money<Currency, UT2>> : true_type {};
}
}
namespace meta
{
template <class Currency, class UT1, class UT2>
struct rebind<money<Currency, UT1>, UT2> : id<money<Currency, UT2>> {};
}
}
}

// fixme: Should be Rep, Currency?
// fixme: Should we add a Period parameter to count in Million Dollars, or in cents?
//      Don't having a period, forces almost to work with double.
//      Having a period allows to work with integers most of the time

template <class Currency, class Rep>
struct money
    : stdex::strong_type<money<Currency, Rep>, Rep>, stdex::mixins<money<Currency, Rep>
        , stdex::meta_mixin::additive_with_if<>
        , stdex::meta_mixin::comparable<>
        , stdex::meta_mixin::integer_multiplicative_with<Rep>
        , stdex::meta_mixin::modable<>
        , stdex::meta_mixin::hashable<>
        //, mixin::streamable<>
        //, mixin::ordinal<>
    >

{
    using base_type = stdex::strong_type<money<Currency, Rep>, Rep>;
    using base_type::base_type;

    using currency_type = Currency;

    template <class UT2>
    using rebind = money<Currency, UT2>;

    money() = default;

    // todo: Add template <class Rep2> as duration
    explicit constexpr money(Rep v) : base_type(v) {}

    constexpr money(money const& m) = default;

    // fixme: Should we restrict it as do duration and require a money_cast?
    // or just define an explicit constructor as does pair.
    template <class C, class R>
    constexpr money(money<C, R> m)
    : base_type (currency::convert<C, Currency>(m.count()))
    {
    }
    JASEL_CXX14_CONSTEXPR money& operator=(money const& m) = default;
#if 0
    template <class C, class R>
    constexpr money& operator=(money<C, R> m)
    {
        rep = currency::convert<C, Currency>(m.count());
        return *this;
    }
#endif

    constexpr Rep count() const noexcept
        { return this->underlying(); }

    //constexpr Currency currency() const noexcept { return Currency{}; }

    // todo: add zero, min, max

};

namespace money_expr
{
/// helper function that converts a money to a specific currency using the conversion_factor customization point
template <class Currency, class C, class R>
double to_currency(money<C, R> const& m)
{
    return currency::convert<C, Currency>(m.count());
}

template <class M1, class M2, class Op>
struct binary
{
    M1 m1;
    M2 m2;

    // fixme:: shouldn't this require that the representation is convertible from the common_type?
    template <class C, class R>
    JASEL_CXX14_CONSTEXPR operator money<C,R>() {
        return money<C,R>( Op{}(to_currency<C>(m1), to_currency<C>(m2)));
    }
};

template <class M1, class M2>
struct add
{
    M1 m1;
    M2 m2;

    // fixme:: shouldn't this requires that the representation is convertible from the common_type?
    template <class C, class R>
    JASEL_CXX14_CONSTEXPR operator money<C,R>()
    {
        return money<C,R>( to_currency<C>(m1) + to_currency<C>(m2));
    }
};
template <class M1, class M2>
struct substract
{
    M1 m1;
    M2 m2;

    template <class C, class R>
    JASEL_CXX14_CONSTEXPR operator money<C,R>()
    {
        return money<C,R>( to_currency<C>(m1) - to_currency<C>(m2));
    }
};

template <class C, class M1, class M2>
struct divide
{
    M1 m1;
    M2 m2;

    template <class R>
    JASEL_CXX14_CONSTEXPR operator R()
    {
        return money<C,R>( to_currency<C>(m1) / to_currency<C>(m2));
    }
};

template <class M1, class M2>
struct modulo
{
    M1 m1;
    M2 m2;

    template <class C, class R>
    JASEL_CXX14_CONSTEXPR operator money<C,R>()
    {
        return money<C,R>( to_currency<C>(m1) % to_currency<C>(m2));
    }
};

}

template <class C1, class R1, class C2, class R2, typename = std::enable_if_t< ! std::is_same<C1, C2>::value>>
constexpr auto operator+(money<C1,R1> m1, money<C2,R2> m2) noexcept
-> money_expr::add<money<C1,R1>, money<C2,R2>>
{
    return money_expr::add<money<C1,R1>, money<C2,R2>>{m1, m2} ;
    //return money_expr::binary<money<C1,R1>, money<C2,R2>, std::plus>{m1, m2} ;
}

template <class C1, class R1, class C2, class R2, typename = std::enable_if_t< ! std::is_same<C1, C2>::value>>
constexpr auto operator-(money<C1,R1> m1, money<C2,R2> m2) noexcept
-> money_expr::substract<money<C1,R1>, money<C2,R2>>
{
    return money_expr::substract<money<C1,R1>, money<C2,R2>>{m1, m2} ;
}

template <class OSTREAM, class C, class R>
OSTREAM& operator<<(OSTREAM& os, money<C, R> m)
{
    os<< m.count() << C{}.symbol();
    return os;
}

using dollars = money<currency::dollar>;
using euros = money<currency::euro>;

#include <boost/detail/lightweight_test.hpp>
#include <iostream>

int main()
{
    {
        dollars d(5);
        BOOST_TEST(long(d.count()) == 5);
    }
    {
        dollars d;
        d = dollars(5);
        BOOST_TEST(long(d.count()) == 5);
    }
    {
        dollars d;
        d = dollars(5);
        BOOST_TEST(d == dollars(5));
    }
    {
        dollars d1(5), d2(10);
        BOOST_TEST(d1+d2 == dollars(15));
    }
    {
        dollars d(5);
        BOOST_TEST(+d  == d);
    }
    {
        dollars d1(5), d2(10);
        BOOST_TEST(d1-d2 == dollars(-5));
    }
    {
        dollars d(5);
        BOOST_TEST(-d  == dollars(-5));
    }
    {
        dollars d(5);
        BOOST_TEST(d * 3  == dollars(15));
    }
    {
        dollars d(5);
        BOOST_TEST(3 * d  == dollars(15));
    }
    {
        dollars d(15);
        BOOST_TEST(d / 3  == dollars(5));
    }
    {
        using idollars = money<currency::dollar, int>;
        idollars d(16);
        BOOST_TEST(d % 3  == idollars(1));
    }
//    {
//        using CR = std::common_type_t<double,int>;
//        double d(16);
//        BOOST_TEST(CR(d) % 3  == 1);
//    }
    {
        dollars d(10);
        euros e(5);
        euros x = d + e;
        std::cout <<x << std::endl;
        BOOST_TEST(x == euros(25));
    }
    {
        euros e1(5);
        dollars d(10);
        euros e2(5);
        euros x = e1 + d + e2;
        std::cout <<x << std::endl;
        BOOST_TEST(x == euros(30));
    }
    {
        dollars d(10);
        euros e(5);
        e =  d;
        BOOST_TEST(e == euros(20));
    }
    {
        dollars d(10);
        euros e(5);
        euros x = d - e;
        std::cout <<x << std::endl;
        BOOST_TEST(x == euros(15));
    }
    {
        dollars d(10);
        euros e(5);
        euros x = d - e;
        std::cout <<x << std::endl;
        BOOST_TEST(x == euros(15));
    }
    {
        euros e1(5);
        dollars d(10);
        euros e2(5);
        euros x = e1 - d - e2;
        std::cout <<x << std::endl;
        BOOST_TEST(x == euros(-20));
    }
    return ::boost::report_errors();
}