//          Copyright (C) 2012, Michele Caini.
// 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)

//          Two Graphs Common Spanning Trees Algorithm
//      Based on academic article of Mint, Read and Tarjan
//     Efficient Algorithm for Common Spanning Tree Problem
// Electron. Lett., 28 April 1983, Volume 19, Issue 9, p.346-347

#ifndef BOOST_GRAPH_TWO_GRAPHS_COMMON_SPANNING_TREES_HPP
#define BOOST_GRAPH_TWO_GRAPHS_COMMON_SPANNING_TREES_HPP

#include <boost/config.hpp>

#include <boost/bimap.hpp>
#include <boost/type_traits.hpp>
#include <boost/concept/requires.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/undirected_dfs.hpp>
#include <boost/graph/connected_components.hpp>
#include <boost/graph/filtered_graph.hpp>
#include <vector>
#include <stack>
#include <map>

namespace boost
{

namespace detail
{

    template < typename TreeMap, typename PredMap, typename DistMap,
        typename LowMap, typename Buffer >
    struct bridges_visitor : public default_dfs_visitor
    {
        bridges_visitor(TreeMap tree, PredMap pred, DistMap dist, LowMap low,
            Buffer& buffer)
        : mTree(tree), mPred(pred), mDist(dist), mLow(low), mBuffer(buffer)
        {
            mNum = -1;
        }

        template < typename Vertex, typename Graph >
        void initialize_vertex(const Vertex& u, const Graph& g)
        {
            put(mPred, u, u);
            put(mDist, u, -1);
        }

        template < typename Vertex, typename Graph >
        void discover_vertex(const Vertex& u, const Graph& g)
        {
            put(mDist, u, ++mNum);
            put(mLow, u, get(mDist, u));
        }

        template < typename Edge, typename Graph >
        void tree_edge(const Edge& e, const Graph& g)
        {
            put(mPred, target(e, g), source(e, g));
            put(mTree, target(e, g), e);
        }

        template < typename Edge, typename Graph >
        void back_edge(const Edge& e, const Graph& g)
        {
            put(mLow, source(e, g),
                (std::min)(get(mLow, source(e, g)), get(mDist, target(e, g))));
        }

        template < typename Vertex, typename Graph >
        void finish_vertex(const Vertex& u, const Graph& g)
        {
            Vertex parent = get(mPred, u);
            if (get(mLow, u) > get(mDist, parent))
                mBuffer.push(get(mTree, u));
            put(mLow, parent, (std::min)(get(mLow, parent), get(mLow, u)));
        }

        TreeMap mTree;
        PredMap mPred;
        DistMap mDist;
        LowMap mLow;
        Buffer& mBuffer;
        int mNum;
    };

    template < typename Buffer >
    struct cycle_finder : public base_visitor< cycle_finder< Buffer > >
    {
        typedef on_back_edge event_filter;
        cycle_finder() : mBuffer(0) {}
        cycle_finder(Buffer* buffer) : mBuffer(buffer) {}
        template < typename Edge, typename Graph >
        void operator()(const Edge& e, const Graph& g)
        {
            if (mBuffer)
                mBuffer->push(e);
        }
        Buffer* mBuffer;
    };

    template < typename DeletedMap > struct deleted_edge_status
    {
        deleted_edge_status() {}
        deleted_edge_status(DeletedMap map) : mMap(map) {}
        template < typename Edge > bool operator()(const Edge& e) const
        {
            return (!get(mMap, e));
        }
        DeletedMap mMap;
    };

    template < typename InLMap > struct inL_edge_status
    {
        inL_edge_status() {}
        inL_edge_status(InLMap map) : mMap(map) {}
        template < typename Edge > bool operator()(const Edge& e) const
        {
            return get(mMap, e);
        }
        InLMap mMap;
    };

