//
// Copyright 2020 Debabrata Mandal <mandaldebabrata123@gmail.com>
//
// 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
//

#ifndef BOOST_GIL_HISTOGRAM_HPP
#define BOOST_GIL_HISTOGRAM_HPP

#include <boost/gil/concepts/concept_check.hpp>
#include <boost/gil/metafunctions.hpp>
#include <boost/gil/pixel.hpp>

#include <boost/mp11.hpp>
#include <boost/type_traits.hpp>
#include <boost/functional/hash.hpp>

#include <iostream>
#include <tuple>
#include <utility>
#include <vector>
#include <type_traits>
#include <map>
#include <unordered_map>

namespace boost { namespace gil {

//////////////////////////////////////////////////////////
/// Histogram
//////////////////////////////////////////////////////////
/// \defgroup Histogram Histogram
/// \brief Contains description of the boost.gil.histogram class, extensions provided in place
///        of the default class, algorithms over the histogram class (both extensions and the
///        default class)
///

namespace detail {

/// \defgroup Histogram-Helpers Histogram-Helpers
/// \brief Helper implementations supporting the histogram class.

/// \ingroup Histogram-Helpers
///
template <std::size_t Index, typename... T>
inline typename std::enable_if<Index == sizeof...(T), void>::type
    hash_tuple_impl(std::size_t&, std::tuple<T...> const&)
{
}

/// \ingroup Histogram-Helpers
///
template <std::size_t Index, typename... T>
inline typename std::enable_if<Index != sizeof...(T), void>::type
    hash_tuple_impl(std::size_t& seed, std::tuple<T...> const& t)
{
    boost::hash_combine(seed, std::get<Index>(t));
    hash_tuple_impl<Index + 1>(seed, t);
}

/// \ingroup Histogram-Helpers
/// \brief Functor provided for the hashing of tuples.
///        The following approach makes use hash_combine from
///        boost::container_hash. Although there is a direct hashing
///        available for tuples, this approach will ease adopting in
///        future to a std::hash_combine. In case std::hash extends
///        support to tuples this functor as well as the helper
///        implementation hash_tuple_impl can be removed.
///
template <typename... T>
struct hash_tuple
{
    std::size_t operator()(std::tuple<T...> const& t) const
    {
        std::size_t seed = 0;
        hash_tuple_impl<0>(seed, t);
        return seed;
    }
};

/// \ingroup Histogram-Helpers
/// \todo With C++14 and using auto we don't need the decltype anymore
///
template <typename Pixel, std::size_t... I>
auto pixel_to_tuple(Pixel const& p, boost::mp11::index_sequence<I...>)
    -> decltype(std::make_tuple(p[I]...))
{
    return std::make_tuple(p[I]...);
}

/// \ingroup Histogram-Helpers
/// \todo With C++14 and using auto we don't need the decltype anymore
///
template <typename Tuple, std::size_t... I>
auto tuple_to_tuple(Tuple const& t, boost::mp11::index_sequence<I...>)
    -> decltype(std::make_tuple(std::get<I>(t)...))
{
    return std::make_tuple(std::get<I>(t)...);
}

/// \ingroup Histogram-Helpers
///
template <typename Tuple, std::size_t... I>
bool tuple_compare(Tuple const& t1, Tuple const& t2, boost::mp11::index_sequence<I...>)
{
    std::array<bool, std::tuple_size<Tuple>::value> comp_list;
    comp_list = {std::get<I>(t1) <= std::get<I>(t2)...};
    bool comp = true;
    for (std::size_t i = 0; i < comp_list.size(); i++)
    {
        comp = comp & comp_list[i];
    }
    return comp;
}

/// \ingroup Histogram-Helpers
/// \brief Compares 2 tuples and outputs t1 <= t2
///        Comparison is not in a lexicographic manner but on every element of the tuple hence
///        (2, 2) > (1, 3) evaluates to false
///
template <typename Tuple>
bool tuple_compare(Tuple const& t1, Tuple const& t2)
{
    std::size_t const tuple_size = std::tuple_size<Tuple>::value;
    auto index_list              = boost::mp11::make_index_sequence<tuple_size>{};
    return tuple_compare(t1, t2, index_list);
}

/// \ingroup Histogram-Helpers
/// \brief Provides equivalent of std::numeric_limits for type std::tuple
///        tuple_limit gets called with only tuples having integral elements
///
template <typename Tuple>
struct tuple_limit
{
    static constexpr Tuple min()
    {
        return min_impl(boost::mp11::make_index_sequence<std::tuple_size<Tuple>::value>{});
    }
    static constexpr Tuple max()
    {
        return max_impl(boost::mp11::make_index_sequence<std::tuple_size<Tuple>::value>{});
    }

private:
    template <std::size_t... I>
    static constexpr Tuple min_impl(boost::mp11::index_sequence<I...>)
    {
        return std::make_tuple(
            std::numeric_limits<typename std::tuple_element<I, Tuple>::type>::min()...);
    }

    template <std::size_t... I>
    static constexpr Tuple max_impl(boost::mp11::index_sequence<I...>)
    {
        return std::make_tuple(
            std::numeric_limits<typename std::tuple_element<I, Tuple>::type>::max()...);
    }
};

/// \ingroup Histogram-Helpers
/// \brief Filler is used to fill the histogram class with all values between a specified range
///        This functor is used when sparsefill is false, since all the keys need to be present
///        in that case.
///        Currently on 1D implementation is available, extend by adding specialization for 2D
///        and higher dimensional cases.
///
template <std::size_t Dimension>
struct filler
{
    template <typename Container, typename Tuple>
    void operator()(Container&, Tuple&, Tuple&, std::size_t)
    {
    }
};

/// \ingroup Histogram-Helpers
/// \brief Specialisation for 1D histogram.
template <>
struct filler<1>
{
    template <typename Container, typename Tuple>
    void operator()(Container& hist, Tuple& lower, Tuple& upper, std::size_t bin_width = 1)
    {
        for (auto i = std::get<0>(lower); static_cast<std::size_t>(std::get<0>(upper) - i) >= bin_width; i += bin_width)
        {
            hist(i / bin_width) = 0;
        }
        hist(std::get<0>(upper) / bin_width) = 0;
    }
};

}  //namespace detail

///
/// \class boost::gil::histogram
/// \ingroup Histogram
/// \brief Default histogram class provided by boost::gil.
///
/// The class inherits over the std::unordered_map provided by STL. A complete example/tutorial
/// of how to use the class resides in the docs.
/// Simple calling syntax for a 3D dimensional histogram :
/// \code
/// histogram<int, int , int> h;
/// h(1, 1, 1) = 0;
/// \endcode
/// This is just a starter to what all can be achieved with it, refer to the docs for the
/// full demo.
///
template <typename... T>
class histogram : public std::unordered_map<std::tuple<T...>, double, detail::hash_tuple<T...>>
{
    using base_t   = std::unordered_map<std::tuple<T...>, double, detail::hash_tuple<T...>>;
    using bin_t    = boost::mp11::mp_list<T...>;
    using key_t    = typename base_t::key_type;
    using mapped_t = typename base_t::mapped_type;
    using value_t  = typename base_t::value_type;

public:
    histogram() = default;

    /// \brief Returns the number of dimensions(axes) the class supports.
    static constexpr std::size_t dimension()
    {
        return std::tuple_size<key_t>::value;
    }

    /// \brief Returns bin value corresponding to specified tuple
    mapped_t& operator()(T... indices)
    {
        auto key                              = std::make_tuple(indices...);
        std::size_t const index_dimension     = std::tuple_size<std::tuple<T...>>::value;
        std::size_t const histogram_dimension = dimension();
        static_assert(histogram_dimension == index_dimension, "Dimensions do not match.");

        return base_t::operator[](key);
    }

    /// \brief Checks if 2 histograms are equal. Ignores type, and checks if
    ///        the keys (after type casting) match.
    template <typename OtherType>
    bool equals(OtherType const& otherhist) const
    {
        bool check = (dimension() == otherhist.dimension());

        using other_value_t = typename OtherType::value_type;
        std::for_each(otherhist.begin(), otherhist.end(), [&](other_value_t const& v) {
            key_t key = key_from_tuple(v.first);
            if (base_t::find(key) != base_t::end())
            {
                check = check & (base_t::at(key) == otherhist.at(v.first));
            }
            else
            {
                check = false;
            }
        });
        return check;
    }

    /// \brief Checks if the histogram class is compatible to be used with
    ///        a GIL image type
    static constexpr bool is_pixel_compatible()
    {
        using bin_types = boost::mp11::mp_list<T...>;
        return boost::mp11::mp_all_of<bin_types, std::is_arithmetic>::value;
    }

    /// \brief Checks if the histogram class is compatible to be used with
    ///        the specified tuple type
    template <typename Tuple>
    bool is_tuple_compatible(Tuple const&)
    {
        std::size_t const tuple_size     = std::tuple_size<Tuple>::value;
        std::size_t const histogram_size = dimension();
        // TODO : Explore consequence of using if-constexpr
        using sequence_type = typename std::conditional
        <
            tuple_size >= histogram_size,
            boost::mp11::make_index_sequence<histogram_size>,
            boost::mp11::make_index_sequence<tuple_size>
        >::type;

        if (is_tuple_size_compatible<Tuple>())
            return is_tuple_type_compatible<Tuple>(sequence_type{});
        else
            return false;
    }

    /// \brief Returns a key compatible to be used as the histogram key
    ///        from the input tuple
    template <std::size_t... Dimensions, typename Tuple>
    key_t key_from_tuple(Tuple const& t) const
    {
        using index_list                      = boost::mp11::mp_list_c<std::size_t, Dimensions...>;
        std::size_t const index_list_size     = boost::mp11::mp_size<index_list>::value;
        std::size_t const tuple_size          = std::tuple_size<Tuple>::value;
        std::size_t const histogram_dimension = dimension();

        static_assert(
            ((index_list_size != 0 && index_list_size == histogram_dimension) ||
             (tuple_size == histogram_dimension)),
            "Tuple and histogram key of different sizes");

        using new_index_list = typename std::conditional
        <
            index_list_size == 0,
            boost::mp11::mp_list_c<std::size_t, 0>,
            index_list
        >::type;

        std::size_t const min =
            boost::mp11::mp_min_element<new_index_list, boost::mp11::mp_less>::value;

        std::size_t const max =
            boost::mp11::mp_max_element<new_index_list, boost::mp11::mp_less>::value;

        static_assert((0 <= min && max < tuple_size) || index_list_size == 0, "Index out of Range");

        using seq1 = boost::mp11::make_index_sequence<histogram_dimension>;
        using seq2 = boost::mp11::index_sequence<Dimensions...>;
        // TODO : Explore consequence of using if-constexpr
        using sequence_type = typename std::conditional<index_list_size == 0, seq1, seq2>::type;

        auto key = detail::tuple_to_tuple(t, sequence_type{});
        static_assert(
            is_tuple_type_compatible<Tuple>(seq1{}),
            "Tuple type and histogram type not compatible.");

        return make_histogram_key(key, seq1{});
    }

    /// \brief Returns a histogram compatible key from the input pixel which
    ///        can be directly used
    template <std::size_t... Dimensions, typename Pixel>
    key_t key_from_pixel(Pixel const& p) const
    {
        using index_list                      = boost::mp11::mp_list_c<std::size_t, Dimensions...>;
        std::size_t const index_list_size     = boost::mp11::mp_size<index_list>::value;
        std::size_t const pixel_dimension     = num_channels<Pixel>::value;
        std::size_t const histogram_dimension = dimension();

        static_assert(
            ((index_list_size != 0 && index_list_size == histogram_dimension) ||
            (index_list_size == 0 && pixel_dimension == histogram_dimension)) &&
            is_pixel_compatible(),
            "Pixels and histogram key are not compatible.");

        using  new_index_list = typename std::conditional
        <
            index_list_size == 0,
            boost::mp11::mp_list_c<std::size_t, 0>,
            index_list
        >::type;

        std::size_t const min =
            boost::mp11::mp_min_element<new_index_list, boost::mp11::mp_less>::value;

        std::size_t const max =
            boost::mp11::mp_max_element<new_index_list, boost::mp11::mp_less>::value;

        static_assert(
            (0 <= min && max < pixel_dimension) || index_list_size == 0, "Index out of Range");

        using seq1          = boost::mp11::make_index_sequence<histogram_dimension>;
        using seq2          = boost::mp11::index_sequence<Dimensions...>;
        using sequence_type = typename std::conditional<index_list_size == 0, seq1, seq2>::type;

        auto key = detail::pixel_to_tuple(p, sequence_type{});
        return make_histogram_key(key, seq1{});
    }

    /// \brief Return nearest smaller key to specified histogram key
    key_t nearest_key(key_t const& k) const
    {
        using check_list = boost::mp11::mp_list<boost::has_less<T>...>;
        static_assert(
            boost::mp11::mp_all_of<check_list, boost::mp11::mp_to_bool>::value,
            "Keys are not comparable.");
        auto nearest_k = k;
        if (base_t::find(k) != base_t::end())
        {
            return nearest_k;
        }
        else
        {
            bool once = true;
            std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) {
                if (v.first <= k)
                {
                    if (once)
                    {
                        once      = !once;
                        nearest_k = v.first;
                    }
                    else if (nearest_k < v.first)
                        nearest_k = v.first;
                }
            });
            return nearest_k;
        }
    }

