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

// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2013 Adam Wulkiewicz, Lodz, Poland.

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

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

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

#ifndef BOOST_GEOMETRY_STRATEGY_AGNOSTIC_POINT_IN_POLY_WINDING_HPP
#define BOOST_GEOMETRY_STRATEGY_AGNOSTIC_POINT_IN_POLY_WINDING_HPP

#include <boost/core/ignore_unused.hpp>

#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/select_calculation_type.hpp>

#include <boost/geometry/strategies/side.hpp>
#include <boost/geometry/strategies/covered_by.hpp>
#include <boost/geometry/strategies/within.hpp>

namespace geofeatures_boost {} namespace boost = geofeatures_boost; namespace geofeatures_boost { namespace geometry
{

namespace strategy { namespace within
{

// Fix for https://svn.boost.org/trac/boost/ticket/9628
// For floating point coordinates, the <1> coordinate of a point is compared
// with the segment's points using some EPS. If the coordinates are "equal"
// the sides are calculated. Therefore we can treat a segment as a long areal
// geometry having some width. There is a small ~triangular area somewhere
// between the segment's effective area and a segment's line used in sides
// calculation where the segment is on the one side of the line but on the
// other side of a segment (due to the width).
// For the s1 of a segment going NE the real side is RIGHT but the point may
// be detected as LEFT, like this:
//                     RIGHT
//                 ___----->
//                  ^      O Pt  __ __
//                 EPS     __ __
//                  v__ __ BUT DETECTED AS LEFT OF THIS LINE
//             _____7
//       _____/
// _____/
template <typename CSTag>
struct winding_side_equal
{
    typedef typename strategy::side::services::default_strategy
        <
            CSTag
        >::type strategy_side_type;

    template <size_t D, typename Point, typename PointOfSegment>
    static inline int apply(Point const& point,
                            PointOfSegment const& se,
                            int count)
    {
        // Create a vertical segment intersecting the original segment's endpoint
        // equal to the point, with the derived direction (UP/DOWN).
        // Set only the 2 first coordinates, the other ones are ignored
        PointOfSegment ss1, ss2;
        set<1-D>(ss1, get<1-D>(se));
        set<1-D>(ss2, get<1-D>(se));
        if (count > 0) // UP
        {
            set<D>(ss1, 0);
            set<D>(ss2, 1);
        }
        else // DOWN
        {
            set<D>(ss1, 1);
            set<D>(ss2, 0);
        }
        // Check the side using this vertical segment
        return strategy_side_type::apply(ss1, ss2, point);
    }
};

// The optimization for cartesian
template <>
struct winding_side_equal<cartesian_tag>
{
    template <size_t D, typename Point, typename PointOfSegment>
    static inline int apply(Point const& point,
                            PointOfSegment const& se,
                            int count)
    {
        return math::equals(get<1-D>(point), get<1-D>(se)) ?
                0 :
                get<1-D>(point) < get<1-D>(se) ?
                    // assuming count is equal to 1 or -1
                    count : // ( count > 0 ? 1 : -1) :
                    -count; // ( count > 0 ? -1 : 1) ;
    }
};

template <typename CSTag>
struct winding_side_between
{
    typedef typename strategy::side::services::default_strategy
        <
            CSTag
        >::type strategy_side_type;

    template <size_t D, typename Point, typename PointOfSegment>
    static inline int apply(Point const& point,
                            PointOfSegment const& s1, PointOfSegment const& s2,
                            int count)
    {
        // Create a vertical segment intersecting the original segment's endpoint
        // equal to the point, with the derived direction (UP/DOWN).
        // Set only the 2 first coordinates, the other ones are ignored
        PointOfSegment ss1, ss2;
        set<1-D>(ss1, get<1-D>(s1));
        set<1-D>(ss2, get<1-D>(s1));

        if (count > 0) // UP
        {
            set<D>(ss1, 0);
            set<D>(ss2, 1);
        }
        else // DOWN
        {
            set<D>(ss1, 1);
            set<D>(ss2, 0);
        }

        int const seg_side = strategy_side_type::apply(ss1, ss2, s2);

        if (seg_side != 0) // segment not vertical
        {
            if (strategy_side_type::apply(ss1, ss2, point) == -seg_side) // point on the opposite side than s2
            {
                return -seg_side;
            }
            else
            {
                set<1-D>(ss1, get<1-D>(s2));
                set<1-D>(ss2, get<1-D>(s2));

                if (strategy_side_type::apply(ss1, ss2, point) == seg_side) // point behind s2
                {
                    return seg_side;
                }
            }
        }

        // segment is vertical or point is between p1 and p2
        return strategy_side_type::apply(s1, s2, point);
    }
};

// The specialization for cartesian
template <>
struct winding_side_between<cartesian_tag>
{
    typedef strategy::side::services::default_strategy
        <
            cartesian_tag
        >::type strategy_side_type;

    template <size_t D, typename Point, typename PointOfSegment>
    static inline int apply(Point const& point,
                            PointOfSegment const& s1, PointOfSegment const& s2,
                            int /*count*/)
    {
        return strategy_side_type::apply(s1, s2, point);
    }
};

/*!
\brief Within detection using winding rule
\ingroup strategies
\tparam Point \tparam_point
\tparam PointOfSegment \tparam_segment_point
\tparam CalculationType \tparam_calculation
\author Barend Gehrels
\note The implementation is inspired by terralib http://www.terralib.org (LGPL)
\note but totally revised afterwards, especially for cases on segments
\note Only dependant on "side", -> agnostic, suitable for spherical/latlong

\qbk{
[heading See also]
[link geometry.reference.algorithms.within.within_3_with_strategy within (with strategy)]
}
 */
template
<
    typename Point,
    typename PointOfSegment = Point,
    typename CalculationType = void
>
class winding
{
    typedef typename select_calculation_type
        <
            Point,
            PointOfSegment,
            CalculationType
        >::type calculation_type;

    typedef typename strategy::side::services::default_strategy
        <
            typename cs_tag<Point>::type
        >::type strategy_side_type;

    /*! subclass to keep state */
    class counter
    {
        int m_count;
        bool m_touches;

        inline int code() const
        {
            return m_touches ? 0 : m_count == 0 ? -1 : 1;
        }

    public :
        friend class winding;

        inline counter()
            : m_count(0)
            , m_touches(false)
        {}

    };

    template <size_t D>
    static inline int check_touch(Point const& point,
                PointOfSegment const& seg1, PointOfSegment const& seg2,
                counter& state)
    {
        calculation_type const p = get<D>(point);
        calculation_type const s1 = get<D>(seg1);
        calculation_type const s2 = get<D>(seg2);
        if ((s1 <= p && s2 >= p) || (s2 <= p && s1 >= p))
        {
            state.m_touches = true;
        }
        return 0;
    }

    template <size_t D>
    static inline int check_segment(Point const& point,
                PointOfSegment const& seg1, PointOfSegment const& seg2,
                counter& state, bool& eq1, bool& eq2)
    {
        calculation_type const p = get<D>(point);
        calculation_type const s1 = get<D>(seg1);
        calculation_type const s2 = get<D>(seg2);

        // Check if one of segment endpoints is at same level of point
        eq1 = math::equals(s1, p);
        eq2 = math::equals(s2, p);

        if (eq1 && eq2)
        {
            // Both equal p -> segment is horizontal (or vertical for D=0)
            // The only thing which has to be done is check if point is ON segment
            return check_touch<1 - D>(point, seg1, seg2, state);
        }

        return
              eq1 ? (s2 > p ?  1 : -1)  // Point on level s1, UP/DOWN depending on s2
            : eq2 ? (s1 > p ? -1 :  1)  // idem
            : s1 < p && s2 > p ?  2     // Point between s1 -> s2 --> UP
            : s2 < p && s1 > p ? -2     // Point between s2 -> s1 --> DOWN
            : 0;
    }

public :

    // Typedefs and static methods to fulfill the concept
    typedef Point point_type;
    typedef PointOfSegment segment_point_type;
    typedef counter state_type;

    static inline bool apply(Point const& point,
                PointOfSegment const& s1, PointOfSegment const& s2,
                counter& state)
    {
        typedef typename cs_tag<Point>::type cs_t;

        bool eq1 = false;
        bool eq2 = false;
        geofeatures_boost::ignore_unused(eq2);

        int count = check_segment<1>(point, s1, s2, state, eq1, eq2);
        if (count != 0)
        {
            int side = 0;
            if (count == 1 || count == -1)
            {
                side = winding_side_equal<cs_t>
                            ::template apply<1>(point, eq1 ? s1 : s2, count);
            }
            else // count == 2 || count == -2
            {
                side = winding_side_between<cs_t>
                            ::template apply<1>(point, s1, s2, count);
            }

            if (side == 0)
            {
                // Point is lying on segment
                state.m_touches = true;
                state.m_count = 0;
                return false;
            }

            // Side is NEG for right, POS for left.
            // The count is -2 for down, 2 for up (or -1/1)
            // Side positive thus means UP and LEFTSIDE or DOWN and RIGHTSIDE
            // See accompagnying figure (TODO)
            if (side * count > 0)
            {
                state.m_count += count;
            }
        }
        return ! state.m_touches;
    }

    static inline int result(counter const& state)
    {
        return state.code();
    }
};

#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS

namespace services
{

// Register using "areal_tag" for ring, polygon, multi-polygon
template <typename AnyTag, typename Point, typename Geometry>
struct default_strategy<point_tag, AnyTag, point_tag, areal_tag, cartesian_tag, cartesian_tag, Point, Geometry>
{
    typedef winding<Point, typename geometry::point_type<Geometry>::type> type;
};

template <typename AnyTag, typename Point, typename Geometry>
struct default_strategy<point_tag, AnyTag, point_tag, areal_tag, spherical_tag, spherical_tag, Point, Geometry>
{
    typedef winding<Point, typename geometry::point_type<Geometry>::type> type;
};

// TODO: use linear_tag and pointlike_tag the same way how areal_tag is used

template <typename Point, typename Geometry, typename AnyTag>
struct default_strategy<point_tag, AnyTag, point_tag, AnyTag, cartesian_tag, cartesian_tag, Point, Geometry>
{
    typedef winding<Point, typename geometry::point_type<Geometry>::type> type;
};

template <typename Point, typename Geometry, typename AnyTag>
struct default_strategy<point_tag, AnyTag, point_tag, AnyTag, spherical_tag, spherical_tag, Point, Geometry>
{
    typedef winding<Point, typename geometry::point_type<Geometry>::type> type;
};

} // namespace services

#endif

}} // namespace strategy::within

#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
namespace strategy { namespace covered_by { namespace services
{

// Register using "areal_tag" for ring, polygon, multi-polygon
template <typename AnyTag, typename Point, typename Geometry>
struct default_strategy<point_tag, AnyTag, point_tag, areal_tag, cartesian_tag, cartesian_tag, Point, Geometry>
{
    typedef strategy::within::winding<Point, typename geometry::point_type<Geometry>::type> type;
};

template <typename AnyTag, typename Point, typename Geometry>
struct default_strategy<point_tag, AnyTag, point_tag, areal_tag, spherical_tag, spherical_tag, Point, Geometry>
{
    typedef strategy::within::winding<Point, typename geometry::point_type<Geometry>::type> type;
};

// TODO: use linear_tag and pointlike_tag the same way how areal_tag is used

template <typename Point, typename Geometry, typename AnyTag>
struct default_strategy<point_tag, AnyTag, point_tag, AnyTag, cartesian_tag, cartesian_tag, Point, Geometry>
{
    typedef strategy::within::winding<Point, typename geometry::point_type<Geometry>::type> type;
};

template <typename Point, typename Geometry, typename AnyTag>
struct default_strategy<point_tag, AnyTag, point_tag, AnyTag, spherical_tag, spherical_tag, Point, Geometry>
{
    typedef strategy::within::winding<Point, typename geometry::point_type<Geometry>::type> type;
};

}}} // namespace strategy::covered_by::services
#endif

}} // namespace geofeatures_boost::geometry