    template < typename Graph, typename Func, typename Seq, typename Map >
    void rec_two_graphs_common_spanning_trees(const Graph& iG,
        bimap< bimaps::set_of< int >,
            bimaps::set_of< typename graph_traits< Graph >::edge_descriptor > >
            iG_bimap,
        Map aiG_inL, Map diG, const Graph& vG,
        bimap< bimaps::set_of< int >,
            bimaps::set_of< typename graph_traits< Graph >::edge_descriptor > >
            vG_bimap,
        Map avG_inL, Map dvG, Func func, Seq inL)
    {
        typedef graph_traits< Graph > GraphTraits;

        typedef typename GraphTraits::vertex_descriptor vertex_descriptor;
        typedef typename GraphTraits::edge_descriptor edge_descriptor;

        typedef typename Seq::size_type seq_size_type;

        int edges = num_vertices(iG) - 1;
        //
        //  [ Michele Caini ]
        //
        //  Using the condition (edges != 0) leads to the accidental submission
        //  of
        //    sub-graphs ((V-1+1)-fake-tree, named here fat-tree).
        //  Remove this condition is a workaround for the problem of fat-trees.
        //  Please do not add that condition, even if it improves performance.
        //
        //  Here is proposed the previous guard (that was wrong):
        //     for(seq_size_type i = 0; (i < inL.size()) && (edges != 0); ++i)
        //
        {
            for (seq_size_type i = 0; i < inL.size(); ++i)
                if (inL[i])
                    --edges;

            if (edges < 0)
                return;
        }

        bool is_tree = (edges == 0);
        if (is_tree)
        {
            func(inL);
        }
        else
        {
            std::map< vertex_descriptor, default_color_type > vertex_color;
            std::map< edge_descriptor, default_color_type > edge_color;

            std::stack< edge_descriptor > iG_buf, vG_buf;
            bool found = false;

            seq_size_type m;
            for (seq_size_type j = 0; j < inL.size() && !found; ++j)
            {
                if (!inL[j] && !get(diG, iG_bimap.left.at(j))
                    && !get(dvG, vG_bimap.left.at(j)))
                {
                    put(aiG_inL, iG_bimap.left.at(j), true);
                    put(avG_inL, vG_bimap.left.at(j), true);

                    undirected_dfs(
                        make_filtered_graph(iG,
                            detail::inL_edge_status< associative_property_map<
                                std::map< edge_descriptor, bool > > >(aiG_inL)),
                        make_dfs_visitor(detail::cycle_finder<
                            std::stack< edge_descriptor > >(&iG_buf)),
                        associative_property_map<
                            std::map< vertex_descriptor, default_color_type > >(
                            vertex_color),
                        associative_property_map<
                            std::map< edge_descriptor, default_color_type > >(
                            edge_color));
                    undirected_dfs(
                        make_filtered_graph(vG,
                            detail::inL_edge_status< associative_property_map<
                                std::map< edge_descriptor, bool > > >(avG_inL)),
                        make_dfs_visitor(detail::cycle_finder<
                            std::stack< edge_descriptor > >(&vG_buf)),
                        associative_property_map<
                            std::map< vertex_descriptor, default_color_type > >(
                            vertex_color),
                        associative_property_map<
                            std::map< edge_descriptor, default_color_type > >(
                            edge_color));

                    if (iG_buf.empty() && vG_buf.empty())
                    {
                        inL[j] = true;
                        found = true;
                        m = j;
                    }
                    else
                    {
                        while (!iG_buf.empty())
                            iG_buf.pop();
                        while (!vG_buf.empty())
                            vG_buf.pop();
                        put(aiG_inL, iG_bimap.left.at(j), false);
                        put(avG_inL, vG_bimap.left.at(j), false);
                    }
                }
            }

            if (found)
            {

                std::stack< edge_descriptor > iG_buf_copy, vG_buf_copy;
                for (seq_size_type j = 0; j < inL.size(); ++j)
                {
                    if (!inL[j] && !get(diG, iG_bimap.left.at(j))
                        && !get(dvG, vG_bimap.left.at(j)))
                    {

                        put(aiG_inL, iG_bimap.left.at(j), true);
                        put(avG_inL, vG_bimap.left.at(j), true);

                        undirected_dfs(
                            make_filtered_graph(iG,
                                detail::inL_edge_status<
                                    associative_property_map<
                                        std::map< edge_descriptor, bool > > >(
                                    aiG_inL)),
                            make_dfs_visitor(detail::cycle_finder<
                                std::stack< edge_descriptor > >(&iG_buf)),
                            associative_property_map< std::map<
                                vertex_descriptor, default_color_type > >(
                                vertex_color),
                            associative_property_map< std::map< edge_descriptor,
                                default_color_type > >(edge_color));
                        undirected_dfs(
                            make_filtered_graph(vG,
                                detail::inL_edge_status<
                                    associative_property_map<
                                        std::map< edge_descriptor, bool > > >(
                                    avG_inL)),
                            make_dfs_visitor(detail::cycle_finder<
                                std::stack< edge_descriptor > >(&vG_buf)),
                            associative_property_map< std::map<
                                vertex_descriptor, default_color_type > >(
                                vertex_color),
                            associative_property_map< std::map< edge_descriptor,
                                default_color_type > >(edge_color));

                        if (!iG_buf.empty() || !vG_buf.empty())
                        {
                            while (!iG_buf.empty())
                                iG_buf.pop();
                            while (!vG_buf.empty())
                                vG_buf.pop();
                            put(diG, iG_bimap.left.at(j), true);
                            put(dvG, vG_bimap.left.at(j), true);
                            iG_buf_copy.push(iG_bimap.left.at(j));
                            vG_buf_copy.push(vG_bimap.left.at(j));
                        }

                        put(aiG_inL, iG_bimap.left.at(j), false);
                        put(avG_inL, vG_bimap.left.at(j), false);
                    }
                }

                // REC
                detail::rec_two_graphs_common_spanning_trees< Graph, Func, Seq,
                    Map >(iG, iG_bimap, aiG_inL, diG, vG, vG_bimap, aiG_inL,
                    dvG, func, inL);

                while (!iG_buf_copy.empty())
                {
                    put(diG, iG_buf_copy.top(), false);
                    put(dvG,
                        vG_bimap.left.at(iG_bimap.right.at(iG_buf_copy.top())),
                        false);
                    iG_buf_copy.pop();
                }
                while (!vG_buf_copy.empty())
                {
                    put(dvG, vG_buf_copy.top(), false);
                    put(diG,
                        iG_bimap.left.at(vG_bimap.right.at(vG_buf_copy.top())),
                        false);
                    vG_buf_copy.pop();
                }

                inL[m] = false;
                put(aiG_inL, iG_bimap.left.at(m), false);
                put(avG_inL, vG_bimap.left.at(m), false);

                put(diG, iG_bimap.left.at(m), true);
                put(dvG, vG_bimap.left.at(m), true);

                std::map< vertex_descriptor, edge_descriptor > tree_map;
                std::map< vertex_descriptor, vertex_descriptor > pred_map;
                std::map< vertex_descriptor, int > dist_map, low_map;

                detail::bridges_visitor<
                    associative_property_map<
                        std::map< vertex_descriptor, edge_descriptor > >,
                    associative_property_map<
                        std::map< vertex_descriptor, vertex_descriptor > >,
                    associative_property_map<
                        std::map< vertex_descriptor, int > >,
                    associative_property_map<
                        std::map< vertex_descriptor, int > >,
                    std::stack< edge_descriptor > >
                iG_vis(associative_property_map<
                           std::map< vertex_descriptor, edge_descriptor > >(
                           tree_map),
                    associative_property_map<
                        std::map< vertex_descriptor, vertex_descriptor > >(
                        pred_map),
                    associative_property_map<
                        std::map< vertex_descriptor, int > >(dist_map),
                    associative_property_map<
                        std::map< vertex_descriptor, int > >(low_map),
                    iG_buf),
                    vG_vis(associative_property_map<
                               std::map< vertex_descriptor, edge_descriptor > >(
                               tree_map),
                        associative_property_map<
                            std::map< vertex_descriptor, vertex_descriptor > >(
                            pred_map),
                        associative_property_map<
                            std::map< vertex_descriptor, int > >(dist_map),
                        associative_property_map<
                            std::map< vertex_descriptor, int > >(low_map),
                        vG_buf);

                undirected_dfs(
                    make_filtered_graph(iG,
                        detail::deleted_edge_status< associative_property_map<
                            std::map< edge_descriptor, bool > > >(diG)),
                    iG_vis,
                    associative_property_map<
                        std::map< vertex_descriptor, default_color_type > >(
                        vertex_color),
                    associative_property_map<
                        std::map< edge_descriptor, default_color_type > >(
                        edge_color));
                undirected_dfs(
                    make_filtered_graph(vG,
                        detail::deleted_edge_status< associative_property_map<
                            std::map< edge_descriptor, bool > > >(dvG)),
                    vG_vis,
                    associative_property_map<
                        std::map< vertex_descriptor, default_color_type > >(
                        vertex_color),
                    associative_property_map<
                        std::map< edge_descriptor, default_color_type > >(
                        edge_color));

                found = false;
                std::stack< edge_descriptor > iG_buf_tmp, vG_buf_tmp;
                while (!iG_buf.empty() && !found)
                {
                    if (!inL[iG_bimap.right.at(iG_buf.top())])
                    {
                        put(aiG_inL, iG_buf.top(), true);
                        put(avG_inL,
                            vG_bimap.left.at(iG_bimap.right.at(iG_buf.top())),
                            true);

                        undirected_dfs(
                            make_filtered_graph(iG,
                                detail::inL_edge_status<
                                    associative_property_map<
                                        std::map< edge_descriptor, bool > > >(
                                    aiG_inL)),
                            make_dfs_visitor(detail::cycle_finder<
                                std::stack< edge_descriptor > >(&iG_buf_tmp)),
                            associative_property_map< std::map<
                                vertex_descriptor, default_color_type > >(
                                vertex_color),
                            associative_property_map< std::map< edge_descriptor,
                                default_color_type > >(edge_color));
                        undirected_dfs(
                            make_filtered_graph(vG,
                                detail::inL_edge_status<
                                    associative_property_map<
                                        std::map< edge_descriptor, bool > > >(
                                    avG_inL)),
                            make_dfs_visitor(detail::cycle_finder<
                                std::stack< edge_descriptor > >(&vG_buf_tmp)),
                            associative_property_map< std::map<
                                vertex_descriptor, default_color_type > >(
                                vertex_color),
                            associative_property_map< std::map< edge_descriptor,
                                default_color_type > >(edge_color));

                        if (!iG_buf_tmp.empty() || !vG_buf_tmp.empty())
                        {
                            found = true;
                        }
                        else
                        {
                            while (!iG_buf_tmp.empty())
                                iG_buf_tmp.pop();
                            while (!vG_buf_tmp.empty())
                                vG_buf_tmp.pop();
                            iG_buf_copy.push(iG_buf.top());
                        }

                        put(aiG_inL, iG_buf.top(), false);
                        put(avG_inL,
                            vG_bimap.left.at(iG_bimap.right.at(iG_buf.top())),
                            false);
                    }
                    iG_buf.pop();
                }
                while (!vG_buf.empty() && !found)
                {
                    if (!inL[vG_bimap.right.at(vG_buf.top())])
                    {
                        put(avG_inL, vG_buf.top(), true);
                        put(aiG_inL,
                            iG_bimap.left.at(vG_bimap.right.at(vG_buf.top())),
                            true);

                        undirected_dfs(
                            make_filtered_graph(iG,
                                detail::inL_edge_status<
                                    associative_property_map<
                                        std::map< edge_descriptor, bool > > >(
                                    aiG_inL)),
                            make_dfs_visitor(detail::cycle_finder<
                                std::stack< edge_descriptor > >(&iG_buf_tmp)),
                            associative_property_map< std::map<
                                vertex_descriptor, default_color_type > >(
                                vertex_color),
                            associative_property_map< std::map< edge_descriptor,
                                default_color_type > >(edge_color));
                        undirected_dfs(
                            make_filtered_graph(vG,
                                detail::inL_edge_status<
                                    associative_property_map<
                                        std::map< edge_descriptor, bool > > >(
                                    avG_inL)),
                            make_dfs_visitor(detail::cycle_finder<
                                std::stack< edge_descriptor > >(&vG_buf_tmp)),
                            associative_property_map< std::map<
                                vertex_descriptor, default_color_type > >(
                                vertex_color),
                            associative_property_map< std::map< edge_descriptor,
                                default_color_type > >(edge_color));

                        if (!iG_buf_tmp.empty() || !vG_buf_tmp.empty())
                        {
                            found = true;
                        }
                        else
                        {
                            while (!iG_buf_tmp.empty())
                                iG_buf_tmp.pop();
                            while (!vG_buf_tmp.empty())
                                vG_buf_tmp.pop();
                            vG_buf_copy.push(vG_buf.top());
                        }

                        put(avG_inL, vG_buf.top(), false);
                        put(aiG_inL,
                            iG_bimap.left.at(vG_bimap.right.at(vG_buf.top())),
                            false);
                    }
                    vG_buf.pop();
                }

                if (!found)
                {

                    while (!iG_buf_copy.empty())
                    {
                        inL[iG_bimap.right.at(iG_buf_copy.top())] = true;
                        put(aiG_inL, iG_buf_copy.top(), true);
                        put(avG_inL,
                            vG_bimap.left.at(
                                iG_bimap.right.at(iG_buf_copy.top())),
                            true);
                        iG_buf.push(iG_buf_copy.top());
                        iG_buf_copy.pop();
                    }
                    while (!vG_buf_copy.empty())
                    {
                        inL[vG_bimap.right.at(vG_buf_copy.top())] = true;
                        put(avG_inL, vG_buf_copy.top(), true);
                        put(aiG_inL,
                            iG_bimap.left.at(
                                vG_bimap.right.at(vG_buf_copy.top())),
                            true);
                        vG_buf.push(vG_buf_copy.top());
                        vG_buf_copy.pop();
                    }

                    // REC
                    detail::rec_two_graphs_common_spanning_trees< Graph, Func,
                        Seq, Map >(iG, iG_bimap, aiG_inL, diG, vG, vG_bimap,
                        aiG_inL, dvG, func, inL);

                    while (!iG_buf.empty())
                    {
                        inL[iG_bimap.right.at(iG_buf.top())] = false;
                        put(aiG_inL, iG_buf.top(), false);
                        put(avG_inL,
                            vG_bimap.left.at(iG_bimap.right.at(iG_buf.top())),
                            false);
                        iG_buf.pop();
                    }
                    while (!vG_buf.empty())
                    {
                        inL[vG_bimap.right.at(vG_buf.top())] = false;
                        put(avG_inL, vG_buf.top(), false);
                        put(aiG_inL,
                            iG_bimap.left.at(vG_bimap.right.at(vG_buf.top())),
                            false);
                        vG_buf.pop();
                    }
                }

                put(diG, iG_bimap.left.at(m), false);
                put(dvG, vG_bimap.left.at(m), false);
            }
        }
    }

} // namespace detail

template < typename Coll, typename Seq > struct tree_collector
{

public:
    BOOST_CONCEPT_ASSERT((BackInsertionSequence< Coll >));
    BOOST_CONCEPT_ASSERT((RandomAccessContainer< Seq >));
    BOOST_CONCEPT_ASSERT((CopyConstructible< Seq >));

    typedef typename Coll::value_type coll_value_type;
    typedef typename Seq::value_type seq_value_type;

    BOOST_STATIC_ASSERT((is_same< coll_value_type, Seq >::value));
    BOOST_STATIC_ASSERT((is_same< seq_value_type, bool >::value));

    tree_collector(Coll& seqs) : mSeqs(seqs) {}

    inline void operator()(Seq seq) { mSeqs.push_back(seq); }

private:
    Coll& mSeqs;
};

template < typename Graph, typename Order, typename Func, typename Seq >
BOOST_CONCEPT_REQUIRES(
    ((RandomAccessContainer< Order >))((IncidenceGraphConcept< Graph >))(
        (UnaryFunction< Func, void, Seq >))(
        (Mutable_RandomAccessContainer< Seq >))(
        (VertexAndEdgeListGraphConcept< Graph >)),
    (void))
two_graphs_common_spanning_trees(const Graph& iG, Order iG_map, const Graph& vG,
    Order vG_map, Func func, Seq inL)
{
    typedef graph_traits< Graph > GraphTraits;

    typedef typename GraphTraits::directed_category directed_category;
    typedef typename GraphTraits::vertex_descriptor vertex_descriptor;
    typedef typename GraphTraits::edge_descriptor edge_descriptor;

    typedef typename GraphTraits::edges_size_type edges_size_type;
    typedef typename GraphTraits::edge_iterator edge_iterator;

    typedef typename Seq::value_type seq_value_type;
    typedef typename Seq::size_type seq_size_type;

    typedef typename Order::value_type order_value_type;
    typedef typename Order::size_type order_size_type;

    BOOST_STATIC_ASSERT((is_same< order_value_type, edge_descriptor >::value));
    BOOST_CONCEPT_ASSERT((Convertible< order_size_type, edges_size_type >));

    BOOST_CONCEPT_ASSERT((Convertible< seq_size_type, edges_size_type >));
    BOOST_STATIC_ASSERT((is_same< seq_value_type, bool >::value));

    BOOST_STATIC_ASSERT((is_same< directed_category, undirected_tag >::value));

    if (num_vertices(iG) != num_vertices(vG))
        return;

    if (inL.size() != num_edges(iG) || inL.size() != num_edges(vG))
        return;

    if (iG_map.size() != num_edges(iG) || vG_map.size() != num_edges(vG))
        return;

    typedef bimaps::bimap< bimaps::set_of< int >,
        bimaps::set_of< order_value_type > >
        bimap_type;
    typedef typename bimap_type::value_type bimap_value;

    bimap_type iG_bimap, vG_bimap;
    for (order_size_type i = 0; i < iG_map.size(); ++i)
        iG_bimap.insert(bimap_value(i, iG_map[i]));
    for (order_size_type i = 0; i < vG_map.size(); ++i)
        vG_bimap.insert(bimap_value(i, vG_map[i]));

    edge_iterator current, last;
    boost::tuples::tie(current, last) = edges(iG);
    for (; current != last; ++current)
        if (iG_bimap.right.find(*current) == iG_bimap.right.end())
            return;
    boost::tuples::tie(current, last) = edges(vG);
    for (; current != last; ++current)
        if (vG_bimap.right.find(*current) == vG_bimap.right.end())
            return;

    std::stack< edge_descriptor > iG_buf, vG_buf;

    std::map< vertex_descriptor, edge_descriptor > tree_map;
    std::map< vertex_descriptor, vertex_descriptor > pred_map;
    std::map< vertex_descriptor, int > dist_map, low_map;

    detail::bridges_visitor< associative_property_map< std::map<
                                 vertex_descriptor, edge_descriptor > >,
        associative_property_map<
            std::map< vertex_descriptor, vertex_descriptor > >,
        associative_property_map< std::map< vertex_descriptor, int > >,
        associative_property_map< std::map< vertex_descriptor, int > >,
        std::stack< edge_descriptor > >
    iG_vis(associative_property_map<
               std::map< vertex_descriptor, edge_descriptor > >(tree_map),
        associative_property_map<
            std::map< vertex_descriptor, vertex_descriptor > >(pred_map),
        associative_property_map< std::map< vertex_descriptor, int > >(
            dist_map),
        associative_property_map< std::map< vertex_descriptor, int > >(low_map),
        iG_buf),
        vG_vis(associative_property_map<
                   std::map< vertex_descriptor, edge_descriptor > >(tree_map),
            associative_property_map<
                std::map< vertex_descriptor, vertex_descriptor > >(pred_map),
            associative_property_map< std::map< vertex_descriptor, int > >(
                dist_map),
            associative_property_map< std::map< vertex_descriptor, int > >(
                low_map),
            vG_buf);

    std::map< vertex_descriptor, default_color_type > vertex_color;
    std::map< edge_descriptor, default_color_type > edge_color;

    undirected_dfs(iG, iG_vis,
        associative_property_map<
            std::map< vertex_descriptor, default_color_type > >(vertex_color),
        associative_property_map<
            std::map< edge_descriptor, default_color_type > >(edge_color));
    undirected_dfs(vG, vG_vis,
        associative_property_map<
            std::map< vertex_descriptor, default_color_type > >(vertex_color),
        associative_property_map<
            std::map< edge_descriptor, default_color_type > >(edge_color));

    while (!iG_buf.empty())
    {
        inL[iG_bimap.right.at(iG_buf.top())] = true;
        iG_buf.pop();
    }
    while (!vG_buf.empty())
    {
        inL[vG_bimap.right.at(vG_buf.top())] = true;
        vG_buf.pop();
    }

    std::map< edge_descriptor, bool > iG_inL, vG_inL;
    associative_property_map< std::map< edge_descriptor, bool > > aiG_inL(
        iG_inL),
        avG_inL(vG_inL);

    for (seq_size_type i = 0; i < inL.size(); ++i)
    {
        if (inL[i])
        {
            put(aiG_inL, iG_bimap.left.at(i), true);
            put(avG_inL, vG_bimap.left.at(i), true);
        }
        else
        {
            put(aiG_inL, iG_bimap.left.at(i), false);
            put(avG_inL, vG_bimap.left.at(i), false);
        }
    }

    undirected_dfs(
        make_filtered_graph(iG,
            detail::inL_edge_status<
                associative_property_map< std::map< edge_descriptor, bool > > >(
                aiG_inL)),
        make_dfs_visitor(
            detail::cycle_finder< std::stack< edge_descriptor > >(&iG_buf)),
        associative_property_map<
            std::map< vertex_descriptor, default_color_type > >(vertex_color),
        associative_property_map<
            std::map< edge_descriptor, default_color_type > >(edge_color));
    undirected_dfs(
        make_filtered_graph(vG,
            detail::inL_edge_status<
                associative_property_map< std::map< edge_descriptor, bool > > >(
                avG_inL)),
        make_dfs_visitor(
            detail::cycle_finder< std::stack< edge_descriptor > >(&vG_buf)),
        associative_property_map<
            std::map< vertex_descriptor, default_color_type > >(vertex_color),
        associative_property_map<
            std::map< edge_descriptor, default_color_type > >(edge_color));

    if (iG_buf.empty() && vG_buf.empty())
    {

        std::map< edge_descriptor, bool > iG_deleted, vG_deleted;
        associative_property_map< std::map< edge_descriptor, bool > > diG(
            iG_deleted);
        associative_property_map< std::map< edge_descriptor, bool > > dvG(
            vG_deleted);

        boost::tuples::tie(current, last) = edges(iG);
        for (; current != last; ++current)
            put(diG, *current, false);
        boost::tuples::tie(current, last) = edges(vG);
        for (; current != last; ++current)
            put(dvG, *current, false);

        for (seq_size_type j = 0; j < inL.size(); ++j)
        {
            if (!inL[j])
            {
                put(aiG_inL, iG_bimap.left.at(j), true);
                put(avG_inL, vG_bimap.left.at(j), true);

                undirected_dfs(
                    make_filtered_graph(iG,
                        detail::inL_edge_status< associative_property_map<
                            std::map< edge_descriptor, bool > > >(aiG_inL)),
                    make_dfs_visitor(
                        detail::cycle_finder< std::stack< edge_descriptor > >(
                            &iG_buf)),
                    associative_property_map<
                        std::map< vertex_descriptor, default_color_type > >(
                        vertex_color),
                    associative_property_map<
                        std::map< edge_descriptor, default_color_type > >(
                        edge_color));
                undirected_dfs(
                    make_filtered_graph(vG,
                        detail::inL_edge_status< associative_property_map<
                            std::map< edge_descriptor, bool > > >(avG_inL)),
                    make_dfs_visitor(
                        detail::cycle_finder< std::stack< edge_descriptor > >(
                            &vG_buf)),
                    associative_property_map<
                        std::map< vertex_descriptor, default_color_type > >(
                        vertex_color),
                    associative_property_map<
                        std::map< edge_descriptor, default_color_type > >(
                        edge_color));

                if (!iG_buf.empty() || !vG_buf.empty())
                {
                    while (!iG_buf.empty())
                        iG_buf.pop();
                    while (!vG_buf.empty())
                        vG_buf.pop();
                    put(diG, iG_bimap.left.at(j), true);
                    put(dvG, vG_bimap.left.at(j), true);
                }

                put(aiG_inL, iG_bimap.left.at(j), false);
                put(avG_inL, vG_bimap.left.at(j), false);
            }
        }

        int cc = 0;

        std::map< vertex_descriptor, int > com_map;
        cc += connected_components(
            make_filtered_graph(iG,
                detail::deleted_edge_status< associative_property_map<
                    std::map< edge_descriptor, bool > > >(diG)),
            associative_property_map< std::map< vertex_descriptor, int > >(
                com_map));
        cc += connected_components(
            make_filtered_graph(vG,
                detail::deleted_edge_status< associative_property_map<
                    std::map< edge_descriptor, bool > > >(dvG)),
            associative_property_map< std::map< vertex_descriptor, int > >(
                com_map));

        if (cc != 2)
            return;

        // REC
        detail::rec_two_graphs_common_spanning_trees< Graph, Func, Seq,
            associative_property_map< std::map< edge_descriptor, bool > > >(
            iG, iG_bimap, aiG_inL, diG, vG, vG_bimap, aiG_inL, dvG, func, inL);
    }
}

template < typename Graph, typename Func, typename Seq >
BOOST_CONCEPT_REQUIRES(
    ((IncidenceGraphConcept< Graph >))((EdgeListGraphConcept< Graph >)), (void))
two_graphs_common_spanning_trees(
    const Graph& iG, const Graph& vG, Func func, Seq inL)
{
    typedef graph_traits< Graph > GraphTraits;

    typedef typename GraphTraits::edge_descriptor edge_descriptor;
    typedef typename GraphTraits::edge_iterator edge_iterator;

    std::vector< edge_descriptor > iGO, vGO;
    edge_iterator curr, last;

    boost::tuples::tie(curr, last) = edges(iG);
    for (; curr != last; ++curr)
        iGO.push_back(*curr);

    boost::tuples::tie(curr, last) = edges(vG);
    for (; curr != last; ++curr)
        vGO.push_back(*curr);

    two_graphs_common_spanning_trees(iG, iGO, vG, vGO, func, inL);
}

} // namespace boost