    /// \brief Fills the histogram with the input image view
    template <std::size_t... Dimensions, typename SrcView>
    void fill(
        SrcView const& srcview,
        std::size_t bin_width               = 1,
        bool applymask                      = false,
        std::vector<std::vector<bool>> mask = {},
        key_t lower                         = key_t(),
        key_t upper                         = key_t(),
        bool setlimits                      = false)
    {
        gil_function_requires<ImageViewConcept<SrcView>>();
        using channel_t = typename channel_type<SrcView>::type;

        for (std::ptrdiff_t src_y = 0; src_y < srcview.height(); ++src_y)
        {
            auto src_it = srcview.row_begin(src_y);
            for (std::ptrdiff_t src_x = 0; src_x < srcview.width(); ++src_x)
            {
                if (applymask && !mask[src_y][src_x])
                    continue;
                auto scaled_px = src_it[src_x];
                static_for_each(scaled_px, [&](channel_t& ch) {
                    ch = ch / bin_width;
                });
                auto key = key_from_pixel<Dimensions...>(scaled_px);
                if (!setlimits ||
                    (detail::tuple_compare(lower, key) && detail::tuple_compare(key, upper)))
                    base_t::operator[](key)++;
            }
        }
    }

    /// \brief Can return a subset or a mask over the current histogram
    template <std::size_t... Dimensions, typename Tuple>
    histogram sub_histogram(Tuple const& t1, Tuple const& t2)
    {
        using index_list                      = boost::mp11::mp_list_c<std::size_t, Dimensions...>;
        std::size_t const index_list_size     = boost::mp11::mp_size<index_list>::value;
        std::size_t const histogram_dimension = dimension();

        std::size_t const min =
            boost::mp11::mp_min_element<index_list, boost::mp11::mp_less>::value;

        std::size_t const max =
            boost::mp11::mp_max_element<index_list, boost::mp11::mp_less>::value;

        static_assert(
            (0 <= min && max < histogram_dimension) && index_list_size < histogram_dimension,
            "Index out of Range");

        using seq1 = boost::mp11::make_index_sequence<dimension()>;
        using seq2 = boost::mp11::index_sequence<Dimensions...>;

        static_assert(
            is_tuple_type_compatible<Tuple>(seq1{}),
            "Tuple type and histogram type not compatible.");

        auto low      = make_histogram_key(t1, seq1{});
        auto low_key  = detail::tuple_to_tuple(low, seq2{});
        auto high     = make_histogram_key(t2, seq1{});
        auto high_key = detail::tuple_to_tuple(high, seq2{});

        histogram sub_h;
        std::for_each(base_t::begin(), base_t::end(), [&](value_t const& k) {
            auto tmp_key = detail::tuple_to_tuple(k.first, seq2{});
            if (low_key <= tmp_key && tmp_key <= high_key)
                sub_h[k.first] += base_t::operator[](k.first);
        });
        return sub_h;
    }

    /// \brief Returns a sub-histogram over specified axes
    template <std::size_t... Dimensions>
    histogram<boost::mp11::mp_at<bin_t, boost::mp11::mp_size_t<Dimensions>>...> sub_histogram()
    {
        using index_list                      = boost::mp11::mp_list_c<std::size_t, Dimensions...>;
        std::size_t const index_list_size     = boost::mp11::mp_size<index_list>::value;
        std::size_t const histogram_dimension = dimension();

        std::size_t const min =
            boost::mp11::mp_min_element<index_list, boost::mp11::mp_less>::value;

        std::size_t const max =
            boost::mp11::mp_max_element<index_list, boost::mp11::mp_less>::value;

        static_assert(
            (0 <= min && max < histogram_dimension) && index_list_size < histogram_dimension,
            "Index out of Range");

        histogram<boost::mp11::mp_at<bin_t, boost::mp11::mp_size_t<Dimensions>>...> sub_h;

        std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) {
            auto sub_key =
                detail::tuple_to_tuple(v.first, boost::mp11::index_sequence<Dimensions...>{});
            sub_h[sub_key] += base_t::operator[](v.first);
        });
        return sub_h;
    }

    /// \brief Normalize this histogram class
    void normalize()
    {
        double sum = 0.0;
        std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) {
            sum += v.second;
        });
        // std::cout<<(long int)sum<<"asfe";
        std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) {
            base_t::operator[](v.first) = v.second / sum;
        });
    }

    /// \brief Return the sum count of all bins
    double sum() const
    {
        double sum = 0.0;
        std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) {
            sum += v.second;
        });
        return sum;
    }

    /// \brief Return the minimum key in histogram
    key_t min_key() const
    {
        key_t min_key = base_t::begin()->first;
        std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) {
            if (v.first < min_key)
                min_key = v.first;
        });
        return min_key;
    }

    /// \brief Return the maximum key in histogram
    key_t max_key() const
    {
        key_t max_key = base_t::begin()->first;
        std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) {
            if (v.first > max_key)
                max_key = v.first;
        });
        return max_key;
    }

    /// \brief Return sorted keys in a vector
    std::vector<key_t> sorted_keys() const
    {
        std::vector<key_t> sorted_keys;
        std::for_each(base_t::begin(), base_t::end(), [&](value_t const& v) {
            sorted_keys.push_back(v.first);
        });
        std::sort(sorted_keys.begin(), sorted_keys.end());
        return sorted_keys;
    }

private:
    template <typename Tuple, std::size_t... I>
    key_t make_histogram_key(Tuple const& t, boost::mp11::index_sequence<I...>) const
    {
        return std::make_tuple(
            static_cast<typename boost::mp11::mp_at<bin_t, boost::mp11::mp_size_t<I>>>(
                std::get<I>(t))...);
    }

    template <typename Tuple, std::size_t... I>
    static constexpr bool is_tuple_type_compatible(boost::mp11::index_sequence<I...>)
    {
        using tp = boost::mp11::mp_list
        <
            typename std::is_convertible
            <
                boost::mp11::mp_at<bin_t, boost::mp11::mp_size_t<I>>,
                typename std::tuple_element<I, Tuple>::type
            >::type...
        >;
        return boost::mp11::mp_all_of<tp, boost::mp11::mp_to_bool>::value;
    }

    template <typename Tuple>
    static constexpr bool is_tuple_size_compatible()
    {
        return (std::tuple_size<Tuple>::value == dimension());
    }
};

///
/// \fn void fill_histogram
/// \ingroup Histogram Algorithms
/// \tparam SrcView Input image view
/// \tparam Container Input histogram container
/// \brief Overload this function to provide support for boost::gil::histogram or
/// any other external histogram
///
/// Example :
/// \code
/// histogram<int> h;
/// fill_histogram(view(img), h);
/// \endcode
///
template <typename SrcView, typename Container>
void fill_histogram(SrcView const&, Container&);

///
/// \fn void fill_histogram
/// \ingroup Histogram Algorithms
/// @param srcview     Input  Input image view
/// @param hist        Output Histogram to be filled
/// @param bin_width   Input  Specify the bin widths for the histogram.
/// @param accumulate  Input  Specify whether to accumulate over the values already present in h (default = false)
/// @param sparsaefill Input  Specify whether to have a sparse or continuous histogram (default = true)
/// @param applymask   Input  Specify if image mask is to be specified
/// @param mask        Input  Mask as a 2D vector. Used only if prev argument specified
/// @param lower       Input  Lower limit on the values in histogram (default numeric_limit::min() on axes)
/// @param upper       Input  Upper limit on the values in histogram (default numeric_limit::max() on axes)
/// @param setlimits   Input  Use specified limits if this is true (default is false)
/// \brief Overload version of fill_histogram
///
/// Takes a third argument to determine whether to clear container before filling.
/// For eg, when there is a need to accumulate the histograms do
/// \code
/// fill_histogram(view(img), hist, true);
/// \endcode
///
template <std::size_t... Dimensions, typename SrcView, typename... T>
void fill_histogram(
    SrcView const& srcview,
    histogram<T...>& hist,
    std::size_t bin_width               = 1,
    bool accumulate                     = false,
    bool sparsefill                     = true,
    bool applymask                      = false,
    std::vector<std::vector<bool>> mask = {},
    typename histogram<T...>::key_type lower =
        detail::tuple_limit<typename histogram<T...>::key_type>::min(),
    typename histogram<T...>::key_type upper =
        detail::tuple_limit<typename histogram<T...>::key_type>::max(),
    bool setlimits = false)
{
    if (!accumulate)
        hist.clear();

    detail::filler<histogram<T...>::dimension()> f;
    if (!sparsefill)
        f(hist, lower, upper, bin_width);

    hist.template fill<Dimensions...>(srcview, bin_width, applymask, mask, lower, upper, setlimits);
}

///
/// \fn void cumulative_histogram(Container&)
/// \ingroup Histogram Algorithms
/// \tparam Container Input histogram container
/// \brief Optionally overload this function with any external histogram class
///
/// Cumulative histogram is calculated over any arbitrary dimensional
/// histogram. The only tradeoff could be the runtime complexity which in
/// the worst case would be max( #pixel_values , #bins ) * #dimensions.
/// For single dimensional histograms the complexity has been brought down to
/// #bins * log( #bins ) by sorting the keys and then calculating the cumulative version.
///
template <typename Container>
Container cumulative_histogram(Container const&);

template <typename... T>
histogram<T...> cumulative_histogram(histogram<T...> const& hist)
{
    using check_list = boost::mp11::mp_list<boost::has_less<T>...>;
    static_assert(
        boost::mp11::mp_all_of<check_list, boost::mp11::mp_to_bool>::value,
        "Cumulative histogram not possible of this type");

    using histogram_t = histogram<T...>;
    using pair_t  = std::pair<typename histogram_t::key_type, typename histogram_t::mapped_type>;
    using value_t = typename histogram_t::value_type;

    histogram_t cumulative_hist;
    std::size_t const dims = histogram_t::dimension();
    if (dims == 1)
    {
        std::vector<pair_t> sorted_keys(hist.size());
        std::size_t counter = 0;
        std::for_each(hist.begin(), hist.end(), [&](value_t const& v) {
            sorted_keys[counter++] = std::make_pair(v.first, v.second);
        });
        std::sort(sorted_keys.begin(), sorted_keys.end());
        auto cumulative_counter = static_cast<typename histogram_t::mapped_type>(0);
        for (std::size_t i = 0; i < sorted_keys.size(); ++i)
        {
            cumulative_counter += sorted_keys[i].second;
            cumulative_hist[(sorted_keys[i].first)] = cumulative_counter;
        }
    }
    else
    {
        std::for_each(hist.begin(), hist.end(), [&](value_t const& v1) {
            auto cumulative_counter = static_cast<typename histogram_t::mapped_type>(0);
            std::for_each(hist.begin(), hist.end(), [&](value_t const& v2) {
                bool comp = detail::tuple_compare(
                    v2.first, v1.first,
                    boost::mp11::make_index_sequence<histogram_t::dimension()>{});
                if (comp)
                    cumulative_counter += hist.at(v2.first);
            });
            cumulative_hist[v1.first] = cumulative_counter;
        });
    }
    return cumulative_hist;
}

}}  //namespace boost::gil

#endif


/*
 * @file
 * @author University of Warwick
 * @version 1.0
 *
 * @section LICENSE
 *
 * @section DESCRIPTION
 *
 * Tests for the SparseMatrixCSR class
 */

#define BOOST_TEST_MODULE SparseMatrixCSR
#include <boost/test/unit_test.hpp>
#include <boost/test/output_test_stream.hpp>
#include <stdexcept>

#include "SparseMatrixCSR.h"
#include "Error.h"

using namespace cupcfd::data_structures;

// =========================================================================
// === Constructor Tests ===
// =========================================================================
// Test 1: Default Constructor
BOOST_AUTO_TEST_CASE(constructor_test1)
{
	SparseMatrixCSR<int, int> matrix;

	BOOST_CHECK_EQUAL(matrix.m, 1);
	BOOST_CHECK_EQUAL(matrix.n, 1);
	BOOST_CHECK_EQUAL(matrix.baseIndex, 0);
	BOOST_CHECK_EQUAL(matrix.nnz, 0);
}

// Test 2:
BOOST_AUTO_TEST_CASE(constructor_test2)
{
	SparseMatrixCSR<int, int> matrix(4, 7);

	BOOST_CHECK_EQUAL(matrix.m, 4);
	BOOST_CHECK_EQUAL(matrix.n, 7);
	BOOST_CHECK_EQUAL(matrix.baseIndex, 0);
	BOOST_CHECK_EQUAL(matrix.nnz, 0);
}

// Test 3:
BOOST_AUTO_TEST_CASE(constructor_test3)
{
	SparseMatrixCSR<int, int> matrix(6, 9, 5);

	BOOST_CHECK_EQUAL(matrix.m, 6);
	BOOST_CHECK_EQUAL(matrix.n, 9);
	BOOST_CHECK_EQUAL(matrix.baseIndex, 5);
	BOOST_CHECK_EQUAL(matrix.nnz, 0);
}