#endif // BOOST_GEOMETRY_STRATEGY_AGNOSTIC_POINT_IN_POLY_WINDING_HPP


/**
 * @file
 * This file is part of SeisSol.
 *
 * @author Sebastian Rettenberger (sebastian.rettenberger @ tum.de, http://www5.in.tum.de/wiki/index.php/Sebastian_Rettenberger)
 *
 * @section LICENSE
 * Copyright (c) 2015, SeisSol Group
 * 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.
 *
 * 3. Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * @section DESCRIPTION
 **/

#include <cassert>
#include <cmath>

#include <hdf5.h>

#include <Eigen/Dense>

#include "utils/args.h"

#include "Geometry/refinement/RefinerUtils.h"

template<typename T>
T pow2(T v)
{
	return v*v;
}

int main(int argc, char* argv[])
{
	utils::Args args;
	args.addOption("diff", 'd', "Maximum allowed difference (Default 0.001)", utils::Args::Required, false);
	args.addOption("verbose", 'V', "Enable verbose output", utils::Args::No, false);
	args.addAdditionalOption("output.h5", "The SeisSol wave field output file");

	switch (args.parse(argc, argv)) {
	case utils::Args::Error:
		return 1;
	case utils::Args::Help:
		return 127;
	default:
		break;
	}

	bool verbose = args.getArgument("verbose", false);

	hid_t hdfFile = H5Fopen(args.getAdditionalArgument<const char*>("output.h5"),
			H5F_ACC_RDONLY, H5P_DEFAULT);

	// Read vertices
	hid_t vertData = H5Dopen(hdfFile, "/geometry", H5P_DEFAULT);
	hid_t space = H5Dget_space(vertData);
	hsize_t dims[2]; // Assuming to dimensions
	H5Sget_simple_extent_dims(space, dims, 0L);

	double* vertices = new double[dims[0]*dims[1]];
	H5Dread(vertData, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, vertices);

	H5Sclose(space);
	H5Dclose(vertData);

	// Read cells
	hid_t cellData = H5Dopen(hdfFile, "/connect", H5P_DEFAULT);
	space = H5Dget_space(cellData);
	H5Sget_simple_extent_dims(space, dims, 0L);

	unsigned long* cells = new unsigned long[dims[0]*dims[1]];
	H5Dread(cellData, H5T_NATIVE_ULONG, H5S_ALL, H5S_ALL, H5P_DEFAULT, cells);

	hsize_t numCells = dims[0];

	H5Sclose(space);
	H5Dclose(cellData);

	// Read the 9 variables
	double* var[9];
	const char* varNames[9] = {"/sigma_xx", "/sigma_yy", "/sigma_zz", "/sigma_xy", "/sigma_yz", "/sigma_xz", "/u", "/v", "/w"};

	hid_t memSpace = H5Screate_simple(1, &numCells, &numCells);
	H5Sselect_all(memSpace);

	for (unsigned int i = 0; i < 9; i++) {
		hid_t varData = H5Dopen(hdfFile, varNames[i], H5P_DEFAULT);
		space = H5Dget_space(varData);
		H5Sget_simple_extent_dims(space, dims, 0L);

		assert(dims[1] == numCells);
		var[i] = new double[dims[1]];

		hsize_t start[2] = {0, 0};
		hsize_t count[2] = {1, numCells};
		H5Sselect_hyperslab(space, H5S_SELECT_SET, start, 0L, count, 0L);

		H5Dread(varData, H5T_NATIVE_DOUBLE, memSpace, space, H5P_DEFAULT, var[i]);

		H5Sclose(space);
		H5Dclose(varData);
	}

	H5Sclose(memSpace);

	H5Fclose(hdfFile);

	// Compute parameters
	const double maxDiff = args.getArgument("diff", 0.001);

      Eigen::Vector3d vec_n(1, 1, 1);
      const double n = 1.0/(vec_n.norm());
      const double mu = 1.0;
      const double lambda = 2.0;
      const double rho = 1.0;

      const double r1[9] = {
                      rho * (-2.0*pow2(n)*mu - 2.0*pow2(n)*mu + lambda + 2.0*mu),
			rho * (2.0*pow2(n)*mu + lambda),
			rho * (2.0*pow2(n)*mu + lambda),
			2.0 * n * mu * n * rho,
			2.0 * mu * n * rho * n,
			2.0 * mu * n * rho * n,
			n * sqrt(rho * (lambda + 2.0*mu)),
			n * sqrt(rho * (lambda + 2.0*mu)),
			sqrt(rho * (lambda + 2.0*mu)) * n
	};

	const double r8[9] = {
			2.0 * mu * n * rho * pow2(n) * n,
			-2.0 * mu * n * rho * pow2(n) * n,
			0,
			mu * rho * n * (2.0*pow2(n) + pow2(n) - 1) * n,
			-pow2(n) * mu * rho * pow2(n),
			mu * n * pow2(n) * rho * n,
			-n * n * n * sqrt(rho*mu),
			pow2(n) * n * sqrt(rho*mu),
			0
	};

	const Eigen::Vector3d k = 2.0 * M_PI / 100 * Eigen::Vector3d(1, 1, 1);

	// Check cells
	bool failed = false;

	for (unsigned int i = 0; i < numCells; i++) {
		const Eigen::Vector3d a = Eigen::Vector3d(&vertices[cells[i*4] * 3]);
		const Eigen::Vector3d b = Eigen::Vector3d(&vertices[cells[i*4 + 1] * 3]);
		const Eigen::Vector3d c = Eigen::Vector3d(&vertices[cells[i*4 + 2] * 3]);
		const Eigen::Vector3d d = Eigen::Vector3d(&vertices[cells[i*4 + 3] * 3]);

		seissol::refinement::Tetrahedron<double> tet(a, b, c, d);
		const Eigen::Vector3d center = tet.center();

		const double kx = k.dot(center);

		for (unsigned int j = 0; j < 9; j++) {
			const double value = r1[j]*sin(kx) + r8[j]*sin(kx);

			if (fabs(var[j][i] - value) > maxDiff) {
				failed = true;
				if (verbose)
					std::cout << var[j][i] << ' ' << value << ' ' << fabs(var[j][i] - value) << ' ';
			} else {
				if (verbose)
					std::cout << "ok" << ' ';
			}
		}

		if (verbose)
			std::cout << std::endl;

		if (failed && !verbose)
			// We can stop in this case
			break;
	}

	// Cleanup
	for (unsigned int i = 0; i < 9; i++) {
		delete [] var[i];
	}

	delete [] vertices;
	delete [] cells;

	return failed ? 1 : 0;
}


//=======================================================================
// Copyright (c) 2013 Piotr Wygocki
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================
/**
 * @file facility_location_swap.hpp
* @brief
 * @author Piotr Wygocki
 * @version 1.0
 * @date 2013-07-08
 */
#ifndef PAAL_FACILITY_LOCATION_SWAP_HPP
#define PAAL_FACILITY_LOCATION_SWAP_HPP

#include "paal/data_structures/facility_location/facility_location_solution_traits.hpp"
#include "paal/utils/type_functions.hpp"
#include "paal/data_structures/combine_iterator.hpp"

#include <boost/iterator/iterator_adaptor.hpp>
#include <boost/iterator/transform_iterator.hpp>

#include <cassert>
#include <vector>
#include <numeric>
#include <cstdlib>

namespace paal {
namespace local_search {

/**
 * @brief swap type
 *
 * @tparam T
 */
template <typename T> class Swap {
  public:
    /**
     * @brief constructor
     *
     * @param from
     * @param to
     */
    Swap(T from, T to) : m_from(from), m_to(to) {}

    Swap() = default;

    /**
     * @brief from getter
     *
     * @return
     */
    T get_from() const { return m_from; }

    /**
     * @brief to getter
     *
     * @return
     */
    T get_to() const { return m_to; }

    /**
     * @brief form setter
     *
     * @param from
     */
    void set_from(T from) { m_from = from; }

    /**
     * @brief from setter
     *
     * @param to
     */
    void set_to(T to) { m_to = to; }

  private:
    T m_from;
    T m_to;
};

/// operator() creates Swap  from (from, to)
struct make_swap {
    /// operator()
    template <typename T> Swap<T> operator()(T from, T to) const {
        return Swap<T>(from, to);
    }
};

/**
 * @brief gain functor for swap in facility location problem.
 *
 * @tparam VertexType
 */
struct facility_location_gain_swap {
    /**
     * @brief operator()
     *
     * @tparam Solution
     * @param sol
     * @param s
     *
     * @return
     */
    template <class Solution, class VertexType>
    auto operator()(Solution &sol, const Swap<VertexType> &s) const {

        auto ret = sol.add_facility_tentative(s.get_to());
        ret += sol.remove_facility_tentative(s.get_from());
        auto back = sol.add_facility_tentative(s.get_from());
        back += sol.remove_facility_tentative(s.get_to());
        assert(ret == -back);
        return -ret;
    }
};

/**
 * @brief commit functor for facility location problem
 */
struct facility_location_commit_swap {
    /**
     * @brief operator()
     *
     * @tparam Solution
     * @param sol
     * @param s
     */
    template <typename Solution, typename VertexType>
    bool operator()(Solution &sol, const Swap<VertexType> &s) const {
        sol.add_facility(s.get_to());
        sol.remove_facility(s.get_from());
        return true;
    }
};

/**
 * @brief get moves functor for facility location problem
 */
struct facility_locationget_moves_swap {

    /// operator()
    template <typename Solution>
    auto operator()(const Solution &s) const  {
        auto begin = data_structures::make_combine_iterator(
            make_swap{}, s.getChosenCopy(), s.getUnchosenCopy());
        decltype(begin) end;
        return boost::make_iterator_range(begin, end);
    }
};

} // local_search
} // paal

#endif // PAAL_FACILITY_LOCATION_SWAP_HPP


// Copyright 2006. Peter Gottschling, Matthias Troyer, Rolf Bonderer
// Software License for MTL
//
// Copyright (c) 2007 The Trustees of Indiana University.
//               2008 Dresden University of Technology and the Trustees of Indiana University.
//               2010 SimuNova UG (haftungsbeschränkt), www.simunova.com.
// All rights reserved.
// Authors: Peter Gottschling and Andrew Lumsdaine
//
// This file is part of the Matrix Template Library
//
// See also license.mtl.txt in the distribution.

#ifndef LA_CONCEPTS_INCLUDE
#define LA_CONCEPTS_INCLUDE

#include <boost/config/concept_macros.hpp>

#ifdef __GXX_CONCEPTS__
#  include <concepts>
#else
#  ifdef LA_SHOW_WARNINGS
#    warning "Concepts are not used"
#  endif
#endif

#include <boost/numeric/linear_algebra/identity.hpp>
#include <boost/numeric/linear_algebra/is_invertible.hpp>
#include <boost/numeric/linear_algebra/inverse.hpp>
#include <boost/numeric/linear_algebra/operators.hpp>
#include <boost/numeric/linear_algebra/algebraic_concepts.hpp>
#include <complex>

