/******************************************************************************

  Triangular mesh class.

  Copyright (c) 2010 - 2013
  Alexander Rukletsov <rukletsov@gmail.com>
  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions
  are met:
  1.  Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
  2.  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 AUTHOR 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 AUTHOR 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.

*******************************************************************************/

#ifndef MESH_HPP_5839D2AB_1DFF_4DCE_A5A2_051A5102190D_
#define MESH_HPP_5839D2AB_1DFF_4DCE_A5A2_051A5102190D_

#include <cstddef>
#include <vector>
#include <set>
#include <string>
#include <iostream>
#include <limits>
#include <stdexcept>
#include <functional>
#include <boost/assert.hpp>
#include <boost/format.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/scoped_ptr.hpp>

#include "bo/core/vector.hpp"
#include "bo/core/triangle.hpp"
#include "bo/core/kdtree.hpp"
#include "bo/distances/distances_3d.hpp"

namespace bo {

// A basic class for a 3D triangular mesh. Consumes more memory than a possible
// minimum (a standard graph storage) but provides faster access to frequently
// used structures and operations. NOT thread-safe in the current implemetation.
template <typename T>
class Mesh
{
public:
    typedef Mesh<T> SelfType;

    // Mesh vertices. Their order shouldn't be changed, since other collections
    // use vertex indices as references.
    typedef Vector<T, 3> Vertex;
    typedef std::vector<Vertex> Vertices;

    // Face is a triangle with vertices representing indices of the mesh vertices.
    typedef Triangle<std::size_t> Face;
    typedef std::vector<Face> Faces;

    // Normal is a 3-vector can be attached to a vertex or to a face. A normal
    // corresponds to a component with the same index in relevant collection.
    typedef Vector<double, 3> Normal;
    typedef std::vector<Normal> Normals;

    // Neighbours for each vertex. Each neighbour contains indices of vertex in
    // vertices collection.
    typedef std::set<std::size_t> AdjacentVerticesPerVertex;
    typedef std::vector<AdjacentVerticesPerVertex> AdjacentVertices;

    // Neighbouring faces for each vertex. Each adjacent face is an index of a face
    // in faces collection.
    typedef std::set<std::size_t> AdjacentFacesPerVertex;
    typedef std::vector<AdjacentFacesPerVertex> AdjacentFaces;

private:
    typedef std::pair<Vertex, std::size_t> TreeNode;
    typedef KDTree<3, TreeNode, boost::function<T (TreeNode, std::size_t)> > Tree;
    typedef boost::scoped_ptr<Tree> TreePtr;

public:
    // Creates an empty mesh ready to store initial_count vertices.
    Mesh(std::size_t initial_count = 0);

    // Custom copy c-tor for correct copying of the k-d tree.
    Mesh(const Mesh& other);

    // Idiomatic swap.
    void swap(Mesh& other);

    // Idiomatic assignment operator for correct assignment.
    Mesh& operator=(Mesh other);

    // Adds a new vertex to the mesh and returns its index.
    std::size_t add_vertex(const Vertex& vertex);

    // Adds a new vertex to the mesh only if there are no vertices at the distance
    // of search_radius. Returns either the index of the new vertex or the index
    // of the closest vertex that already exists in the mesh.
    std::size_t add_vertex_checked(const Vertex& vertex, T search_radius);

    // Adds several vertices to the mesh and returns the index of the first vertex.
    // The indices of the other vertices can be easily calculated by adding 1 successively.
    std::size_t add_vertices(const Vertices& vertices);

    // Adds a new face and returns its index. Updates dependent collections.
    std::size_t add_face(const Face& face);

    // Returns face normal. Throws if face_index is out of range.
    Normal get_face_normal(std::size_t face_index) const;

    // Creates and returns vertex normal, computed according to
    // N.Max, "Weights for Computing Vertex Normals from Facet Normals",
    // Journal of Graphics Tools, Vol. 4, No. 2, 1999.
    // Throws if face index is out of range.
    Normal get_vertex_normal(std::size_t vertex_index) const;

    // Finds closest point on the mesh to a given one. Uses naive implementation,
    // which measures the distance to each face and then takes the minimum.
    Vertex get_closest_point(const Vertex& point) const;

    // Computes the distance between the given point and the mesh. Returns the
    // euclidean norm of the difference between the given point and the closest to it
    // point on the mesh.
    double distance(const Vertex& point) const;

    // Joins the given mesh into the current one. First adds points, then recalculates
    // faces and finally adds faces.
    SelfType& join(const SelfType& other);

    // Joins the given mesh into the current one. Instead of adding all points, tries
    // to use existed vertices that are close to candidates for insertion.
    SelfType& join_checked(const SelfType& other, T search_radius);

    static SelfType from_vertices(const Vertices& vertices);

    // Returns data from connectivity structures. Throws if face index is out of range.
    const AdjacentVerticesPerVertex& get_neighbouring_vertices(
        std::size_t vertex_index) const;
    const AdjacentFacesPerVertex& get_neighbouring_faces_by_vertex(
        std::size_t vertex_index) const;

    // Temporary accessor methods.
    const Vertices& get_all_vertices() const;
    const Faces& get_all_faces() const;
    const Normals& get_all_face_normals() const;

    // The k-d tree management functions.
    void build_tree();
    void destroy_tree();

    // Allow mesh stream operator<< access Mesh members.
    template <typename V>
    friend std::ostream& operator<<(std::ostream& os, const Mesh<V>& obj);

private:
    // Adds connectivity relations. Return false in case of new relation leads to
    // a duplicate.
    bool add_edge_(std::size_t vertex1, std::size_t vertex2);
    bool add_adjacent_face_(std::size_t vertex, std::size_t face);

    // Computes and returns a normal for the given face.
    Normal compute_face_normal_(const Face& face) const;

    // Finds the closest point on the given mesh face to the given point. For
    // more information see
    //    http://www.geometrictools.com/Documentation/DistancePoint3Triangle3.pdf
    Vertex closest_point_on_face(std::size_t face_index, const Vertex& P) const;

    template <typename F>
    SelfType& join(const SelfType& other, F inserter);

    // Range checkers. Throw if an index is out of range.
    void vertex_rangecheck_(std::size_t vertex_index) const;
    void face_rangecheck_(std::size_t face_index) const;

    // Helper function for k-d tree.
    static T point3D_accessor_(TreeNode pt, std::size_t k);

private:
    // Basic mesh data.
    Vertices vertices_;
    Faces faces_;
    Normals face_normals_;
    // Some other properties can be used, e.g. triangle strips (for speeding up
    // rendering), curvature information, BBox, grid, etc (See TriMesh implementation
    // by Szymon Rusinkiewicz as an example.)

    // The k-d tree for mesh vertices. Created on demand, can be used for finding
    // existed vertices when trying inserting a new one.
    TreePtr tree_;