// === setElement Tests ===
// Test 1: Add non-existing entry
BOOST_AUTO_TEST_CASE(setElement_test1)
{
	// Setup
	SparseMatrixCSR<int, int> matrix(2, 2, 0);

	int elements[4] = {2, 4, 3, 20};
	int rows[4] = {0, 1, 0, 1};
	int columns[4] = {0, 1, 1, 0};

	for(int i = 0; i < 4; i++)
	{
		cupcfd::error::eCodes status = matrix.setElement(rows[i], columns[i], elements[i]);
		BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	}

	int rowsCmp[3] = {0, 2, 4};
	int colsCmp[4] = {0, 1, 0, 1};
	int valCmp[4] = {2, 3, 20, 4};

	BOOST_CHECK_EQUAL_COLLECTIONS(&matrix.IA[0], &matrix.IA[0] + 3, rowsCmp, rowsCmp + 3);
	BOOST_CHECK_EQUAL_COLLECTIONS(&matrix.JA[0], &matrix.JA[0] + 4, colsCmp, colsCmp + 4);
	BOOST_CHECK_EQUAL_COLLECTIONS(&matrix.A[0], &matrix.A[0] + 4, valCmp, valCmp + 4);

	BOOST_CHECK_EQUAL(matrix.nnz, 4);
}

// Test 2: Overwrite existing non-zero entry
BOOST_AUTO_TEST_CASE(setElement_test2)
{
	// Setup
	SparseMatrixCSR<int, int> matrix(2, 2, 0);

	// Set the values such that (0, 0) = 20, (1, 0) = 3
	// but these are overwriting 2 and 4 respectively.
	int elements[4] = {2, 4, 3, 20};
	int rows[4] = {0, 1, 1, 0};
	int columns[4] = {0, 0, 0, 0};

	// Check nnz count is 4
	BOOST_CHECK_EQUAL(matrix.nnz, 0);

	for(int i = 0; i < 4; i++)
	{
		cupcfd::error::eCodes status = matrix.setElement(rows[i], columns[i], elements[i]);
		BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	}

	int rowsCmp[3] = {0, 1, 2};
	int colsCmp[2] = {0, 0};
	int valCmp[2] = {20, 3};

	BOOST_CHECK_EQUAL_COLLECTIONS(&matrix.IA[0], &matrix.IA[0] + 3, rowsCmp, rowsCmp + 3);
	BOOST_CHECK_EQUAL_COLLECTIONS(&matrix.JA[0], &matrix.JA[0] + 2, colsCmp, colsCmp + 2);
	BOOST_CHECK_EQUAL_COLLECTIONS(&matrix.A[0], &matrix.A[0] + 2, valCmp, valCmp + 2);

	// NNZ Count should be 2 since we added 2, and overwrote 2
	BOOST_CHECK_EQUAL(matrix.nnz, 2);
}

// Test 3: Attempt to set an element outside the lower row bounds
BOOST_AUTO_TEST_CASE(setElement_test3)
{
	// Setup
	SparseMatrixCSR<int, int> matrix(2, 2, 0);

	cupcfd::error::eCodes status = matrix.setElement(-1, 0, 2);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_MATRIX_ROW_OOB);
}

// Test 4: Attempt to set an element outside the lower column bounds
BOOST_AUTO_TEST_CASE(setElement_test4)
{
	// Setup
	SparseMatrixCSR<int, int> matrix(2, 2, 0);

	cupcfd::error::eCodes status = matrix.setElement(0, -1, 2);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_MATRIX_COL_OOB);
}

// Test 5: Attempt to set an element outside the upper row bounds
BOOST_AUTO_TEST_CASE(setElement_test5)
{
	// Setup
	SparseMatrixCSR<int, int> matrix(2, 2, 0);

	cupcfd::error::eCodes status = matrix.setElement(2, 0, 2);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_MATRIX_ROW_OOB);
}

// Test 6: Attempt to set an element outside the upper column bounds
BOOST_AUTO_TEST_CASE(setElement_test6)
{
	// Setup
	SparseMatrixCSR<int, int> matrix(2, 2, 0);

	cupcfd::error::eCodes status = matrix.setElement(0, 2, 2);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_MATRIX_COL_OOB);
}

// Test 7: Overwrite non-existing zero entry with a non-zero base index
BOOST_AUTO_TEST_CASE(setElement_test7)
{
	// Setup
	SparseMatrixCSR<int, int> matrix(2, 2, 4);

	int elements[4] = {2, 4, 3, 20};
	int rows[4] = {4, 5, 4, 5};
	int columns[4] = {4, 5, 5, 4};

	for(int i = 0; i < 4; i++)
	{
		cupcfd::error::eCodes status = matrix.setElement(rows[i], columns[i], elements[i]);
		BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	}

	int rowsCmp[3] = {0, 2, 4};
	int colsCmp[4] = {4, 5, 4, 5};
	int valCmp[4] = {2, 3, 20, 4};

	BOOST_CHECK_EQUAL_COLLECTIONS(&matrix.IA[0], &matrix.IA[0] + 3, rowsCmp, rowsCmp + 3);
	BOOST_CHECK_EQUAL_COLLECTIONS(&matrix.JA[0], &matrix.JA[0] + 4, colsCmp, colsCmp + 4);
	BOOST_CHECK_EQUAL_COLLECTIONS(&matrix.A[0], &matrix.A[0] + 4, valCmp, valCmp + 4);
}

// Test 8: Overwrite existing non-zero entry with a non-zero base matrix index
BOOST_AUTO_TEST_CASE(setElement_test8)
{
	// Setup
	SparseMatrixCSR<int, int> matrix(2, 2, 4);

	// Set the values such that (4, 4) = 20, (5, 4) = 3
	// but these are overwriting 2 and 4 respectively.
	int elements[4] = {2, 4, 3, 20};
	int rows[4] = {4, 5, 5, 4};
	int columns[4] = {4, 4, 4, 4};

	for(int i = 0; i < 4; i++)
	{
		cupcfd::error::eCodes status = matrix.setElement(rows[i], columns[i], elements[i]);
		BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	}

	int rowsCmp[3] = {0, 1, 2};
	int colsCmp[2] = {4, 4};
	int valCmp[2] = {20, 3};

	BOOST_CHECK_EQUAL_COLLECTIONS(&matrix.IA[0], &matrix.IA[0] + 3, rowsCmp, rowsCmp + 3);
	BOOST_CHECK_EQUAL_COLLECTIONS(&matrix.JA[0], &matrix.JA[0] + 2, colsCmp, colsCmp + 2);
	BOOST_CHECK_EQUAL_COLLECTIONS(&matrix.A[0], &matrix.A[0] + 2, valCmp, valCmp + 2);
}

// Test 9: Attempt to set an element outside the lower row bounds with a non-zero base matrix index
BOOST_AUTO_TEST_CASE(setElement_test9)
{
	// Setup
	SparseMatrixCSR<int, int> matrix(2, 2, 4);

	cupcfd::error::eCodes status = matrix.setElement(3, 4, 2);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_MATRIX_ROW_OOB);
}

// Test 10: Attempt to set an element outside the lower column bounds with a non-zero base matrix index
BOOST_AUTO_TEST_CASE(setElement_test10)
{
	// Setup
	SparseMatrixCSR<int, int> matrix(2, 2, 4);

	cupcfd::error::eCodes status = matrix.setElement(4, 3, 2);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_MATRIX_COL_OOB);
}

// Test 11: Attempt to set an element outside the upper row bounds with a non-zero base matrix index
BOOST_AUTO_TEST_CASE(setElement_test11)
{
	// Setup
	SparseMatrixCSR<int, int> matrix(2, 2, 4);

	cupcfd::error::eCodes status = matrix.setElement(6, 4, 2);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_MATRIX_ROW_OOB);
}

// Test 12: Attempt to set an element outside the upper column bounds with a non-zero base matrix index
BOOST_AUTO_TEST_CASE(setElement_test12)
{
	// Setup
	SparseMatrixCSR<int, int> matrix(2, 2, 4);

	cupcfd::error::eCodes status = matrix.setElement(4, 6, 2);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_MATRIX_COL_OOB);
}

// === getElement Tests ===
// Test 1: Correctly get a value that is non-zero with a base index of 0
BOOST_AUTO_TEST_CASE(getElement_test1)
{
	// Setup
	SparseMatrixCSR<int, int> matrix(8, 8, 0);

	int rows[4] = {0, 4, 6, 7};
	int columns[4] = {0, 1, 5, 3};
	int elements[4] = {2, 4, 3, 20};

	for(int i = 0; i < 4; i++)
	{
		cupcfd::error::eCodes status = matrix.setElement(rows[i], columns[i], elements[i]);
		BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	}

	int vals[4];

	for(int i = 0; i < 4; i++)
	{
		cupcfd::error::eCodes status = matrix.getElement(rows[i], columns[i], vals + i);
		BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	}

	BOOST_CHECK_EQUAL_COLLECTIONS(vals, vals + 4, elements, elements + 4);
}

// Test 2: Correctly get a value that is not set (zero) with a base index of 0
BOOST_AUTO_TEST_CASE(getElement_test2)
{
	// Setup
	SparseMatrixCSR<int, int> matrix(8, 8, 0);

	int rows[4] = {0, 4, 6, 7};
	int columns[4] = {0, 1, 5, 3};
	int elements[4] = {2, 4, 3, 20};

	for(int i = 0; i < 4; i++)
	{
		cupcfd::error::eCodes status = matrix.setElement(rows[i], columns[i], elements[i]);
		BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	}

	int val;
	cupcfd::error::eCodes status = matrix.getElement(3, 6, &val);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	BOOST_CHECK_EQUAL(val, 0);
}

// Test 3: Catch the error case where we exceed the row bounds
// of an index scheme where the base Index is 0
BOOST_AUTO_TEST_CASE(getElement_test3)
{
	// Setup
	SparseMatrixCSR<int, int> matrix(8, 8, 0);

	int val;
	cupcfd::error::eCodes status = matrix.getElement(-1, 4, &val);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_MATRIX_ROW_OOB);
}

// Test 4: Catch the error case where we exceed the column bounds
// of an index scheme where the base Index is 0
BOOST_AUTO_TEST_CASE(getElement_test4)
{
	// Setup
	SparseMatrixCSR<int, int> matrix(8, 8, 0);

	int val;
	cupcfd::error::eCodes status = matrix.getElement(4, -1, &val);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_MATRIX_COL_OOB);
}

// Test 5: Catch the error case where we exceed the upper row bounds
// of an index scheme where the base Index is 0
BOOST_AUTO_TEST_CASE(getElement_test5)
{
	// Setup
	SparseMatrixCSR<int, int> matrix(8, 8, 0);

	int val;
	cupcfd::error::eCodes status = matrix.getElement(8, 4, &val);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_MATRIX_ROW_OOB);
}

// Test 6: Catch the error case where we exceed the upper column bounds
// of an index scheme where the base Index is 0
BOOST_AUTO_TEST_CASE(getElement_test6)
{
	// Setup
	SparseMatrixCSR<int, int> matrix(8, 8, 0);

	int val;
	cupcfd::error::eCodes status = matrix.getElement(4, 8, &val);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_MATRIX_COL_OOB);
}

// Test 7: Correctly get a value that is non-zero with a non-zero base-index
BOOST_AUTO_TEST_CASE(getElement_test7)
{
	// Setup
	SparseMatrixCSR<int, int> matrix(8, 8, 7);

	int rows[4] = {7, 11, 13, 14};
	int columns[4] = {7, 8, 12, 10};
	int elements[4] = {2, 4, 3, 20};

	for(int i = 0; i < 4; i++)
	{
		cupcfd::error::eCodes status = matrix.setElement(rows[i], columns[i], elements[i]);
		BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	}

	int vals[4];

	for(int i = 0; i < 4; i++)
	{
		cupcfd::error::eCodes status = matrix.getElement(rows[i], columns[i], vals + i);
		BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	}

	BOOST_CHECK_EQUAL_COLLECTIONS(vals, vals + 4, elements, elements + 4);
}

// Test 8: Correctly set a value that is not set (zero) with a non-zero base-index
BOOST_AUTO_TEST_CASE(getElement_test8)
{
	// Setup
	SparseMatrixCSR<int, int> matrix(8, 8, 7);

	int rows[4] = {7, 11, 13, 14};
	int columns[4] = {7, 8, 12, 10};
	int elements[4] = {2, 4, 3, 20};

	for(int i = 0; i < 4; i++)
	{
		cupcfd::error::eCodes status = matrix.setElement(rows[i], columns[i], elements[i]);
		BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	}

	int val;
	cupcfd::error::eCodes status = matrix.getElement(10, 13, &val);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	BOOST_CHECK_EQUAL(val, 0);
}

// Test 9: Catch the error case where we exceed the lower row bounds
// of an index scheme with a non-zero base-index
BOOST_AUTO_TEST_CASE(getElement_test9)
{
	// Setup
	SparseMatrixCSR<int, int> matrix(8, 8, 8);

	int val;
	cupcfd::error::eCodes status = matrix.getElement(7, 9, &val);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_MATRIX_ROW_OOB);
}

// Test 10: Catch the error case where we exceed the lower column bounds
// of an index scheme with a non-zero base-index
BOOST_AUTO_TEST_CASE(getElement_test10)
{
	// Setup
	SparseMatrixCSR<int, int> matrix(8, 8, 8);

	int val;
	cupcfd::error::eCodes status = matrix.getElement(9, 7, &val);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_MATRIX_COL_OOB);
}