#endif // BOOST_GRAPH_TWO_GRAPHS_COMMON_SPANNING_TREES_HPP


/* Copyright © 2017 Apple Inc. All rights reserved.
 *
 * Use of this source code is governed by a BSD-3-clause license that can
 * be found in the LICENSE.txt file or at https://opensource.org/licenses/BSD-3-Clause
 */
#ifndef TURI_UTIL_SPMAT_H_
#define TURI_UTIL_SPMAT_H_

#include <vector>
#include <map>
#include <core/random/alias.hpp>
#include <Eigen/Core>

/**
 * \ingroup toolkit_util
 * A simple utility class for representing sparse matrices of counts.
 * It exposes getting particular elements, incrementing elements by a
 * value, and removing zero elements from the internal data structure.
 * It is row-based, so only exposes get_row and num_rows.
 */
class spmat {
 public:

  /**
   * Create a sparse matrix with a fixed number of rows.
   */
  spmat(size_t num_rows = 0) {
    m = std::vector<std::map<size_t, size_t>>(num_rows);
  }

  /**
   * Get a vector of nonzero elements in a single row.
   */
  const std::map<size_t, size_t>& get_row(size_t i) {
    return m[i];
  }

  /**
   * Get the count at element(i,j).
   */
  size_t get(const size_t i, const size_t j) {
    if (m[i].count(j) == 0) {
      return 0;
    } else {
      return m[i].at(j);
    }
  }