    // Connectivity structures.
    AdjacentVertices neighbours_;
    AdjacentFaces adjacent_faces_;
    // We can also add, e.g. faces adjacent to faces over edges, i.e. each face will
    // have maximum 3 these neighbouring faces.
};

// Prints formatted mesh data to a given stream. See boost.format library for more
// details about formatting.
template <typename T>
std::ostream& operator<<(std::ostream& os, const Mesh<T>& obj)
{
    // TODO: Add syncro primitives to stream operator and, perhaps, verbose levels.

    // Print full vertex info.
    os << boost::format("Mesh object %1$#x, %2% bytes: ") % &obj % sizeof(obj)
       << std::endl << "Vertices: " << obj.vertices_.size() << std::endl;

    typename Mesh<T>::Vertices::const_iterator vertices_end = obj.vertices_.end();
    for (typename Mesh<T>::Vertices::const_iterator it = obj.vertices_.begin();
         it != vertices_end; ++it)
    {
        // Print vertex coordinates.
        std::size_t index = it - obj.vertices_.begin();
        os << boost::format("vertex %1%: ") % index << std::endl << "\t"
           << boost::format("x: %1%, %|18t|y: %2%, %|36t|z: %3%,") % it->x()
           % it->y() % it->z();

        // Print neighbouring vertices.
        os << std::endl << "\t"
           << boost::format("neighbours (%1%): ") % obj.neighbours_[index].size();

        typename Mesh<T>::AdjacentVerticesPerVertex::const_iterator neighbours_end =
            obj.neighbours_[index].end();
        for (typename Mesh<T>::AdjacentVerticesPerVertex::const_iterator neighbour =
            obj.neighbours_[index].begin(); neighbour != neighbours_end; ++neighbour)
        {
            os << boost::format("%1%, %|4t|") % (*neighbour);
        }

        // Print adjacent faces.
        os << std::endl << "\t" << boost::format("adjacent faces (%1%): ")
           % obj.adjacent_faces_[index].size();

        typename Mesh<T>::AdjacentFacesPerVertex::const_iterator faces_end =
            obj.adjacent_faces_[index].end();
        for (typename Mesh<T>::AdjacentFacesPerVertex::const_iterator face =
            obj.adjacent_faces_[index].begin(); face != faces_end; ++face)
        {
            os << boost::format("%1%, %|4t|") % (*face);
        }

        os << std::endl;
    }

    // Print full faces info.
    os << "Faces: " << obj.faces_.size() << std::endl;

    typename Mesh<T>::Faces::const_iterator faces_end = obj.faces_.end();
    for (typename Mesh<T>::Faces::const_iterator face = obj.faces_.begin();
        face != faces_end; ++face)
    {
        // Print face's vertices.
        std::size_t index = face - obj.faces_.begin();
        os << boost::format("face %1%: ") % index << std::endl << "\t"
           << boost::format("A: %1%, %|18t|B: %2%, %|36t|C: %3%,")
           % face->A() % face->B() % face->C();

        // Print face's normal.
        typename Mesh<T>::Normal normal = obj.face_normals_[index];
        os << std::endl << "\t"
           << boost::format("normal: (%1%, %2%, %3%)") % normal.x() % normal.y()
           % normal.z();

        os << std::endl;
    }

    // Print footer and return.
    os << boost::format("end of object %1$#x.") % &obj << std::endl;

    return os;
}

template <typename T>
Mesh<T>::Mesh(std::size_t initial_count)
{
    vertices_.reserve(initial_count);
    faces_.reserve(initial_count);
    face_normals_.reserve(initial_count);
    neighbours_.reserve(initial_count);
    adjacent_faces_.reserve(initial_count);
}

template <typename T>
Mesh<T>::Mesh(const Mesh& other): vertices_(other.vertices_), faces_(other.faces_),
    face_normals_(other.face_normals_), neighbours_(other.neighbours_),
    adjacent_faces_(other.adjacent_faces_)
{
    // Copies k-d tree if it exists. Can be expensive.
    if (other.tree_)
        tree_.reset(new Tree(*other.tree_));
}

template <typename T>
void Mesh<T>::swap(Mesh& other)
{
    vertices_.swap(other.vertices_);
    faces_.swap(other.faces_);
    face_normals_.swap(other.face_normals_);
    tree_.swap(other.tree_);
    neighbours_.swap(other.neighbours_);
    adjacent_faces_.swap(other.adjacent_faces_);
}

template <typename T>
Mesh<T>& Mesh<T>::operator=(Mesh other)
{
    other.swap(*this);
    return *this;
}

template <typename T>
std::size_t Mesh<T>::add_vertex(const Vertex& vertex)
{
    // Actually, a syncro primitive should be added here.
    vertices_.push_back(vertex);
    std::size_t new_vertex_index = vertices_.size() - 1;

    // Add empty connectivity containers.
    neighbours_.push_back(AdjacentVerticesPerVertex());
    adjacent_faces_.push_back(AdjacentFacesPerVertex());

    // Check for post-conditions. These include sizes of connectivity structures
    // and vertex container. If any of the condition is not satisfied consider
    // this as an internal bug. Therefore no need to throw.
    BOOST_ASSERT(((neighbours_.size() == vertices_.size()) ||
                  (adjacent_faces_.size() == vertices_.size())) &&
                  "Vertex connectivity structures are of different sizes.");

    return new_vertex_index;
}

template <typename T>
std::size_t Mesh<T>::add_vertex_checked(const Vertex& vertex, T search_radius)
{
    // If k-d tree is not built, build it.
    if (!tree_)
        build_tree();

    // Search for an existed vertex in a given radius. A coversion is needed so that
    // candidate type and tree node type are identical.
    typename Tree::const_iterator candidate = tree_->find_nearest(
        std::make_pair(vertex, std::numeric_limits<std::size_t>::max()), search_radius).first;

    // If found
    std::size_t vertex_id = (candidate == tree_->end()) ?
                add_vertex(vertex) : candidate->second;

    return vertex_id;
}

template <typename T>
std::size_t Mesh<T>::add_vertices(const Vertices& vertices)
{
    // Calculate the index of the first inserted vertex.
    std::size_t first_inserted_index = vertices_.size();

    // Insert all vertices at once.
    vertices_.insert(vertices_.end(), vertices.begin(), vertices.end());

    // Add empty connectivity structures.
    neighbours_.insert(neighbours_.end(), vertices.size(), AdjacentVerticesPerVertex());
    adjacent_faces_.insert(adjacent_faces_.end(), vertices.size(), AdjacentFacesPerVertex());

    // Check for post-conditions. See add_vertex() for more details.
    BOOST_ASSERT(((neighbours_.size() == vertices_.size()) ||
                  (adjacent_faces_.size() == vertices_.size())) &&
                  "Vertex connectivity structures are of different sizes.");

    return first_inserted_index;
}

template <typename T>
std::size_t Mesh<T>::add_face(const Face& face)
{
    // Check if all vertices exist in the mesh.
    if ((vertices_.size() <= face.A()) ||
        (vertices_.size() <= face.B()) ||
        (vertices_.size() <= face.C()))
    {
        BOOST_ASSERT(false && "Bad vertex indices in the face.");
        throw std::out_of_range("Face cannot be added to the mesh because it "
                                "references non-existent vertices.");
    }

    // Add the face and get its index. Syncro primitive needed.
    faces_.push_back(face);
    std::size_t new_face_index = faces_.size() - 1;

    // Update vertex neighbours (insert edges).
    add_edge_(face.A(), face.B());
    add_edge_(face.B(), face.C());
    add_edge_(face.C(), face.A());

    // Update adjacent faces.
    add_adjacent_face_(face.A(), new_face_index);
    add_adjacent_face_(face.B(), new_face_index);
    add_adjacent_face_(face.C(), new_face_index);

    // Compute and save face normal.
    face_normals_.push_back(compute_face_normal_(face));

    // Check for post-conditions. These include sizes of connectivity structures
    // and face container. If any of the condition is not satisfied consider this
    // as an internal bug. Therefore no need to throw.
    BOOST_ASSERT((face_normals_.size() == faces_.size()) &&
                 "Vertex connectivity structures are of different sizes.");

    return new_face_index;
}

template <typename T>
typename Mesh<T>::Normal Mesh<T>::get_face_normal(std::size_t face_index) const
{
    // Check if the given face exists in the mesh.
    face_rangecheck_(face_index);

    return face_normals_[face_index];
}

template <typename T>
typename Mesh<T>::Normal Mesh<T>::get_vertex_normal(std::size_t vertex_index) const
{
    // TODO: add caching for computed normals.

    // Check if the given vertex exists in the mesh.
    vertex_rangecheck_(vertex_index);

    // A normal of a vertex is a sum of weighted normals of adjacent faces.
    Normal normal;

    AdjacentFacesPerVertex::const_iterator faces_end =
        adjacent_faces_[vertex_index].end();
    for (AdjacentFacesPerVertex::const_iterator face_index =
        adjacent_faces_[vertex_index].begin(); face_index != faces_end; ++face_index)
    {
        // Determine to which face vertex considered vertex belong. Without loss of
        // generality, suppose, that face[2] == vertex_index.
        const Face& face = faces_[*face_index];
        std::size_t pt1 = face[0];
        std::size_t pt2 = face[1];
        if (face[0] == vertex_index)
            pt1 = face[2];
        else if (face[1] == vertex_index)
            pt2 = face[2];

        // Compute face's normal weight (multiplied dot products of two edges).
        Vector<T, 3> edge1 = vertices_[pt1] - vertices_[vertex_index];
        Vector<T, 3> edge2 = vertices_[pt2] - vertices_[vertex_index];
        T weight = ((edge1 * edge1) * (edge2 * edge2));

        // Append weighted face's normal.
        normal += face_normals_[*face_index] * (1.0 / weight);
    }

    // Face normals can be the null vectors.
    try {
        normal = normal.normalized();
    }
    catch(const std::logic_error&)
    { }

    return normal;
}

template <typename T>
typename Mesh<T>::Vertex Mesh<T>::get_closest_point(const Vertex& point) const
{
    Vertex closest;
    double min_distance = std::numeric_limits<double>::max();

    std::size_t faces_size = faces_.size();
    for (std::size_t face_index = 0; face_index < faces_size; ++face_index)
    {
        Vertex face_closest = closest_point_on_face(face_index, point);
        double face_distance = (face_closest - point).eucl_norm();
        if (face_distance < min_distance)
        {
            min_distance = face_distance;
            closest = face_closest;
        }
    }

    return closest;
}

template <typename T>
double Mesh<T>::distance(const Vertex& point) const
{
    return
        distances::euclidean_distance_d(get_closest_point(point), point);
}

template <typename T>
Mesh<T>& Mesh<T>::join(const Mesh<T>& other)
{
    return
        join(other, std::mem_fun(&SelfType::add_vertex));
}

template <typename T>
Mesh<T>& Mesh<T>::join_checked(const Mesh<T>& other, T search_radius)
{
    return
        join(other, boost::bind(&SelfType::add_vertex_checked, _1, _2, search_radius));
}

template <typename T>
Mesh<T> Mesh<T>::from_vertices(const Vertices& vertices)
{
    SelfType mesh(vertices.size());
    for (typename Vertices::const_iterator it = vertices.begin(); it != vertices.end(); ++it)
        mesh.add_vertex(*it);

    return mesh;
}

template <typename T>
const typename Mesh<T>::AdjacentVerticesPerVertex& Mesh<T>::get_neighbouring_vertices(
    std::size_t vertex_index) const
{
    // Check if the given vertex exists in the mesh.
    vertex_rangecheck_(vertex_index);

    return neighbours_[vertex_index];
}

template <typename T>
const typename Mesh<T>::AdjacentFacesPerVertex& Mesh<T>::get_neighbouring_faces_by_vertex(
    std::size_t vertex_index) const
{
    // Check if the given vertex exists in the mesh.
    vertex_rangecheck_(vertex_index);

    return adjacent_faces_[vertex_index];
}

template <typename T> inline
const typename Mesh<T>::Vertices& Mesh<T>::get_all_vertices() const
{
    return vertices_;
}

template <typename T> inline
const typename Mesh<T>::Faces& Mesh<T>::get_all_faces() const
{
    return faces_;
}

template <typename T> inline
const typename Mesh<T>::Normals& Mesh<T>::get_all_face_normals() const
{
    return face_normals_;
}

template <typename T>
void Mesh<T>::build_tree()
{
    // Copy vertex data with corresponding indices to a separate container. This is
    // necessary as we are going to store in the tree vertices ids in addition to
    // vertices themselves. For this reason, std::transform cannot be used.
    std::vector<TreeNode> nodes_container;
    std::size_t vertices_count = vertices_.size();
    nodes_container.reserve(vertices_count);
    for (std::size_t idx = 0; idx < vertices_count; ++idx)
        nodes_container.push_back(std::make_pair(vertices_[idx], idx));

    // Create an empty k-d tree and then construct it from the prepared container.
    // The container is changed, but it is OK.
    tree_.reset(new Tree(std::ptr_fun(point3D_accessor_)));
    tree_->efficient_replace_and_optimise(nodes_container);
}

template <typename T>
void Mesh<T>::destroy_tree()
{
    tree_.reset();
}

// Private utility functions.

template <typename T>
bool Mesh<T>::add_edge_(std::size_t vertex1, std::size_t vertex2)
{
    // If the neighbouring relation between the given vertices already exists,
    // set::insert signal this and won't add a duplicate. A neighbouring relation
    // must be mutual. In case one vertex has another as a neighbour and another
    // has not, report an error through assertion. Consider this situation a severe
    // internal bug, therefore no exception throwing needed.
    bool exists1 = neighbours_[vertex1].insert(vertex2).second;
    bool exists2 = neighbours_[vertex2].insert(vertex1).second;

    BOOST_ASSERT(!(exists1 ^ exists2) && "Neighbouring relation is not mutual.");

    return (exists1 && exists2);
}

template <typename T>
bool Mesh<T>::add_adjacent_face_(std::size_t vertex, std::size_t face)
{
    // If the face is already attached to the vertex, set::insert won't add a
    // duplicate. See set::insert documentation.
    return
        adjacent_faces_[vertex].insert(face).second;
}

template <typename T>
typename Mesh<T>::Normal Mesh<T>::compute_face_normal_(const Face& face) const
{
    // Get two vectors representing the given face.
    Vector<T, 3> a = vertices_[face.A()] - vertices_[face.B()];
    Vector<T, 3> b = vertices_[face.B()] - vertices_[face.C()];

    // If these vectors are collinear (face points are lying on the same line) its
    // cross product will be the null vector and its normalization is meaningless.
    Normal normal(0);
    try {
        Vector<T, 3> cross_pr = a.cross_product(b);
        normal = cross_pr.normalized();
    }
    catch(const std::logic_error&)
    { }

    return normal;
}

template <typename T>
typename Mesh<T>::Vertex Mesh<T>::closest_point_on_face(std::size_t face_index,
                                                        const Vertex& P) const
{
    // No need to check if the given face exists in the mesh since the function is
    // private and a debug assertion would suffice.
    BOOST_ASSERT((faces_.size() > face_index) && "Specified face doesn't exist.");

    Vertex closest_point = distances::find_closest_point_on_triangle(P,
        vertices_[faces_[face_index].A()], vertices_[faces_[face_index].B()],
        vertices_[faces_[face_index].C()]);

    return closest_point;
}

template <typename T> template <typename F>
Mesh<T>& Mesh<T>::join(const Mesh<T>& other, F inserter)
{
    // Cache vertices count.
    std::size_t other_size = other.vertices_.size();

    // Lookup table for faces transformation.
    std::vector<std::size_t> lookup_table(other_size);

    // Insert vertices and fill lookup table.
    for (std::size_t other_idx = 0; other_idx < other_size; ++other_idx)
    {
        std::size_t new_idx = inserter(this, other.vertices_[other_idx]);
        lookup_table[other_idx] = new_idx;
    }

    // Transform and insert faces.
    typename Faces::const_iterator other_end = other.faces_.end();
    for (typename Faces::const_iterator old_face = other.faces_.begin();
         old_face != other_end; ++old_face)
    {
        std::size_t new_a = lookup_table[old_face->A()];
        std::size_t new_b = lookup_table[old_face->B()];
        std::size_t new_c = lookup_table[old_face->C()];
        this->add_face(Face(new_a, new_b, new_c));
    }

    return *this;
}

template <typename T>
void Mesh<T>::vertex_rangecheck_(std::size_t vertex_index) const
{
    if (vertices_.size() <= vertex_index)
        throw std::out_of_range("Specified vertex doesn't exist.");
}

template <typename T>
void Mesh<T>::face_rangecheck_(std::size_t face_index) const
{
    if (faces_.size() <= face_index)
        throw std::out_of_range("Specified face doesn't exist.");
}

template <typename T> inline
T Mesh<T>::point3D_accessor_(TreeNode pt, std::size_t k)
{
    return pt.first[k];
}

} // namespace bo