// If desired one can disable the default concept maps with LA_NO_CONCEPT_MAPS

// We consider to change the namespace from math to numeric
// More precisely, the concepts may be moved into namespace numeric and the standard functions stay in math

/// Namespace for mathematical concepts
/** In contrast to the ones in algebra the concepts can require
    basic implementation concepts like std::CopyAssignable */
namespace math {

#ifdef __GXX_CONCEPTS__

// ==================================
// Classification of Arithmetic Types
// ==================================

// We omit this now because it became part of the standard concepts

#if 0

// In addtion to std::Integral
concept Float<typename T>
  : std::DefaultConstructible<T>, std::CopyConstructible<T>,
    std::LessThanComparable<T>, std::EqualityComparable<T>
{
  T operator+(T);
  T operator+(T, T);
  T& operator+=(T&, T);
  T operator-(T, T);
  T operator-(T);
  T& operator-=(T&, T);
  T operator*(T, T);
  T& operator*=(T&, T);
  T operator/(T, T);
  T& operator/=(T&, T);

  requires std::CopyAssignable<T>
        && std::SameType<std::CopyAssignable<T>::result_type, T&>;
}

concept_map Float<float> {}
concept_map Float<double> {}
concept_map Float<long double> {}

// The difference to Float is the lack of LessThanComparable
concept Complex<typename T>
  : std::DefaultConstructible<T>, std::CopyConstructible<T>,
    std::EqualityComparable<T>
{
  T operator+(T);
  T operator+(T, T);
  T& operator+=(T&, T);
  T operator-(T, T);
  T operator-(T);
  T& operator-=(T&, T);
  T operator*(T, T);
  T& operator*=(T&, T);
  T operator/(T, T);
  T& operator/=(T&, T);

  requires std::CopyAssignable<T>
        && std::SameType<std::CopyAssignable<T>::result_type, T&>;
}

template <typename T>
  requires Float<T>
concept_map Complex<std::complex<T> > {}

// TBD: Concept Arithmetic is useless like this, it should have operations and then be the base for
// Integral, Float and Complex
concept Arithmetic<typename T> {}

template <typename T>
  requires std::Integral<T>
concept_map Arithmetic<T> {}

template <typename T>
  requires Float<T>
concept_map Arithmetic<T> {}

template <typename T>
  requires Arithmetic<T>
concept_map Arithmetic< std::complex<T> > {}

#endif

// The following concepts are used to classify intrinsic arithmetic types.
// The standard concepts already define the syntactic requirements,
// i.e. the interface.
// However they sey nothing about the semantics.
// Therefore, user-defined types can model the syntactic/interface
// requirements while still having a different mathematical behavior.
// For that reason, we introduce concepts that are only used for intrinsic types.
// For them we can define concept_maps regarding semantic behavior as monoids.

concept IntrinsicSignedIntegral<typename T>
  : std::SignedIntegralLike<T>
{}

concept IntrinsicUnsignedIntegral<typename T>
  : std::UnsignedIntegralLike<T>
{}

concept IntrinsicFloatingPoint<typename T>
  : std::FloatingPointLike<T>
{}

// Intrinsic types are chategorized:

concept_map IntrinsicSignedIntegral<char> {}
concept_map IntrinsicSignedIntegral<signed char> {}
concept_map IntrinsicUnsignedIntegral<unsigned char> {}
concept_map IntrinsicSignedIntegral<short> {}
concept_map IntrinsicUnsignedIntegral<unsigned short> {}
concept_map IntrinsicSignedIntegral<int> {}
concept_map IntrinsicUnsignedIntegral<unsigned int> {}
concept_map IntrinsicSignedIntegral<long> {}
concept_map IntrinsicUnsignedIntegral<unsigned long> {}
concept_map IntrinsicSignedIntegral<long long> {}
concept_map IntrinsicUnsignedIntegral<unsigned long long> {}

concept_map IntrinsicFloatingPoint<float> {}
concept_map IntrinsicFloatingPoint<double> {}

// ================
// Utility Concepts
// ================

// Concepts for functions mapping to same type or convertible
auto concept UnaryIsoFunction<typename Operation, typename Element>
{
    requires std::Callable1<Operation, Element>;
    requires std::Convertible<std::Callable1<Operation, Element>::result_type, Element>;

    typename result_type = std::Callable1<Operation, Element>::result_type;
};

auto concept BinaryIsoFunction<typename Operation, typename Element>
{
    requires std::Callable2<Operation, Element, Element>;
    requires std::Convertible<std::Callable2<Operation, Element, Element>::result_type, Element>;

    typename result_type = std::Callable2<Operation, Element, Element>::result_type;
};

#if 0
auto concept CompatibleBinaryFunction<typename A1, typename A2, typename Result>
{
    typename result_type;
    result_type F(A1, A2);
    requires std::Convertible<result_type, Result>;
}
#endif

// ==================
// Algebraic Concepts
// ==================

auto concept Magma<typename Operation, typename Element>
    : BinaryIsoFunction<Operation, Element>
{
    requires std::CopyAssignable<Element>
	  && std::CopyAssignable<Element, BinaryIsoFunction<Operation, Element>::result_type>;
};

// For algebraic structures that are commutative but not associative
// As an example floating point numbers are commutative but not associative
//   w.r.t. addition and multiplication
auto concept CommutativeMagma<typename Operation, typename Element>
  : Magma<Operation, Element>,
    algebra::Commutative<Operation, Element>
{};

// SemiGroup is a refinement which must be nominal
auto concept SemiGroup<typename Operation, typename Element>
  : Magma<Operation, Element>,
    algebra::SemiGroup<Operation, Element>
{};

auto concept CommutativeSemiGroup<typename Operation, typename Element>
  : SemiGroup<Operation, Element>,
    CommutativeMagma<Operation, Element>
{};

// Adding identity
// auto
concept Monoid<typename Operation, typename Element>
  : SemiGroup<Operation, Element>,
    algebra::Monoid<Operation, Element>
{
    requires std::Convertible<identity_result_type, Element>;
};

auto concept CommutativeMonoid<typename Operation, typename Element>
  : CommutativeSemiGroup<Operation, Element>,
    Monoid<Operation, Element>
{};

concept PartiallyInvertibleMonoid<typename Operation, typename Element>
  : Monoid<Operation, Element>,
    algebra::Inversion<Operation, Element>
{
    typename is_invertible_result_type;
    is_invertible_result_type is_invertible(Operation, Element);
    requires std::Convertible<is_invertible_result_type, bool>;

    requires std::Convertible<inverse_result_type, Element>;

    // Does it overwrites the axiom from algebra::Inversion
    axiom Inversivity(Operation op, Element x)
    {
	// Only for invertible elements:
	if (is_invertible(op, x))
	    op( x, inverse(op, x) ) == identity(op, x);
	if ( is_invertible(op, x) )
	    op( inverse(op, x), x ) == identity(op, x);
    }
};

auto concept PartiallyInvertibleCommutativeMonoid<typename Operation, typename Element>
  : PartiallyInvertibleMonoid<Operation, Element>,
    CommutativeMonoid<Operation, Element>
{};

concept Group<typename Operation, typename Element>
  : PartiallyInvertibleMonoid<Operation, Element>,
    algebra::Group<Operation, Element>
{
    axiom AlwaysInvertible(Operation op, Element x)
    {
	is_invertible(op, x);
    }

    axiom GlobalInversivity(Operation op, Element x)
    {
	// In fact this is implied by AlwaysInvertible and inherited Inversion axiom
	// However, we don't rely on the compiler to deduce this
	op( x, inverse(op, x) ) == identity(op, x);
	op( inverse(op, x), x ) == identity(op, x);
    }
};

auto concept AbelianGroup<typename Operation, typename Element>
  : Group<Operation, Element>,
    PartiallyInvertibleCommutativeMonoid<Operation, Element>,
    algebra::AbelianGroup<Operation, Element>
{};

// ========================
// Additive scalar concepts
// ========================

concept AdditiveMagma<typename Element>
  : Magma< math::add<Element>, Element >
{
    typename plus_assign_result_type;
    plus_assign_result_type operator+=(Element& x, Element y);
    // requires std::Convertible<plus_assign_result_type, Element>;

    // Operator + is by default defined with +=
    typename addition_result_type;
    addition_result_type operator+(Element x, Element y);
#if 0
    {
	Element tmp(x);
	return tmp += y;                      defaults NYS
    }
#endif
    requires std::Convertible<addition_result_type, Element>;

    // Type consistency with Magma
    requires std::Convertible< addition_result_type,
                            Magma< math::add<Element>, Element >::result_type>;

    // SameType requires more rigorous specializations on pure algebraic functors
    // requires std::SameType< addition_result_type,
    // 	                    Magma< math::add<Element>, Element >::result_type>;

    axiom Consistency(math::add<Element> op, Element x, Element y)
    {
	op(x, y) == x + y;

	// Consistency definition between + and += might change later
        x + y == x += y;
	// Element tmp = x; tmp+= y; tmp == x + y; not proposal-compliant
    }
}

auto concept AdditiveCommutativeMagma<typename Element>
  : AdditiveMagma<Element>,
    CommutativeMagma< math::add<Element>, Element >
{};

auto concept AdditiveSemiGroup<typename Element>
  : AdditiveMagma<Element>,
    SemiGroup< math::add<Element>, Element >
{};

// We really need only one of the additive concepts for the requirements,
// the requirements of the other would be implied.
// Vice versa, to derive concept maps of nested concepts from
// concept maps of refined concepts, they are needed all.
auto concept AdditiveCommutativeSemiGroup<typename Element>
  : AdditiveSemiGroup<Element>,
    AdditiveCommutativeMagma<Element>,
    CommutativeSemiGroup< math::add<Element>, Element >
{};

concept AdditiveMonoid<typename Element>
  : AdditiveSemiGroup<Element>,
    Monoid< math::add<Element>, Element >
{
    Element zero(Element v);

    axiom Consistency (math::add<Element> op, Element x)
    {
	zero(x) == identity(op, x);
    }
};

// We really need only one of the additive concepts for the requirements,
// the requirements of the other would be implied.
// Vice versa, to derive concept maps of nested concepts from
// concept maps of refined concepts, they are needed all.
auto concept AdditiveCommutativeMonoid<typename Element>
  : AdditiveMonoid<Element>,
    AdditiveCommutativeSemiGroup<Element>,
    CommutativeMonoid< math::add<Element>, Element >
{};

concept AdditivePartiallyInvertibleMonoid<typename Element>
  : AdditiveMonoid<Element>,
    PartiallyInvertibleMonoid< math::add<Element>, Element >
{
    typename minus_assign_result_type;
    minus_assign_result_type operator-=(Element& x, Element y);
    // requires std::Convertible<minus_assign_result_type, Element>;

    // Operator - by default defined with -=
    typename subtraction_result_type;
    subtraction_result_type operator-(Element& x, Element y);
#if 0
    {
	Element tmp(x);
	return tmp -= y;                      defaults NYS
    }
#endif
    requires std::Convertible<subtraction_result_type, Element>;

    typename unary_result_type;
    unary_result_type operator-(Element x);
#if 0
    {
	return zero(x) - x;      defaults NYS
    }
#endif
    requires std::Convertible<unary_result_type, Element>;

    axiom Consistency(math::add<Element> op, Element x, Element y)
    {
	// consistency between additive and pure algebraic concept
	if ( is_invertible(op, y) )
	    op(x, inverse(op, y)) == x - y;
	if ( is_invertible(op, y) )
	    inverse(op, y) == -y;

	// consistency between unary and binary -
	if ( is_invertible(op, x) )
	    identity(op, x) - x == -x;

	// Might change later
	if ( is_invertible(op, y) )
	    x - y == x -= y;
	// Element tmp = x; tmp-= y; tmp == x - y; not proposal-compliant
    }

};

auto concept AdditivePartiallyInvertibleCommutativeMonoid<typename Element>
  : AdditivePartiallyInvertibleMonoid<Element>,
    AdditiveCommutativeMonoid<Element>,
    PartiallyInvertibleCommutativeMonoid< math::add<Element>, Element >
{};

auto concept AdditiveGroup<typename Element>
  : AdditivePartiallyInvertibleMonoid<Element>,
    Group< math::add<Element>, Element >
{};

auto concept AdditiveAbelianGroup<typename Element>
  : AdditiveGroup<Element>,
    AdditiveCommutativeMonoid<Element>,
    AbelianGroup< math::add<Element>, Element >
{};

// ============================
// Multiplitive scalar concepts
// ============================

concept MultiplicativeMagma<typename Element>
  : Magma< math::mult<Element>, Element >
{
    typename mult_assign_result_type;
    mult_assign_result_type operator*=(Element& x, Element y);
    // requires std::Convertible<mult_assign_result_type, Element>;

    // Operator * is by default defined with *=
    typename mult_result_type;
    mult_result_type operator*(Element x, Element y);
#if 0
    {
	Element tmp(x);
	return tmp *= y;                      defaults NYS
    }
#endif
    requires std::Convertible<mult_result_type, Element>;

    // Type consistency with Magma
    requires std::Convertible< mult_result_type,
                            Magma< math::mult<Element>, Element >::result_type>;

    // SameType requires more rigorous specializations on pure algebraic functors
    // requires std::SameType< mult_result_type,
    // 	                    Magma< math::mult<Element>, Element >::result_type>;

    axiom Consistency(math::mult<Element> op, Element x, Element y)
    {
	op(x, y) == x * y;

	// Consistency definition between * and *= might change later
        x * y == x *= y;
	// Element tmp = x; tmp*= y; tmp == x * y; not proposal-compliant
    }

}

auto concept MultiplicativeSemiGroup<typename Element>
  : MultiplicativeMagma<Element>,
    SemiGroup< math::mult<Element>, Element >
{};

auto concept MultiplicativeCommutativeSemiGroup<typename Element>
  : MultiplicativeSemiGroup<Element>,
    CommutativeSemiGroup< math::mult<Element>, Element >
{};

concept MultiplicativeMonoid<typename Element>
  : MultiplicativeSemiGroup<Element>,
    Monoid< math::mult<Element>, Element >
{
    Element one(Element v);

    axiom Consistency (math::mult<Element> op, Element x)
    {
	one(x) == identity(op, x);
    }
};

auto concept MultiplicativeCommutativeMonoid<typename Element>
  : MultiplicativeMonoid<Element>,
    MultiplicativeCommutativeSemiGroup<Element>,
    CommutativeMonoid< math::mult<Element>, Element >
{};

concept MultiplicativePartiallyInvertibleMonoid<typename Element>
  : MultiplicativeMonoid<Element>,
    PartiallyInvertibleMonoid< math::mult<Element>, Element >
{
    typename divide_assign_result_type;
    divide_assign_result_type operator/=(Element& x, Element y);
    // requires std::Convertible<divide_assign_result_type, Element>;

    // Operator / by default defined with /=
    typename division_result_type = Element;
    division_result_type operator/(Element x, Element y);
#if 0
    {
	Element tmp(x);
	return tmp /= y;                      defaults NYS
    }
#endif
    requires std::Convertible<division_result_type, Element>;

    axiom Consistency(math::mult<Element> op, Element x, Element y)
    {
	// consistency between multiplicative and pure algebraic concept
	if ( is_invertible(op, y) )
	    op(x, inverse(op, y)) == x / y;

	// Consistency between / and /=, might change later
	if ( is_invertible(op, y) )
	    x / y == x /= y;
	// Element tmp = x; tmp/= y; tmp == x / y; not proposal-compliant
    }
};

auto concept MultiplicativePartiallyInvertibleCommutativeMonoid<typename Element>
  : MultiplicativePartiallyInvertibleMonoid<Element>,
    MultiplicativeCommutativeMonoid<Element>,
    PartiallyInvertibleCommutativeMonoid< math::mult<Element>, Element >
{};

auto concept MultiplicativeGroup<typename Element>
  : MultiplicativeMonoid<Element>,
    Group< math::mult<Element>, Element >
{};

auto concept MultiplicativeAbelianGroup<typename Element>
  : MultiplicativeGroup<Element>,
    MultiplicativeCommutativeMonoid<Element>,
    AbelianGroup< math::mult<Element>, Element >
{};

// ======================================
// Algebraic concepts with two connectors
// ======================================

// -----------------
// Based on functors
// -----------------

// More generic, less handy to use

auto concept GenericRing<typename AddOp, typename MultOp, typename Element>
  : AbelianGroup<AddOp, Element>,
    SemiGroup<MultOp, Element>,
    algebra::Ring<AddOp, MultOp, Element>
{};

auto concept GenericCommutativeRing<typename AddOp, typename MultOp, typename Element>
  : GenericRing<AddOp, MultOp, Element>,
    CommutativeSemiGroup<MultOp, Element>
{};

auto concept GenericRingWithIdentity<typename AddOp, typename MultOp, typename Element>
  : GenericRing<AddOp, MultOp, Element>,
    Monoid<MultOp, Element>,
    algebra::RingWithIdentity<AddOp, MultOp, Element>
{};

auto concept GenericCommutativeRingWithIdentity<typename AddOp, typename MultOp, typename Element>
  : GenericRingWithIdentity<AddOp, MultOp, Element>,
    GenericCommutativeRing<AddOp, MultOp, Element>,
    CommutativeMonoid<MultOp, Element>
{};

// auto
concept GenericDivisionRing<typename AddOp, typename MultOp, typename Element>
  : GenericRingWithIdentity<AddOp, MultOp, Element>,
    algebra::DivisionRing<AddOp, MultOp, Element>
{
    requires std::Convertible<inverse_result_type, Element>;
};

auto concept GenericField<typename AddOp, typename MultOp, typename Element>
  : GenericDivisionRing<AddOp, MultOp, Element>,
    GenericCommutativeRingWithIdentity<AddOp, MultOp, Element>,
    algebra::Field<AddOp, MultOp, Element>
{};

// ------------------
// Based on operators
// ------------------

// Handier, less generic

// Alternative definitions use MultiplicativeMonoid<Element> for Ring
// and call such concepts Pseudo-Ring

auto concept Ring<typename Element>
  : AdditiveAbelianGroup<Element>,
    MultiplicativeSemiGroup<Element>,
    GenericRing<math::add<Element>, math::mult<Element>, Element>
{};

auto concept CommutativeRing<typename Element>
  : Ring<Element>,
    MultiplicativeCommutativeSemiGroup<Element>,
    GenericCommutativeRing<math::add<Element>, math::mult<Element>, Element>
{};

auto concept RingWithIdentity<typename Element>
  : Ring<Element>,
    MultiplicativeMonoid<Element>,
    GenericRingWithIdentity<math::add<Element>, math::mult<Element>, Element>
{};

auto concept CommutativeRingWithIdentity<typename Element>
  : RingWithIdentity<Element>,
    CommutativeRing<Element>,
    MultiplicativeCommutativeMonoid<Element>,
    GenericCommutativeRingWithIdentity<math::add<Element>, math::mult<Element>, Element>
{};

concept DivisionRing<typename Element>
  : RingWithIdentity<Element>,
    MultiplicativePartiallyInvertibleMonoid<Element>,
    GenericDivisionRing<math::add<Element>, math::mult<Element>, Element>
{
    axiom NonZeroDivisibility(Element x)
    {
	if (x != zero(x))
	    x / x == one(x);
    }
};

auto concept Field<typename Element>
  : DivisionRing<Element>,
    CommutativeRingWithIdentity<Element>,
    GenericField<math::add<Element>, math::mult<Element>, Element>
{};

// ======================
// Miscellaneous concepts
// ======================

// that shall find a better place later

// EqualityComparable will have the != when defaults are supported
// At this point the following won't needed anymore
auto concept FullEqualityComparable<typename T, typename U = T>
{
  //requires std::EqualityComparable<T, U>;

    bool operator==(const T&, const U&);
    bool operator!=(const T&, const U&);
};

// Closure of EqualityComparable under a binary operation:
// That is, the result of this binary operation is also EqualityComparable
// with itself and with the operand type.
auto concept Closed2EqualityComparable<typename Operation, typename Element>
  : BinaryIsoFunction<Operation, Element>
{
    requires FullEqualityComparable<Element>;
    requires FullEqualityComparable< BinaryIsoFunction<Operation, Element>::result_type >;
    requires FullEqualityComparable< Element, BinaryIsoFunction<Operation, Element>::result_type >;
    requires FullEqualityComparable< BinaryIsoFunction<Operation, Element>::result_type, Element >;
};

// LessThanComparable will have the other operators when defaults are supported
// At this point the following won't needed anymore
auto concept FullLessThanComparable<typename T, typename U = T>
{
    bool operator<(const T&, const U&);
    bool operator<=(const T&, const U&);
    bool operator>(const T&, const U&);
    bool operator>=(const T&, const U&);
};

// Same for LessThanComparable
auto concept Closed2LessThanComparable<typename Operation, typename Element>
  : BinaryIsoFunction<Operation, Element>
{
    requires FullLessThanComparable<Element>;
    requires FullLessThanComparable< BinaryIsoFunction<Operation, Element>::result_type >;
    requires FullLessThanComparable< Element, BinaryIsoFunction<Operation, Element>::result_type >;
    requires FullLessThanComparable< BinaryIsoFunction<Operation, Element>::result_type, Element >;
};

#if 0
auto concept NumericOperatorResultConvertible<typename T>
  : AddableWithAssign<T>,
    SubtractableWithAssign<T>,
    MultiplicableWithAssign<T>,
    DivisibleWithAssign<T>
{
    requires std::Convertible< AddableWithAssign<T>::result_type, T>;
    requires std::Convertible< SubtractableWithAssign<T>::result_type, T>;
    requires std::Convertible< MultiplicableWithAssign<T>::result_type, T>;
    requires std::Convertible< DivisibleWithAssign<T>::result_type, T>;
}
#endif

auto concept AdditionResultConvertible<typename T>
{
    typename result_type;
    result_type operator+(T t, T u);
    requires std::Convertible<result_type, T>;

    typename result_type;
    result_type operator+=(T& t, T u);
    requires std::Convertible<result_type, T>;
};

auto concept SubtractionResultConvertible<typename T>
{
    typename result_type;
    result_type operator-(T t, T u);
    requires std::Convertible<result_type, T>;

    typename result_type;
    result_type operator-=(T& t, T u);
    requires std::Convertible<result_type, T>;
};

auto concept NumericOperatorResultConvertible<typename T>
  : AdditionResultConvertible<T>,
    SubtractionResultConvertible<T>
{};

// ====================
// Default Concept Maps
// ====================

#ifndef LA_NO_CONCEPT_MAPS

// ==============
// Integral Types
// ==============

template <typename T>
  requires IntrinsicSignedIntegral<T>
concept_map CommutativeRingWithIdentity<T> {}

template <typename T>
  requires IntrinsicUnsignedIntegral<T>
concept_map AdditiveCommutativeMonoid<T> {}

template <typename T>
  requires IntrinsicUnsignedIntegral<T>
concept_map MultiplicativeCommutativeMonoid<T> {}

// ====================
// Floating Point Types
// ====================

template <typename T>
  requires IntrinsicFloatingPoint<T>
concept_map Field<T> {}

template <typename T>
  requires IntrinsicFloatingPoint<T>
concept_map Field< std::complex<T> > {}

// ===========
// Min and Max
// ===========

// Draft version: defined generously unless there will be problems with some types

template <typename Element>
concept_map CommutativeMonoid< max<Element>, Element >
{
    // Why do we need this?
    typedef Element identity_result_type;
}

template <typename Element>
concept_map CommutativeMonoid< min<Element>, Element >
{
    // Why do we need this?
    typedef Element identity_result_type;
}

#endif // LA_NO_CONCEPT_MAPS

// Here come some mathematical concepts to be defined later

/// Specify the semantic Behavior of natural numbers (TBD)
/** Mathematic properties are in most cases only approximated
    but not held exactly. Rigorous definition would impede
    usage of real processors. **/
concept NaturalNumber<typename T> {}

/// Specify the semantic Behavior of integral numbers (TBD)
/** Mathematic properties are in most cases only approximated
    but not held exactly. Rigorous definition would impede
    usage of real processors.
    It is arguable if this is really a refinement **/
concept IntegralNumber<typename T> : NaturalNumber<T> {}

/// Specify the semantic Behavior of complex numbers (TBD)
/** Mathematic properties are in most cases only approximated
    but not held exactly. Rigorous definition would impede
    usage of real processors. **/
concept ComplexNumber<typename T> {}

/// Specify the semantic Behavior of real numbers (TBD)
/** Mathematic properties are in most cases only approximated
    but not held exactly. Rigorous definition would impede
    usage of real processors. **/
concept RealNumber<typename T> : ComplexNumber<T> {}

#endif // __GXX_CONCEPTS__

// =================================================
// Concept to specify return type of abs (and norms)
// =================================================

#ifdef __GXX_CONCEPTS__

// Concept to specify to specify projection of scalar value to comparable type
// For instance as return type of abs
// Minimalist definition for maximal applicability
auto concept Magnitude<typename T>
{
    typename type = T;
};

template <typename T>
concept_map Magnitude<std::complex<T> >
{
    typedef T type;
}

// Concept for norms etc., which are real values in mathematical definitions
auto concept RealMagnitude<typename T>
  : Magnitude<T>
{
    requires FullEqualityComparable<type>;
    requires FullLessThanComparable<type>;

    requires Field<type>;

    type sqrt(type);
    // typename sqrt_result;
    // sqrt_result sqrt(type);
    // requires std::Convertible<sqrt_result, type>;

    // using std::abs;
    type abs(T);
}

#else  // now without concepts

template <typename T>
struct Magnitude
{
    typename type = T;
};

template <typename T>
struct Magnitude<std::complex<T> >
{
    typedef T type;
}

template <typename T> struct RealMagnitude
  : public Magnitude<T>
{}

#endif  // __GXX_CONCEPTS__

// Type trait version both available with and w/o concepts (TBD: Macro finally :-( )
// For the moment everything is its own magnitude type, unless stated otherwise
template <typename T>
struct magnitude_type_trait
{
    typedef T type;
};

template <typename T>
struct magnitude_type_trait< std::complex<T> >
{
    typedef T type;
};

// =========================================
// Concepts for convenience (many from Rolf)
// =========================================

#ifdef __GXX_CONCEPTS__

//The following concepts Addable, Subtractable etc. differ from std::Addable, std::Subtractable
//etc. in so far that no default for result_type is provided, thus allowing automated return type deduction

auto concept Addable<typename T, typename U = T>
{
    typename result_type;
    result_type operator+(const T& t, const U& u);
};

// Usually + and += are both defined
// + can be efficiently derived from += but not vice versa
auto concept AddableWithAssign<typename T, typename U = T>
{
    typename assign_result_type;
    assign_result_type operator+=(T& x, U y);

    // Operator + is by default defined with +=
    typename result_type;
    result_type operator+(T x, U y);
#if 0
    {
	// Default requires std::CopyConstructible, without default not needed
	Element tmp(x);
	return tmp += y;                      defaults NYS
    }
#endif
};

auto concept Subtractable<typename T, typename U = T>
{
    typename result_type;
    result_type operator-(const T& t, const U& u);
};

// Usually - and -= are both defined
// - can be efficiently derived from -= but not vice versa
auto concept SubtractableWithAssign<typename T, typename U = T>
{
    typename assign_result_type;
    assign_result_type operator-=(T& x, U y);

    // Operator - is by default defined with -=
    typename result_type;
    result_type operator-(T x, U y);
#if 0
    {
	// Default requires std::CopyConstructible, without default not needed
	Element tmp(x);
	return tmp -= y;                      defaults NYS
    }
#endif
};

auto concept Multiplicable<typename T, typename U = T>
{
    typename result_type;
    result_type operator*(const T& t, const U& u);
};

// Usually * and *= are both defined
// * can be efficiently derived from *= but not vice versa
auto concept MultiplicableWithAssign<typename T, typename U = T>
{
    typename assign_result_type;
    assign_result_type operator*=(T& x, U y);

    // Operator * is by default defined with *=
    typename result_type;
    result_type operator*(T x, U y);
#if 0
    {
	// Default requires std::CopyConstructible, without default not needed
	Element tmp(x);
	return tmp *= y;                      defaults NYS
    }
#endif
};

auto concept Divisible<typename T, typename U = T>
{
    typename result_type;
    result_type operator / (const T&, const U&);
};

// Usually * and *= are both defined
// * can be efficiently derived from *= but not vice versa
auto concept DivisibleWithAssign<typename T, typename U = T>
{
    typename assign_result_type;
    assign_result_type operator*=(T& x, U y);

    // Operator * is by default defined with *=
    typename result_type;
    result_type operator*(T x, U y);
#if 0
    {
	// Default requires std::CopyConstructible, without default not needed
	Element tmp(x);
	return tmp *= y;                      defaults NYS
    }
#endif
};

auto concept Transposable<typename T>
{
    typename result_type;
    result_type trans(T&);
};

// Unary Negation -> Any suggestions for better names?! Is there a word as "negatable"?!
auto concept Negatable<typename S>
{
    typename result_type = S;
    result_type operator-(const S&);
};

// Or HasAbs?
using std::abs;
auto concept AbsApplicable<typename S>
{
    // There are better ways to define abs than the way it is done in std
    // Likely we replace the using one day
    typename result_type;
    result_type abs(const S&);
};

using std::conj;
auto concept HasConjugate<typename S>
{
    typename result_type;
    result_type conj(const S&);
};

// We need the following; might be placed somewhere else later
template <Float T>
concept_map HasConjugate<T>
{
    typedef T result_type;
    result_type conj(const T& s) {return s;}
}

// Dot product to be defined:
auto concept Dottable<typename T, typename U = T>
{
    typename result_type = T;
    result_type dot(const T&t, const U& u);
};

auto concept OneNormApplicable<typename V>
{
    typename result_type;
    result_type one_norm(const V&);
};

auto concept TwoNormApplicable<typename V>
{
    typename result_type;
    result_type two_norm(const V&);
};

auto concept InfinityNormApplicable<typename V>
{
    typename result_type;
    result_type inf_norm(const V&);
};

#endif  // __GXX_CONCEPTS__

} // namespace math