// Test 11: Catch the error case where we exceed the upper row bounds
// of an index scheme with a non-zero base-index
BOOST_AUTO_TEST_CASE(getElement_test11)
{
	// Setup
	SparseMatrixCSR<int, int> matrix(8, 8, 8);

	int val;
	cupcfd::error::eCodes status = matrix.getElement(16, 9, &val);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_MATRIX_ROW_OOB);
}

// Test 12: Catch the error case where we exceed the upper column bounds
// of an index scheme with a non-zero base-index
BOOST_AUTO_TEST_CASE(getElement_test12)
{
	// Setup
	SparseMatrixCSR<int, int> matrix(8, 8, 8);

	int val;
	cupcfd::error::eCodes status = matrix.getElement(9, 16, &val);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_MATRIX_COL_OOB);
}

// === clear ===
// Test 1: Check that the matrix vectors are cleared after calling clear
BOOST_AUTO_TEST_CASE(clear_test1)
{
	cupcfd::error::eCodes status;

	// Setup
	SparseMatrixCSR<int, int> matrix(8, 8, 0);

	// Setup data so that there is at least two elements
	status = matrix.setElement(3, 4, 15);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	status = matrix.setElement(7, 7, 19);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	status = matrix.clear();
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	// Check sizes are 0
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	BOOST_CHECK_EQUAL(matrix.IA.size(), 9);
	BOOST_CHECK_EQUAL(matrix.JA.size(), 0);
	BOOST_CHECK_EQUAL(matrix.A.size(), 0);

	// Check that retrieving the previously set index now has a value of 0
	int val;
	status = matrix.getElement(3, 4, &val);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	BOOST_CHECK_EQUAL(val, 0);
}

// === resize ===
// Test 1: Check that an existing matrix resets to appropriate values after a resize
// Data should also be cleared
BOOST_AUTO_TEST_CASE(resize_test1)
{
	cupcfd::error::eCodes status;

	// Setup
	SparseMatrixCSR<int, int> matrix(8, 8, 2);

	// Setup data so that there is at least two elements
	status = matrix.setElement(4, 5, 15);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	status = matrix.setElement(8, 8, 19);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	BOOST_CHECK_EQUAL(matrix.m, 8);
	BOOST_CHECK_EQUAL(matrix.n, 8);
	BOOST_CHECK_EQUAL(matrix.nnz, 2);

	// Test and Check
	status = matrix.resize(4, 4);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	// Check that the values of m, n and nnz are reset
	BOOST_CHECK_EQUAL(matrix.m, 4);
	BOOST_CHECK_EQUAL(matrix.n, 4);
	BOOST_CHECK_EQUAL(matrix.nnz, 0);

	// Check that the baseIndex is still the same
	BOOST_CHECK_EQUAL(matrix.baseIndex, 2);

	// Check sizes are correct
	BOOST_CHECK_EQUAL(matrix.A.size(), 0);
	BOOST_CHECK_EQUAL(matrix.IA.size(), 5);	// Rows + 1
	BOOST_CHECK_EQUAL(matrix.JA.size(), 0);

	// Check that retrieving the previously set index now has a value of 0
	int val;
	status = matrix.getElement(4, 5, &val);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	BOOST_CHECK_EQUAL(val, 0);
}

// === getNonZeroRowIndexes ===
// Test 1: Get correct row indexes for rows with set values
BOOST_AUTO_TEST_CASE(getNonZeroRowIndexes_test1)
{
	cupcfd::error::eCodes status;

	SparseMatrixCSR<int, int> matrix(4, 4, 0);

	status = matrix.setElement(0, 0, 2);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	status = matrix.setElement(3, 1, 4);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	status = matrix.setElement(0, 1, 3);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	status = matrix.setElement(3, 0, 20);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	int * rowIndexes;
	int  nRowIndexes;

	status = matrix.getNonZeroRowIndexes(&rowIndexes, &nRowIndexes);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	BOOST_CHECK_EQUAL(nRowIndexes, 2);
	BOOST_CHECK_EQUAL(rowIndexes[0], 0);
	BOOST_CHECK_EQUAL(rowIndexes[1], 3);
	free(rowIndexes);
}

// === getRowColumnIndexes ===
// Test 1: Get correct column indexes for a row
BOOST_AUTO_TEST_CASE(getRowColumnIndexes_test1)
{
	cupcfd::error::eCodes status;

	SparseMatrixCSR<int, int> matrix(4, 4, 0);

	status = matrix.setElement(0, 3, 3);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	status = matrix.setElement(0, 0, 2);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	status = matrix.setElement(3, 1, 4);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	status = matrix.setElement(3, 0, 20);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	status = matrix.setElement(1, 1, 2);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	int * colIndexes;
	int  nColIndexes;

	status = matrix.getRowColumnIndexes(0, &colIndexes, &nColIndexes);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	BOOST_CHECK_EQUAL(nColIndexes, 2);
	BOOST_CHECK_EQUAL(colIndexes[0], 0);
	BOOST_CHECK_EQUAL(colIndexes[1], 3);
	free(colIndexes);

	status = matrix.getRowColumnIndexes(1, &colIndexes, &nColIndexes);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	BOOST_CHECK_EQUAL(nColIndexes, 1);
	BOOST_CHECK_EQUAL(colIndexes[0], 1);
	free(colIndexes);

	status = matrix.getRowColumnIndexes(3, &colIndexes, &nColIndexes);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	BOOST_CHECK_EQUAL(nColIndexes, 2);
	BOOST_CHECK_EQUAL(colIndexes[0], 0);
	BOOST_CHECK_EQUAL(colIndexes[1], 1);
	free(colIndexes);
}

// Test 2: Check for error if row index is lower than matrix index range
BOOST_AUTO_TEST_CASE(getRowColumnIndexes_test2)
{
	cupcfd::error::eCodes status;

	SparseMatrixCSR<int, int> matrix(4, 4, 3);

	status = matrix.setElement(3, 3, 4);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	int * colIndexes;
	int  nColIndexes;

	status = matrix.getRowColumnIndexes(2, &colIndexes, &nColIndexes);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_MATRIX_ROW_OOB);
}

// Test 3: Check for error if row index is higher than matrix index range
BOOST_AUTO_TEST_CASE(getRowColumnIndexes_test3)
{
	cupcfd::error::eCodes status;

	SparseMatrixCSR<int, int> matrix(4, 4, 3);

	status = matrix.setElement(3, 3, 4);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	int * colIndexes;
	int  nColIndexes;

	status = matrix.getRowColumnIndexes(8, &colIndexes, &nColIndexes);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_MATRIX_ROW_OOB);
}

// === getRowNNZValues ===
// Test 1: Get correct values for a row
BOOST_AUTO_TEST_CASE(getRowNNZValues_test1)
{
	cupcfd::error::eCodes status;

	SparseMatrixCSR<int, int> matrix(4, 4, 0);

	status = matrix.setElement(0, 3, 3);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	status = matrix.setElement(0, 0, 2);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	status = matrix.setElement(3, 1, 4);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	status = matrix.setElement(3, 0, 20);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	status = matrix.setElement(1, 1, 2);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	int * nnzValues;
	int  nNNZValues;

	status = matrix.getRowNNZValues(0, &nnzValues, &nNNZValues);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	BOOST_CHECK_EQUAL(nNNZValues, 2);
	BOOST_CHECK_EQUAL(nnzValues[0], 2);
	BOOST_CHECK_EQUAL(nnzValues[1], 3);
	free(nnzValues);

	status = matrix.getRowNNZValues(1, &nnzValues, &nNNZValues);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	BOOST_CHECK_EQUAL(nNNZValues, 1);
	BOOST_CHECK_EQUAL(nnzValues[0], 2);
	free(nnzValues);

	status = matrix.getRowNNZValues(3, &nnzValues, &nNNZValues);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	BOOST_CHECK_EQUAL(nNNZValues, 2);
	BOOST_CHECK_EQUAL(nnzValues[0], 20);
	BOOST_CHECK_EQUAL(nnzValues[1], 4);
	free(nnzValues);
}

// Test 2: Check for error if row index is lower than matrix index range
BOOST_AUTO_TEST_CASE(getRowNNZValues_test2)
{
	cupcfd::error::eCodes status;

	SparseMatrixCSR<int, int> matrix(4, 4, 3);

	status = matrix.setElement(3, 3, 4);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	int * nnzValues;
	int  nNNZValues;

	status = matrix.getRowNNZValues(2, &nnzValues, &nNNZValues);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_MATRIX_ROW_OOB);
}

// Test 3: Check for error if row index is higher than matrix index range
BOOST_AUTO_TEST_CASE(getRowNNZValues_test3)
{
	cupcfd::error::eCodes status;

	SparseMatrixCSR<int, int> matrix(4, 4, 3);

	status = matrix.setElement(3, 3, 4);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

	int * nnzValues;
	int  nNNZValues;

	status = matrix.getRowNNZValues(8, &nnzValues, &nNNZValues);
	BOOST_CHECK_EQUAL(status, cupcfd::error::E_MATRIX_ROW_OOB);
}


//  Copyright (c) 2011 David Bellot
//
//  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)

#include <cmath>
#include <boost/numeric/ublas/traits/const_iterator_type.hpp>
#include <boost/numeric/ublas/traits/iterator_type.hpp>
#include <boost/numeric/ublas/traits/c_array.hpp>
#include <boost/numeric/ublas/fwd.hpp>
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/matrix_expression.hpp>
#include <boost/numeric/ublas/operation/begin.hpp>
#include <boost/numeric/ublas/operation/end.hpp>
#include <boost/numeric/ublas/tags.hpp>
#include <boost/numeric/ublas/vector.hpp>
#include <boost/numeric/ublas/vector_expression.hpp>
#include <iostream>
#include "utils.hpp"

static const double TOL(1.0e-5); ///< Used for comparing two real numbers.

#ifdef BOOST_UBLAS_NO_NESTED_CLASS_RELATION
#error "sorry this feature is not supported by your compiler"
#endif

BOOST_UBLAS_TEST_DEF( test_vector_iteration )
{
    BOOST_UBLAS_DEBUG_TRACE( "TEST Vector Iteration" );

    typedef double value_type;
    typedef boost::numeric::ublas::vector<value_type> vector_type;

    vector_type v(5);

    v(0) = 0.555950;
    v(1) = 0.108929;
    v(2) = 0.948014;
    v(3) = 0.023787;
    v(4) = 1.023787;

    vector_type::size_type ix = 0;
    for (
            boost::numeric::ublas::iterator_type<vector_type>::type it = boost::numeric::ublas::begin<vector_type>(v);
            it != boost::numeric::ublas::end<vector_type>(v);
            ++it
    ) {
        BOOST_UBLAS_DEBUG_TRACE( "*it = " << *it << " ==> " << v(ix) );
        BOOST_UBLAS_TEST_CHECK( std::abs(*it - v(ix)) <= TOL );
        ++ix;
    }
}

BOOST_UBLAS_TEST_DEF( test_vector_const_iteration )
{
    BOOST_UBLAS_DEBUG_TRACE( "TEST Vector Const Iteration" );

    typedef double value_type;
    typedef boost::numeric::ublas::vector<value_type> vector_type;

    vector_type v(5);

    v(0) = 0.555950;
    v(1) = 0.108929;
    v(2) = 0.948014;
    v(3) = 0.023787;
    v(4) = 1.023787;

    vector_type::size_type ix = 0;
    for (
            boost::numeric::ublas::const_iterator_type<vector_type>::type it = boost::numeric::ublas::begin<vector_type>(v);
            it != boost::numeric::ublas::end<vector_type>(v);
            ++it
    ) {
        BOOST_UBLAS_DEBUG_TRACE( "*it = " << *it << " ==> " << v(ix) );
        BOOST_UBLAS_TEST_CHECK( std::abs(*it - v(ix)) <= TOL );
        ++ix;
    }
}

BOOST_UBLAS_TEST_DEF( test_row_major_matrix_iteration )
{
    BOOST_UBLAS_DEBUG_TRACE( "TEST Row-major Matrix Iteration" );

    typedef double value_type;
    typedef boost::numeric::ublas::matrix<value_type, boost::numeric::ublas::row_major> matrix_type;
    typedef boost::numeric::ublas::iterator_type<matrix_type, boost::numeric::ublas::tag::major>::type outer_iterator_type;
    typedef boost::numeric::ublas::iterator_type<matrix_type, boost::numeric::ublas::tag::minor>::type inner_iterator_type;

    matrix_type A(5,4);

    A(0,0) = 0.555950; A(0,1) = 0.274690; A(0,2) = 0.540605; A(0,3) = 0.798938;
    A(1,0) = 0.108929; A(1,1) = 0.830123; A(1,2) = 0.891726; A(1,3) = 0.895283;
    A(2,0) = 0.948014; A(2,1) = 0.973234; A(2,2) = 0.216504; A(2,3) = 0.883152;
    A(3,0) = 0.023787; A(3,1) = 0.675382; A(3,2) = 0.231751; A(3,3) = 0.450332;
    A(4,0) = 1.023787; A(4,1) = 1.675382; A(4,2) = 1.231751; A(4,3) = 1.450332;

    matrix_type::size_type row(0);
    for (
            outer_iterator_type outer_it = boost::numeric::ublas::begin<boost::numeric::ublas::tag::major>(A);
            outer_it != boost::numeric::ublas::end<boost::numeric::ublas::tag::major>(A);
            ++outer_it
    ) {
        matrix_type::size_type col(0);

        for (
                inner_iterator_type inner_it = boost::numeric::ublas::begin(outer_it);
                inner_it != boost::numeric::ublas::end(outer_it);
                ++inner_it
        ) {
            BOOST_UBLAS_DEBUG_TRACE( "*it = " << *inner_it << " ==> " << A(row,col) );
            BOOST_UBLAS_TEST_CHECK( std::abs(*inner_it - A(row,col)) <= TOL );

            ++col;
        }

        ++row;
    }
}