#endif // MESH_HPP_5839D2AB_1DFF_4DCE_A5A2_051A5102190D_


#include <burst/integer/to_unsigned.hpp>

#include <doctest/doctest.h>

#include <boost/mpl/vector.hpp>

#include <limits>
#include <type_traits>

TEST_SUITE("to_unsigned")
{
    TEST_CASE_TEMPLATE("Сдвигает область знакового целого так, чтобы она совпадала с областью "
        "равного по размеру беззнакового целого", signed_integer_type,
        char, signed char, short, int, long, long long)
    {
        using unsigned_integer_type = std::make_unsigned_t<signed_integer_type>;
        CHECK
        (
            burst::to_unsigned(std::numeric_limits<signed_integer_type>::min()) ==
            std::numeric_limits<unsigned_integer_type>::min()
        );
        CHECK
        (
            burst::to_unsigned(std::numeric_limits<signed_integer_type>::max()) ==
            std::numeric_limits<unsigned_integer_type>::max()
        );
        CHECK
        (
            burst::to_unsigned(signed_integer_type{0}) ==
            std::numeric_limits<unsigned_integer_type>::max() / 2 + 1
        );
    }

    TEST_CASE_TEMPLATE("Беззнаковые целые оставляются без изменений", unsigned_integer_type,
        unsigned char, unsigned short, unsigned, unsigned long, unsigned long long)
    {
        CHECK
        (
            burst::to_unsigned(std::numeric_limits<unsigned_integer_type>::min()) ==
            std::numeric_limits<unsigned_integer_type>::min()
        );
        CHECK
        (
            burst::to_unsigned(std::numeric_limits<unsigned_integer_type>::max()) ==
            std::numeric_limits<unsigned_integer_type>::max()
        );
        CHECK
        (
            burst::to_unsigned(unsigned_integer_type{0}) ==
            unsigned_integer_type{0}
        );
        CHECK
        (
            burst::to_unsigned(unsigned_integer_type{1}) ==
            unsigned_integer_type{1}
        );
    }

    TEST_CASE("Может быть вычислена на этапе компиляции")
    {
        constexpr auto r = burst::to_unsigned(-1);
        static_assert(r == std::numeric_limits<unsigned>::max () / 2, "");
    }
}


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

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

#include "assembly/assembly_options.hpp"
#include "assembly/context.hpp"
#include "assembly/grid_function.hpp"
#include "assembly/l2_norm.hpp"
#include "assembly/numerical_quadrature_strategy.hpp"
#include "assembly/surface_normal_independent_function.hpp"

#include "common/scalar_traits.hpp"

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

#include "space/piecewise_polynomial_continuous_scalar_space.hpp"

#include <boost/test/floating_point_comparison.hpp>

using namespace Bempp;

template <typename ValueType_>
class LinearFunction
{
public:
    typedef ValueType_ ValueType;
    typedef typename ScalarTraits<ValueType>::RealType CoordinateType;

    int argumentDimension() const { return 3; }
    int resultDimension() const { return 1; }

    inline void evaluate(const arma::Col<CoordinateType>& point,
                         arma::Col<ValueType>& result) const {
        CoordinateType x = point(0), y = point(1), z = point(2);
        result(0) = x + y + z;
    }
};

template <typename ValueType_>
class QuadraticFunction
{
public:
    typedef ValueType_ ValueType;
    typedef typename ScalarTraits<ValueType>::RealType CoordinateType;

    int argumentDimension() const { return 3; }
    int resultDimension() const { return 1; }

    inline void evaluate(const arma::Col<CoordinateType>& point,
                         arma::Col<ValueType>& result) const {
        CoordinateType x = point(0), y = point(1), z = point(2);
        result(0) = x*x + y*y + z*z;
    }
};

template <typename ValueType_>
class CubicFunction
{
public:
    typedef ValueType_ ValueType;
    typedef typename ScalarTraits<ValueType>::RealType CoordinateType;

    int argumentDimension() const { return 3; }
    int resultDimension() const { return 1; }

    inline void evaluate(const arma::Col<CoordinateType>& point,
                         arma::Col<ValueType>& result) const {
        CoordinateType x = point(0), y = point(1), z = point(2);
        result(0) = x*x*x + y*y*y + z*z*z;
    }
};

template <typename ValueType_>
class QuarticFunction
{
public:
    typedef ValueType_ ValueType;
    typedef typename ScalarTraits<ValueType>::RealType CoordinateType;

    int argumentDimension() const { return 3; }
    int resultDimension() const { return 1; }

    inline void evaluate(const arma::Col<CoordinateType>& point,
                         arma::Col<ValueType>& result) const {
        CoordinateType x = point(0), y = point(1), z = point(2);
        result(0) = x*x*x*x + y*y*y*y + z*z*z*z;
    }
};

// Tests

BOOST_AUTO_TEST_SUITE(PiecewisePolynomialContinuousScalarSpace_)

BOOST_AUTO_TEST_CASE_TEMPLATE(linear_function_can_be_expanded_in_linear_space, ResultType, result_types)
{
    typedef ResultType RT;
    typedef typename ScalarTraits<RT>::RealType BFT;
    typedef typename ScalarTraits<RT>::RealType CT;

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

    shared_ptr<Space<BFT> > space(
        new PiecewisePolynomialContinuousScalarSpace<BFT>(grid, 1));

    AccuracyOptions accuracyOptions;
    accuracyOptions.singleRegular.setRelativeQuadratureOrder(2);
    shared_ptr<NumericalQuadratureStrategy<BFT, RT> > quadStrategy(
                new NumericalQuadratureStrategy<BFT, RT>(accuracyOptions));
    AssemblyOptions assemblyOptions;
    assemblyOptions.setVerbosityLevel(VerbosityLevel::LOW);
    shared_ptr<Context<BFT, RT> > context(
        new Context<BFT, RT>(quadStrategy, assemblyOptions));

    GridFunction<BFT, RT> function(
        context, space, space,
        surfaceNormalIndependentFunction(LinearFunction<RT>()));
    CT absoluteError, relativeError;
    estimateL2Error(
        function, surfaceNormalIndependentFunction(LinearFunction<RT>()),
        *quadStrategy, absoluteError, relativeError);
    BOOST_CHECK_SMALL(relativeError, 1000 * std::numeric_limits<CT>::epsilon() /* percent */);
}

BOOST_AUTO_TEST_CASE_TEMPLATE(quadratic_function_can_be_expanded_in_quadratic_space, ResultType, result_types)
{
    typedef ResultType RT;
    typedef typename ScalarTraits<RT>::RealType BFT;
    typedef typename ScalarTraits<RT>::RealType CT;

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

    shared_ptr<Space<BFT> > space(
        new PiecewisePolynomialContinuousScalarSpace<BFT>(grid, 2));

    AccuracyOptions accuracyOptions;
    accuracyOptions.singleRegular.setRelativeQuadratureOrder(2);
    shared_ptr<NumericalQuadratureStrategy<BFT, RT> > quadStrategy(
        new NumericalQuadratureStrategy<BFT, RT>(accuracyOptions));
    AssemblyOptions assemblyOptions;
    assemblyOptions.setVerbosityLevel(VerbosityLevel::LOW);
    shared_ptr<Context<BFT, RT> > context(
        new Context<BFT, RT>(quadStrategy, assemblyOptions));

    GridFunction<BFT, RT> function(
        context, space, space,
        surfaceNormalIndependentFunction(QuadraticFunction<RT>()));
    CT absoluteError, relativeError;
    estimateL2Error(
        function, surfaceNormalIndependentFunction(QuadraticFunction<RT>()),
        *quadStrategy, absoluteError, relativeError);
    BOOST_CHECK_SMALL(relativeError, 1000 * std::numeric_limits<CT>::epsilon() /* percent */);
}

BOOST_AUTO_TEST_CASE_TEMPLATE(quadratic_function_can_be_expanded_in_cubic_space, ResultType, result_types)
{
    typedef ResultType RT;
    typedef typename ScalarTraits<RT>::RealType BFT;
    typedef typename ScalarTraits<RT>::RealType CT;

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

    shared_ptr<Space<BFT> > space(
        new PiecewisePolynomialContinuousScalarSpace<BFT>(grid, 3));

    AccuracyOptions accuracyOptions;
    accuracyOptions.singleRegular.setRelativeQuadratureOrder(2);
    shared_ptr<NumericalQuadratureStrategy<BFT, RT> > quadStrategy(
        new NumericalQuadratureStrategy<BFT, RT>(accuracyOptions));
    AssemblyOptions assemblyOptions;
    assemblyOptions.setVerbosityLevel(VerbosityLevel::LOW);
    shared_ptr<Context<BFT, RT> > context(
        new Context<BFT, RT>(quadStrategy, assemblyOptions));

    GridFunction<BFT, RT> function(
        context, space, space,
        surfaceNormalIndependentFunction(QuadraticFunction<RT>()));
    CT absoluteError, relativeError;
    estimateL2Error(
        function, surfaceNormalIndependentFunction(QuadraticFunction<RT>()),
        *quadStrategy, absoluteError, relativeError);
    BOOST_CHECK_SMALL(relativeError, 1000 * std::numeric_limits<CT>::epsilon() /* percent */);
}

BOOST_AUTO_TEST_CASE_TEMPLATE(cubic_function_can_be_expanded_in_cubic_space, ResultType, result_types)
{
    typedef ResultType RT;
    typedef typename ScalarTraits<RT>::RealType BFT;
    typedef typename ScalarTraits<RT>::RealType CT;

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

    shared_ptr<Space<BFT> > space(
        new PiecewisePolynomialContinuousScalarSpace<BFT>(grid, 3));

    AccuracyOptions accuracyOptions;
    accuracyOptions.singleRegular.setRelativeQuadratureOrder(2);
    shared_ptr<NumericalQuadratureStrategy<BFT, RT> > quadStrategy(
        new NumericalQuadratureStrategy<BFT, RT>(accuracyOptions));
    AssemblyOptions assemblyOptions;
    assemblyOptions.setVerbosityLevel(VerbosityLevel::LOW);
    shared_ptr<Context<BFT, RT> > context(
        new Context<BFT, RT>(quadStrategy, assemblyOptions));

    GridFunction<BFT, RT> function(
        context, space, space,
        surfaceNormalIndependentFunction(CubicFunction<RT>()));
    CT absoluteError, relativeError;
    estimateL2Error(
        function, surfaceNormalIndependentFunction(CubicFunction<RT>()),
        *quadStrategy, absoluteError, relativeError);
    BOOST_CHECK_SMALL(relativeError, 1000 * std::numeric_limits<CT>::epsilon() /* percent */);
}