#endif // LA_CONCEPTS_INCLUDE


/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\
|  Phycas: Python software for phylogenetic analysis                          |
|  Copyright (C) 2006 Mark T. Holder, Paul O. Lewis and David L. Swofford     |
|                                                                             |
|  This program is free software; you can redistribute it and/or modify       |
|  it under the terms of the GNU General Public License as published by       |
|  the Free Software Foundation; either version 2 of the License, or          |
|  (at your option) any later version.                                        |
|                                                                             |
|  This program is distributed in the hope that it will be useful,            |
|  but WITHOUT ANY WARRANTY; without even the implied warranty of             |
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              |
|  GNU General Public License for more details.                               |
|                                                                             |
|  You should have received a copy of the GNU General Public License along    |
|  with this program; if not, write to the Free Software Foundation, Inc.,    |
|  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.                |
\~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
#include <boost/cast.hpp>       //temporary!
#include <boost/math/special_functions/fpclassify.hpp>
#include "probability_distribution.hpp"
#include "relative_rate_distribution.hpp"
#include "model.hpp"
#include "basic_tree_node.hpp"
#include "tree_likelihood.hpp"
#include "xlikelihood.hpp"
#include "mcmc_chain_manager.hpp"
#include "dirichlet_move.hpp"
#include "state_freq_move.hpp"
#include "basic_tree.hpp"
#include "tree_manip.hpp"
#include "gtr.hpp"
#include "hky.hpp"