  /**
   * Get the number of rows.
   */
  size_t num_rows() const { return m.size(); }

  /**
   * Delete zeros in a single row
   */
  void trim(const size_t i) {
    auto it = m[i].begin();
    for( ; it != m[i].end();) {
      if (it->second == 0) {
        it = m[i].erase(it);
      } else {
        ++it;
      }
    }
  }

  /**
   * Increment the element (a, b) by v.
   */
  void increment(const size_t& a, const size_t& b, const size_t& v) {
    auto it = m[a].find(b);
    if (it == m[a].end()) {
      m[a][b] = v;
    } else {
      it->second += v;
    }
  }

  /**
   * Convert to Eigen matrix.
   */
  Eigen::MatrixXi as_matrix() {
    size_t nrows = m.size();
    size_t ncols = 0;
    for (size_t i = 0; i < nrows; ++i) {
      auto row = get_row(i);
      for (auto it = row.begin(); it != row.end(); ++it) {
        auto col = it->first;
        if (col >= ncols) ncols = col+1; // zero-based indexing
      }
    }
    auto ret = Eigen::MatrixXi(nrows, ncols);
    for (size_t i = 0; i < nrows; ++i) {
      auto row = get_row(i);
      for (auto it = row.begin(); it != row.end(); ++it) {
        auto j = it->first;
        auto v = it->second;
        ret(i, j) = v;
      }
    }
    return ret;
  }

 private:
  std::vector<std::map<size_t, size_t>> m;
};

#endif


#include <inttypes.h>
#include <string>
#include <boost/unordered_map.hpp>

typedef boost::unordered_map<int64_t, int64_t, std::hash<int64_t>> hash_t;
typedef boost::unordered_map<std::string, int64_t, std::hash<std::string>> str_hash_t;

#include "hash_map_int_base.h"
#include "hash_map_str_base.h"

#include "template.c"


//---------------------------------------------------------------------------//
// Copyright (c) 2014 Roshan <thisisroshansmail@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
//
// See http://boostorg.github.com/compute for more information.
//---------------------------------------------------------------------------//

#define BOOST_TEST_MODULE TestIsPermutation
#include <boost/test/unit_test.hpp>

#include <boost/compute/system.hpp>
#include <boost/compute/functional.hpp>
#include <boost/compute/command_queue.hpp>
#include <boost/compute/algorithm/is_permutation.hpp>
#include <boost/compute/container/vector.hpp>

#include "check_macros.hpp"
#include "context_setup.hpp"

namespace bc = boost::compute;

BOOST_AUTO_TEST_CASE(is_permutation_int)
{
    int dataset1[] = {1, 3, 1, 2, 5};
    bc::vector<bc::int_> vector1(dataset1, dataset1 + 5, queue);

    int dataset2[] = {3, 1, 5, 1, 2};
    bc::vector<bc::int_> vector2(dataset2, dataset2 + 5, queue);

    bool result =
        bc::is_permutation(vector1.begin(), vector1.begin() + 5,
                           vector2.begin(), vector2.begin() + 5, queue);

    BOOST_VERIFY(result == true);

    vector2[0] = 1;

    result = bc::is_permutation(vector1.begin(), vector1.begin() + 5,
                                vector2.begin(), vector2.begin() + 5,
                                queue);

    BOOST_VERIFY(result == false);
}

BOOST_AUTO_TEST_CASE(is_permutation_string)
{
    char dataset1[] = "abade";
    bc::vector<bc::char_> vector1(dataset1, dataset1 + 5, queue);

    char dataset2[] = "aadeb";
    bc::vector<bc::char_> vector2(dataset2, dataset2 + 5, queue);

    bool result =
        bc::is_permutation(vector1.begin(), vector1.begin() + 5,
                           vector2.begin(), vector2.begin() + 5, queue);

    BOOST_VERIFY(result == true);

    vector2[0] = 'b';

    result = bc::is_permutation(vector1.begin(), vector1.begin() + 5,
                                vector2.begin(), vector2.begin() + 5,
                                queue);

    BOOST_VERIFY(result == false);
}

BOOST_AUTO_TEST_SUITE_END()


#include "base/mp_utils.hpp"

#include <gtest/gtest.h>

#include <boost/multiprecision/cpp_int.hpp>

using boost::multiprecision::uint128_t;
using boost::multiprecision::uint256_t;
using sgns::base::bytes_to_uint128_t;
using sgns::base::bytes_to_uint256_t;
using sgns::base::uint128_t_to_bytes;
using sgns::base::uint256_t_to_bytes;