BOOST_AUTO_TEST_CASE_TEMPLATE(local2global_matches_global2local_for_quadratic_space, ResultType, result_types)
{
    typedef ResultType RT;
    typedef typename ScalarTraits<RT>::RealType BFT;
    typedef typename ScalarTraits<RT>::RealType CT;

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

    shared_ptr<Space<BFT> > space(
        (new PiecewisePolynomialContinuousScalarSpace<BFT>(grid, 2)));

    local2global_matches_global2local<BFT>(*space);
}

BOOST_AUTO_TEST_CASE_TEMPLATE(global2local_matches_local2global_for_quadratic_space, ResultType, result_types)
{
    typedef ResultType RT;
    typedef typename ScalarTraits<RT>::RealType BFT;
    typedef typename ScalarTraits<RT>::RealType CT;

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

    shared_ptr<Space<BFT> > space(
        (new PiecewisePolynomialContinuousScalarSpace<BFT>(grid, 2)));

    global2local_matches_local2global<BFT>(*space);
}

BOOST_AUTO_TEST_CASE_TEMPLATE(local2global_matches_global2local_for_cubic_space, ResultType, result_types)
{
    typedef ResultType RT;
    typedef typename ScalarTraits<RT>::RealType BFT;
    typedef typename ScalarTraits<RT>::RealType CT;

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

    shared_ptr<Space<BFT> > space(
        (new PiecewisePolynomialContinuousScalarSpace<BFT>(grid, 3)));

    local2global_matches_global2local<BFT>(*space);
}

BOOST_AUTO_TEST_CASE_TEMPLATE(global2local_matches_local2global_for_cubic_space, ResultType, result_types)
{
    typedef ResultType RT;
    typedef typename ScalarTraits<RT>::RealType BFT;
    typedef typename ScalarTraits<RT>::RealType CT;

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

    shared_ptr<Space<BFT> > space(
        (new PiecewisePolynomialContinuousScalarSpace<BFT>(grid, 3)));

    global2local_matches_local2global<BFT>(*space);
}

BOOST_AUTO_TEST_CASE_TEMPLATE(local2global_matches_global2local_for_quadratic_space_and_segment, ResultType, result_types)
{
    typedef ResultType RT;
    typedef typename ScalarTraits<RT>::RealType BFT;
    typedef typename ScalarTraits<RT>::RealType CT;

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

    GridSegment segment = gridSegmentWithPositiveX(*grid);
    shared_ptr<Space<BFT> > space(
        (new PiecewisePolynomialContinuousScalarSpace<BFT>(grid, 2, segment)));

    local2global_matches_global2local<BFT>(*space);
}

BOOST_AUTO_TEST_CASE_TEMPLATE(global2local_matches_local2global_for_cubic_space_and_segment, ResultType, result_types)
{
    typedef ResultType RT;
    typedef typename ScalarTraits<RT>::RealType BFT;
    typedef typename ScalarTraits<RT>::RealType CT;

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

    GridSegment segment = gridSegmentWithPositiveX(*grid);
    shared_ptr<Space<BFT> > space(
        (new PiecewisePolynomialContinuousScalarSpace<BFT>(grid, 3, segment)));

    global2local_matches_local2global<BFT>(*space);
}

BOOST_AUTO_TEST_CASE_TEMPLATE(local2global_matches_global2local_for_quadratic_space_and_segment_with_strictlyOnSegment_enabled, ResultType, result_types)
{
    typedef ResultType RT;
    typedef typename ScalarTraits<RT>::RealType BFT;
    typedef typename ScalarTraits<RT>::RealType CT;

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

    GridSegment segment = gridSegmentWithPositiveX(*grid);
    shared_ptr<Space<BFT> > space(
        (new PiecewisePolynomialContinuousScalarSpace<BFT>(
             grid, 2, segment, true /* strictlyOnSegment */)));

    local2global_matches_global2local<BFT>(*space);
}

BOOST_AUTO_TEST_CASE_TEMPLATE(global2local_matches_local2global_for_cubic_space_and_segment_with_strictlyOnSegment_enabled, ResultType, result_types)
{
    typedef ResultType RT;
    typedef typename ScalarTraits<RT>::RealType BFT;
    typedef typename ScalarTraits<RT>::RealType CT;

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

    GridSegment segment = gridSegmentWithPositiveX(*grid);
    shared_ptr<Space<BFT> > space(
        (new PiecewisePolynomialContinuousScalarSpace<BFT>(
             grid, 3, segment, true /* strictlyOnSegment */)));

    global2local_matches_local2global<BFT>(*space);
}

BOOST_AUTO_TEST_CASE_TEMPLATE(complement_is_really_a_complement_for_quadratic_space, ResultType, result_types)
{
    typedef ResultType RT;
    typedef typename ScalarTraits<RT>::RealType BFT;
    typedef typename ScalarTraits<RT>::RealType CT;

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

    shared_ptr<Space<BFT> > space(
        (new PiecewisePolynomialContinuousScalarSpace<BFT>(grid, 2)));
    GridSegment segment = gridSegmentWithPositiveX(*grid);
    shared_ptr<Space<BFT> > space1(
        (new PiecewisePolynomialContinuousScalarSpace<BFT>(grid, 2, segment)));
    GridSegment complement = segment.complement();
    shared_ptr<Space<BFT> > space2(
        (new PiecewisePolynomialContinuousScalarSpace<BFT>(grid, 2, complement)));

    complement_is_really_a_complement(space, space1, space2);
}

BOOST_AUTO_TEST_CASE_TEMPLATE(complement_is_really_a_complement_for_cubic_space, ResultType, result_types)
{
    typedef ResultType RT;
    typedef typename ScalarTraits<RT>::RealType BFT;
    typedef typename ScalarTraits<RT>::RealType CT;

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

    shared_ptr<Space<BFT> > space(
        (new PiecewisePolynomialContinuousScalarSpace<BFT>(grid, 3)));
    GridSegment segment = gridSegmentWithPositiveX(*grid);
    shared_ptr<Space<BFT> > space1(
        (new PiecewisePolynomialContinuousScalarSpace<BFT>(grid, 3, segment)));
    GridSegment complement = segment.complement();
    shared_ptr<Space<BFT> > space2(
        (new PiecewisePolynomialContinuousScalarSpace<BFT>(grid, 3, complement)));

    complement_is_really_a_complement(space, space1, space2);
}

BOOST_AUTO_TEST_SUITE_END()


/* Greg Anderson
 * Define a neural network.
 */

#ifndef _EIGEN_WRAPPER_H_
#define _EIGEN_WRAPPER_H_

#include <cstdlib>
#include <string>
#include <vector>
#include <memory>

#include <Eigen/Dense>
#include <Python.h>

/* A tensor is represented as a `std::vector<Eigen::MatrixXd>`.
*/
typedef Eigen::VectorXd Vec;
typedef Eigen::MatrixXd Mat;
typedef std::vector<Mat> Tensor;

static PyObject* eigen_vector_to_python_list(const Vec& b) {
    PyObject* ret = PyList_New(b.size());
    for (int i = 0; i < b.size(); i++) {
        PyObject* pi = PyFloat_FromDouble(b(i));
        PyList_SetItem(ret, i, pi);
    }
    return ret;
}

static PyObject* eigen_matrix_to_python_list(const Mat& w) {
    PyObject* ret = PyList_New(w.rows());
    for (int i = 0; i < w.rows(); i++) {
        PyObject* row = eigen_vector_to_python_list(w.row(i));
        PyList_SetItem(ret, i, row);
    }
    return ret;
}

static Vec python_list_to_eigen_vector(PyObject* pValue) {
    int size = PyList_Size(pValue);
    Vec ret(size);
    for (int i = 0; i < size; i++) {
        PyObject* elem = PyList_GetItem(pValue, i);
        ret(i) = PyFloat_AsDouble(elem);
    }
    return ret;
}
static std::string vec_to_str(Vec& v) {
    if (v.size()<=0) return "()";
    std::stringstream ss;
    ss << "(" << v(0);
    for (unsigned int i = 1; i < v.size(); i++) {
        ss << "," << v(i);
    }
    ss << ")";
    return ss.str();
}

#endif


/*
 * enumerate_flips.hpp
 *
 *  Created on: May 1, 2011
 *      Author: stewie
 */

#ifndef ENUMERATE_FLIPS_HPP_
#define ENUMERATE_FLIPS_HPP_

#include <cstddef>

#include <boost/iterator/iterator_traits.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/range/iterator_range.hpp>

#include "../core/network_state.hpp"

namespace bn {

namespace gen {

namespace detail {

struct FlipIterator : boost::iterator_facade<FlipIterator, State,
		boost::single_pass_traversal_tag, State> {
public:
	FlipIterator() {
	}

	FlipIterator(State s, std::size_t i) :
		s(s), i(i) {
	}

private:
	friend class boost::iterator_core_access;
	State s;
	std::size_t i;

	void increment() {
		++i;
	}

	bool equal(const FlipIterator& other) const {
		return i == other.i;
	}

	reference dereference() const {
		return State(s).flip(i);
	}
};

FlipIterator operator++(FlipIterator& it, int) {
	FlipIterator tmp(it);
	++it;
	return tmp;
}

} // namespace detail

struct AllFlips : boost::iterator_range<detail::FlipIterator> {
	typedef boost::iterator_range<detail::FlipIterator> base;
	AllFlips() {
	}
	AllFlips(const State& s) :
		base(iterator(s, 0), iterator(s, s.size())) {
	}
};

namespace functional {

struct all_flips {
	typedef AllFlips result_type;
	result_type operator()(const State& s) const {
		return AllFlips(s);
	}
};

} // namespace functional

static const functional::all_flips all_flips = { };

} // namespace gen

} // namespace bn

#endif /* ENUMERATE_FLIPS_HPP_ */


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

// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.

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

#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_INTERSECTION_INSERT_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_INTERSECTION_INSERT_HPP

#include <cstddef>

#include <boost/mpl/if.hpp>
#include <boost/mpl/assert.hpp>
#include <boost/range/metafunctions.hpp>

#include <boost/geometry/core/is_areal.hpp>
#include <boost/geometry/core/point_order.hpp>
#include <boost/geometry/core/reverse_dispatch.hpp>
#include <boost/geometry/geometries/concepts/check.hpp>
#include <boost/geometry/algorithms/convert.hpp>
#include <boost/geometry/algorithms/detail/point_on_border.hpp>
#include <boost/geometry/algorithms/detail/overlay/clip_linestring.hpp>
#include <boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp>
#include <boost/geometry/algorithms/detail/overlay/overlay.hpp>
#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp>
#include <boost/geometry/algorithms/detail/overlay/follow.hpp>
#include <boost/geometry/views/segment_view.hpp>

#if defined(BOOST_GEOMETRY_DEBUG_FOLLOW)
#include <boost/foreach.hpp>
#endif