BOOST_UBLAS_TEST_DEF( test_col_major_matrix_iteration )
{
    BOOST_UBLAS_DEBUG_TRACE( "TEST Column-major Matrix Iteration" );

    typedef double value_type;
    typedef boost::numeric::ublas::matrix<value_type, boost::numeric::ublas::column_major> matrix_type;
    typedef boost::numeric::ublas::iterator_type<matrix_type, boost::numeric::ublas::tag::major>::type outer_iterator_type;
    typedef boost::numeric::ublas::iterator_type<matrix_type, boost::numeric::ublas::tag::minor>::type inner_iterator_type;

    matrix_type A(5,4);

    A(0,0) = 0.555950; A(0,1) = 0.274690; A(0,2) = 0.540605; A(0,3) = 0.798938;
    A(1,0) = 0.108929; A(1,1) = 0.830123; A(1,2) = 0.891726; A(1,3) = 0.895283;
    A(2,0) = 0.948014; A(2,1) = 0.973234; A(2,2) = 0.216504; A(2,3) = 0.883152;
    A(3,0) = 0.023787; A(3,1) = 0.675382; A(3,2) = 0.231751; A(3,3) = 0.450332;
    A(4,0) = 1.023787; A(4,1) = 1.675382; A(4,2) = 1.231751; A(4,3) = 1.450332;

    matrix_type::size_type col(0);
    for (
            outer_iterator_type outer_it = boost::numeric::ublas::begin<boost::numeric::ublas::tag::major>(A);
            outer_it != boost::numeric::ublas::end<boost::numeric::ublas::tag::major>(A);
            ++outer_it
    ) {
        matrix_type::size_type row(0);

        for (
                inner_iterator_type inner_it = boost::numeric::ublas::begin(outer_it);
                inner_it != boost::numeric::ublas::end(outer_it);
                ++inner_it
        ) {
            BOOST_UBLAS_DEBUG_TRACE( "*it = " << *inner_it << " ==> " << A(row,col) );
            BOOST_UBLAS_TEST_CHECK( std::abs(*inner_it - A(row,col)) <= TOL );

            ++row;
        }

        ++col;
    }
}

int main()
{
    BOOST_UBLAS_TEST_BEGIN();

    BOOST_UBLAS_TEST_DO( test_vector_iteration );
    BOOST_UBLAS_TEST_DO( test_vector_const_iteration );
    BOOST_UBLAS_TEST_DO( test_row_major_matrix_iteration );
    BOOST_UBLAS_TEST_DO( test_col_major_matrix_iteration );

    BOOST_UBLAS_TEST_END();
}


// (C) Copyright 2007-2009 Andrew Sutton
//
// Use, modification and distribution are subject to the
// Boost Software License, Version 1.0 (See accompanying file
// LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_GRAPH_CLIQUE_HPP
#define BOOST_GRAPH_CLIQUE_HPP

#include <vector>
#include <deque>
#include <boost/config.hpp>

#include <boost/graph/graph_concepts.hpp>
#include <boost/graph/lookup_edge.hpp>

#include <boost/concept/detail/concept_def.hpp>
namespace boost {
    namespace concepts {
        BOOST_concept(CliqueVisitor,(Visitor)(Clique)(Graph))
        {
            BOOST_CONCEPT_USAGE(CliqueVisitor)
            {
                vis.clique(k, g);
            }
        private:
            Visitor vis;
            Graph g;
            Clique k;
        };
    } /* namespace concepts */
using concepts::CliqueVisitorConcept;
} /* namespace boost */
#include <boost/concept/detail/concept_undef.hpp>

namespace boost
{
// The algorithm implemented in this paper is based on the so-called
// Algorithm 457, published as:
//
//     @article{362367,
//         author = {Coen Bron and Joep Kerbosch},
//         title = {Algorithm 457: finding all cliques of an undirected graph},
//         journal = {Communications of the ACM},
//         volume = {16},
//         number = {9},
//         year = {1973},
//         issn = {0001-0782},
//         pages = {575--577},
//         doi = {http://doi.acm.org/10.1145/362342.362367},
//             publisher = {ACM Press},
//             address = {New York, NY, USA},
//         }
//
// Sort of. This implementation is adapted from the 1st version of the
// algorithm and does not implement the candidate selection optimization
// described as published - it could, it just doesn't yet.
//
// The algorithm is given as proportional to (3.14)^(n/3) power. This is
// not the same as O(...), but based on time measures and approximation.
//
// Unfortunately, this implementation may be less efficient on non-
// AdjacencyMatrix modeled graphs due to the non-constant implementation
// of the edge(u,v,g) functions.
//
// TODO: It might be worthwhile to provide functionality for passing
// a connectivity matrix to improve the efficiency of those lookups
// when needed. This could simply be passed as a BooleanMatrix
// s.t. edge(u,v,B) returns true or false. This could easily be
// abstracted for adjacency matricies.
//
// The following paper is interesting for a number of reasons. First,
// it lists a number of other such algorithms and second, it describes
// a new algorithm (that does not appear to require the edge(u,v,g)
// function and appears fairly efficient. It is probably worth investigating.
//
//      @article{DBLP:journals/tcs/TomitaTT06,
//          author = {Etsuji Tomita and Akira Tanaka and Haruhisa Takahashi},
//          title = {The worst-case time complexity for generating all maximal cliques and computational experiments},
//          journal = {Theor. Comput. Sci.},
//          volume = {363},
//          number = {1},
//          year = {2006},
//          pages = {28-42}
//          ee = {http://dx.doi.org/10.1016/j.tcs.2006.06.015}
//      }

/**
 * The default clique_visitor supplies an empty visitation function.
 */
struct clique_visitor
{
    template <typename VertexSet, typename Graph>
    void clique(const VertexSet&, Graph&)
    { }
};

/**
 * The max_clique_visitor records the size of the maximum clique (but not the
 * clique itself).
 */
struct max_clique_visitor
{
    max_clique_visitor(std::size_t& max)
        : maximum(max)
    { }

    template <typename Clique, typename Graph>
    inline void clique(const Clique& p, const Graph& g)
    {
        BOOST_USING_STD_MAX();
        maximum = max BOOST_PREVENT_MACRO_SUBSTITUTION (maximum, p.size());
    }
    std::size_t& maximum;
};

inline max_clique_visitor find_max_clique(std::size_t& max)
{ return max_clique_visitor(max); }

namespace detail
{
    template <typename Graph>
    inline bool
    is_connected_to_clique(const Graph& g,
                            typename graph_traits<Graph>::vertex_descriptor u,
                            typename graph_traits<Graph>::vertex_descriptor v,
                            typename graph_traits<Graph>::undirected_category)
    {
        return lookup_edge(u, v, g).second;
    }

    template <typename Graph>
    inline bool
    is_connected_to_clique(const Graph& g,
                            typename graph_traits<Graph>::vertex_descriptor u,
                            typename graph_traits<Graph>::vertex_descriptor v,
                            typename graph_traits<Graph>::directed_category)
    {
        // Note that this could alternate between using an || to determine
        // full connectivity. I believe that this should produce strongly
        // connected components. Note that using && instead of || will
        // change the results to a fully connected subgraph (i.e., symmetric
        // edges between all vertices s.t., if a->b, then b->a.
        return lookup_edge(u, v, g).second && lookup_edge(v, u, g).second;
    }

    template <typename Graph, typename Container>
    inline void
    filter_unconnected_vertices(const Graph& g,
                                typename graph_traits<Graph>::vertex_descriptor v,
                                const Container& in,
                                Container& out)
    {
        function_requires< GraphConcept<Graph> >();

        typename graph_traits<Graph>::directed_category cat;
        typename Container::const_iterator i, end = in.end();
        for(i = in.begin(); i != end; ++i) {
            if(is_connected_to_clique(g, v, *i, cat)) {
                out.push_back(*i);
            }
        }
    }

    template <
        typename Graph,
        typename Clique,        // compsub type
        typename Container,     // candidates/not type
        typename Visitor>
    void extend_clique(const Graph& g,
                        Clique& clique,
                        Container& cands,
                        Container& nots,
                        Visitor vis,
                        std::size_t min)
    {
        function_requires< GraphConcept<Graph> >();
        function_requires< CliqueVisitorConcept<Visitor,Clique,Graph> >();
        typedef typename graph_traits<Graph>::vertex_descriptor Vertex;

        // Is there vertex in nots that is connected to all vertices
        // in the candidate set? If so, no clique can ever be found.
        // This could be broken out into a separate function.
        {
            typename Container::iterator ni, nend = nots.end();
            typename Container::iterator ci, cend = cands.end();
            for(ni = nots.begin(); ni != nend; ++ni) {
                for(ci = cands.begin(); ci != cend; ++ci) {
                    // if we don't find an edge, then we're okay.
                    if(!lookup_edge(*ni, *ci, g).second) break;
                }
                // if we iterated all the way to the end, then *ni
                // is connected to all *ci
                if(ci == cend) break;
            }
            // if we broke early, we found *ni connected to all *ci
            if(ni != nend) return;
        }

        // TODO: the original algorithm 457 describes an alternative
        // (albeit really complicated) mechanism for selecting candidates.
        // The given optimizaiton seeks to bring about the above
        // condition sooner (i.e., there is a vertex in the not set
        // that is connected to all candidates). unfortunately, the
        // method they give for doing this is fairly unclear.

        // basically, for every vertex in not, we should know how many
        // vertices it is disconnected from in the candidate set. if
        // we fix some vertex in the not set, then we want to keep
        // choosing vertices that are not connected to that fixed vertex.
        // apparently, by selecting fix point with the minimum number
        // of disconnections (i.e., the maximum number of connections
        // within the candidate set), then the previous condition wil
        // be reached sooner.

        // there's some other stuff about using the number of disconnects
        // as a counter, but i'm jot really sure i followed it.

        // TODO: If we min-sized cliques to visit, then theoretically, we
        // should be able to stop recursing if the clique falls below that
        // size - maybe?

        // otherwise, iterate over candidates and and test
        // for maxmimal cliquiness.
        typename Container::iterator i, j, end = cands.end();
        for(i = cands.begin(); i != cands.end(); ) {
            Vertex candidate = *i;

            // add the candidate to the clique (keeping the iterator!)
            // typename Clique::iterator ci = clique.insert(clique.end(), candidate);
            clique.push_back(candidate);

            // remove it from the candidate set
            i = cands.erase(i);

            // build new candidate and not sets by removing all vertices
            // that are not connected to the current candidate vertex.
            // these actually invert the operation, adding them to the new
            // sets if the vertices are connected. its semantically the same.
            Container new_cands, new_nots;
            filter_unconnected_vertices(g, candidate, cands, new_cands);
            filter_unconnected_vertices(g, candidate, nots, new_nots);

            if(new_cands.empty() && new_nots.empty()) {
                // our current clique is maximal since there's nothing
                // that's connected that we haven't already visited. If
                // the clique is below our radar, then we won't visit it.
                if(clique.size() >= min) {
                    vis.clique(clique, g);
                }
            }
            else {
                // recurse to explore the new candidates
                extend_clique(g, clique, new_cands, new_nots, vis, min);
            }

            // we're done with this vertex, so we need to move it
            // to the nots, and remove the candidate from the clique.
            nots.push_back(candidate);
            clique.pop_back();
        }
    }
} /* namespace detail */

template <typename Graph, typename Visitor>
inline void
bron_kerbosch_all_cliques(const Graph& g, Visitor vis, std::size_t min)
{
    function_requires< IncidenceGraphConcept<Graph> >();
    function_requires< VertexListGraphConcept<Graph> >();
    function_requires< VertexIndexGraphConcept<Graph> >();
    function_requires< AdjacencyMatrixConcept<Graph> >(); // Structural requirement only
    typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
    typedef typename graph_traits<Graph>::vertex_iterator VertexIterator;
    typedef std::vector<Vertex> VertexSet;
    typedef std::deque<Vertex> Clique;
    function_requires< CliqueVisitorConcept<Visitor,Clique,Graph> >();

    // NOTE: We're using a deque to implement the clique, because it provides
    // constant inserts and removals at the end and also a constant size.

    VertexIterator i, end;
    tie(i, end) = vertices(g);
    VertexSet cands(i, end);    // start with all vertices as candidates
    VertexSet nots;             // start with no vertices visited

    Clique clique;              // the first clique is an empty vertex set
    detail::extend_clique(g, clique, cands, nots, vis, min);
}

// NOTE: By default the minimum number of vertices per clique is set at 2
// because singleton cliques aren't really very interesting.
template <typename Graph, typename Visitor>
inline void
bron_kerbosch_all_cliques(const Graph& g, Visitor vis)
{ bron_kerbosch_all_cliques(g, vis, 2); }

template <typename Graph>
inline std::size_t
bron_kerbosch_clique_number(const Graph& g)
{
    std::size_t ret = 0;
    bron_kerbosch_all_cliques(g, find_max_clique(ret));
    return ret;
}

} /* namespace boost */