#define ASSERT_TO_FROM_BYTES_EQUAL(value, integer_size)    \
  {                                                        \
    auto v = value;                                        \
    auto v_bytes = uint##integer_size##_t_to_bytes(v);     \
    ASSERT_EQ(bytes_to_uint##integer_size##_t(v_bytes), v); \
  }

/**
 * @given a uint128
 * @when converting it to and then from bytes
 * @then the result matches with the original one
 */
TEST(MpUtilsTest, Uint128) {
  ASSERT_TO_FROM_BYTES_EQUAL(std::numeric_limits<uint128_t>::max(), 128);
  ASSERT_TO_FROM_BYTES_EQUAL(std::numeric_limits<uint128_t>::min(), 128);
  ASSERT_TO_FROM_BYTES_EQUAL(static_cast<uint128_t>(std::numeric_limits<uint64_t>::max())*4+1, 128);
  ASSERT_TO_FROM_BYTES_EQUAL(1337, 128);
}

/**
 * @given a uint256
 * @when converting it to and then from bytes
 * @then the result matches with the original one
 */
TEST(MpUtilsTest, Uint256) {
  ASSERT_TO_FROM_BYTES_EQUAL(std::numeric_limits<uint256_t>::max(), 256);
  ASSERT_TO_FROM_BYTES_EQUAL(std::numeric_limits<uint256_t>::min(), 256);
  ASSERT_TO_FROM_BYTES_EQUAL(static_cast<uint256_t>(std::numeric_limits<uint128_t>::max())*4+1, 256);
  ASSERT_TO_FROM_BYTES_EQUAL(1337, 256);
}

/**
 * @given bigint value and known serialized representation of it
 * @when serialize bigint to bytes
 * @then expected serialized bytes are returned
 */
TEST(MPUtilsTest, UInt128ConvertTest) {
  boost::multiprecision::uint128_t a{"4961875008018162238211470133173564236"};

  std::array<uint8_t, 16> encoded;
  encoded[0] = 'L';
  encoded[1] = '3';
  encoded[2] = '\xa2';
  encoded[3] = '\n';
  encoded[4] = 'C';
  encoded[5] = '\xf4';
  encoded[6] = '5';
  encoded[7] = '\x93';
  encoded[8] = '\xc5';
  encoded[9] = '\x05';
  encoded[10] = '\xe0';
  encoded[11] = ']';
  encoded[12] = 'S';
  encoded[13] = '\x9f';
  encoded[14] = '\xbb';
  encoded[15] = '\x03';

  auto a_encoded = uint128_t_to_bytes(a);
  ASSERT_EQ(encoded, a_encoded) << "a = " << a;

  auto a_decoded = bytes_to_uint128_t(a_encoded);
  ASSERT_EQ(a_decoded, a);
}


/*-----------------------------------------------------------------------------+
Copyright (c) 2008-2009: Joachim Faulhaber
+------------------------------------------------------------------------------+
   Distributed under the Boost Software License, Version 1.0.
      (See accompanying file LICENCE.txt or copy at
           http://www.boost.org/LICENSE_1_0.txt)
+-----------------------------------------------------------------------------*/
#define BOOST_TEST_MODULE icl::interval_map_mixed unit test
#include <libs/icl/test/disable_test_warnings.hpp>
#include <string>
#include <boost/mpl/list.hpp>
#include "../unit_test_unwarned.hpp"
#include <boost/test/test_case_template.hpp>

// interval instance types
#include "../test_type_lists.hpp"
#include "../test_value_maker.hpp"

#include <boost/icl/interval_set.hpp>
#include <boost/icl/separate_interval_set.hpp>
#include <boost/icl/split_interval_set.hpp>
#include <boost/icl/interval_map.hpp>
#include <boost/icl/split_interval_map.hpp>

using namespace std;
using namespace boost;
using namespace unit_test;
using namespace boost::icl;

#include "../test_interval_map_mixed.hpp"

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_map_mixed_add_4_bicremental_types, T, bicremental_types)
{         interval_map_mixed_add_4_bicremental_types<T, int>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_map_mixed_add2_4_bicremental_types, T, bicremental_types)
{         interval_map_mixed_add2_4_bicremental_types<T, int>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_map_mixed_subtract_4_bicremental_types, T, bicremental_types)
{         interval_map_mixed_subtract_4_bicremental_types<T, int>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_map_mixed_erase_4_bicremental_types, T, bicremental_types)
{         interval_map_mixed_erase_4_bicremental_types<T, int>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_map_mixed_erase2_4_bicremental_types, T, bicremental_types)
{         interval_map_mixed_erase2_4_bicremental_types<T, int>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_map_mixed_insert_erase_4_bicremental_types, T, bicremental_types)
{         interval_map_mixed_insert_erase_4_bicremental_types<T, int>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_map_mixed_insert_erase2_4_bicremental_types, T, bicremental_types)
{         interval_map_mixed_insert_erase2_4_bicremental_types<T, int>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_map_mixed_basic_intersect_4_bicremental_types, T, bicremental_types)
{         interval_map_mixed_basic_intersect_4_bicremental_types<T, int>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_map_mixed_basic_intersect2_4_bicremental_types, T, bicremental_types)
{         interval_map_mixed_basic_intersect2_4_bicremental_types<T, int>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_map_mixed_intersect_4_bicremental_types, T, bicremental_types)
{         interval_map_mixed_intersect_4_bicremental_types<T, int>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_map_mixed_intersect2_4_bicremental_types, T, bicremental_types)
{         interval_map_mixed_intersect2_4_bicremental_types<T, int>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_map_mixed_disjoint_4_bicremental_types, T, bicremental_types)
{         interval_map_mixed_disjoint_4_bicremental_types<T, int>(); }

BOOST_AUTO_TEST_CASE_TEMPLATE
(test_icl_interval_map_mixed_erase_if_4_integral_types, T, integral_types)
{         interval_map_mixed_erase_if_4_integral_types<T, int>(); }


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

class bfloat16 : boost::totally_ordered<bfloat16, boost::arithmetic<bfloat16>>
{
    public:
    bfloat16() : data_{0} {}
    explicit bfloat16(float rhs)
    {
        union
        {
            float float_st;
            std::uint32_t bf16_st;
        } bits_st = {rhs};

        // BF16 round and NaN preservation code matches
        // https://github.com/ROCmSoftwarePlatform/rocBLAS/blob/develop/library/include/rocblas_bfloat16.h
        if((~bits_st.bf16_st & 0x7f800000) == 0) // Inf or NaN
        {
            // When all of the exponent bits are 1, the value is Inf or NaN.
            // Inf is indicated by a zero mantissa. NaN is indicated by any nonzero
            // mantissa bit. Quiet NaN is indicated by the most significant mantissa
            // bit being 1. Signaling NaN is indicated by the most significant
            // mantissa bit being 0 but some other bit(s) being 1. If any of the
            // lower 16 bits of the mantissa are 1, we set the least significant bit
            // of the bfloat16 mantissa, in order to preserve signaling NaN in case
            // the bloat16's mantissa bits are all 0.
            if((bits_st.bf16_st & 0xffff) != 0)
            {
                bits_st.bf16_st |= 0x10000; // Preserve signaling NaN
            }
        }
        else
        {
#if MIOPEN_USE_RNE_BFLOAT16 == 1
            // When the exponent bits are not all 1s, then the value is zero, normal,
            // or subnormal. We round the bfloat16 mantissa up by adding 0x7FFF, plus
            // 1 if the least significant bit of the bfloat16 mantissa is 1 (odd).
            // This causes the bfloat16's mantissa to be incremented by 1 if the 16
            // least significant bits of the float mantissa are greater than 0x8000,
            // or if they are equal to 0x8000 and the least significant bit of the
            // bfloat16 mantissa is 1 (odd). This causes it to be rounded to even when
            // the lower 16 bits are exactly 0x8000. If the bfloat16 mantissa already
            // has the value 0x7f, then incrementing it causes it to become 0x00 and
            // the exponent is incremented by one, which is the next higher FP value
            // to the unrounded bfloat16 value. When the bfloat16 value is subnormal
            // with an exponent of 0x00 and a mantissa of 0x7F, it may be rounded up
            // to a normal value with an exponent of 0x01 and a mantissa of 0x00.
            // When the bfloat16 value has an exponent of 0xFE and a mantissa of 0x7F,
            // incrementing it causes it to become an exponent of 0xFF and a mantissa
            // of 0x00, which is Inf, the next higher value to the unrounded value.
            bits_st.bf16_st +=
                (0x7fff + ((bits_st.bf16_st >> 16) & 1)); // Round to nearest, round to even
#else                                                     // truncation
// do nothing
#endif
        }
        data_ = bits_st.bf16_st >> 16;
    }
    operator float() const
    {
        union
        {
            std::uint32_t bf16_st;
            float float_st;
        } bits_st = {data_};

        bits_st.bf16_st = bits_st.bf16_st << 16;
        return bits_st.float_st;
    }

    bfloat16 operator-() const { return bfloat16(-static_cast<float>(*this)); }
    bfloat16 operator+() const { return *this; }

    bfloat16& operator=(const float rhs)
    {
        *this = bfloat16(rhs);
        return *this;
    }
    bfloat16& operator+=(bfloat16 rhs)
    {
        *this = bfloat16(static_cast<float>(*this) + static_cast<float>(rhs));
        return *this;
    }

    bfloat16& operator+=(float rhs)
    {
        *this = bfloat16(static_cast<float>(*this) + rhs);
        return *this;
    }

    bfloat16& operator-=(bfloat16 rhs)
    {
        *this += -rhs;
        return *this;
    }
    bfloat16& operator*=(bfloat16 rhs)
    {
        *this = bfloat16(static_cast<float>(*this) * static_cast<float>(rhs));
        return *this;
    }
    bfloat16& operator*=(float rhs)
    {
        *this = bfloat16(static_cast<float>(*this) * rhs);
        return *this;
    }

    bfloat16& operator/=(bfloat16 rhs)
    {
        *this = bfloat16(static_cast<float>(*this) / static_cast<float>(rhs));
        return *this;
    }
    bool operator<(bfloat16 rhs) const
    {
        return static_cast<float>(*this) < static_cast<float>(rhs);
    }
    bool operator==(bfloat16 rhs) const { return std::equal_to<float>()(*this, rhs); }

    static constexpr bfloat16 generate(uint16_t val) { return bfloat16{val, true}; }

    private:
    constexpr bfloat16(std::uint16_t val, bool) : data_{val} {}

    std::uint16_t data_;
};

namespace std {
template <>
class numeric_limits<bfloat16>
{
    public:
    static constexpr bool is_specialized = true;
    static constexpr bfloat16 min() noexcept { return bfloat16::generate(0x007F); }
    static constexpr bfloat16 max() noexcept { return bfloat16::generate(0x7F7F); }
    static constexpr bfloat16 lowest() noexcept { return bfloat16::generate(0xFF7F); }
    static constexpr bfloat16 epsilon() noexcept { return bfloat16::generate(0x3C00); }
    static constexpr bfloat16 infinity() noexcept { return bfloat16::generate(0x7F80); }
    static constexpr bfloat16 quiet_NaN() noexcept { return bfloat16::generate(0x7FC0); }
    static constexpr bfloat16 signaling_NaN() noexcept { return bfloat16::generate(0x7FC0); }
    static constexpr bfloat16 denorm_min() noexcept { return bfloat16::generate(0); }
};
} // namespace std
#endif