using namespace phycas;

/*----------------------------------------------------------------------------------------------------------------------
|	The constructor simply calls the base class (DirichletMove) constructor.
*/
StateFreqMove::StateFreqMove() : DirichletMove()
	{
	dim = 4;
	}

/*----------------------------------------------------------------------------------------------------------------------
|	Sets the state frequencies of the associated HKY or GTR model to those in the supplied vector `v'.
*/
void StateFreqMove::sendCurrValuesToModel(const double_vect_t & v)
	{
	PHYCAS_ASSERT(dim == v.size());
	model->setStateFreqsUnnorm(v);
	}

/*----------------------------------------------------------------------------------------------------------------------
|	Obtains the current state frequencies from the model, storing them in the supplied vector `v'.
*/
void StateFreqMove::getCurrValuesFromModel(double_vect_t & v) const
	{
	PHYCAS_ASSERT(dim > 0);
	if (model)
		{
    	const std::vector<double> & rfreqs = model->getStateFreqs();
    	v.resize(rfreqs.size());
		PHYCAS_ASSERT(dim == rfreqs.size());
    	std::copy(rfreqs.begin(), rfreqs.end(), v.begin());
    	}
    else
    	{
    	v.assign(dim, 1.0/(double)dim);
    	}
	}

/*----------------------------------------------------------------------------------------------------------------------
|	Obtains the current state frequencies from the model, returning them as an anonymous vector.
*/
double_vect_t StateFreqMove::listCurrValuesFromModel()
	{
	PHYCAS_ASSERT(dim > 0);
	double_vect_t v(dim);
	if (model)
		{
    	const std::vector<double> & rfreqs = model->getStateFreqs();
		PHYCAS_ASSERT(dim == rfreqs.size());
    	std::copy(rfreqs.begin(), rfreqs.end(), v.begin());
    	}
    else
    	{
    	v.assign(dim, 1.0/(double)dim);
    	}
	return v;
	}

/*----------------------------------------------------------------------------------------------------------------------
|	Obtains the current state frequencies from the model, storing them in the data member `orig_params'.
*/
void StateFreqMove::getParams()
	{
	getCurrValuesFromModel(orig_params);
	}

/*----------------------------------------------------------------------------------------------------------------------
|	Replaces the state frequencies in the model with those supplied in the vector `v'.
*/
void StateFreqMove::setParams(
  const std::vector<double> & v)    /*< is the vector of parameter values to send to the model */
	{
    model->setStateFreqsUnnorm(v);
	}


/*
* Copyright 2019 © Centre Interdisciplinaire de développement en Cartographie des Océans (CIDCO), Tous droits réservés
*/

#ifndef SYSTEMGEOMETRY_H
#define SYSTEMGEOMETRY_H