#endif


/*! \file demo_1d_simple.cpp
    \brief Example of a simple 1D plot of two vectors of data.
    \details Creates file demo_1d_simple.svg
    \author Jacob Voytko and Paul A. Bristow
    \date 2007
  */

// Copyright Jacob Voytko 2007
// Copyright Paul A. Bristow 2009

// Distributed under the Boost Software License, Version 1.0.
// For more information, see http://www.boost.org

// An example to demonstrate very simple 1D settings.
// See also demo_1d_plot.cpp for a wider range of use.

// This file is written to be included from a Quickbook .qbk document.
// It can be compiled by the C++ compiler, and run. Any output can
// also be added here as comment or included or pasted in elsewhere.

// Caution: this file contains Quickbook markup as well as code
// and comments: don't change any of the special comment markups!

//[demo_1d_simple_1

/*`First we need a few includes to use Boost.Plot, and an STL 1D container vector.
*/

#include <boost/svg_plot/svg_1d_plot.hpp>
using namespace boost::svg;

#include <vector>
using std::vector;

int main()
{ // Construct two STL containers for the two data series to plot.
  vector<double> dan_times;
  vector<double> elaine_times;

  // Fill the two containers with some data:
  dan_times.push_back(3.1);
  dan_times.push_back(4.2);
  elaine_times.push_back(2.1);
  elaine_times.push_back(7.8);

  svg_1d_plot my_plot; // Construct a plot.

  my_plot.legend_on(true) // Set title and legend, and X-axis range and label.
         .title("Race Times")
         .x_label("time (sec)")
         .x_range(-1, 11);
  // There are hundreds of other possible options here!

  // Add the two containers of data to the plot, choosing two different colors.
  my_plot.plot(dan_times, "Dan").stroke_color(blue);
  my_plot.plot(elaine_times, "Elaine").stroke_color(orange);

  my_plot.write("./demo_1d_simple.svg"); // Finally write the plot to a file.

  return 0;
} // int main()

//] [/demo_1d_simple_1]


// Copyright (c) 2018-2019, University of Bremen, M. Farzalipour Tabriz
// Copyrights licensed under the 2-Clause BSD License.
// See the accompanying LICENSE.txt file for terms.

#pragma once
#include <armadillo>
#include <iomanip>
#include <iostream>

using namespace std;
using namespace arma;

//single-line output for vec
template<typename T>
ostream &operator << (ostream &o, const Row<T> &vec) {
	for (uword elem = 0; elem < vec.n_elem; ++elem) {
		o << vec(elem);
		if (elem != vec.n_elem - 1) {
			o << " ";
		}
	}
	return o;
}

template<typename T>
ostream &operator << (ostream &o, const subview<T> &vec) {
	for (uword elem = 0; elem < vec.n_elem; ++elem) {
		o << vec(elem);
		if (elem != vec.n_elem - 1) {
			o  << " ";
		}
	}

	return o;
}

//single-line output for mat
template<typename T>
ostream &operator << (ostream &o, const Mat<T> &mat) {
	mat.each_row([&o](const Row<T> &row) { o << row << "; "; });
	return o;
}

template<typename T>
istream &operator >> (istream &o, Row<T> &vec) {
	vec.for_each([&o](T &elem) { o >> elem; });
	return o;
}

template<typename T>
istream &operator >> (istream &o, subview_row<T> vec) {
	vec.for_each([&o](T &elem) { o >> elem; });
	return o;
}

template<typename T>
istream &operator >> (istream &o, Cube<T> &c) {
	c.for_each([&o](T &elem) { o >> elem; });
	return o;
}

template<typename T>
istream &operator >> (istream &o, Mat<T> &m) {
	m.for_each([&o](T &elem) { o >> elem; });
	return o;
}

template <typename T>
string to_string(T input) {
	ostringstream output;
	output << setprecision(15);
	output << input;
	return output.str();
}


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

// 这个教程程序很奇怪，与其他大多数步骤不同，介绍中已经提供了关于如何使用各种策略来生成网格的大部分信息。因此，这里没有什么需要评论的，我们在代码中穿插了相对较少的文字。从本质上讲，这里的代码只是提供了一个在介绍中已经描述过的内容的参考实现。

//  @sect3{Include files}
#include <deal.II/grid/tria.h>
#include <deal.II/grid/grid_generator.h>
#include <deal.II/grid/grid_tools.h>
#include <deal.II/grid/manifold_lib.h>
#include <deal.II/grid/grid_out.h>
#include <deal.II/grid/grid_in.h>

#include <iostream>
#include <fstream>

#include <map>

using namespace dealii;
// @sect3{Generating output for a given mesh}

// 下面的函数为我们将在本程序的剩余部分中生成的任何网格生成一些输出。特别是，它生成了以下信息。

// - 一些关于这个网格所处的空间维数和它的单元数的一般信息。

// - 使用每个边界指标的边界面的数量，这样就可以和我们预期的情况进行比较。

// 最后，该函数将网格输出为VTU格式，可以方便地在Paraview或VisIt中进行可视化。

template <int dim>
void print_mesh_info(const Triangulation<dim> &triangulation,
                     const std::string &       filename)
{
  std::cout << "Mesh info:" << std::endl
            << " dimension: " << dim << std::endl
            << " no. of cells: " << triangulation.n_active_cells() << std::endl;

// 接下来循环所有单元格的所有面，找出每个边界指标的使用频率（请记住，如果你访问一个不存在的 std::map 对象的元素，它将被隐式创建并默认初始化--在当前情况下为零--然后我们再将其增加）。

  {
    std::map<types::boundary_id, unsigned int> boundary_count;
    for (const auto &face : triangulation.active_face_iterators())
      if (face->at_boundary())
        boundary_count[face->boundary_id()]++;

    std::cout << " boundary indicators: ";
    for (const std::pair<const types::boundary_id, unsigned int> &pair :
         boundary_count)
      {
        std::cout << pair.first << "(" << pair.second << " times) ";
      }
    std::cout << std::endl;
  }

// 最后，产生一个网格的图形表示到一个输出文件。

  std::ofstream out(filename);
  GridOut       grid_out;
  grid_out.write_vtu(triangulation, out);
  std::cout << " written to " << filename << std::endl << std::endl;
}
// @sect3{Main routines}
// @sect4{grid_1: Loading a mesh generated by gmsh}

// 在这第一个例子中，我们展示了如何加载我们在介绍中讨论过的如何生成的网格。这与 step-5 中加载网格的模式相同，尽管那里是以不同的文件格式（UCD而不是MSH）编写。

void grid_1()
{
  Triangulation<2> triangulation;

  GridIn<2> gridin;
  gridin.attach_triangulation(triangulation);
  std::ifstream f("example.msh");
  gridin.read_msh(f);

  print_mesh_info(triangulation, "grid-1.vtu");
}
// @sect4{grid_2: Merging triangulations}

// 在这里，我们首先创建两个三角形，然后将它们合并成一个。 正如介绍中所讨论的，必须确保共同界面的顶点位于相同的坐标上。

void grid_2()
{
  Triangulation<2> tria1;
  GridGenerator::hyper_cube_with_cylindrical_hole(tria1, 0.25, 1.0);

  Triangulation<2>          tria2;
  std::vector<unsigned int> repetitions(2);
  repetitions[0] = 3;
  repetitions[1] = 2;
  GridGenerator::subdivided_hyper_rectangle(tria2,
                                            repetitions,
                                            Point<2>(1.0, -1.0),
                                            Point<2>(4.0, 1.0));

  Triangulation<2> triangulation;
  GridGenerator::merge_triangulations(tria1, tria2, triangulation);

  print_mesh_info(triangulation, "grid-2.vtu");
}
// @sect4{grid_3: Moving vertices}

// 在这个函数中，我们移动一个网格的顶点。这比人们通常想象的要简单：如果你用 cell-@>vertex(i) 询问一个单元格的 i 的顶点的坐标，它不只是提供这个顶点的位置，实际上是对存储这些坐标的位置的引用。然后我们可以修改存储在那里的值。

// 所以这就是我们在这个函数的第一部分所做的。我们创建一个几何形状为 $[-1,1]^2$ 的正方形，在原点处有一个半径为0.25的圆孔。然后我们在所有单元格和所有顶点上循环，如果一个顶点的 $y$ 坐标等于1，我们就把它向上移动0.5。

// 注意，这种程序通常不是这样工作的，因为通常会多次遇到相同的顶点，并且可能会多次移动它们。它在这里起作用是因为我们根据顶点的几何位置来选择要使用的顶点，而移动过一次的顶点在未来将无法通过这个测试。解决这个问题的一个更普遍的方法是保留一个 std::set ，即那些我们已经移动过的顶点索引（我们可以用 cell-@>vertex_index(i) 获得，并且只移动那些索引还不在这个集合中的顶点。

void grid_3()
{
  Triangulation<2> triangulation;
  GridGenerator::hyper_cube_with_cylindrical_hole(triangulation, 0.25, 1.0);

  for (const auto &cell : triangulation.active_cell_iterators())
    {
      for (const auto i : cell->vertex_indices())
        {
          Point<2> &v = cell->vertex(i);
          if (std::abs(v(1) - 1.0) < 1e-5)
            v(1) += 0.5;
        }
    }

// 在第二步，我们将对网格进行两次细化。为了正确做到这一点，我们应该沿着以原点为中心的圆的表面在内部边界上放置新的点。幸运的是， GridGenerator::hyper_cube_with_cylindrical_hole 已经在内部边界上附加了一个Manifold对象，所以我们不需要做任何事情，只需要细化网格（参见results section中一个完全可行的例子，我们  做  附加一个Manifold对象）。

  triangulation.refine_global(2);
  print_mesh_info(triangulation, "grid-3.vtu");
}

// 如上图所示，做事有一个障碍。如果像这里所示的那样移动边界上的节点，由于内部的节点没有被移动，所以经常会出现内部的单元被严重扭曲的情况。在目前的情况下，这并不是一个很大的问题，因为当节点被移动时，网格并不包含任何内部节点--它是粗略的网格，而且恰好所有的顶点都在边界上。还有一种情况是，我们在这里的移动，与平均单元的大小相比，并没有太大影响。然而，有时我们确实想把顶点移动一段距离，在这种情况下，我们也需要移动内部节点。一个自动完成的方法是调用函数 GridTools::laplace_transform ，该函数接收一组转换后的顶点坐标并移动所有其他的顶点，使产生的网格在某种意义上有一个小的变形。

//  @sect4{grid_4: Demonstrating extrude_triangulation}

// 这个例子从前面的函数中获取初始网格，并简单地将其挤压到第三空间维度。

void grid_4()
{
  Triangulation<2> triangulation;
  Triangulation<3> out;
  GridGenerator::hyper_cube_with_cylindrical_hole(triangulation, 0.25, 1.0);

  GridGenerator::extrude_triangulation(triangulation, 3, 2.0, out);
  print_mesh_info(out, "grid-4.vtu");
}
// @sect4{grid_5: Demonstrating GridTools::transform, part 1}

// 这个例子和下一个例子首先创建一个网格，然后根据一个函数移动网格的每个节点，这个函数接收一个点并返回一个映射的点。在这个例子中，我们转换  $(x,y) \mapsto (x,y+\sin(\pi x/5))$  。

//  GridTools::transform()  需要一个三角形和一个参数，这个参数可以像一个函数一样被调用，接收一个点并返回一个点。有不同的方式来提供这样一个参数。它可以是一个函数的指针；它可以是一个具有`operator()`的类的对象；它可以是一个lambda函数；或者它可以是任何通过 std::function@<Point@<2@>(const Point@<2@>)@> 对象描述的东西。

// 更现代的方法是使用一个接受一个点并返回一个点的lambda函数，这就是我们在下面所做的。

void grid_5()
{
  Triangulation<2>          triangulation;
  std::vector<unsigned int> repetitions(2);
  repetitions[0] = 14;
  repetitions[1] = 2;
  GridGenerator::subdivided_hyper_rectangle(triangulation,
                                            repetitions,
                                            Point<2>(0.0, 0.0),
                                            Point<2>(10.0, 1.0));

  GridTools::transform(
    [](const Point<2> &in) {
      return Point<2>(in[0], in[1] + std::sin(numbers::PI * in[0] / 5.0));
    },
    triangulation);
  print_mesh_info(triangulation, "grid-5.vtu");
}

//  @sect4{grid_6: Demonstrating GridTools::transform, part 2}