/**************************************************************************\
|
|    Copyright (C) 2009 Marc Stevens
|
|    This program is free software: you can redistribute it and/or modify
|    it under the terms of the GNU General Public License as published by
|    the Free Software Foundation, either version 3 of the License, or
|    (at your option) any later version.
|
|    This program is distributed in the hope that it will be useful,
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
|    GNU General Public License for more details.
|
|    You should have received a copy of the GNU General Public License
|    along with this program.  If not, see <http://www.gnu.org/licenses/>.
|
\**************************************************************************/

#ifndef HASHCLASH_BOOLEANFUNCTION_HPP
#define HASHCLASH_BOOLEANFUNCTION_HPP

#include <vector>
#include <string>
#include <set>
#include <map>

#include <boost/function.hpp>

#include "types.hpp"
#include "sdr.hpp"
#include "md5detail.hpp"
#include "conditions.hpp"

namespace hashclash {

	typedef triple<bitcondition, bitcondition, bitcondition> bf_conditions;

	struct bf_outcome {
		unsigned char c;

		static const unsigned char fconstant = 1;
		static const unsigned char fplus = 2;
		static const unsigned char fminus = 4;
		bf_outcome(): c(0) {}
		bf_outcome(unsigned char k): c(k) {}
		bf_outcome& operator=(const bf_outcome& r)
		{ c = r.c; return *this; }
		bf_outcome& operator=(unsigned char k)
		{ c = k; return *this; }

		bool constant() const
		{ return (c&fconstant) != 0; }
		bool plus() const
		{ return (c&fplus) != 0; }
		bool minus() const
		{ return (c&fminus) != 0; }
		unsigned size() const
		{
			unsigned w = 0;
			if (constant()) ++w;
			if (plus()) ++w;
			if (minus()) ++w;
			return w;
		}
		uint32 operator()(unsigned index, unsigned b) const
		{
			if (index == 0) {
				if (constant()) return 0;
				if (plus()) return 0+(1<<b);
				return 0-(1<<b);
			} else if (index == 1) {
				if (constant())	{
					if (plus()) return 0+(1<<b);
					return 0-(1<<b);
				} else
					return 0-(1<<b);
			} else
				return 0-(1<<b);
		}
		bitcondition operator[](unsigned index) const
		{
			if (index == 0) {
				if (constant()) return bc_constant;
				if (plus()) return bc_plus;
				return bc_minus;
			} else if (index == 1) {
				if (constant())	{
					if (plus()) return bc_plus;
					return bc_minus;
				} else
					return bc_minus;
			} else
				return bc_minus;
		}
	};

	class booleanfunction {
	public:
		booleanfunction(const boost::function<uint32(uint32,uint32,uint32)>& F, const std::string& description = "");

		const bf_outcome& outcome(bitcondition input1, bitcondition input2, bitcondition input3)
		{ return outcome_table[(input1<<8) + (input2<<4) + input3]; }
		const bf_outcome& outcome(const bf_conditions& c)
		{ return outcome(c.first, c.second, c.third); }

		const bf_conditions& forwardconditions(bitcondition input1, bitcondition input2, bitcondition input3, bitcondition outcome)
		{ return forward_table[(outcome<<12)+(input1<<8)+(input2<<4)+input3]; }
		const bf_conditions& forwardconditions(const bf_conditions& c, bitcondition outcome)
		{ return forwardconditions(c.first, c.second, c.third, outcome); }

		const bf_conditions& backwardconditions(bitcondition input1, bitcondition input2, bitcondition input3, bitcondition outcome)
		{ return backward_table[(outcome<<12)+(input1<<8)+(input2<<4)+input3]; }
		const bf_conditions& backwardconditions(const bf_conditions& c, bitcondition outcome)
		{ return backwardconditions(c.first, c.second, c.third, outcome); }

		uint32 F(uint32 input1, uint32 input2, uint32 input3) const
		{ return f(input1, input2, input3); }

		const std::string& description() const
		{ return f_description; }

	private:
		boost::function<uint32(uint32,uint32,uint32)> f;
		std::string f_description;

		std::vector<bf_outcome> outcome_table;
		std::vector<bf_conditions> forward_table;
		std::vector<bf_conditions> backward_table;

		std::map<bf_conditions, std::set<uint32> > conds_to_values;

		void find_booleanfunction_outcomes(const bf_conditions& cond, bf_outcome& outcomes, std::set<uint32>& values);
		bf_conditions preferred_conditions(const bf_conditions& cond, std::vector<bf_conditions>& vec_conds);
	};

	extern booleanfunction MD5_F_data, MD5_G_data, MD5_H_data, MD5_I_data;

} // namespace hashclash

#endif //HASHCLASH_BOOLEANFUNCTION_HPP


////////////////////////////////////////////////////////////////////////////////
//  Copyright (c) 2015 Bryce Adelstein Lelbach aka wash
//
//  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 <mdspan>

#include <boost/core/lightweight_test.hpp>

using std::experimental::dyn;
using std::experimental::dimensions;

int main()
{
    {
        dimensions<3            > e0;
        dimensions<3,   5       > e1;
        dimensions<3,   5,   9  > e2;

        BOOST_TEST_EQ((e0[0]), 3);

        BOOST_TEST_EQ((e1[0]), 3);
        BOOST_TEST_EQ((e1[1]), 5);

        BOOST_TEST_EQ((e2[0]), 3);
        BOOST_TEST_EQ((e2[1]), 5);
        BOOST_TEST_EQ((e2[2]), 9);

        BOOST_TEST_EQ((e0.size()), 3    );
        BOOST_TEST_EQ((e1.size()), 3*5  );
        BOOST_TEST_EQ((e2.size()), 3*5*9);
    }

    {
        dimensions<dyn, 5,   9  > e0(3);
        dimensions<3,   dyn, 9  > e1(5);
        dimensions<3,   5,   dyn> e2(9);

        BOOST_TEST_EQ((e0[0]), 3);
        BOOST_TEST_EQ((e0[1]), 5);
        BOOST_TEST_EQ((e0[2]), 9);

        BOOST_TEST_EQ((e1[0]), 3);
        BOOST_TEST_EQ((e1[1]), 5);
        BOOST_TEST_EQ((e1[2]), 9);

        BOOST_TEST_EQ((e2[0]), 3);
        BOOST_TEST_EQ((e2[1]), 5);
        BOOST_TEST_EQ((e2[2]), 9);

        BOOST_TEST_EQ((e0.size()), 3*5*9);
        BOOST_TEST_EQ((e1.size()), 3*5*9);
        BOOST_TEST_EQ((e2.size()), 3*5*9);
    }

    {
        dimensions<3,   dyn, dyn> e0(5, 9);
        dimensions<dyn, 5,   dyn> e1(3, 9);
        dimensions<dyn, dyn, 9  > e2(3, 5);

        BOOST_TEST_EQ((e0[0]), 3);
        BOOST_TEST_EQ((e0[1]), 5);
        BOOST_TEST_EQ((e0[2]), 9);

        BOOST_TEST_EQ((e1[0]), 3);
        BOOST_TEST_EQ((e1[1]), 5);
        BOOST_TEST_EQ((e1[2]), 9);

        BOOST_TEST_EQ((e2[0]), 3);
        BOOST_TEST_EQ((e2[1]), 5);
        BOOST_TEST_EQ((e2[2]), 9);

        BOOST_TEST_EQ((e0.size()), 3*5*9);
        BOOST_TEST_EQ((e1.size()), 3*5*9);
        BOOST_TEST_EQ((e2.size()), 3*5*9);
    }

    {
        dimensions<dyn>           e0(3);
        dimensions<dyn, dyn>      e1(3, 5);
        dimensions<dyn, dyn, dyn> e2(3, 5, 9);

        BOOST_TEST_EQ((e0[0]), 3);

        BOOST_TEST_EQ((e1[0]), 3);
        BOOST_TEST_EQ((e1[1]), 5);

        BOOST_TEST_EQ((e2[0]), 3);
        BOOST_TEST_EQ((e2[1]), 5);
        BOOST_TEST_EQ((e2[2]), 9);

        BOOST_TEST_EQ((e0.size()), 3    );
        BOOST_TEST_EQ((e1.size()), 3*5  );
        BOOST_TEST_EQ((e2.size()), 3*5*9);
    }

    return boost::report_errors();
}


// some "useful" typedefs and simple classes
//

#ifndef INCLUDED_types_HH
#define INCLUDED_types_HH

// #include <boost/foreach.hpp>
// #include <boost/algorithm/string.hpp>

#include <iostream>
#include <fstream>
#include <sstream>
// #include <iterator>
#include <algorithm>
#include <string>
#include <map>
#include <vector>
#include <cassert>

#include <math.h>

using namespace std;

#define foreach_ BOOST_FOREACH

typedef std::size_t  Size;
typedef double  Real;
typedef std::pair< Size, Size > SizePair;

typedef vector< SizePair > SizePairs;
typedef vector< string > strings;
typedef vector< Size > Sizes;
typedef vector< bool > bools;
typedef vector< Real > Reals;

struct Feature {
	string name;
	Sizes poslist;
	Sizes neglist;
};

typedef vector< Feature > Features;

#endif