#include <Eigen/Dense>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include "utils/Constants.hpp"
#include "utils/Exception.hpp"
#include "Attitude.hpp"

/*!
* \brief Survey system class
* \author Guillaume Labbe-Morissette, Jordan McManus
* \date August 20, 2018, 1:07 PM
*/
class SurveySystem {
public:

  /**Creates a Survey system*/
  SurveySystem();

  /**Destroys the Survey system*/
  ~SurveySystem();

  /**Returns the MBES model*/
  std::string & getMBES_model() {
    return MBES_model;
  }

  /**Returns the antenna position*/
  Eigen::Vector3d & getAntennaPosition() {
    return antennaPosition;
  }

  /**Returns the attitude accuracy*/
  Attitude * getAttitudeAccuracy() {
    return attitudeAccuracy;
  }

  /**Returns the boresigth patch test*/
  Attitude * getBoresightPatchTest() {
    return boresightPatchTest;
  }

  /**Returns the draft*/
  double getDraft() {
    return draft;
  }

  /**Returns the echo sounder receiver position*/
  Eigen::Vector3d & getEchosounderReceivererPosition() {
    return echosounderReceivererPosition;
  }

  /**Returns the echo sounder transmitter position*/
  Eigen::Vector3d & getEchosounderTransmitterPosition() {
    return echosounderTransmitterPosition;
  }

  /**Returns the position accuracy*/
  Eigen::Vector3d & getPositionAccuracy() {
    return positionAccuracy;
  }

  /**
  * Change the Survey system values by reading a file
  * Return false if the file is not valid
  *
  * @param filename name of the file that will be read
  */
  bool readFile(const std::string & fileName);

private:

  /**Name of the Survey system MBES model*/
  std::string MBES_model;

  /**Value of the Survey system draft (meter)*/
  double draft; // in meters

  // positions in the IMU reference frame in meters

  /**Vector3d of the Survey system antenna position*/
  Eigen::Vector3d antennaPosition;

  /**Vector3d of the Survey system echo sounder transmitter position*/
  Eigen::Vector3d echosounderTransmitterPosition;

  /**Vector3d of the Survey system echo sounder receiver position*/
  Eigen::Vector3d echosounderReceivererPosition;

  // angles in degrees
  // will be converted internally to radians
  /**Value of the Survey system boresigth path test (Attitude)*/
  Attitude* boresightPatchTest = NULL;

  // accuracy in 2 sigma
  /**Value of the Survey system attitude accuracy (Attitude)*/
  Attitude* attitudeAccuracy = NULL;

  /**Vector3d of the Survey system position accuracy*/
  Eigen::Vector3d positionAccuracy;
};

SurveySystem::SurveySystem() {
}

SurveySystem::~SurveySystem() {
  if (boresightPatchTest) {
    delete boresightPatchTest;
  }

  if (attitudeAccuracy) {
    delete attitudeAccuracy;
  }
}

bool SurveySystem::readFile(const std::string & fileName) {

  // Temporary placeholder variables
  std::string NameDevice;
  double Patch_Roll = 0, Patch_Pitch = 0, Patch_Heading = 0;
  double MBES_X = 0, MBES_Y = 0, MBES_Z = 0;
  double MBES_RX = 0, MBES_RY = 0, MBES_RZ = 0;
  double AntX = 0, AntY = 0, AntZ = 0;
  double Draft = 0;

  double PitchRollAcc, HeadingAcc;
  double PosHorAcc, PosVerAcc;

  //double Eroll = 0, Epitch = 0, Eyaw = 0, Rroll = 0, Rpitch = 0, Ryaw = 0, Mroll = 0, Mpitch = 0, Myaw = 0;

  // Open file
  std::string line;
  std::ifstream file(fileName);

  /*
  * This is cancer, no validation of correctness or presence of data
  * TODO:
  * Should return false if data is missing or is invalid
  */
  if (!file) {
    return false;
  } else {
    for (std::string line; getline(file, line);) {
      //make a stream for the line itself
      std::istringstream in(line);

      std::string type;
      in >> type;

      if (type == "MultibeamModel") {
        in >> NameDevice;
      } else if (type == "AntennaPositionOffsetX") {
        in >> AntX;
      } else if (type == "AntennaPositionOffsetY") {
        in >> AntY;
      } else if (type == "AntennaPositionOffsetZ") {
        in >> AntZ;
      } else if (type == "MBETransmitterOffsetX") {
        in >> MBES_X;
      } else if (type == "MBETransmitterOffsetY") {
        in >> MBES_Y;
      } else if (type == "MBETransmitterOffsetZ") {
        in >> MBES_Z;
      } else if (type == "MBEDraft") {
        in >> Draft;
      } else if (type == "MBEReceiverOffsetX") {
        in >> MBES_RX;
      } else if (type == "MBEReceiverOffsetY") {
        in >> MBES_RY;
      } else if (type == "MBEReceiverOffsetZ") {
        in >> MBES_RZ;
      } else if (type == "PositionAccuracy") {
        in >> PosHorAcc;
        PosVerAcc = PosHorAcc * 1.5;
      } else if (type == "PitchRollAccuracy") {
        in >> PitchRollAcc;
      } else if (type == "HeadingAccuracy") {
        in >> HeadingAcc;
      } else if (type == "RollAlignment") {
        in >> Patch_Roll;
      } else if (type == "PitchAlignment") {
        in >> Patch_Pitch;
      } else if (type == "HeadingAlignment") {
        in >> Patch_Heading;
      }/* else if (type == "MBEOffsetR") {
        in >> Eroll;
      } else if (type == "MBEOffsetP") {
      in >> Epitch;
    } else if (type == "MBEOffsetH") {
    in >> Eyaw;
  } else if (type == "MBEOffset2R") {
  in >> Rroll;
} else if (type == "MBEOffset2P") {
in >> Rpitch;
} else if (type == "MBEOffset2H") {
in >> Ryaw;
} else if (type == "MotionSensorR") {
in >> Mroll;
} else if (type == "MotionSensorP") {
in >> Mpitch;
} else if (type == "MotionSensorH") {
in >> Myaw;
}*/
}

file.close();

MBES_model = NameDevice;

draft = Draft;

antennaPosition << AntX, AntY, -AntZ;
echosounderTransmitterPosition << MBES_X, MBES_Y, -MBES_Z;
echosounderReceivererPosition << MBES_RX, MBES_RY, -MBES_RZ;

boresightPatchTest = new Attitude(0,Patch_Roll, Patch_Pitch, Patch_Heading);

attitudeAccuracy = new Attitude(0,PitchRollAcc, PitchRollAcc, HeadingAcc);

positionAccuracy << PosHorAcc, PosHorAcc, PosVerAcc;

return true;
}
}

#endif /* SYSTEMGEOMETRY_H */


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

#pragma once

#include <array>
#include <boost/functional/hash.hpp>  // IWYU pragma: keep
#include <cstddef>
#include <limits>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <utility>

#include "DataStructures/DataBox/DataBoxTag.hpp"
#include "DataStructures/Tensor/Tensor.hpp"
#include "Domain/Element.hpp"  // IWYU pragma: keep
#include "ErrorHandling/Assert.hpp"
#include "NumericalAlgorithms/LinearOperators/MeanValue.hpp"
#include "Options/Options.hpp"
#include "Utilities/ForceInline.hpp"
#include "Utilities/Gsl.hpp"
#include "Utilities/MakeArray.hpp"
#include "Utilities/TMPL.hpp"
#include "Utilities/TaggedTuple.hpp"

/// \cond
class DataVector;
template <size_t VolumeDim>
class Direction;
template <size_t VolumeDim>
class ElementId;
template <size_t VolumeDim>
class Mesh;
template <size_t VolumeDim>
class OrientationMap;

namespace PUP {
class er;
}  // namespace PUP

namespace SlopeLimiters {
template <size_t VolumeDim, typename TagsToLimit>
class Minmod;
}  // namespace SlopeLimiters

namespace Tags {
template <size_t Dim, typename Frame>
struct Coordinates;
template <size_t VolumeDim>
struct Element;
template <size_t VolumeDim>
struct Mesh;
template <size_t VolumeDim>
struct SizeOfElement;
}  // namespace Tags
/// \endcond

namespace SlopeLimiters {
/// \ingroup SlopeLimitersGroup
/// \brief Possible types of the minmod slope limiter.
///
/// \see SlopeLimiters::Minmod
enum class MinmodType { LambdaPi1, LambdaPiN, Muscl };
}  // namespace SlopeLimiters

namespace Minmod_detail {
// Encodes the return status of the minmod_tvbm function.
struct MinmodResult {
  const double value;
  const bool activated;
};

// The TVBM-corrected minmod function, see e.g. Cockburn reference Eq. 2.26.
MinmodResult minmod_tvbm(double a, double b, double c,
                         double tvbm_scale) noexcept;

// Implements the minmod limiter for one Tensor<DataVector>.
//
// The interface is designed to erase the tensor structure information, because
// this way the implementation can be moved out of the header file. This is
// achieved by receiving Tensor<DataVector>::iterators into the tensor to limit,
// and Tensor<double>::iterators into the neighbor tensors.
//
// Note: because the interface erases the tensor structure information, we can
// no longer rely on the compiler to enforce that the local and neighbor tensors
// share the same Structure.
template <size_t VolumeDim>
bool limit_one_tensor(
    gsl::not_null<DataVector*> tensor_begin,
    gsl::not_null<DataVector*> tensor_end,
    const SlopeLimiters::MinmodType& minmod_type, double tvbm_constant,
    const Element<VolumeDim>& element, const Mesh<VolumeDim>& mesh,
    const tnsr::I<DataVector, VolumeDim, Frame::Logical>& logical_coords,
    const std::array<double, VolumeDim>& element_size,
    const std::unordered_map<
        std::pair<Direction<VolumeDim>, ElementId<VolumeDim>>,
        gsl::not_null<const double*>,
        boost::hash<std::pair<Direction<VolumeDim>, ElementId<VolumeDim>>>>&
        neighbor_tensor_begin,
    const std::unordered_map<
        std::pair<Direction<VolumeDim>, ElementId<VolumeDim>>,
        std::array<double, VolumeDim>,
        boost::hash<std::pair<Direction<VolumeDim>, ElementId<VolumeDim>>>>&
        neighbor_sizes) noexcept;

template <typename Tag>
struct to_tensor_double : db::PrefixTag, db::SimpleTag {
  using type = TensorMetafunctions::swap_type<double, db::item_type<Tag>>;
  using tag = Tag;
  static std::string name() noexcept {
    return "TensorDouble(" + Tag::name() + ")";
  }
};
}  // namespace Minmod_detail