namespace boost { namespace geometry
{

#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace intersection
{

template <typename PointOut>
struct intersection_segment_segment_point
{
    template
    <
        typename Segment1, typename Segment2,
        typename OutputIterator, typename Strategy
    >
    static inline OutputIterator apply(Segment1 const& segment1,
            Segment2 const& segment2, OutputIterator out,
            Strategy const& )
    {
        typedef typename point_type<PointOut>::type point_type;

        // Get the intersection point (or two points)
        segment_intersection_points<point_type> is
            = strategy::intersection::relate_cartesian_segments
            <
                policies::relate::segments_intersection_points
                    <
                        Segment1,
                        Segment2,
                        segment_intersection_points<point_type>
                    >
            >::apply(segment1, segment2);

        for (std::size_t i = 0; i < is.count; i++)
        {
            PointOut p;
            geometry::convert(is.intersections[i], p);
            *out++ = p;
        }
        return out;
    }
};

template <typename PointOut>
struct intersection_linestring_linestring_point
{
    template
    <
        typename Linestring1, typename Linestring2,
        typename OutputIterator, typename Strategy
    >
    static inline OutputIterator apply(Linestring1 const& linestring1,
            Linestring2 const& linestring2, OutputIterator out,
            Strategy const& )
    {
        typedef typename point_type<PointOut>::type point_type;

        typedef detail::overlay::turn_info<point_type> turn_info;
        std::deque<turn_info> turns;

        geometry::get_intersection_points(linestring1, linestring2, turns);

        for (typename boost::range_iterator<std::deque<turn_info> const>::type
            it = boost::begin(turns); it != boost::end(turns); ++it)
        {
            PointOut p;
            geometry::convert(it->point, p);
            *out++ = p;
        }
        return out;
    }
};

/*!
\brief Version of linestring with an areal feature (polygon or multipolygon)
*/
template
<
    bool ReverseAreal,
    typename LineStringOut,
    overlay_type OverlayType
>
struct intersection_of_linestring_with_areal
{
#if defined(BOOST_GEOMETRY_DEBUG_FOLLOW)
        template <typename Turn, typename Operation>
        static inline void debug_follow(Turn const& turn, Operation op,
                    int index)
        {
            std::cout << index
                << " at " << op.seg_id
                << " meth: " << method_char(turn.method)
                << " op: " << operation_char(op.operation)
                << " vis: " << visited_char(op.visited)
                << " of:  " << operation_char(turn.operations[0].operation)
                << operation_char(turn.operations[1].operation)
                << " " << geometry::wkt(turn.point)
                << std::endl;
        }
#endif

    template
    <
        typename LineString, typename Areal,
        typename OutputIterator, typename Strategy
    >
    static inline OutputIterator apply(LineString const& linestring, Areal const& areal,
            OutputIterator out,
            Strategy const& )
    {
        if (boost::size(linestring) == 0)
        {
            return out;
        }

        typedef detail::overlay::follow
                <
                    LineStringOut,
                    LineString,
                    Areal,
                    OverlayType
                > follower;

        typedef typename point_type<LineStringOut>::type point_type;

        typedef detail::overlay::traversal_turn_info<point_type> turn_info;
        std::deque<turn_info> turns;

        detail::get_turns::no_interrupt_policy policy;
        geometry::get_turns
            <
                false,
                (OverlayType == overlay_intersection ? ReverseAreal : !ReverseAreal),
                detail::overlay::calculate_distance_policy
            >(linestring, areal, turns, policy);

        if (turns.empty())
        {
            // No intersection points, it is either completely
            // inside (interior + borders)
            // or completely outside

            // Use border point (on a segment) to check this
            // (because turn points might skip some cases)
            point_type border_point;
            if (! geometry::point_on_border(border_point, linestring, true))
            {
                return out;
            }

            if (follower::included(border_point, areal))
            {
                LineStringOut copy;
                geometry::convert(linestring, copy);
                *out++ = copy;
            }
            return out;
        }

#if defined(BOOST_GEOMETRY_DEBUG_FOLLOW)
        int index = 0;
        BOOST_FOREACH(turn_info const& turn, turns)
        {
            debug_follow(turn, turn.operations[0], index++);
        }
#endif

        return follower::apply
                (
                    linestring, areal,
                    geometry::detail::overlay::operation_intersection,
                    turns, out
                );
    }
};

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

#ifndef DOXYGEN_NO_DISPATCH
namespace dispatch
{

template
<
    // real types
    typename Geometry1, typename Geometry2,
    typename GeometryOut,
    overlay_type OverlayType,
    // orientation
    bool Reverse1 = detail::overlay::do_reverse<geometry::point_order<Geometry1>::value>::value,
    bool Reverse2 = detail::overlay::do_reverse<geometry::point_order<Geometry2>::value>::value,
    bool ReverseOut = detail::overlay::do_reverse<geometry::point_order<GeometryOut>::value>::value,
    // tag dispatching:
    typename TagIn1 = typename geometry::tag<Geometry1>::type,
    typename TagIn2 = typename geometry::tag<Geometry2>::type,
    typename TagOut = typename geometry::tag<GeometryOut>::type,
    // metafunction finetuning helpers:
    bool Areal1 = geometry::is_areal<Geometry1>::value,
    bool Areal2 = geometry::is_areal<Geometry2>::value,
    bool ArealOut = geometry::is_areal<GeometryOut>::value
>
struct intersection_insert
{
    BOOST_MPL_ASSERT_MSG
        (
            false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPES_OR_ORIENTATIONS
            , (types<Geometry1, Geometry2, GeometryOut>)
        );
};

template
<
    typename Geometry1, typename Geometry2,
    typename GeometryOut,
    overlay_type OverlayType,
    bool Reverse1, bool Reverse2, bool ReverseOut,
    typename TagIn1, typename TagIn2, typename TagOut
>
struct intersection_insert
    <
        Geometry1, Geometry2,
        GeometryOut,
        OverlayType,
        Reverse1, Reverse2, ReverseOut,
        TagIn1, TagIn2, TagOut,
        true, true, true
    > : detail::overlay::overlay
        <Geometry1, Geometry2, Reverse1, Reverse2, ReverseOut, GeometryOut, OverlayType>
{};

// Any areal type with box:
template
<
    typename Geometry, typename Box,
    typename GeometryOut,
    overlay_type OverlayType,
    bool Reverse1, bool Reverse2, bool ReverseOut,
    typename TagIn, typename TagOut
>
struct intersection_insert
    <
        Geometry, Box,
        GeometryOut,
        OverlayType,
        Reverse1, Reverse2, ReverseOut,
        TagIn, box_tag, TagOut,
        true, true, true
    > : detail::overlay::overlay
        <Geometry, Box, Reverse1, Reverse2, ReverseOut, GeometryOut, OverlayType>
{};

template
<
    typename Segment1, typename Segment2,
    typename GeometryOut,
    overlay_type OverlayType,
    bool Reverse1, bool Reverse2, bool ReverseOut
>
struct intersection_insert
    <
        Segment1, Segment2,
        GeometryOut,
        OverlayType,
        Reverse1, Reverse2, ReverseOut,
        segment_tag, segment_tag, point_tag,
        false, false, false
    > : detail::intersection::intersection_segment_segment_point<GeometryOut>
{};

template
<
    typename Linestring1, typename Linestring2,
    typename GeometryOut,
    overlay_type OverlayType,
    bool Reverse1, bool Reverse2, bool ReverseOut
>
struct intersection_insert
    <
        Linestring1, Linestring2,
        GeometryOut,
        OverlayType,
        Reverse1, Reverse2, ReverseOut,
        linestring_tag, linestring_tag, point_tag,
        false, false, false
    > : detail::intersection::intersection_linestring_linestring_point<GeometryOut>
{};

template
<
    typename Linestring, typename Box,
    typename GeometryOut,
    overlay_type OverlayType,
    bool Reverse1, bool Reverse2, bool ReverseOut
>
struct intersection_insert
    <
        Linestring, Box,
        GeometryOut,
        OverlayType,
        Reverse1, Reverse2, ReverseOut,
        linestring_tag, box_tag, linestring_tag,
        false, true, false
    >
{
    template <typename OutputIterator, typename Strategy>
    static inline OutputIterator apply(Linestring const& linestring,
            Box const& box, OutputIterator out, Strategy const& )
    {
        typedef typename point_type<GeometryOut>::type point_type;
        strategy::intersection::liang_barsky<Box, point_type> lb_strategy;
        return detail::intersection::clip_range_with_box
            <GeometryOut>(box, linestring, out, lb_strategy);
    }
};

template
<
    typename Linestring, typename Polygon,
    typename GeometryOut,
    overlay_type OverlayType,
    bool ReverseLinestring, bool ReversePolygon, bool ReverseOut
>
struct intersection_insert
    <
        Linestring, Polygon,
        GeometryOut,
        OverlayType,
        ReverseLinestring, ReversePolygon, ReverseOut,
        linestring_tag, polygon_tag, linestring_tag,
        false, true, false
    > : detail::intersection::intersection_of_linestring_with_areal
            <
                ReversePolygon,
                GeometryOut,
                OverlayType
            >
{};

template
<
    typename Linestring, typename Ring,
    typename GeometryOut,
    overlay_type OverlayType,
    bool ReverseLinestring, bool ReverseRing, bool ReverseOut
>
struct intersection_insert
    <
        Linestring, Ring,
        GeometryOut,
        OverlayType,
        ReverseLinestring, ReverseRing, ReverseOut,
        linestring_tag, ring_tag, linestring_tag,
        false, true, false
    > : detail::intersection::intersection_of_linestring_with_areal
            <
                ReverseRing,
                GeometryOut,
                OverlayType
            >
{};

template
<
    typename Segment, typename Box,
    typename GeometryOut,
    overlay_type OverlayType,
    bool Reverse1, bool Reverse2, bool ReverseOut
>
struct intersection_insert
    <
        Segment, Box,
        GeometryOut,
        OverlayType,
        Reverse1, Reverse2, ReverseOut,
        segment_tag, box_tag, linestring_tag,
        false, true, false
    >
{
    template <typename OutputIterator, typename Strategy>
    static inline OutputIterator apply(Segment const& segment,
            Box const& box, OutputIterator out, Strategy const& )
    {
        geometry::segment_view<Segment> range(segment);

        typedef typename point_type<GeometryOut>::type point_type;
        strategy::intersection::liang_barsky<Box, point_type> lb_strategy;
        return detail::intersection::clip_range_with_box
            <GeometryOut>(box, range, out, lb_strategy);
    }
};

template
<
    typename Geometry1, typename Geometry2,
    typename PointOut,
    overlay_type OverlayType,
    bool Reverse1, bool Reverse2, bool ReverseOut,
    typename Tag1, typename Tag2,
    bool Areal1, bool Areal2
>
struct intersection_insert
    <
        Geometry1, Geometry2,
        PointOut,
        OverlayType,
        Reverse1, Reverse2, ReverseOut,
        Tag1, Tag2, point_tag,
        Areal1, Areal2, false
    >
{
    template <typename OutputIterator, typename Strategy>
    static inline OutputIterator apply(Geometry1 const& geometry1,
            Geometry2 const& geometry2, OutputIterator out, Strategy const& )
    {

        typedef detail::overlay::turn_info<PointOut> turn_info;
        std::vector<turn_info> turns;

        detail::get_turns::no_interrupt_policy policy;
        geometry::get_turns
            <
                false, false, detail::overlay::assign_null_policy
            >(geometry1, geometry2, turns, policy);
        for (typename std::vector<turn_info>::const_iterator it
            = turns.begin(); it != turns.end(); ++it)
        {
            *out++ = it->point;
        }

        return out;
    }
};

template
<
    typename Geometry1, typename Geometry2, typename GeometryOut,
    overlay_type OverlayType,
    bool Reverse1, bool Reverse2, bool ReverseOut
>
struct intersection_insert_reversed
{
    template <typename OutputIterator, typename Strategy>
    static inline OutputIterator apply(Geometry1 const& g1,
                Geometry2 const& g2, OutputIterator out,
                Strategy const& strategy)
    {
        return intersection_insert
            <
                Geometry2, Geometry1, GeometryOut,
                OverlayType,
                Reverse2, Reverse1, ReverseOut
            >::apply(g2, g1, out, strategy);
    }
};

} // namespace dispatch
#endif // DOXYGEN_NO_DISPATCH

#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace intersection
{

template
<
    typename GeometryOut,
    bool ReverseSecond,
    overlay_type OverlayType,
    typename Geometry1, typename Geometry2,
    typename OutputIterator,
    typename Strategy
>
inline OutputIterator insert(Geometry1 const& geometry1,
            Geometry2 const& geometry2,
            OutputIterator out,
            Strategy const& strategy)
{
    return boost::mpl::if_c
        <
            geometry::reverse_dispatch<Geometry1, Geometry2>::type::value,
            geometry::dispatch::intersection_insert_reversed
            <
                Geometry1, Geometry2,
                GeometryOut,
                OverlayType,
                overlay::do_reverse<geometry::point_order<Geometry1>::value>::value,
                overlay::do_reverse<geometry::point_order<Geometry2>::value, ReverseSecond>::value,
                overlay::do_reverse<geometry::point_order<GeometryOut>::value>::value
            >,
            geometry::dispatch::intersection_insert
            <
                Geometry1, Geometry2,
                GeometryOut,
                OverlayType,
                geometry::detail::overlay::do_reverse<geometry::point_order<Geometry1>::value>::value,
                geometry::detail::overlay::do_reverse<geometry::point_order<Geometry2>::value, ReverseSecond>::value
            >
        >::type::apply(geometry1, geometry2, out, strategy);
}

/*!
\brief \brief_calc2{intersection} \brief_strategy
\ingroup intersection
\details \details_calc2{intersection_insert, spatial set theoretic intersection}
    \brief_strategy. \details_insert{intersection}
\tparam GeometryOut \tparam_geometry{\p_l_or_c}
\tparam Geometry1 \tparam_geometry
\tparam Geometry2 \tparam_geometry
\tparam OutputIterator \tparam_out{\p_l_or_c}
\tparam Strategy \tparam_strategy_overlay
\param geometry1 \param_geometry
\param geometry2 \param_geometry
\param out \param_out{intersection}
\param strategy \param_strategy{intersection}
\return \return_out

\qbk{distinguish,with strategy}
\qbk{[include reference/algorithms/intersection.qbk]}
*/
template
<
    typename GeometryOut,
    typename Geometry1,
    typename Geometry2,
    typename OutputIterator,
    typename Strategy
>
inline OutputIterator intersection_insert(Geometry1 const& geometry1,
            Geometry2 const& geometry2,
            OutputIterator out,
            Strategy const& strategy)
{
    concept::check<Geometry1 const>();
    concept::check<Geometry2 const>();

    return detail::intersection::insert
        <
            GeometryOut, false, overlay_intersection
        >(geometry1, geometry2, out, strategy);
}

/*!
\brief \brief_calc2{intersection}
\ingroup intersection
\details \details_calc2{intersection_insert, spatial set theoretic intersection}.
    \details_insert{intersection}
\tparam GeometryOut \tparam_geometry{\p_l_or_c}
\tparam Geometry1 \tparam_geometry
\tparam Geometry2 \tparam_geometry
\tparam OutputIterator \tparam_out{\p_l_or_c}
\param geometry1 \param_geometry
\param geometry2 \param_geometry
\param out \param_out{intersection}
\return \return_out

\qbk{[include reference/algorithms/intersection.qbk]}
*/
template
<
    typename GeometryOut,
    typename Geometry1,
    typename Geometry2,
    typename OutputIterator
>
inline OutputIterator intersection_insert(Geometry1 const& geometry1,
            Geometry2 const& geometry2,
            OutputIterator out)
{
    concept::check<Geometry1 const>();
    concept::check<Geometry2 const>();

    typedef strategy_intersection
        <
            typename cs_tag<GeometryOut>::type,
            Geometry1,
            Geometry2,
            typename geometry::point_type<GeometryOut>::type
        > strategy;

    return intersection_insert<GeometryOut>(geometry1, geometry2, out,
                strategy());
}

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

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_INTERSECTION_INSERT_HPP


//
// Copyright © 2017 Arm Ltd. All rights reserved.
// SPDX-License-Identifier: MIT
//

#include <boost/test/unit_test.hpp>
#include "armnnTfParser/ITfParser.hpp"
#include "ParserPrototxtFixture.hpp"

BOOST_AUTO_TEST_SUITE(TensorflowParser)

struct ExpandDimsFixture : public armnnUtils::ParserPrototxtFixture<armnnTfParser::ITfParser>
{
    ExpandDimsFixture(const std::string& expandDim)
    {
        m_Prototext =
                "node { \n"
                "    name: \"graphInput\" \n"
                "    op: \"Placeholder\" \n"
                "    attr { \n"
                "      key: \"dtype\" \n"
                "      value { \n"
                "        type: DT_FLOAT \n"
                "      } \n"
                "    } \n"
                "    attr { \n"
                "      key: \"shape\" \n"
                "      value { \n"
                "        shape { \n"
                "        } \n"
                "      } \n"
                "    } \n"
                "  } \n"
                "node { \n"
                "  name: \"ExpandDims\" \n"
                "  op: \"ExpandDims\" \n"
                "  input: \"graphInput\" \n"
                "  attr { \n"
                "    key: \"T\" \n"
                "    value { \n"
                "      type: DT_FLOAT \n"
                "    } \n"
                "  } \n"
                "  attr { \n"
                "    key: \"Tdim\" \n"
                "    value { \n";
            m_Prototext += "i:" + expandDim;
            m_Prototext +=
                "    } \n"
                "  } \n"
                "} \n";

        SetupSingleInputSingleOutput({ 2, 3, 5 }, "graphInput", "ExpandDims");
    }
};

struct ExpandZeroDim : ExpandDimsFixture
{
    ExpandZeroDim() : ExpandDimsFixture("0") {}
};

struct ExpandTwoDim : ExpandDimsFixture
{
    ExpandTwoDim() : ExpandDimsFixture("2") {}
};

struct ExpandThreeDim : ExpandDimsFixture
{
    ExpandThreeDim() : ExpandDimsFixture("3") {}
};

struct ExpandMinusOneDim : ExpandDimsFixture
{
    ExpandMinusOneDim() : ExpandDimsFixture("-1") {}
};

struct ExpandMinusThreeDim : ExpandDimsFixture
{
    ExpandMinusThreeDim() : ExpandDimsFixture("-3") {}
};

BOOST_FIXTURE_TEST_CASE(ParseExpandZeroDim, ExpandZeroDim)
{
    BOOST_TEST((m_Parser->GetNetworkOutputBindingInfo("ExpandDims").second.GetShape() ==
                armnn::TensorShape({1, 2, 3, 5})));
}

BOOST_FIXTURE_TEST_CASE(ParseExpandTwoDim, ExpandTwoDim)
{
    BOOST_TEST((m_Parser->GetNetworkOutputBindingInfo("ExpandDims").second.GetShape() ==
                armnn::TensorShape({2, 3, 1, 5})));
}

BOOST_FIXTURE_TEST_CASE(ParseExpandThreeDim, ExpandThreeDim)
{
    BOOST_TEST((m_Parser->GetNetworkOutputBindingInfo("ExpandDims").second.GetShape() ==
                armnn::TensorShape({2, 3, 5, 1})));
}

BOOST_FIXTURE_TEST_CASE(ParseExpandMinusOneDim, ExpandMinusOneDim)
{
    BOOST_TEST((m_Parser->GetNetworkOutputBindingInfo("ExpandDims").second.GetShape() ==
                armnn::TensorShape({2, 3, 5, 1})));
}

BOOST_FIXTURE_TEST_CASE(ParseExpandMinusThreeDim, ExpandMinusThreeDim)
{
    BOOST_TEST((m_Parser->GetNetworkOutputBindingInfo("ExpandDims").second.GetShape() ==
                armnn::TensorShape({2, 1, 3, 5})));
}

BOOST_AUTO_TEST_SUITE_END()


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

// Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2008-2015 Bruno Lalande, Paris, France.
// Copyright (c) 2009-2015 Mateusz Loskot, London, UK.

// This file was modified by Oracle on 2014, 2015.
// Modifications copyright (c) 2014-2015, Oracle and/or its affiliates.

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

// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.

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

#ifndef TTMATH_STUB
#define TTMATH_STUB

#include <boost/math/constants/constants.hpp>
#include <boost/numeric/conversion/cast.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/coordinate_cast.hpp>

#include <ttmath.h>
namespace ttmath
{
    template <uint Exponent, uint Mantissa>
    inline Big<Exponent, Mantissa> sqrt(Big<Exponent, Mantissa> const& v)
    {
        return Sqrt(v);
    }

    template <uint Exponent, uint Mantissa>
    inline Big<Exponent, Mantissa> abs(Big<Exponent, Mantissa> const& v)
    {
        return Abs(v);
    }

    template <uint Exponent, uint Mantissa>
    inline Big<Exponent, Mantissa> ceil(Big<Exponent, Mantissa> const& v)
    {
        return Ceil(v);
    }

    template <uint Exponent, uint Mantissa>
    inline Big<Exponent, Mantissa> floor(Big<Exponent, Mantissa> const& v)
    {
        return Floor(v);
    }

    template <uint Exponent, uint Mantissa>
    inline Big<Exponent, Mantissa> asin(Big<Exponent, Mantissa> const& v)
    {
        return ASin(v);
    }

    template <uint Exponent, uint Mantissa>
    inline Big<Exponent, Mantissa> sin(Big<Exponent, Mantissa> const& v)
    {
        return Sin(v);
    }

    template <uint Exponent, uint Mantissa>
    inline Big<Exponent, Mantissa> cos(Big<Exponent, Mantissa> const& v)
    {
        return Cos(v);
    }

    template <uint Exponent, uint Mantissa>
    inline Big<Exponent, Mantissa> tan(Big<Exponent, Mantissa> const& v)
    {
        return Tan(v);
    }

    template <uint Exponent, uint Mantissa>
    inline Big<Exponent, Mantissa> atan(Big<Exponent, Mantissa> const& v)
    {
        return ATan(v);
    }

    template <uint Exponent, uint Mantissa>
    inline Big<Exponent, Mantissa> acos(Big<Exponent, Mantissa> const& v)
    {
        return ACos(v);
    }

    template <uint Exponent, uint Mantissa>
    inline Big<Exponent, Mantissa> atan2(Big<Exponent, Mantissa> const& y, Big<Exponent, Mantissa> const& x)
    {
        // return ATan2(y, 2); does not (yet) exist in ttmath...

        // See http://en.wikipedia.org/wiki/Atan2

        Big<Exponent, Mantissa> const zero(0);
        Big<Exponent, Mantissa> const two(2);

        if (y == zero)
        {
            // return x >= 0 ? 0 : pi and pi=2*arccos(0)
            return x >= zero ? zero : two * ACos(zero);
        }

        return two * ATan((sqrt(x * x + y * y) - x) / y);
    }

    // needed in order to work with boost::geometry::math::mod
    template <uint Exponent, uint Mantissa>
    inline Big<Exponent, Mantissa> mod(Big<Exponent, Mantissa> const& x,
                                       Big<Exponent, Mantissa> const& y)
    {
        return Mod(x, y);
    }
}

// Specific structure implementing constructor
// (WHICH IS NECESSARY FOR Boost.Geometry because it enables using T() !! )
struct ttmath_big : ttmath::Big<1,4>
{
    ttmath_big(double v = 0)
        : ttmath::Big<1,4>(v)
    {}
    ttmath_big(ttmath::Big<1,4> const& v)
        : ttmath::Big<1,4>(v)
    {}

    // unary operator+() is implemented for completeness
    inline ttmath_big const& operator+() const
    {
        return *this;
    }

    // needed in order to work with boost::geometry::math::abs
    inline ttmath_big operator-() const
    {
        return ttmath::Big<1,4>::operator-();
    }

    /*
    inline operator double() const
    {
        return atof(this->ToString().c_str());
    }

    inline operator int() const
    {
        return atol(ttmath::Round(*this).ToString().c_str());
    }
    */
};

// arithmetic operators for ttmath_big objects, defined as free functions
inline ttmath_big operator+(ttmath_big const& x, ttmath_big const& y)
{
    return static_cast<ttmath::Big<1,4> const&>(x).operator+(y);
}

inline ttmath_big operator-(ttmath_big const& x, ttmath_big const& y)
{
    return static_cast<ttmath::Big<1,4> const&>(x).operator-(y);
}

inline ttmath_big operator*(ttmath_big const& x, ttmath_big const& y)
{
    return static_cast<ttmath::Big<1,4> const&>(x).operator*(y);
}

inline ttmath_big operator/(ttmath_big const& x, ttmath_big const& y)
{
    return static_cast<ttmath::Big<1,4> const&>(x).operator/(y);
}

namespace boost{ namespace geometry { namespace math
{

namespace detail
{
    // Workaround for boost::math::constants::pi:
    // 1) lexical cast -> stack overflow and
    // 2) because it is implemented as a function, generic implementation not possible

    // Partial specialization for ttmath
    template <ttmath::uint Exponent, ttmath::uint Mantissa>
    struct define_half_pi<ttmath::Big<Exponent, Mantissa> >
    {
        static inline ttmath::Big<Exponent, Mantissa> apply()
        {
            static ttmath::Big<Exponent, Mantissa> const half_pi(
                "1.57079632679489661923132169163975144209858469968755291048747229615390820314310449931401741267105853399107404325664115332354692230477529111586267970406424055872514205135096926055277982231147447746519098");
            return half_pi;
        }
    };

    // Partial specialization for ttmath
    template <ttmath::uint Exponent, ttmath::uint Mantissa>
    struct define_pi<ttmath::Big<Exponent, Mantissa> >
    {
        static inline ttmath::Big<Exponent, Mantissa> apply()
        {
            static ttmath::Big<Exponent, Mantissa> const the_pi(
                "3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196");
            return the_pi;
        }
    };

    // Partial specialization for ttmath
    template <ttmath::uint Exponent, ttmath::uint Mantissa>
    struct define_two_pi<ttmath::Big<Exponent, Mantissa> >
    {
        static inline ttmath::Big<Exponent, Mantissa> apply()
        {
            static ttmath::Big<Exponent, Mantissa> const two_pi(
                "6.28318530717958647692528676655900576839433879875021164194988918461563281257241799725606965068423413596429617302656461329418768921910116446345071881625696223490056820540387704221111928924589790986076392");
            return two_pi;
        }
    };

    template <>
    struct define_half_pi<ttmath_big>
            : public define_half_pi<ttmath::Big<1,4> >
    {};

    template <>
    struct define_pi<ttmath_big>
            : public define_pi<ttmath::Big<1,4> >
    {};

    template <>
    struct define_two_pi<ttmath_big>
            : public define_two_pi<ttmath::Big<1,4> >
    {};

    template <ttmath::uint Exponent, ttmath::uint Mantissa>
    struct equals_with_epsilon<ttmath::Big<Exponent, Mantissa>, false>
    {
        static inline bool apply(ttmath::Big<Exponent, Mantissa> const& a, ttmath::Big<Exponent, Mantissa> const& b)
        {
            // See implementation in util/math.hpp
            // But here borrow the tolerance for double, to avoid exact comparison
            ttmath::Big<Exponent, Mantissa> const epsilon = std::numeric_limits<double>::epsilon();
            return ttmath::Abs(a - b) <= epsilon * ttmath::Abs(a);
        }
    };

    template <>
    struct equals_with_epsilon<ttmath_big, false>
            : public equals_with_epsilon<ttmath::Big<1, 4>, false>
    {};

} // detail

} // ttmath

namespace detail
{

template <ttmath::uint Exponent, ttmath::uint Mantissa>
struct coordinate_cast<ttmath::Big<Exponent, Mantissa> >
{
    static inline ttmath::Big<Exponent, Mantissa> apply(std::string const& source)
    {
        return ttmath::Big<Exponent, Mantissa> (source);
    }
};

template <>
struct coordinate_cast<ttmath_big>
{
    static inline ttmath_big apply(std::string const& source)
    {
        return ttmath_big(source);
    }
};

} // namespace detail

}} // boost::geometry

// Support for boost::numeric_cast to int and to double (necessary for SVG-mapper)
namespace boost { namespace numeric
{

template
<
    ttmath::uint Exponent, ttmath::uint Mantissa,
    typename Traits,
    typename OverflowHandler,
    typename Float2IntRounder,
    typename RawConverter,
    typename UserRangeChecker
>
struct converter<int, ttmath::Big<Exponent, Mantissa>, Traits, OverflowHandler, Float2IntRounder, RawConverter, UserRangeChecker>
{
    static inline int convert(ttmath::Big<Exponent, Mantissa> arg)
    {
        int v;
        arg.ToInt(v);
        return v;
    }
};

template
<
    ttmath::uint Exponent, ttmath::uint Mantissa,
    typename Traits,
    typename OverflowHandler,
    typename Float2IntRounder,
    typename RawConverter,
    typename UserRangeChecker
>
struct converter<double, ttmath::Big<Exponent, Mantissa>, Traits, OverflowHandler, Float2IntRounder, RawConverter, UserRangeChecker>
{
    static inline double convert(ttmath::Big<Exponent, Mantissa> arg)
    {
        double v;
        arg.ToDouble(v);
        return v;
    }
};

template
<
    typename Traits,
    typename OverflowHandler,
    typename Float2IntRounder,
    typename RawConverter,
    typename UserRangeChecker
>
struct converter<int, ttmath_big, Traits, OverflowHandler, Float2IntRounder, RawConverter, UserRangeChecker>
{
    static inline int convert(ttmath_big arg)
    {
        int v;
        arg.ToInt(v);
        return v;
    }
};

template
<
    typename Traits,
    typename OverflowHandler,
    typename Float2IntRounder,
    typename RawConverter,
    typename UserRangeChecker
>
struct converter<double, ttmath_big, Traits, OverflowHandler, Float2IntRounder, RawConverter, UserRangeChecker>
{
    static inline double convert(ttmath_big arg)
    {
        double v;
        arg.ToDouble(v);
        return v;
    }
};

}}

#endif


#include <iostream>
#include <cmath>

#include <boost/numeric/linear_algebra/operators.hpp>
#include <boost/numeric/linear_algebra/inverse.hpp>
#include <boost/numeric/linear_algebra/is_invertible.hpp>
#include <boost/numeric/linear_algebra/new_concepts.hpp>
//#include <boost/numeric/linear_algebra/concept_maps.hpp>
#include <boost/numeric/linear_algebra/power.hpp>

#if 0 // only for testing other concepts
namespace math {
    concept_map AbelianGroup< add<float>, float > {}
    concept_map Monoid< mult<float>, float > {}
    concept_map SignedIntegral<int> {}
} // namespace math
#endif

int main(int, char* [])
{
    using math::power; using math::mult; using math::add;
    float a= 3.14;

    std::cout << "power(a, 5, mult<float>) " << power(a, 5, mult<float>()) << '\n';
#if 0
    std::cout << "power(a, 5, add<float>) " << power(a, 5, add<float>()) << '\n';
    std::cout << "power(a, 0, add<float>) " << power(a, 0, add<float>()) << '\n';
#endif

    return 0;
}


#pragma once

#include <armadillo>

#include <vector>

#include "composition.hpp"
#include "heattransfer/pipewall.hpp"
#include "heattransfer/burialmedium.hpp"
#include "heattransfer/ambientfluid.hpp"
#include "heattransfer/heattransferbase.hpp"
#include "advection/batchtrackingstate.hpp"

class BoundaryConditions;

class Pipeline
{
public:
    ////////////////////////////////////////////////////////////////////////////////
    class State
    {
    public:
        friend class Pipeline;

        State(
                const arma::vec& gridPoints,
                const arma::vec& pressure,
                const arma::vec& temperature,
                const arma::vec& flow,
                const std::vector<Composition>& composition);

        State(
                const arma::vec& gridPoints,
                const arma::vec& pressure,
                const arma::vec& temperature,
                const arma::vec& flow,
                const Composition& composition = Composition::defaultComposition);

        // getters
        // main/governing properties
        const arma::vec& flow() const                           { return m_flow; }
        const arma::vec& pressure() const                       { return m_pressure; }
        const arma::vec& temperature() const                    { return m_temperature; }
        const std::vector<Composition>& composition() const     { return m_composition; }

        // derived properties
        const arma::vec& heatCapacityConstantVolume() const     { return m_heatCapacityConstantVolume; }
        const arma::vec& heatCapacityConstantPressure() const   { return m_heatCapacityConstantPressure; }
        const arma::vec& density() const                        { return m_density; }
        const arma::vec& viscosity() const                      { return m_viscosity; }
        const arma::vec& specificGasConstant() const            { return m_specificGasConstant; }
        const arma::vec& molarMass() const                      { return m_molarMass; }

        const arma::vec& compressibilityFactor() const          { return m_compressibilityFactor; }
        const arma::vec& dZdtAtConstantPressure() const         { return m_temperatureDerivativeConstantPressure; }
        const arma::vec& dZdpAtConstantTemperature() const      { return m_pressureDerivativeConstantTemperature; }
        const arma::vec& dZdtAtConstantDensity() const          { return m_temperatureDerivativeConstantDensity; }

        const arma::vec& velocity() const                       { return m_velocity; }
        const arma::vec& frictionFactor() const                 { return m_frictionFactor; }
        const arma::vec& reynoldsNumber() const                 { return m_reynoldsNumber; }

        const arma::vec& ambientTemperature() const             { return m_ambientTemperature; }
        const arma::vec& heatFlow() const                       { return m_heatFlow; }

//        const std::vector<HeatTransferState>& heatTransferState() const { return m_heatTransferState; }
//        const BatchTrackingState& batchTrackingState() const  { return m_batchTrackingState; }
//        bool batchTrackingIsInitialized() const                 { return m_batchTrackingIsInitialized; }

//        // setters
//        // main/governing properties
//        arma::vec& flow()                           { return m_flow; }
//        arma::vec& pressure()                       { return m_pressure; }
//        arma::vec& temperature()                    { return m_temperature; }
//        std::vector<Composition>& composition();

//        // derived properties
//        arma::vec& heatCapacityConstantVolume()     { return m_heatCapacityConstantVolume; }
//        arma::vec& heatCapacityConstantPressure()   { return m_heatCapacityConstantPressure; }
//        arma::vec& density()                        { return m_density; }
//        arma::vec& viscosity()                      { return m_viscosity; }
//        arma::vec& specificGasConstant()            { return m_specificGasConstant; }
//        arma::vec& molarMass()                      { return m_molarMass; }

//        arma::vec& compressibilityFactor()          { return m_compressibilityFactor; }
//        arma::vec& dZdtAtConstantPressure()         { return m_temperatureDerivativeConstantPressure; }
//        arma::vec& dZdpAtConstantTemperature()      { return m_pressureDerivativeConstantTemperature; }
//        arma::vec& dZdtAtConstantDensity()          { return m_temperatureDerivativeConstantDensity; }

//        arma::vec& velocity()                       { return m_velocity; }
//        arma::vec& frictionFactor()                 { return m_frictionFactor; }
//        arma::vec& reynoldsNumber()                 { return m_reynoldsNumber; }

//        arma::vec& ambientTemperature()             { return m_ambientTemperature; }
//        arma::vec& heatFlow()                       { return m_heatFlow; }

//        std::vector<HeatTransferState>& heatTransferState() { return m_heatTransferState; }
//        BatchTrackingState& batchTrackingState()  { return m_batchTrackingState; }
//        bool& batchTrackingIsInitialized()          { return m_batchTrackingIsInitialized; }

        /*!
         * \brief For pretty printing.
         *
         * So we can do
         *
         *     std::cout << Pipeline::State;
         *
         * and get nice printing.
         */
        friend std::ostream& operator << (std::ostream &out, const State& c);

    protected:
        State(const arma::vec& gridPoints);

        // input/governing properties
        arma::vec m_flow; //!< Flow [kg/s]
        arma::vec m_pressure; //!< Gas pressure [Pa]
        arma::vec m_temperature; //!< Gas temperature [K]
        std::vector<Composition> m_composition; //!< Gas composition (fractions)

        // output/derived properties
        arma::vec m_heatCapacityConstantVolume; //!< Gas heat capacity at constant volume \f$c_v\f$ [J/(kg K)]
        arma::vec m_heatCapacityConstantPressure; //!< Gas heat capacity at constant pressure \f$c_p\f$ [J/(kg K)]
        arma::vec m_density; //!< Gas density [kg/m3]
        arma::vec m_viscosity; //!< Gas dynamic viscosity [Pa s] = [kg/m*s]
        arma::vec m_specificGasConstant; //!< Specific gas constant of the gas [J/(kg K)]
        arma::vec m_molarMass; //!< Molar mass of the gas [g/mol]

        arma::vec m_compressibilityFactor; //!< Compressibility factor [-]
        //! Partial derivative of the compressibility factor with respect to
        //! temperature, at constant pressure,
        //! \f$\frac{\partial Z}{\partial T}|_p\f$ [-]
        arma::vec m_temperatureDerivativeConstantPressure;
        //! Partial derivative of the compressibility factor with respect to
        //! pressure, at constant temperature,
        //! \f$\frac{\partial Z}{\partial p}|_T\f$ [-]
        arma::vec m_pressureDerivativeConstantTemperature;
        //! Partial derivative of the compressibility factor with respect to
        //! temperature, at constant density,
        //! \f$\frac{\partial Z}{\partial T}|_\rho\f$ [-]
        arma::vec m_temperatureDerivativeConstantDensity;

        arma::vec m_velocity; //!< Gas velocity [m/s]
        arma::vec m_frictionFactor; //!< Friction factor [-]
        arma::vec m_reynoldsNumber; //!< Reynolds number [-]

        arma::vec m_ambientTemperature; //!< Ambient temperature [K]
        arma::vec m_heatFlow; //!< Heat flow q [W/m2]

        std::vector<HeatTransferState> m_heatTransferState; //!< Heat transfer state
        bool m_heatTransferIsInitialized; //!< If heat transfer is initialized

        BatchTrackingState m_batchTrackingState; //!< Batch tracking state
        bool m_batchTrackingIsInitialized; //!< If batch tracking is initialized
    };
    ////////////////////////////////////////////////////////////////////////////////

    //!
    //! \brief Construct from number of grid points and pipeline length.
    //! \param nGridPoints Number of grid points
    //! \param length Total length [m]
    //!
    Pipeline(const arma::uword nGridPoints = 100, const double length = 100e3);

    arma::uword size() const                                { return m_gridPoints.n_elem; }

    //! Get current inlet and outlet state as BoundaryConditions instance.
    BoundaryConditions getBoundaryConditions() const;

    //! Enable batch tracking. Also initializes the batch tracking state.
    void enableBatchTracking();

    //! Initialize the batch tracking state.
    void initializeBatchTracking();

    //! Set the length of the pipeline. This updates the grid points and the
    //! batch tracking state.
    void setLength(const double length);

    arma::uword timestamp() const { return m_timestamp; }
    arma::uword& timestamp() { return m_timestamp; }

    const Pipeline::State& state() const { return m_state; }

    // getters
    // constants
    double length() const                                   { return m_length; }
    const arma::vec& gridPoints() const                     { return m_gridPoints; }
    const arma::vec& diameter() const                       { return m_diameter; }
    const arma::vec& height() const                         { return m_height; }
    const arma::vec& elevation() const                      { return m_height; }
    const arma::vec& roughness() const                      { return m_roughness; }

    const arma::vec& burialDepth() const                    { return m_burialDepth; }
    const std::vector<PipeWall>& pipeWall() const           { return m_pipeWall; }
    const std::vector<BurialMedium>& burialMedium() const   { return m_burialMedium; }
    const std::vector<AmbientFluid>& ambientFluid() const   { return m_ambientFluid; }

    // main/governing properties
    const arma::vec& flow() const                           { return prop().m_flow; }
    const arma::vec& pressure() const                       { return prop().m_pressure; }
    const arma::vec& temperature() const                    { return prop().m_temperature; }
    const std::vector<Composition>& composition() const     { return prop().m_composition; }

    // derived properties
    const arma::vec& heatCapacityConstantVolume() const     { return prop().m_heatCapacityConstantVolume; }
    const arma::vec& heatCapacityConstantPressure() const   { return prop().m_heatCapacityConstantPressure; }
    const arma::vec& density() const                        { return prop().m_density; }
    const arma::vec& viscosity() const                      { return prop().m_viscosity; }
    const arma::vec& specificGasConstant() const            { return prop().m_specificGasConstant; }
    const arma::vec& molarMass() const                      { return prop().m_molarMass; }

    const arma::vec& compressibilityFactor() const          { return prop().m_compressibilityFactor; }
    const arma::vec& dZdtAtConstantPressure() const         { return prop().m_temperatureDerivativeConstantPressure; }
    const arma::vec& dZdpAtConstantTemperature() const      { return prop().m_pressureDerivativeConstantTemperature; }
    const arma::vec& dZdtAtConstantDensity() const          { return prop().m_temperatureDerivativeConstantDensity; }

    const arma::vec& velocity() const                       { return prop().m_velocity; }
    const arma::vec& frictionFactor() const                 { return prop().m_frictionFactor; }
    const arma::vec& reynoldsNumber() const                 { return prop().m_reynoldsNumber; }

    const arma::vec& ambientTemperature() const             { return prop().m_ambientTemperature; }
    const arma::vec& heatFlow() const                       { return prop().m_heatFlow; }

    const std::vector<HeatTransferState>& heatTransferState() const { return prop().m_heatTransferState; }
    bool heatTransferIsInitialized() const                  { return prop().m_heatTransferIsInitialized; }

    const BatchTrackingState& batchTrackingState() const    { return prop().m_batchTrackingState; }
    bool batchTrackingIsInitialized() const                 { return prop().m_batchTrackingIsInitialized; }

    // settings
    bool constantComposition() const                        { return m_constantComposition; }

    // helpers
    //! Get inlet composition
    const arma::vec& inletComposition() const;
    //! Get outlet composition
    const arma::vec& outletComposition() const;

    // setters
    // constants
    arma::vec& gridPoints()                     { return m_gridPoints; }
    arma::vec& diameter()                       { return m_diameter; }
    arma::vec& height()                         { return m_height; }
    arma::vec& elevation()                      { return m_height; }
    arma::vec& roughness()                      { return m_roughness; }

    arma::vec& burialDepth()                    { return m_burialDepth; }
    std::vector<PipeWall>& pipeWall()           { return m_pipeWall; }
    std::vector<BurialMedium>& burialMedium()   { return m_burialMedium; }
    std::vector<AmbientFluid>& ambientFluid()   { return m_ambientFluid; }

    // main/governing properties
    arma::vec& flow()                           { return prop().m_flow; }
    arma::vec& pressure()                       { return prop().m_pressure; }
    arma::vec& temperature()                    { return prop().m_temperature; }

    // derived properties
    arma::vec& heatCapacityConstantVolume()     { return prop().m_heatCapacityConstantVolume; }
    arma::vec& heatCapacityConstantPressure()   { return prop().m_heatCapacityConstantPressure; }
    arma::vec& density()                        { return prop().m_density; }
    arma::vec& viscosity()                      { return prop().m_viscosity; }
    arma::vec& specificGasConstant()            { return prop().m_specificGasConstant; }
    arma::vec& molarMass()                      { return prop().m_molarMass; }

    arma::vec& compressibilityFactor()          { return prop().m_compressibilityFactor; }
    arma::vec& dZdtAtConstantPressure()         { return prop().m_temperatureDerivativeConstantPressure; }
    arma::vec& dZdpAtConstantTemperature()      { return prop().m_pressureDerivativeConstantTemperature; }
    arma::vec& dZdtAtConstantDensity()          { return prop().m_temperatureDerivativeConstantDensity; }

    arma::vec& velocity()                       { return prop().m_velocity; }
    arma::vec& frictionFactor()                 { return prop().m_frictionFactor; }
    arma::vec& reynoldsNumber()                 { return prop().m_reynoldsNumber; }

    arma::vec& ambientTemperature()             { return prop().m_ambientTemperature; }
    arma::vec& heatFlow()                       { return prop().m_heatFlow; }

    std::vector<HeatTransferState>& heatTransferState() { return prop().m_heatTransferState; }
    bool& heatTransferIsInitialized()           { return prop().m_heatTransferIsInitialized; }

    BatchTrackingState& batchTrackingState()    { return prop().m_batchTrackingState; }
    bool& batchTrackingIsInitialized()          { return prop().m_batchTrackingIsInitialized; }

    // settings
    bool& constantComposition()                 { return m_constantComposition; }

    /*!
     * \brief Set the composition. This also updates the batch tracking state to
     * reflect the composition change.
     * \param composition New composition at each grid point
     */
    void updateComposition(const std::vector<Composition>& composition);

    /*!
     * \brief Set the composition of the whole pipeline. This also updates the
     * batch tracking state to reflect the composition change.
     * \param composition New composition (same for the whole pipeline)
     */
    void updateComposition(const Composition& composition);

    /*!
     * \brief Set the composition without updating batch tracking state. This is
     * meant for internal use in Solver etc.
     * \param composition New composition at each grid point
     */
    void setCompositionUnsafe(const std::vector<Composition>& composition);

private:
    // constants
    double m_length; //!< Total length [m]
    arma::vec m_gridPoints; //!< Grid points [m]
    arma::vec m_diameter; //!< Inner diameter [m]
    arma::vec m_height; //!< Height profile/elevation [m]
    arma::vec m_roughness; //!< Sand grain equivalent roughness [m]

    arma::vec m_burialDepth; //!< Distance from burial medium to top of pipe [m]
    std::vector<PipeWall> m_pipeWall; //!< Pipe wall description
    std::vector<BurialMedium> m_burialMedium; //!< Burial medium description
    std::vector<AmbientFluid> m_ambientFluid; //!< Ambient fluid description

    // settings
    bool m_constantComposition; //!< If we simulate using constant composition or not

    // other
    State m_state; //!< Pipeline::State instance with all properties like flow etc.
    arma::uword m_timestamp = 0; //!< Current timestamp [s]

    // shorthand for internal use
    const State& prop() const { return m_state; }
    State& prop() { return m_state; }
};