/*    Copyright (c) 2010-2015, Delft University of Technology
 *    All rights reserved.
 *
 *    Redistribution and use in source and binary forms, with or without modification, are
 *    permitted provided that the following conditions are met:
 *      - Redistributions of source code must retain the above copyright notice, this list of
 *        conditions and the following disclaimer.
 *      - Redistributions in binary form must reproduce the above copyright notice, this list of
 *        conditions and the following disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *      - Neither the name of the Delft University of Technology nor the names of its contributors
 *        may be used to endorse or promote products derived from this software without specific
 *        prior written permission.
 *
 *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
 *    OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *    MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *    COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 *    GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 *    AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 *    OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *    Changelog
 *      YYMMDD    Author            Comment
 *      101215    K. Kumar          File created.
 *      101216    K. Kumar          Updated to include test of getPotential( ).
 *      101230    K. Kumar          Updated to include test of getGradient( ) and getLaplacian( ).
 *      110104    J. Melman         Some minor comment and layout changes.
 *      110106    K. Kumar          Updated test using predefined gravity field and added machine
 *                                  precision variable.
 *      110107    K. Kumar          Updated call to predefined gravity field. Updated unit test to
 *                                  new protocol, added namespace, new filename, new file location.
 *      110113    K. Kumar          Added cerr statements.
 *      110115    J. Melman         Changed the error messages.
 *      110128    K. Kumar          Updated code to work with pointers.
 *      110202    K. Kumar          Updated code to work with State.
 *      110204    K. Kumar          Removed "vector" from naming.
 *      110310    K. Kumar          Changed naming from Laplacian to gradient tensor.
 *      120326    D. Dirkx          Changed raw pointers to shared pointers.
 *      120521    F. Belien         Boostified unit test.
 *      15XXXX    D. Dirkx          Changed some stuff.
 *
 *    References
 *
 *    Notes
 *
 */

#define BOOST_TEST_MAIN

#include <boost/test/unit_test.hpp>

#include "Tudat/Astrodynamics/Gravitation/sphericalHarmonicsGravityField.h"
#include "Tudat/Basics/testMacros.h"
#include "Tudat/Astrodynamics/Gravitation/gravityFieldModel.h"

#include <cmath>
#include <limits>

#include <Eigen/Core>

namespace tudat
{
namespace unit_tests
{

//! Test the functionality of the spherical harmonics gravity field class.
BOOST_AUTO_TEST_SUITE( test_spherical_harmonics_gravity_field )

//! Test setting and getting gravitational parameter.
BOOST_AUTO_TEST_CASE( testSettingAndGettingGravitationalParameter )
{
    // Set gravitational parameter of myPlanet.
    const double gravitationalParameterOfMyPlanet = 22032.00;

    // Create gravity field for myPlanet.
    using gravitation::SphericalHarmonicsGravityField;
    SphericalHarmonicsGravityField myPlanetGravityField( gravitationalParameterOfMyPlanet, 1.0 );

    // Get the value for the gravitational parameter
    const double computedResultForTest1 = myPlanetGravityField.getGravitationalParameter( );

    // Determine the expected result.
    const double expectedResultForTest1 = gravitationalParameterOfMyPlanet;

    // Check that expected and computed values are equal.
    BOOST_CHECK_EQUAL( expectedResultForTest1, computedResultForTest1 );
}

//! Test getting gravitational parameter for predefined Earth central body gravity field.
BOOST_AUTO_TEST_CASE( testGetGravitationalParameterForPredefinedEarth )
{
    // Create predefined Earth central gravity field.
    boost::shared_ptr< gravitation::GravityFieldModel > predefinedEarthCentralGravityField =
            gravitation::getPredefinedCentralGravityField( gravitation::earth );

    const double computedResultForTest2 =
            predefinedEarthCentralGravityField->getGravitationalParameter( );

    // Determine the expected result
    const double expectedResultForTest2 = 3.9859383624e14;

    // Check that expected and computed values are equal.
    BOOST_CHECK_EQUAL ( expectedResultForTest2, computedResultForTest2 );
}

//! Test getting potential, the gradient of the potential and the gradient tensor of a given state.
BOOST_AUTO_TEST_CASE( testGetPotential )
{
    // Set position with respect to geometric center.
    const Eigen::Vector3d cartesianPosition( 5.0e6, 3.0e6, 1.0e6 );

    // Set gravitational parameter of myPlanet.
    const double gravitationalParameterOfMyPlanet = 22032.00;

    // Create gravity field for myPlanet.
    gravitation::SphericalHarmonicsGravityField myPlanetGravityField(
                gravitationalParameterOfMyPlanet, 1.0 );

    // Get the potential of myPlanet
    double computedResultForTest3 = myPlanetGravityField.getGravitationalPotential(
                cartesianPosition );

    // Generate expected result for the potential of myPlanet
    double expectedResultForTest3 = gravitationalParameterOfMyPlanet / cartesianPosition.norm( );

    // Get the gradient of the potential of myPlanet
    Eigen::Vector3d computedResultForTest4 = myPlanetGravityField.getGradientOfPotential(
                cartesianPosition );

    // Determine the expected result for the gradient of the potential of myPlanet
    const Eigen::VectorXd expectedResultForTest4 = -gravitationalParameterOfMyPlanet
            / std::pow( cartesianPosition.norm( ), 3.0 ) * cartesianPosition;

    // Check that compute values match expected values.
    BOOST_CHECK_EQUAL( expectedResultForTest3, computedResultForTest3 );
    TUDAT_CHECK_MATRIX_CLOSE_FRACTION( expectedResultForTest4, computedResultForTest4,
                                       std::numeric_limits< double >::epsilon( ) );

    // Create central gravity field for myPlanet.
    gravitation::GravityFieldModel myPlanetCentralGravityField(
                gravitationalParameterOfMyPlanet );

    // Get the potential of myPlanet
    computedResultForTest3 = myPlanetGravityField.getGravitationalPotential(
                cartesianPosition );

    // Get the gradient of the potential of myPlanet
    computedResultForTest4 = myPlanetGravityField.getGradientOfPotential(
                cartesianPosition );

    // Check that compute values match expected values.
    BOOST_CHECK_EQUAL( expectedResultForTest3, computedResultForTest3 );
    TUDAT_CHECK_MATRIX_CLOSE_FRACTION( expectedResultForTest4, computedResultForTest4,
                                       std::numeric_limits< double >::epsilon( ) );
}

// Check the sum of all harmonics terms up to degree = 5 and order = 5.
BOOST_AUTO_TEST_CASE( test_SphericalHarmonicsGravitationalFieldFromFiniteDifferenceAcceleration1 )
{
    // Define gravitational parameter of Earth [m^3 s^-2]. The value is obtained from the Earth
    // Gravitational Model 2008 as described by Mathworks [2012].
    const double gravitationalParameter = 3.986004418e14;

    // Define radius of Earth [m]. The value is obtained from the Earth Gravitational Model 2008 as
    // described by Mathworks [2012].
    const double planetaryRadius = 6378137.0;

    // Define geodesy-normalized coefficients up to degree 5 and order 5. The values are obtained
    // from the Earth Gravitational Model 2008 as described by Mathworks [2012].
    const Eigen::MatrixXd cosineCoefficients =
            ( Eigen::MatrixXd( 6, 6 ) <<
              1.0, 0.0, 0.0, 0.0, 0.0, 0.0,
              0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
              -4.841651437908150e-4, -2.066155090741760e-10, 2.439383573283130e-6, 0.0, 0.0, 0.0,
              9.571612070934730e-7, 2.030462010478640e-6, 9.047878948095281e-7,
              7.213217571215680e-7, 0.0, 0.0, 5.399658666389910e-7, -5.361573893888670e-7,
              3.505016239626490e-7, 9.908567666723210e-7, -1.885196330230330e-7, 0.0,
              6.867029137366810e-8, -6.292119230425290e-8, 6.520780431761640e-7,
              -4.518471523288430e-7, -2.953287611756290e-7, 1.748117954960020e-7
              ).finished( );

    const Eigen::MatrixXd sineCoefficients =
            ( Eigen::MatrixXd( 6, 6 ) <<
              0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
              0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
              0.0, 1.384413891379790e-9, -1.400273703859340e-6, 0.0, 0.0, 0.0,
              0.0, 2.482004158568720e-7, -6.190054751776180e-7, 1.414349261929410e-6, 0.0, 0.0,
              0.0, -4.735673465180860e-7, 6.624800262758290e-7, -2.009567235674520e-7,
              3.088038821491940e-7, 0.0, 0.0, -9.436980733957690e-8, -3.233531925405220e-7,
              -2.149554083060460e-7, 4.980705501023510e-8, -6.693799351801650e-7
              ).finished( );

    // Define spherical harmonic gravity field
    gravitation::SphericalHarmonicsGravityField myPlanetGravityField(
                gravitationalParameter, planetaryRadius, cosineCoefficients, sineCoefficients );

    // Define arbitrary Cartesian position [m].
    const Eigen::Vector3d nominalPosition( 7.0e6, 8.0e6, 9.0e6 );

    // Declare variables for finite difference acceleration calculation
    double positionPerturbation = 1.0;
    Eigen::Vector3d finiteDifferenceAcceleration = Eigen::Vector3d::Zero( );
    Eigen::Vector3d currentPerturbedPosition = nominalPosition;

    // Calculate acceleration as finite spatial difference of potential.
    for( unsigned int i = 0; i < 3; i++ )
    {
        // Calculate potential at up-perturbed position
        currentPerturbedPosition( i ) += positionPerturbation;
        finiteDifferenceAcceleration( i ) = myPlanetGravityField.getGravitationalPotential(
                    currentPerturbedPosition );
        currentPerturbedPosition = nominalPosition;

        // Subtract potential at down-perturbed position
        currentPerturbedPosition( i ) -= positionPerturbation;
        finiteDifferenceAcceleration( i ) -= myPlanetGravityField.getGravitationalPotential(
                    currentPerturbedPosition );
        currentPerturbedPosition = nominalPosition;

        // Finalize finite difference calculation of entry i (x=0, y=1, z=2).
        finiteDifferenceAcceleration( i ) = finiteDifferenceAcceleration( i ) /
                ( 2.0 * positionPerturbation );
    }

    // Define expected acceleration according to the MATLAB function 'gravitysphericalharmonic'
    // described by Mathworks [2012] [m s^-2]
    // (see test_SphericalHarmonicsGravitationalAcceleration_Demo4).
    const Eigen::Vector3d expectedAcceleration(
                -1.032215878106932, -1.179683946769393, -1.328040277155269 );

    // Check if expected result matches computed result.
    TUDAT_CHECK_MATRIX_CLOSE_FRACTION(
                expectedAcceleration, finiteDifferenceAcceleration, 5.0e-8 );

    // Calculate potential gradient analytically and compare to expected result.
    Eigen::Vector3d calculatedAcceleration = myPlanetGravityField.getGradientOfPotential(
                nominalPosition );
    TUDAT_CHECK_MATRIX_CLOSE_FRACTION( expectedAcceleration, calculatedAcceleration, 1.0E-15 );

}

BOOST_AUTO_TEST_SUITE_END( )

} // namespace tudat
} // namespace unit_tests