// 在第二个例子中，我们将使用映射  $(x,y) \mapsto (x,\tanh(2y)/\tanh(2))$  将原始网格中的点转换为新的网格。为了使事情更有趣，而不是像前面的例子那样在一个单一的函数中完成，我们在这里创建一个具有  operator()  的对象，这个对象将被  GridTools::transform.  所调用。当然，这个对象实际上可能要复杂得多：这个对象可能有成员变量，在计算顶点的新位置时起作用。

struct Grid6Func
{
  double trans(const double y) const
  {
    return std::tanh(2 * y) / tanh(2);
  }

  Point<2> operator()(const Point<2> &in) const
  {
    return {in(0), trans(in(1))};
  }
};

void grid_6()
{
  Triangulation<2>          triangulation;
  std::vector<unsigned int> repetitions(2);
  repetitions[0] = repetitions[1] = 40;
  GridGenerator::subdivided_hyper_rectangle(triangulation,
                                            repetitions,
                                            Point<2>(0.0, 0.0),
                                            Point<2>(1.0, 1.0));

  GridTools::transform(Grid6Func(), triangulation);
  print_mesh_info(triangulation, "grid-6.vtu");
}
// @sect4{grid_7: Demonstrating distort_random}

// 在这最后一个例子中，我们创建了一个网格，然后通过随机扰动使其（内部）顶点变形。这不是你想在生产计算中做的事情（因为在具有 "良好形状 "单元的网格上的结果通常比在 GridTools::distort_random()), 产生的变形单元上的结果要好，但这是一个有用的工具，可以测试离散化和代码，确保它们不会因为网格恰好是均匀结构和支持超级收敛特性而意外地工作。

void grid_7()
{
  Triangulation<2>          triangulation;
  std::vector<unsigned int> repetitions(2);
  repetitions[0] = repetitions[1] = 16;
  GridGenerator::subdivided_hyper_rectangle(triangulation,
                                            repetitions,
                                            Point<2>(0.0, 0.0),
                                            Point<2>(1.0, 1.0));

  GridTools::distort_random(0.3, triangulation, true);
  print_mesh_info(triangulation, "grid-7.vtu");
}
// @sect3{The main function}

// 最后是主函数。这里没有什么可做的，只是调用我们上面写的所有各种函数。