namespace SlopeLimiters {
/// \ingroup SlopeLimitersGroup
/// \brief A generic Minmod slope limiter
///
/// Implements the minmod-based slope limiter from
/// \ref cockburn_ref "Cockburn (1999)", Section 2.4.
/// Three types of minmod limiter from the reference are implemented:
/// \f$\Lambda\Pi^1\f$, \f$\Lambda\Pi^N\f$, and MUSCL.
///
/// This minmod limiter has a generic implementation that can work on an
/// arbitrary set of tensors. The minmod limiting algorithm is applied to each
/// component of each tensor independently. In general, the limiter linearizes
/// the tensors on every DG element, each time it is applied; additionally, the
/// limiter may reduce the spatial slope of some tensor components if the data
/// look like they may contain oscillations.
///
/// The key features differentiating the three minmod limiter types are:
/// 1. The `Muscl` limiter is the most dissipative; it more aggressively reduces
///    the slopes of the data. This limiter may better handle strong shocks, but
///    also produces the most broadening of features.
/// 2. The `LambdaPiN` limiter is the least aggressive; its "troubled cell"
///    detector tries to avoid limiting in DG elements where the data look
///    smooth enough. Where `LambdaPiN` is able to avoid limiting, the data are
///    _not_ linearized, and the post-limiter data are identical to the
///    pre-limiter data.
/// 3. The `LambdaPi1` limiter is a middle-ground option between the other two.
///    It does not try to avoid limiting as much as `LambdaPiN`, but it allows
///    larger slopes in the data than `Muscl`.
///
/// For all three types of minmod limiter the "total variation bound in the
/// means" (TVBM) correction is implemented, enabling the limiter to avoid
/// limiting away smooth extrema in the solution that would otherwise look like
/// spurious oscillations. The limiter will not reduce the slope (but will still
/// linearize) on elements where the slope is less than \f$m h^2\f$, where
/// \f$m\f$ is the TVBM constant and \f$h\f$ is the size of the DG element.
///
/// The limiter acts in the `Frame::Logical` coordinates, because in these
/// coordinates it is straightforward to formulate the algorithm. This means the
/// limiter can operate on generic deformed grids. However, if the grid is too
/// strongly deformed, some things can start to break down:
/// 1. When an element is deformed so that the Jacobian (from `Frame::Logical`
///    to `Frame::Inertial`) varies across the element, then the limiter fails
///    to be conservative. In other words, the integral of a tensor `u` over the
///    element will change after the limiter activates on `u`. This error is
///    typically small.
/// 2. When there is a sudden change in the size of the elements (perhaps at an
///    h-refinement boundary, or at the boundary between two blocks with very
///    different mappings), a smooth solution in `Frame::Inertial` can appear
///    to have a kink in `Frame::Logical`. The Minmod implementation includes
///    some (untested) tweaks that try to reduce spurious limiter activations
///    near these fake kinks.
///
/// When an element has multiple neighbors in any direction, an effective mean
/// and neighbor size in this direction are computed by averaging over the
/// multiple neighbors. This simple generalization of the minmod limiter enables
/// it to operate on h-refined grids.
///
/// \tparam VolumeDim The number of spatial dimensions.
/// \tparam Tags A typelist of tags specifying the tensors to limit.
///
/// \anchor cockburn_ref [1] B. Cockburn,
/// Discontinuous Galerkin Methods for Convection-Dominated Problems,
/// [Springer (1999)](https://doi.org/10.1007/978-3-662-03882-6_2)
template <size_t VolumeDim, typename... Tags>
class Minmod<VolumeDim, tmpl::list<Tags...>> {
 public:
  /// \brief The MinmodType
  ///
  /// One of `SlopeLimiters::MinmodType`. See `SlopeLimiters::Minmod`
  /// documentation for details.
  struct Type {
    using type = MinmodType;
    static constexpr OptionString help = {"Type of minmod"};
  };
  /// \brief The TVBM constant
  ///
  /// See `SlopeLimiters::Minmod` documentation for details.
  struct TvbmConstant {
    using type = double;
    static type default_value() { return 0.0; }
    static type lower_bound() { return 0.0; }
    static constexpr OptionString help = {"TVBM constant 'm'"};
  };
  using options = tmpl::list<Type, TvbmConstant>;
  static constexpr OptionString help = {
      "A minmod-based slope limiter.\n"
      "The different types of minmod are more or less aggressive in trying\n"
      "to reduce slopes. The TVBM correction allows the limiter to ignore\n"
      "'small' slopes, and helps to avoid limiting of smooth extrema in the\n"
      "solution.\n"};

  /// \brief Constuct a Minmod slope limiter
  ///
  /// \param minmod_type The type of Minmod slope limiter.
  /// \param tvbm_constant The value of the TVBM constant (default: 0).
  explicit Minmod(const MinmodType minmod_type,
                  const double tvbm_constant = 0.0) noexcept
      : minmod_type_(minmod_type), tvbm_constant_(tvbm_constant) {
    ASSERT(tvbm_constant >= 0.0, "The TVBM constant must be non-negative.");
  }

  Minmod() noexcept = default;
  Minmod(const Minmod& /*rhs*/) = default;
  Minmod& operator=(const Minmod& /*rhs*/) = default;
  Minmod(Minmod&& /*rhs*/) noexcept = default;
  Minmod& operator=(Minmod&& /*rhs*/) noexcept = default;
  ~Minmod() = default;

  // clang-tidy: google-runtime-references
  void pup(PUP::er& p) noexcept {  // NOLINT
    p | minmod_type_;
    p | tvbm_constant_;
  }

  const MinmodType& minmod_type() const noexcept { return minmod_type_; }
  const double& tvbm_constant() const noexcept { return tvbm_constant_; }

  /// \brief Data to send to neighbor elements.
  struct PackagedData {
    tuples::TaggedTuple<Minmod_detail::to_tensor_double<Tags>...> means_;
    std::array<double, VolumeDim> element_size_ =
        make_array<VolumeDim>(std::numeric_limits<double>::signaling_NaN());

    // clang-tidy: google-runtime-references
    void pup(PUP::er& p) noexcept {  // NOLINT
      p | means_;
      p | element_size_;
    }
  };

  using package_argument_tags = tmpl::list<Tags..., ::Tags::Mesh<VolumeDim>,
                                           ::Tags::SizeOfElement<VolumeDim>>;

  /// \brief Package data for sending to neighbor elements.
  ///
  /// The following quantities are stored in `PackagedData` and communicated
  /// between neighboring elements:
  /// - the cell-averaged mean of each tensor component, and
  /// - the size of the cell along each logical coordinate direction.
  ///
  /// \param packaged_data The data package to fill with this element's values.
  /// \param tensors The tensors to be averaged and packaged.
  /// \param mesh The mesh on which the tensor values are measured.
  /// \param element_size The size of the element in inertial coordinates, along
  ///        each dimension of logical coordinates.
  /// \param orientation_map The orientation of the neighbor
  void package_data(const gsl::not_null<PackagedData*>& packaged_data,
                    const db::item_type<Tags>&... tensors,
                    const Mesh<VolumeDim>& mesh,
                    const std::array<double, VolumeDim>& element_size,
                    const OrientationMap<VolumeDim>& orientation_map) const
      noexcept {
    const auto wrap_compute_means =
        [&mesh, &packaged_data ](auto tag, const auto& tensor) noexcept {
      for (size_t i = 0; i < tensor.size(); ++i) {
        // Compute the mean using the local orientation of the tensor and mesh:
        // this avoids the work of reorienting the tensor while giving the same
        // result.
        get<Minmod_detail::to_tensor_double<decltype(tag)>>(
            packaged_data->means_)[i] = mean_value(tensor[i], mesh);
      }
      return '0';
    };
    expand_pack(wrap_compute_means(Tags{}, tensors)...);
    packaged_data->element_size_ =
        orientation_map.permute_from_neighbor(element_size);
  }

  using limit_tags = tmpl::list<Tags...>;
  using limit_argument_tags =
      tmpl::list<::Tags::Element<VolumeDim>, ::Tags::Mesh<VolumeDim>,
                 ::Tags::Coordinates<VolumeDim, Frame::Logical>,
                 ::Tags::SizeOfElement<VolumeDim>>;

  /// \brief Limits the solution on the element.
  ///
  /// For each component of each tensor, the limiter will (in general) linearize
  /// the data, then possibly reduce its slope, dimension-by-dimension, until it
  /// no longer looks oscillatory.
  ///
  /// \param tensors The tensors to be limited.
  /// \param element The element on which the tensors to limit live.
  /// \param mesh The mesh on which the tensor values are measured.
  /// \param logical_coords The logical coordinates of the mesh gridpoints.
  /// \param element_size The size of the element, in the inertial coordinates.
  /// \param neighbor_data The data from each neighbor.
  ///
  /// \return whether the limiter modified the solution or not.
  ///
  /// \note The return value is false if the limiter knows it has not modified
  /// the solution. True return values can indicate:
  /// - The solution was limited to reduce the slope, whether by a large factor
  ///   or by a factor only roundoff away from unity.
  /// - The solution was linearized but not limited.
  /// - The solution is identical to the input, if the input was a linear
  ///   function on a higher-order mesh, so that the limiter cannot know that
  ///   the linearization step did not actually modify the data. This is
  ///   somewhat contrived and is unlikely to occur outside of code tests or
  ///   test cases with very clean initial data.
  bool operator()(
      const gsl::not_null<std::add_pointer_t<db::item_type<Tags>>>... tensors,
      const Element<VolumeDim>& element, const Mesh<VolumeDim>& mesh,
      const tnsr::I<DataVector, VolumeDim, Frame::Logical>& logical_coords,
      const std::array<double, VolumeDim>& element_size,
      const std::unordered_map<
          std::pair<Direction<VolumeDim>, ElementId<VolumeDim>>, PackagedData,
          boost::hash<std::pair<Direction<VolumeDim>, ElementId<VolumeDim>>>>&
          neighbor_data) const noexcept {
    bool limiter_activated = false;
    const auto wrap_limit_one_tensor = [
      this, &limiter_activated, &element, &mesh, &logical_coords, &element_size,
      &neighbor_data
    ](auto tag, const auto& tensor) noexcept {
      // Because we hide the types of Tags from limit_one_tensor (we do this so
      // that its implementation isn't templated on Tags and can be moved out of
      // this header file), we cannot pass it PackagedData as currently
      // implemented. So we unpack everything from PackagedData. In the future
      // we may want a PackagedData type that erases types inherently, as this
      // would avoid the need for unpacking as done here.
      //
      // Get iterators into the local and neighbor tensors, because these are
      // independent from the structure of the tensor being limited.
      const auto tensor_begin = make_not_null(tensor->begin());
      const auto tensor_end = make_not_null(tensor->end());
      const auto neighbor_tensor_begin = [&neighbor_data]() noexcept {
        std::unordered_map<
            std::pair<Direction<VolumeDim>, ElementId<VolumeDim>>,
            gsl::not_null<const double*>,
            boost::hash<std::pair<Direction<VolumeDim>, ElementId<VolumeDim>>>>
            result;
        for (const auto& neighbor_and_data : neighbor_data) {
          result.insert(std::make_pair(
              neighbor_and_data.first,
              make_not_null(get<Minmod_detail::to_tensor_double<decltype(tag)>>(
                                neighbor_and_data.second.means_)
                                .cbegin())));
        }
        return result;
      }
      ();
      const auto neighbor_sizes = [&neighbor_data]() noexcept {
        std::unordered_map<
            std::pair<Direction<VolumeDim>, ElementId<VolumeDim>>,
            std::array<double, VolumeDim>,
            boost::hash<std::pair<Direction<VolumeDim>, ElementId<VolumeDim>>>>
            result;
        for (const auto& neighbor_and_data : neighbor_data) {
          result.insert(std::make_pair(neighbor_and_data.first,
                                       neighbor_and_data.second.element_size_));
        }
        return result;
      }
      ();

      limiter_activated =
          Minmod_detail::limit_one_tensor<VolumeDim>(
              tensor_begin, tensor_end, minmod_type_, tvbm_constant_, element,
              mesh, logical_coords, element_size, neighbor_tensor_begin,
              neighbor_sizes) or
          limiter_activated;
      return '0';
    };
    expand_pack(wrap_limit_one_tensor(Tags{}, tensors)...);
    return limiter_activated;
  }