#pragma once

#include "quadrature_base.hpp"

#include <boost/multi_array.hpp>
#include <vector>

namespace boltzmann {

template <typename Q1, typename Q2>
class TensorProductQuadrature : public Quadrature<2>
{
 public:
  typedef Quadrature<2> base_type;
  using base_type::dim;
  using base_type::coord_type;

 public:
  TensorProductQuadrature(const Q1& q1, const Q2& q2);

 private:
  using base_type::pts_;
  using base_type::wts_;
};

// ---------------------------------------------------------------------------
template <typename Q1, typename Q2>
TensorProductQuadrature<Q1, Q2>::TensorProductQuadrature(const Q1& q1, const Q2& q2)
    : base_type(q1.size() * q2.size())
{
  typedef boost::multi_array_ref<double, 2> wts_ref_t;
  typedef boost::multi_array_ref<coord_type, 2> pts_ref_t;

  wts_ref_t wts_ref(this->wts_.data(), boost::extents[q1.size()][q2.size()]);
  pts_ref_t pts_ref(this->pts_.data(), boost::extents[q1.size()][q2.size()]);

  for (unsigned int i = 0; i < q1.size(); ++i) {
    for (unsigned int j = 0; j < q2.size(); ++j) {
      wts_ref[i][j] = q1.wts(i) * q2.wts(j);
      pts_ref[i][j][0] = q1.pts(i);
      pts_ref[i][j][1] = q2.pts(j);
    }
  }
}

// ----------------------------------------------------------------------------
/**
 * @brief Build tensor product quadrature for R^2, from
 *        quad. rules in polar coordinates.
 *        Stores quadrature points as complex number
 *
 * @tparam Q1 Quad. rule in angular direction
 * @tparam Q2 Quad. rule in radial direction
 *
 */
template <typename Q1, typename Q2>
class TensorProductQuadratureC : public Quadrature<2>
{
 public:
  typedef Quadrature<2> base_type;
  using base_type::dim;
  using base_type::coord_type;

 public:
  TensorProductQuadratureC(const Q1& q1, const Q2& q2);

  /**
   * @brief returns quad. points as std::complex<double>
   *
   * @param i index
   */
  const std::complex<double>& ptsC(unsigned int i) const { return ptsC_[i]; }

  const std::vector<std::complex<double> >& ptsC() const { return ptsC_; }
  const std::array<unsigned int, 2>& dims() const { return dims_; }

 private:
  using base_type::pts_;
  using base_type::wts_;
  std::vector<std::complex<double> > ptsC_;
  std::array<unsigned int, 2> dims_;
};

// ---------------------------------------------------------------------------
template <typename Q1, typename Q2>
TensorProductQuadratureC<Q1, Q2>::TensorProductQuadratureC(const Q1& q1, const Q2& q2)
    : base_type(q1.size() * q2.size())
    , dims_({q1.size(), q2.size()})
{
  typedef boost::multi_array_ref<double, 2> wts_ref_t;
  typedef boost::multi_array_ref<coord_type, 2> pts_ref_t;

  wts_ref_t wts_ref(this->wts_.data(), boost::extents[q1.size()][q2.size()]);
  pts_ref_t pts_ref(this->pts_.data(), boost::extents[q1.size()][q2.size()]);

  for (unsigned int i = 0; i < q1.size(); ++i) {
    for (unsigned int j = 0; j < q2.size(); ++j) {
      wts_ref[i][j] = q1.wts(i) * q2.wts(j);
      pts_ref[i][j][0] = q1.pts(i);
      pts_ref[i][j][1] = q2.pts(j);
    }
  }

  std::complex<double> ii(0, 1);
  ptsC_.resize(pts_.size());
  for (unsigned int i = 0; i < pts_.size(); ++i) {
    ptsC_[i] = pts_[i][1] * std::exp(ii * pts_[i][0]);
  }
}

}  // end namespace boltzmann


#include <boost/geometry/arithmetic/normalize.hpp>


//          Copyright Rein Halbersma 2014-2021.
// 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)

#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS
#define BOOST_MPL_LIMIT_VECTOR_SIZE 50

#include <bitset/compatible/bit_set.hpp>        // bit_set
#include <bitset/compatible/bitset.hpp>         // bitset
#include <bitset/compatible/dynamic_bitset.hpp> // dynamic_bitset
#include <bitset/exhaustive.hpp>                // all_values, all_singleton_sets, all_singleton_set_pairs
#include <bitset/primitives.hpp>                // op_bitand_assign, op_bitor_assign, op_xor_assign, op_minus_assign,
                                                // op_shift_left_assign, op_shift_right_assign, op_compl,
                                                // op_equal_to, op_not_equal_to, op_less, op_greater, op_greater_equal, op_less_equal,
                                                // op_shift_left, op_shift_right, op_bitand, op_bitor, op_xor, op_minus,
                                                // mem_is_subset_of, mem_is_superset_of, mem_is_proper_subset_of, mem_is_proper_superset_of,
                                                // mem_intersect, fn_is_disjoint
#include <xstd/bit_set.hpp>                     // bit_set
#include <boost/dynamic_bitset.hpp>             // dynamic_bitset
#include <boost/mpl/vector.hpp>                 // vector
#include <boost/test/unit_test.hpp>             // BOOST_AUTO_TEST_SUITE, BOOST_AUTO_TEST_SUITE_END, BOOST_AUTO_TEST_CASE_TEMPLATE
#include <bitset>                               // bitset
#include <cstdint>                              // uint8_t, uint16_t, uint32_t

BOOST_AUTO_TEST_SUITE(Quadratic)

using namespace xstd;

using bitset_types = boost::mpl::vector
<       std::bitset< 0>
,       std::bitset< 1>
,       std::bitset<31>
,       std::bitset<32>
,       std::bitset<33>
,       std::bitset<63>
,       std::bitset<64>
,       std::bitset<65>
,       boost::dynamic_bitset<>
,       bit_set< 0, uint8_t>
,       bit_set< 1, uint8_t>
,       bit_set< 7, uint8_t>
,       bit_set< 8, uint8_t>
,       bit_set< 9, uint8_t>
,       bit_set<15, uint8_t>
,       bit_set<16, uint8_t>
,       bit_set<17, uint8_t>
,       bit_set<24, uint8_t>
,       bit_set< 0, uint16_t>
,       bit_set< 1, uint16_t>
,       bit_set<15, uint16_t>
,       bit_set<16, uint16_t>
,       bit_set<17, uint16_t>
,       bit_set<31, uint16_t>
,       bit_set<32, uint16_t>
,       bit_set<33, uint16_t>
,       bit_set< 0, uint32_t>
,       bit_set< 1, uint32_t>
,       bit_set<31, uint32_t>
,       bit_set<32, uint32_t>
,       bit_set<33, uint32_t>
,       bit_set<63, uint32_t>
,       bit_set<64, uint32_t>
,       bit_set<65, uint32_t>
>;

BOOST_AUTO_TEST_CASE_TEMPLATE(BitSet, T, bitset_types)
{
        all_singleton_set_pairs<T>(op_bitand_assign());
        all_singleton_set_pairs<T>(op_bitor_assign());
        all_singleton_set_pairs<T>(op_xor_assign());
        all_singleton_set_pairs<T>(op_minus_assign());

        all_values<T>([](auto pos) {
                all_singleton_sets<T>([&](auto& bs1) {
                        op_shift_left_assign()(bs1, pos);
                });
        });
        all_values<T>([](auto pos) {
                all_singleton_sets<T>([&](auto& bs1) {
                        op_shift_right_assign()(bs1, pos);
                });
        });

        all_singleton_set_pairs<T>(op_compl());

        all_singleton_set_pairs<T>(op_equal_to());
        all_singleton_set_pairs<T>(op_not_equal_to());

        all_values<T>([](auto pos) {
                all_singleton_sets<T>([&](auto const& bs1) {
                        op_shift_left()(bs1, pos);
                });
        });
        all_values<T>([](auto pos) {
                all_singleton_sets<T>([&](auto const& bs1) {
                        op_shift_right()(bs1, pos);
                });
        });

        all_singleton_set_pairs<T>(op_bitand());
        all_singleton_set_pairs<T>(op_bitor());
        all_singleton_set_pairs<T>(op_xor());
        all_singleton_set_pairs<T>(op_minus());

        all_singleton_set_pairs<T>(mem_is_subset_of());
        all_singleton_set_pairs<T>(mem_is_superset_of());
        all_singleton_set_pairs<T>(mem_is_proper_subset_of());
        all_singleton_set_pairs<T>(mem_is_proper_superset_of());

        all_singleton_set_pairs<T>(mem_intersects());
        all_singleton_set_pairs<T>(fn_is_disjoint());
}

BOOST_AUTO_TEST_SUITE_END()