int main()
{
  try
    {
      grid_1();
      grid_2();
      grid_3();
      grid_4();
      grid_5();
      grid_6();
      grid_7();
    }
  catch (std::exception &exc)
    {
      std::cerr << std::endl
                << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Exception on processing: " << std::endl
                << exc.what() << std::endl
                << "Aborting!" << std::endl
                << "----------------------------------------------------"
                << std::endl;

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


// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Hauke Heibel <hauke.heibel@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include "main.h"

#include <Eigen/Core>
#include "AnnoyingScalar.h"

using namespace Eigen;

template <typename Scalar, int Storage>
void run_matrix_tests()
{
  typedef Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic, Storage> MatrixType;

  MatrixType m, n;

  // boundary cases ...
  m = n = MatrixType::Random(50,50);
  m.conservativeResize(1,50);
  VERIFY_IS_APPROX(m, n.block(0,0,1,50));

  m = n = MatrixType::Random(50,50);
  m.conservativeResize(50,1);
  VERIFY_IS_APPROX(m, n.block(0,0,50,1));

  m = n = MatrixType::Random(50,50);
  m.conservativeResize(50,50);
  VERIFY_IS_APPROX(m, n.block(0,0,50,50));

  // random shrinking ...
  for (int i=0; i<25; ++i)
  {
    const Index rows = internal::random<Index>(1,50);
    const Index cols = internal::random<Index>(1,50);
    m = n = MatrixType::Random(50,50);
    m.conservativeResize(rows,cols);
    VERIFY_IS_APPROX(m, n.block(0,0,rows,cols));
  }

  // random growing with zeroing ...
  for (int i=0; i<25; ++i)
  {
    const Index rows = internal::random<Index>(50,75);
    const Index cols = internal::random<Index>(50,75);
    m = n = MatrixType::Random(50,50);
    m.conservativeResizeLike(MatrixType::Zero(rows,cols));
    VERIFY_IS_APPROX(m.block(0,0,n.rows(),n.cols()), n);
    VERIFY( rows<=50 || m.block(50,0,rows-50,cols).sum() == Scalar(0) );
    VERIFY( cols<=50 || m.block(0,50,rows,cols-50).sum() == Scalar(0) );
  }
}

template <typename Scalar>
void run_vector_tests()
{
  typedef Matrix<Scalar, 1, Eigen::Dynamic> VectorType;

  VectorType m, n;

  // boundary cases ...
  m = n = VectorType::Random(50);
  m.conservativeResize(1);
  VERIFY_IS_APPROX(m, n.segment(0,1));

  m = n = VectorType::Random(50);
  m.conservativeResize(50);
  VERIFY_IS_APPROX(m, n.segment(0,50));

  m = n = VectorType::Random(50);
  m.conservativeResize(m.rows(),1);
  VERIFY_IS_APPROX(m, n.segment(0,1));

  m = n = VectorType::Random(50);
  m.conservativeResize(m.rows(),50);
  VERIFY_IS_APPROX(m, n.segment(0,50));

  // random shrinking ...
  for (int i=0; i<50; ++i)
  {
    const int size = internal::random<int>(1,50);
    m = n = VectorType::Random(50);
    m.conservativeResize(size);
    VERIFY_IS_APPROX(m, n.segment(0,size));

    m = n = VectorType::Random(50);
    m.conservativeResize(m.rows(), size);
    VERIFY_IS_APPROX(m, n.segment(0,size));
  }

  // random growing with zeroing ...
  for (int i=0; i<50; ++i)
  {
    const int size = internal::random<int>(50,100);
    m = n = VectorType::Random(50);
    m.conservativeResizeLike(VectorType::Zero(size));
    VERIFY_IS_APPROX(m.segment(0,50), n);
    VERIFY( size<=50 || m.segment(50,size-50).sum() == Scalar(0) );

    m = n = VectorType::Random(50);
    m.conservativeResizeLike(Matrix<Scalar,Dynamic,Dynamic>::Zero(1,size));
    VERIFY_IS_APPROX(m.segment(0,50), n);
    VERIFY( size<=50 || m.segment(50,size-50).sum() == Scalar(0) );
  }
}

// Basic memory leak check with a non-copyable scalar type
template<int> void noncopyable()
{
  typedef Eigen::Matrix<AnnoyingScalar,Dynamic,1> VectorType;
  typedef Eigen::Matrix<AnnoyingScalar,Dynamic,Dynamic> MatrixType;

  {
    AnnoyingScalar::dont_throw = true;
    int n = 50;
    VectorType v0(n), v1(n);
    MatrixType m0(n,n), m1(n,n), m2(n,n);
    v0.setOnes(); v1.setOnes();
    m0.setOnes(); m1.setOnes(); m2.setOnes();
    VERIFY(m0==m1);
    m0.conservativeResize(2*n,2*n);
    VERIFY(m0.topLeftCorner(n,n) == m1);

    VERIFY(v0.head(n) == v1);
    v0.conservativeResize(2*n);
    VERIFY(v0.head(n) == v1);
  }
  VERIFY(AnnoyingScalar::instances==0 && "global memory leak detected in noncopyable");
}

EIGEN_DECLARE_TEST(conservative_resize)
{
  for(int i=0; i<g_repeat; ++i)
  {
    CALL_SUBTEST_1((run_matrix_tests<int, Eigen::RowMajor>()));
    CALL_SUBTEST_1((run_matrix_tests<int, Eigen::ColMajor>()));
    CALL_SUBTEST_2((run_matrix_tests<float, Eigen::RowMajor>()));
    CALL_SUBTEST_2((run_matrix_tests<float, Eigen::ColMajor>()));
    CALL_SUBTEST_3((run_matrix_tests<double, Eigen::RowMajor>()));
    CALL_SUBTEST_3((run_matrix_tests<double, Eigen::ColMajor>()));
    CALL_SUBTEST_4((run_matrix_tests<std::complex<float>, Eigen::RowMajor>()));
    CALL_SUBTEST_4((run_matrix_tests<std::complex<float>, Eigen::ColMajor>()));
    CALL_SUBTEST_5((run_matrix_tests<std::complex<double>, Eigen::RowMajor>()));
    CALL_SUBTEST_5((run_matrix_tests<std::complex<double>, Eigen::ColMajor>()));
    CALL_SUBTEST_1((run_matrix_tests<int, Eigen::RowMajor | Eigen::DontAlign>()));

    CALL_SUBTEST_1((run_vector_tests<int>()));
    CALL_SUBTEST_2((run_vector_tests<float>()));
    CALL_SUBTEST_3((run_vector_tests<double>()));
    CALL_SUBTEST_4((run_vector_tests<std::complex<float> >()));
    CALL_SUBTEST_5((run_vector_tests<std::complex<double> >()));

    AnnoyingScalar::dont_throw = true;
    CALL_SUBTEST_6(( run_vector_tests<AnnoyingScalar>() ));
    CALL_SUBTEST_6(( noncopyable<0>() ));
  }
}


/*    This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT.
 *    See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details.
 *    Author(s):       Vincent Rouvreau
 *
 *    Copyright (C) 2014 Inria
 *
 *    Modification(s):
 *      - YYYY/MM Author: Description of the modification
 */

#include <iostream>
#include <vector>
#include <string>

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE "simplex_tree_constructor_and_move"
#include <boost/test/unit_test.hpp>
#include <boost/mpl/list.hpp>

//  ^
// /!\ Nothing else from Simplex_tree shall be included to test includes are well defined.
#include "gudhi/Simplex_tree.h"

using namespace Gudhi;

typedef boost::mpl::list<Simplex_tree<>, Simplex_tree<Simplex_tree_options_fast_persistence>> list_of_tested_variants;

template<typename Simplex_tree>
void print_simplex_filtration(Simplex_tree& st, const std::string& msg) {
  // Required before browsing through filtration values
  st.initialize_filtration();

  std::cout << "********************************************************************\n";
  std::cout << "* " << msg << "\n";
  std::cout << "* The complex contains " << st.num_simplices() << " simplices";
  std::cout << "   - dimension " << st.dimension() << "\n";
  std::cout << "* Iterator on Simplices in the filtration, with [filtration value]:\n";
  for (auto f_simplex : st.filtration_simplex_range()) {
    std::cout << "   "
              << "[" << st.filtration(f_simplex) << "] ";
    for (auto vertex : st.simplex_vertex_range(f_simplex)) std::cout << "(" << vertex << ")";
    std::cout << std::endl;
  }

}

BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_copy_constructor, Simplex_tree, list_of_tested_variants) {
  Simplex_tree st;

  st.insert_simplex_and_subfaces({2, 1, 0}, 3.0);
  st.insert_simplex_and_subfaces({0, 1, 6, 7}, 4.0);
  st.insert_simplex_and_subfaces({3, 0}, 2.0);
  st.insert_simplex_and_subfaces({3, 4, 5}, 3.0);
  st.insert_simplex_and_subfaces({8}, 1.0);
  /* Inserted simplex:        */
  /*    1   6                 */
  /*    o---o                 */
  /*   /X\7/                  */
  /*  o---o---o---o   o       */
  /*  2   0   3\X/4   8       */
  /*            o             */
  /*            5             */
  /*                          */
  /* In other words:          */
  /*   A facet  [2,1,0]       */
  /*   An edge  [0,3]         */
  /*   A facet  [3,4,5]       */
  /*   A cell   [0,1,6,7]     */
  /*   A vertex [8]           */

  print_simplex_filtration(st, "Default Simplex_tree is initialized");

  std::cout << "********************************************************************" << std::endl;
  std::cout << "TEST OF COPY CONSTRUCTOR" << std::endl;

  Simplex_tree st1(st);
  Simplex_tree st2(st);
  print_simplex_filtration(st1, "First copy constructor from the default Simplex_tree");
  print_simplex_filtration(st2, "Second copy constructor from the default Simplex_tree");
  // Cross check
  BOOST_CHECK(st1 == st2);
  BOOST_CHECK(st == st2);
  BOOST_CHECK(st1 == st);

  std::cout << "********************************************************************" << std::endl;
  std::cout << "TEST OF COPY ASSIGNMENT" << std::endl;
  Simplex_tree st3;
  // To check there is no memory leak
  st3.insert_simplex_and_subfaces({9, 10, 11}, 200.0);
  st3 = st;
  print_simplex_filtration(st3, "First copy assignment from the default Simplex_tree");
  Simplex_tree st4;
  st4 = st;
  print_simplex_filtration(st4, "Second copy assignment from the default Simplex_tree");

  // Cross check
  BOOST_CHECK(st3 == st4);
  BOOST_CHECK(st == st4);
  BOOST_CHECK(st3 == st);

  st = st;
  print_simplex_filtration(st4, "Third self copy assignment from the default Simplex_tree");

  BOOST_CHECK(st3 == st);

  std::cout << "********************************************************************" << std::endl;
  std::cout << "TEST OF MOVE CONSTRUCTOR" << std::endl;
  Simplex_tree st5(std::move(st1));
  print_simplex_filtration(st5, "First move constructor from the default Simplex_tree");
  print_simplex_filtration(st1, "First moved Simplex_tree shall be empty");
  Simplex_tree st6(std::move(st2));
  print_simplex_filtration(st6, "Second move constructor from the default Simplex_tree");
  print_simplex_filtration(st2, "Second moved Simplex_tree shall be empty");

  // Cross check
  BOOST_CHECK(st5 == st6);
  BOOST_CHECK(st == st6);
  BOOST_CHECK(st5 == st);

  Simplex_tree empty_st;
  BOOST_CHECK(st1 == st2);
  BOOST_CHECK(empty_st == st2);
  BOOST_CHECK(st1 == empty_st);

  std::cout << "********************************************************************" << std::endl;
  std::cout << "TEST OF MOVE ASSIGNMENT" << std::endl;

  Simplex_tree st7;
  // To check there is no memory leak
  st7.insert_simplex_and_subfaces({9, 10, 11}, 200.0);
  st7 = std::move(st3);
  print_simplex_filtration(st7, "First move assignment from the default Simplex_tree");
  Simplex_tree st8;
  st8 = std::move(st4);
  print_simplex_filtration(st8, "Second move assignment from the default Simplex_tree");

  // Cross check
  BOOST_CHECK(st7 == st8);
  BOOST_CHECK(st == st8);
  BOOST_CHECK(st7 == st);

  st = std::move(st);
  print_simplex_filtration(st, "Third self move assignment from the default Simplex_tree");

  BOOST_CHECK(st7 == st);

}


//
// Copyright (c) 2002--2010
// Toon Knapen, Karl Meerbergen, Kresimir Fresl,
// Thomas Klimpel and Rutger ter Borg
//
// 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)
//
// THIS FILE IS AUTOMATICALLY GENERATED
// PLEASE DO NOT EDIT!
//

#ifndef BOOST_NUMERIC_BINDINGS_BLAS_LEVEL2_TRMV_HPP
#define BOOST_NUMERIC_BINDINGS_BLAS_LEVEL2_TRMV_HPP

#include <boost/assert.hpp>
#include <boost/numeric/bindings/begin.hpp>
#include <boost/numeric/bindings/blas/detail/default_order.hpp>
#include <boost/numeric/bindings/diag_tag.hpp>
#include <boost/numeric/bindings/has_linear_array.hpp>
#include <boost/numeric/bindings/is_mutable.hpp>
#include <boost/numeric/bindings/remove_imaginary.hpp>
#include <boost/numeric/bindings/size.hpp>
#include <boost/numeric/bindings/stride.hpp>
#include <boost/numeric/bindings/trans_tag.hpp>
#include <boost/numeric/bindings/uplo_tag.hpp>
#include <boost/numeric/bindings/value_type.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/type_traits/remove_const.hpp>

//
// The BLAS-backend is selected by defining a pre-processor variable,
//  which can be one of
// * for CBLAS, define BOOST_NUMERIC_BINDINGS_BLAS_CBLAS
// * for CUBLAS, define BOOST_NUMERIC_BINDINGS_BLAS_CUBLAS
// * netlib-compatible BLAS is the default
//
#if defined BOOST_NUMERIC_BINDINGS_BLAS_CBLAS
#include <boost/numeric/bindings/blas/detail/cblas.h>
#include <boost/numeric/bindings/blas/detail/cblas_option.hpp>
#elif defined BOOST_NUMERIC_BINDINGS_BLAS_CUBLAS
#include <boost/numeric/bindings/blas/detail/cublas.h>
#include <boost/numeric/bindings/blas/detail/blas_option.hpp>
#else
#include <boost/numeric/bindings/blas/detail/blas.h>
#include <boost/numeric/bindings/blas/detail/blas_option.hpp>
#endif

namespace boost {
namespace numeric {
namespace bindings {
namespace blas {

//
// The detail namespace contains value-type-overloaded functions that
// dispatch to the appropriate back-end BLAS-routine.
//
namespace detail {

#if defined BOOST_NUMERIC_BINDINGS_BLAS_CBLAS
//
// Overloaded function for dispatching to
// * CBLAS backend, and
// * float value-type.
//
template< typename Order, typename UpLo, typename Trans, typename Diag >
inline void trmv( const Order, const UpLo, const Trans, const Diag,
        const int n, const float* a, const int lda, float* x,
        const int incx ) {
    cblas_strmv( cblas_option< Order >::value, cblas_option< UpLo >::value,
            cblas_option< Trans >::value, cblas_option< Diag >::value, n, a,
            lda, x, incx );
}

//
// Overloaded function for dispatching to
// * CBLAS backend, and
// * double value-type.
//
template< typename Order, typename UpLo, typename Trans, typename Diag >
inline void trmv( const Order, const UpLo, const Trans, const Diag,
        const int n, const double* a, const int lda, double* x,
        const int incx ) {
    cblas_dtrmv( cblas_option< Order >::value, cblas_option< UpLo >::value,
            cblas_option< Trans >::value, cblas_option< Diag >::value, n, a,
            lda, x, incx );
}

//
// Overloaded function for dispatching to
// * CBLAS backend, and
// * complex<float> value-type.
//
template< typename Order, typename UpLo, typename Trans, typename Diag >
inline void trmv( const Order, const UpLo, const Trans, const Diag,
        const int n, const std::complex<float>* a, const int lda,
        std::complex<float>* x, const int incx ) {
    cblas_ctrmv( cblas_option< Order >::value, cblas_option< UpLo >::value,
            cblas_option< Trans >::value, cblas_option< Diag >::value, n, a,
            lda, x, incx );
}

//
// Overloaded function for dispatching to
// * CBLAS backend, and
// * complex<double> value-type.
//
template< typename Order, typename UpLo, typename Trans, typename Diag >
inline void trmv( const Order, const UpLo, const Trans, const Diag,
        const int n, const std::complex<double>* a, const int lda,
        std::complex<double>* x, const int incx ) {
    cblas_ztrmv( cblas_option< Order >::value, cblas_option< UpLo >::value,
            cblas_option< Trans >::value, cblas_option< Diag >::value, n, a,
            lda, x, incx );
}

#elif defined BOOST_NUMERIC_BINDINGS_BLAS_CUBLAS
//
// Overloaded function for dispatching to
// * CUBLAS backend, and
// * float value-type.
//
template< typename Order, typename UpLo, typename Trans, typename Diag >
inline void trmv( const Order, const UpLo, const Trans, const Diag,
        const int n, const float* a, const int lda, float* x,
        const int incx ) {
    BOOST_STATIC_ASSERT( (is_same<Order, tag::column_major>::value) );
    cublasStrmv( blas_option< UpLo >::value, blas_option< Trans >::value,
            blas_option< Diag >::value, n, a, lda, x, incx );
}

//
// Overloaded function for dispatching to
// * CUBLAS backend, and
// * double value-type.
//
template< typename Order, typename UpLo, typename Trans, typename Diag >
inline void trmv( const Order, const UpLo, const Trans, const Diag,
        const int n, const double* a, const int lda, double* x,
        const int incx ) {
    BOOST_STATIC_ASSERT( (is_same<Order, tag::column_major>::value) );
    cublasDtrmv( blas_option< UpLo >::value, blas_option< Trans >::value,
            blas_option< Diag >::value, n, a, lda, x, incx );
}

//
// Overloaded function for dispatching to
// * CUBLAS backend, and
// * complex<float> value-type.
//
template< typename Order, typename UpLo, typename Trans, typename Diag >
inline void trmv( const Order, const UpLo, const Trans, const Diag,
        const int n, const std::complex<float>* a, const int lda,
        std::complex<float>* x, const int incx ) {
    BOOST_STATIC_ASSERT( (is_same<Order, tag::column_major>::value) );
    cublasCtrmv( blas_option< UpLo >::value, blas_option< Trans >::value,
            blas_option< Diag >::value, n, a, lda, x, incx );
}

//
// Overloaded function for dispatching to
// * CUBLAS backend, and
// * complex<double> value-type.
//
template< typename Order, typename UpLo, typename Trans, typename Diag >
inline void trmv( const Order, const UpLo, const Trans, const Diag,
        const int n, const std::complex<double>* a, const int lda,
        std::complex<double>* x, const int incx ) {
    BOOST_STATIC_ASSERT( (is_same<Order, tag::column_major>::value) );
    cublasZtrmv( blas_option< UpLo >::value, blas_option< Trans >::value,
            blas_option< Diag >::value, n, a, lda, x, incx );
}

#else
//
// Overloaded function for dispatching to
// * netlib-compatible BLAS backend (the default), and
// * float value-type.
//
template< typename Order, typename UpLo, typename Trans, typename Diag >
inline void trmv( const Order, const UpLo, const Trans, const Diag,
        const fortran_int_t n, const float* a, const fortran_int_t lda,
        float* x, const fortran_int_t incx ) {
    BOOST_STATIC_ASSERT( (is_same<Order, tag::column_major>::value) );
    BLAS_STRMV( &blas_option< UpLo >::value, &blas_option< Trans >::value,
            &blas_option< Diag >::value, &n, a, &lda, x, &incx );
}

//
// Overloaded function for dispatching to
// * netlib-compatible BLAS backend (the default), and
// * double value-type.
//
template< typename Order, typename UpLo, typename Trans, typename Diag >
inline void trmv( const Order, const UpLo, const Trans, const Diag,
        const fortran_int_t n, const double* a, const fortran_int_t lda,
        double* x, const fortran_int_t incx ) {
    BOOST_STATIC_ASSERT( (is_same<Order, tag::column_major>::value) );
    BLAS_DTRMV( &blas_option< UpLo >::value, &blas_option< Trans >::value,
            &blas_option< Diag >::value, &n, a, &lda, x, &incx );
}

//
// Overloaded function for dispatching to
// * netlib-compatible BLAS backend (the default), and
// * complex<float> value-type.
//
template< typename Order, typename UpLo, typename Trans, typename Diag >
inline void trmv( const Order, const UpLo, const Trans, const Diag,
        const fortran_int_t n, const std::complex<float>* a,
        const fortran_int_t lda, std::complex<float>* x,
        const fortran_int_t incx ) {
    BOOST_STATIC_ASSERT( (is_same<Order, tag::column_major>::value) );
    BLAS_CTRMV( &blas_option< UpLo >::value, &blas_option< Trans >::value,
            &blas_option< Diag >::value, &n, a, &lda, x, &incx );
}

//
// Overloaded function for dispatching to
// * netlib-compatible BLAS backend (the default), and
// * complex<double> value-type.
//
template< typename Order, typename UpLo, typename Trans, typename Diag >
inline void trmv( const Order, const UpLo, const Trans, const Diag,
        const fortran_int_t n, const std::complex<double>* a,
        const fortran_int_t lda, std::complex<double>* x,
        const fortran_int_t incx ) {
    BOOST_STATIC_ASSERT( (is_same<Order, tag::column_major>::value) );
    BLAS_ZTRMV( &blas_option< UpLo >::value, &blas_option< Trans >::value,
            &blas_option< Diag >::value, &n, a, &lda, x, &incx );
}

#endif

} // namespace detail

//
// Value-type based template class. Use this class if you need a type
// for dispatching to trmv.
//
template< typename Value >
struct trmv_impl {

    typedef Value value_type;
    typedef typename remove_imaginary< Value >::type real_type;
    typedef void result_type;

    //
    // Static member function that
    // * Deduces the required arguments for dispatching to BLAS, and
    // * Asserts that most arguments make sense.
    //
    template< typename MatrixA, typename VectorX >
    static result_type invoke( const MatrixA& a, VectorX& x ) {
        namespace bindings = ::boost::numeric::bindings;
        typedef typename detail::default_order< MatrixA >::type order;
        typedef typename result_of::trans_tag< MatrixA, order >::type trans;
        typedef typename result_of::uplo_tag< MatrixA, trans >::type uplo;
        typedef typename result_of::diag_tag< MatrixA >::type diag;
        BOOST_STATIC_ASSERT( (is_same< typename remove_const<
                typename bindings::value_type< MatrixA >::type >::type,
                typename remove_const< typename bindings::value_type<
                VectorX >::type >::type >::value) );
        BOOST_STATIC_ASSERT( (bindings::has_linear_array< MatrixA >::value) );
        BOOST_STATIC_ASSERT( (bindings::has_linear_array< VectorX >::value) );
        BOOST_STATIC_ASSERT( (bindings::is_mutable< VectorX >::value) );
        BOOST_ASSERT( bindings::size_minor(a) == 1 ||
                bindings::stride_minor(a) == 1 );
        detail::trmv( order(), uplo(), trans(), diag(),
                bindings::size_column_op(a, trans()),
                bindings::begin_value(a), bindings::stride_major(a),
                bindings::begin_value(x), bindings::stride(x) );
    }
};

//
// Functions for direct use. These functions are overloaded for temporaries,
// so that wrapped types can still be passed and used for write-access. Calls
// to these functions are passed to the trmv_impl classes. In the
// documentation, the const-overloads are collapsed to avoid a large number of
// prototypes which are very similar.
//

//
// Overloaded function for trmv. Its overload differs for
//
template< typename MatrixA, typename VectorX >
inline typename trmv_impl< typename bindings::value_type<
        MatrixA >::type >::result_type
trmv( const MatrixA& a, VectorX& x ) {
    trmv_impl< typename bindings::value_type<
            MatrixA >::type >::invoke( a, x );
}

} // namespace blas
} // namespace bindings
} // namespace numeric
} // namespace boost

#endif