 private:
  MinmodType minmod_type_;
  double tvbm_constant_;
};

template <size_t VolumeDim, typename TagList>
SPECTRE_ALWAYS_INLINE bool operator==(
    const Minmod<VolumeDim, TagList>& lhs,
    const Minmod<VolumeDim, TagList>& rhs) noexcept {
  return lhs.minmod_type() == rhs.minmod_type() and
         lhs.tvbm_constant() == rhs.tvbm_constant();
}

template <size_t VolumeDim, typename TagList>
SPECTRE_ALWAYS_INLINE bool operator!=(
    const Minmod<VolumeDim, TagList>& lhs,
    const Minmod<VolumeDim, TagList>& rhs) noexcept {
  return not(lhs == rhs);
}

}  // namespace SlopeLimiters

template <>
struct create_from_yaml<SlopeLimiters::MinmodType> {
  static SlopeLimiters::MinmodType create(const Option& options);
};


#ifdef compile_instructions
(echo "#include\""$0"\"" > $0x.cpp) && time clang++ -O3 -std=c++14 -Wfatal-errors -Wall -D_TEST_MULTI_INDEX_RANGE $0x.cpp -o $0x.x && $0x.x $@ && rm -rf $0x.cpp $0x.x; exit
#endif

#ifndef MULTI_INDEX_RANGE_HPP
#define MULTI_INDEX_RANGE_HPP

//#include<boost/iterator/iterator_facade.hpp>

#include<iterator> // std::random_iterator_tag // std::reverse_iterator

namespace boost{

namespace multi{

template<class Self, typename ValueType, class AccessCategory, typename Reference = ValueType&,  typename DifferenceType = typename std::pointer_traits<ValueType*>::difference_type, typename Pointer = ValueType*>
class iterator_facade{
	using self_type = Self;
	self_type& self(){return *this;}
	self_type const& self() const{return static_cast<Self const&>(*this);}
public:
	using value_type = ValueType;
	using reference = Reference;
	using pointer = Pointer;
	using difference_type = DifferenceType;
	using iterator_category = AccessCategory;
	auto operator!=(self_type const& o) const
//	->decltype(not(o == self()))
	{	return not(o == self());}
//	Self& operator++(){return ++self(); return *this;}
	self_type operator+(difference_type n) const{self_type r = self(); r += n; return r;}
	self_type operator-(difference_type n) const{self_type r = self(); r -= n; return r;}
};

//class iterator_core_access{};
}

namespace multi{

template<class IndexType>
class range{
	IndexType first_;
	IndexType last_;
public:
	using value_type = IndexType;
	using difference_type = std::make_signed_t<value_type>;
	using size_type = difference_type;
	using const_reference = value_type const /*&*/;
	using reference = const_reference;
	using const_pointer = value_type;
	using pointer = value_type;
	range() : first_{}, last_{first_}{}
	template<class Range, typename = std::enable_if_t<std::is_same<std::decay_t<Range>, value_type>{}> >
	constexpr range(Range&& o) : first_(o.first()), last_(o.last()){}
	constexpr range(value_type fl) : first_{fl}, last_{fl + 1}{}
	constexpr range(value_type f, value_type l) : first_{f}, last_{l}{}
	class const_iterator
		: public boost::multi::iterator_facade<const_iterator,
			value_type, std::random_access_iterator_tag,
			const_reference, difference_type
		>
	{
		typename const_iterator::value_type curr_;
		typename const_iterator::reference dereference() const{return curr_;}
		void increment(){++curr_;} void decrement(){--curr_;}
		void advance(typename const_iterator::difference_type n){curr_+=n;}
		bool equal(const_iterator const& y) const{return curr_ == y.curr_;}
		auto distance_to(const_iterator const& z) const{return z.curr_-curr_;}
		constexpr const_iterator(value_type current) : curr_(current){}
		friend class range;
	 //   friend class boost::iterator_core_access;
	public:
		auto operator==(const_iterator const& y) const{return curr_ == y.curr_;}
		const_iterator& operator++(){++curr_; return *this;}
		const_iterator& operator--(){--curr_; return *this;}
		typename const_iterator::reference operator*() const{return curr_;}
	};
	using iterator = const_iterator;
	using reverse_iterator = std::reverse_iterator<iterator>;
	using const_reverse_iterator = std::reverse_iterator<const_iterator>;
	constexpr const_reference first() const{return first_;}
	constexpr const_reference last()  const{return last_;}
	constexpr const_reference operator[](difference_type p) const{return first() + p;}
	constexpr const_reference front() const{return first();}
	constexpr const_reference back()  const{return last() - 1;}
	constexpr const_iterator cbegin() const{return const_iterator{first_};}
	constexpr const_iterator cend()   const{return const_iterator{last_};}
	constexpr reverse_iterator rbegin() const{return reverse_iterator{end()};}
	constexpr reverse_iterator rend() const{return reverse_iterator{begin()};}
	constexpr const_iterator begin() const{return cbegin();}
	constexpr const_iterator end() const{return cend();}
	constexpr bool empty() const{return first_ == last_;}
	friend constexpr bool empty(range const& s){return s.empty();}
	constexpr size_type size() const noexcept{return last_ - first_;}
	friend constexpr size_type size(range const& s){return s.size();}
	friend std::ostream& operator<<(std::ostream& os, range const& s){
		return s.empty()?os<<"[)":os <<"["<< s.first() <<", "<< s.last() <<")";
	}
	friend const_iterator begin(range const& self){return self.begin();}
	friend const_iterator end(range const& self){return self.end();}
	range& operator=(range const&) = default;
	friend constexpr bool operator==(range const& a, range const& b){
		return(a.empty()&& b.empty())||(a.first_==b.first_ && a.last_==b.last_);
	}
	friend constexpr
	bool operator!=(range const& r1, range const& r2){return not(r1 == r2);}
	size_type count(value_type const& value) const{
		if(value >= last_ or value < first_) return 0;
		return 1;
	}
	range::const_iterator find(value_type const& value) const{
		auto first = begin();
		if(value >= last_ or value < first_) return end();
		return first += value - *first;
	}
	friend range intersection(range const& r1, range const& r2){
		using std::max; using std::min;
		auto first = max(r1.first(), r2.first());
		auto last = min(r1.last(), r2.last());
		if(first < last) return {first, last};
		return {};
	}
	bool contains(value_type const& v) const{return (v >= first() and v < last())?true:false;}
};

//using index_range = range<index>;
/*
class strided_index_range : index_range{
	index stride_;
public:
	strided_index_range(index first, index last, index stride = 1) : index_range{first, last}, stride_{stride}{}
//	explicit operator index_range() const{return *this;}
	index stride() const{return stride_;}
	using index_range::front;
	index back() const{return front() + size()*stride();}
	size_type size() const{return (this->last_ - first_) / stride_;}
	friend std::ostream& operator<<(std::ostream& os, strided_index_range const& self){
		if(empty()
		if(self.first_ == self.last_) return os << "[)" << '\n';
		return os << '[' << self.first_ << ", " << self.last_ << ')';
	}
};*/

template<class IndexType>
class extension_t : public range<IndexType>{
	using range<IndexType>::range;
	public:
	constexpr extension_t() noexcept : range<IndexType>(0, 0){}
	constexpr extension_t(typename extension_t::value_type last) noexcept : range<IndexType>(0, last){}
	friend constexpr typename extension_t::size_type size(extension_t const& s){return s.size();}
	friend std::ostream& operator<<(std::ostream& os, extension_t const& self){
		if(self.empty()) return os << static_cast<range<IndexType> const&>(self);
		if(self.first() == 0) return os <<"["<< self.last() <<"]";
		return os << static_cast<range<IndexType> const&>(self);
	}
	friend bool operator==(extension_t const& a, extension_t const& b){return static_cast<range<IndexType> const&>(a)==static_cast<range<IndexType> const&>(b);}
	friend bool operator!=(extension_t const& a, extension_t const& b){return not(a==b);}
};

}}

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

#ifdef _TEST_MULTI_INDEX_RANGE

//#include <boost/spirit/include/karma.hpp>

#include<cassert>
#include<iostream>

namespace multi = boost::multi;

using std::cout;
using std::cerr;

int main(){

	cout << multi::range<int>{5, 5} <<'\n';
	cout << multi::extension_t<int>{5} <<'\n';
	multi::extension_t<int> ee{5};
	assert( ee == 5 );
	assert( multi::extension_t<int>{5} == 5 );
	cout << multi::extension_t<int>{5, 7} <<'\n';

	assert(( multi::extension_t<int>{5, 12}.count(10) == 1 ));

	assert( size(multi::range<int>{5, 5}) == 0 );
	assert( empty(multi::range<int>{5, 5}) );

	assert( size(multi::range<int>{}) == 0 );
	assert( empty(multi::range<int>{}) );

	for(auto const& i : multi::range<int>{5, 12}) cout<< i <<' ';
	cout <<'\n';

	cout << intersection(multi::range<int>{5, 12}, multi::range<int>{14, 16}) << '\n';
//	for(auto const& i : intersection(multi::range<int>{5, 12}, multi::range<int>{8, 16})) cout<< i <<' ';
//	cout <<'\n';

	multi::range<int> rr{5, 12};
	assert( rr.contains(6) );
	assert( not rr.contains(12) );
	for(auto it = rr.begin(); it != rr.end(); ++it) cout<< *it <<' ';
	cout<<'\n';
	for(auto it = rr.rbegin(); it != rr.rend(); ++it) cout<< *it <<' ';
	cout<<'\n';

//	cout<< *rr.rbegin() <<'\n';
//	for(auto it = rr.rbegin(); it != rr.rend(); ++it) cout<< *it <<' ';
//	cout <<'\n';

//	multi::extension<int> ei{5, 10};
#if 0
	std::iterator_traits<multi::range<multi::index>::const_iterator>::value_type p = multi::index{4};

	{
		multi::index_range ir{5, 10};
		cout << ir << " = {" << format(index_ % ", ", ir) << "}\n";
		std::vector<multi::index_range::value_type> v(5);
		copy(begin(ir), end(ir), begin(v));
		assert(v[0] == 5);
		for(auto& i : ir) cout << i << ' ';
		cout << '\n';
		auto f = ir.find(6);
		cerr << "*f " << *f << '\n';
		assert(*f == 6);
		using std::find;
		auto f2 = find(ir.begin(), ir.end(), 12);
		assert(f2 == ir.end());
		auto f3 = find(ir.begin(), ir.end(), 2);
		assert(f3 == ir.end());
	}
/*	{
		multi::strided_index_range ir{6, 12, 2};
		cout << ir << " = {" << format(index_ % ", ", ir) << "}\n";
		std::vector<multi::index_range::value_type> v(5);
		copy(begin(ir), end(ir), begin(v));
		assert( v[0] == 6 );
		assert( v[1] == 8 );
		for(auto& i : ir) cout << i <<' ';
		cout <<'\n';
	}*/
	{
		multi::index_range ir(5);
		cout << ir << " = {" << format(index_ % ", ", ir) << "}\n";
		assert(*begin(ir) == 5);
		assert(ir.front() == 5);
		assert(ir.back() == 5);
	}
	{
		multi::index_range ir; // partially formed
		ir = multi::index_range{8, 8};
		assert(ir.empty());
	}
	{
		multi::index_range ir = {};
		assert(ir.empty());
	}
	{
		multi::index_extension ie(5);
		cout << ie << " = {" << format(index_ % ", ", ie) << "}";
	}
#endif
}

#endif
#endif