/*
    SapecNG - Next Generation Symbolic Analysis Program for Electric Circuit
    Copyright (C) 2009, Michele Caini

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

//  Two Graphs Common Spanning Trees Algorithm

#ifndef MRT_H
#define MRT_H

#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
    <
      class TreeMap,
      class PredMap,
      class DistMap,
      class LowMap,
      class Buffer
    >
  struct bridges_visitor: public default_dfs_visitor
  {
    bridges_visitor(
        TreeMap tree,
        PredMap pred,
        DistMap dist,
        LowMap low,
        Buffer& buffer
      ): tree_(tree), pred_(pred), dist_(dist), low_(low), buffer_(buffer)
    { num_ = -1; }

    template <class Vertex, class Graph>
    void initialize_vertex(const Vertex& u, const Graph& g)
    {
      put(pred_, u, u);
      put(dist_, u, -1);
    }

    template <class Vertex, class Graph>
    void discover_vertex(const Vertex& u, const Graph& g)
    {
      put(dist_, u, ++num_);
      put(low_, u, get(dist_, u));
    }

    template <class Edge, class Graph>
    void tree_edge(const Edge& e, const Graph& g)
    {
      put(pred_, target(e, g), source(e, g));
      put(tree_, target(e, g), e);
    }

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

    template <class Vertex, class Graph>
    void finish_vertex(const Vertex& u, const Graph& g)
    {
      Vertex parent = get(pred_, u);
      if(get(low_, u) > get(dist_, parent))
        buffer_.push(get(tree_, u));
      put(low_, parent,
        std::min(get(low_, parent), get(low_, u)));
    }

    TreeMap tree_;
    PredMap pred_;
    DistMap dist_;
    LowMap low_;
    Buffer& buffer_;
    int num_;
  };

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

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

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

  template <
    class Graph,
    class Func,
    class Seq,
    class Map
  >
  void rec_mrt_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;
//
//  Using (edges != 0) condition drives to accidental non-tree
//    ((V-1+1)-fake-tree, named here fat-tree) submission.
//  Remove this condition acts as a workaround to the fat-trees problem.
//  Please do not add that condition to improve performance.
//
//  Here previous (wrong) guard is presented:
//     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_MRT
        detail::rec_mrt_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_MRT
          detail::rec_mrt_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 <class Coll, class 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): seqs_(seqs) { }

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

private:
  Coll& seqs_;

};

template <
  class Graph,
  class Order,
  class Func,
  class Seq
>
BOOST_CONCEPT_REQUIRES(
  ((RandomAccessContainer<Order>))
  ((IncidenceGraphConcept<Graph>))
  ((UnaryFunction<Func, void, Seq>))
  ((Mutable_RandomAccessContainer<Seq>))
  ((VertexAndEdgeListGraphConcept<Graph>)),
  (void)
)
mrt_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::const_iterator seq_const_iterator;
  typedef typename Seq::difference_type seq_diff_type;
  typedef typename Seq::value_type seq_value_type;
  typedef typename Seq::size_type seq_size_type;
  typedef typename Seq::iterator seq_iterator;

  typedef typename Order::const_iterator order_const_iterator;
  typedef typename Order::difference_type order_diff_type;
  typedef typename Order::value_type order_value_type;
  typedef typename Order::size_type order_size_type;
  typedef typename Order::iterator order_iterator;

  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;
  tie(current, last) = edges(iG);
  for(; current != last; ++current)
    if(iG_bimap.right.find(*current) == iG_bimap.right.end())
      return;
  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);

    tie(current, last) = edges(iG);
    for(;current != last; ++current)
      put(diG, *current, false);
    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_MRT
    detail::rec_mrt_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 <
  class Graph,
  class Func,
  class Seq
>
BOOST_CONCEPT_REQUIRES(
  ((IncidenceGraphConcept<Graph>))
  ((EdgeListGraphConcept<Graph>)),
  (void)
)
mrt_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::edges_size_type edges_size_type;
  typedef typename GraphTraits::edge_iterator edge_iterator;

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

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

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

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

} // namespace boost

#endif // MRT_H


// Copyright (c) 2020 ICLUE @ UIUC. All rights reserved.

#include <gflags/gflags.h>
#include <coxeter_properness/search.h>
#include <prettyprint.hpp>
#include <coxeter/constants.h>
#include <coxeter/coxtypes.h>
#include <coxeter/interactive.h>
#include <coxeter/type.h>

#include <boost/math/special_functions/binomial.hpp>
#include <indicators/indicators.hpp>

#include <iostream>
#include <fstream>
#include <map>
#include <memory>
#include <string>
#include <tuple>
#include <vector>

DEFINE_string(type, "", "type");
DEFINE_uint32(rank, 0, "rank");
DEFINE_string(in_file, "", "in-file");
DEFINE_string(out_file, "", "out-file");

using coxeter_properness::Transformer;
using coxeter_properness::proper;

int main(int argc, char** argv) {
  gflags::SetUsageMessage("Iterate over elements of a finite Coxeter group.");
  gflags::ParseCommandLineFlags(&argc, &argv, true);

  const auto type = FLAGS_type;
  const auto rank = FLAGS_rank;
  const auto in_file = FLAGS_in_file;
  const auto out_file = FLAGS_out_file;

  if (type.empty() || rank == 0 || in_file.empty() || out_file.empty()) {
    std::cout << "Need to set each of --type, --rank, --out-file, and --in-file flags." << std::endl;
    return EXIT_FAILURE;
  }

  std::ifstream ifs{in_file};
  if (!ifs.is_open()) {
    std::cout << "Could not open input file '" << in_file << "'" << std::endl;
    return EXIT_FAILURE;
  }

  std::ofstream ofs{out_file};
  if (!ofs.is_open()) {
    std::cout << "Could not open output file '" << out_file << "'" << std::endl;
    return EXIT_FAILURE;
  }

  // Always need to run this line first.
  constants::initConstants();
  const std::shared_ptr<CoxGroup> group{
    interactive::coxeterGroup(type.c_str(), rank)
  };
  Transformer transformer(group);
  CoxWord word;

  // indicators::ProgressSpinner spinner;
  std::string line;
  for (int numLinesProcessed = 0; std::getline(ifs, line); ++numLinesProcessed) {
    if (numLinesProcessed % 100000 == 0) {
      std::cout << "processed: " + std::to_string(numLinesProcessed) << std::endl;
    }

    if (!transformer.parse(line, &word)) {
      std::cout << "could not parse line '" << line << "'" << std::endl;
      continue;
    }
    if (!proper(*group, word)) continue;

    for (int i = 0; i < word.length(); ++i) {
      ofs << std::to_string(word[i]);
    }
    ofs << '\n';
    // ofs << std::endl;

    // spinner.set_option(indicators::option::PrefixText{
    //   "processed: " + std::to_string(numLinesProcessed)
    // });
  }

  return EXIT_SUCCESS;
}


//
//  main.cpp
//  Andrews-Curtis Conjecture
//
//  Created by Kelly Davis on 8/19/11.
//
//

#include <iostream>
#include <boost/mpi/environment.hpp>

#include "relator.h"
#include "searcher.h"
#include "arguments.h"
#include "binary_tree.h"
#include "balanced_presentation.h"

using namespace andrews_curtis;

int main (int argc,char * argv[])
{
    // Initialize the MPI environment
    boost::mpi::environment environment(argc,argv);

    // Initialize the user arguments
    Arguments arguments(argc,argv);

    // Determine if arguments is not valid
    if(!arguments.is_valid())
    {
        // Print error message for invalid arguments
        std::cout << arguments.get_message() << std::endl;

        // Return error
        return arguments.get_return_code();
    }

    // Create searcher
    Searcher searcher(arguments);

    // Search
    searcher.search();

    // Is Andrews-Curtis trivial
    if(searcher.is_trivial())
    {
        // Print derivation
        searcher.print_derivation();
    }
    else
    {
        // Print Andrews-Curtis counterexample
        searcher.print_counterexample();
    }

    // Clean up Relators
    Binary_tree<Relator>::clear();

    // Clean up Balanced_presentation's
    Binary_tree<Balanced_presentation>::clear();

    // Return success
    return 0;
}


/*
 * GeosTools.cpp
 */

#include "PathAdapter.h"

#include <boost/numeric/conversion/cast.hpp>
#include <geos/geom/Coordinate.h>
#include <geos/geom/CoordinateSequence.h>
#include <geos/geom/LineString.h>
#include <geos/geom/LinearRing.h>
#include <geos/geom/MultiLineString.h>
#include <geos/geom/MultiPoint.h>
#include <geos/geom/MultiPolygon.h>
#include <geos/geom/Point.h>
#include <geos/geom/Polygon.h>
#include <stdexcept>

using namespace geos::geom;
using namespace frontier;

namespace
{
// ----------------------------------------------------------------------
/*!
 * \brief Handle LinearRing
 */
// ----------------------------------------------------------------------

void contourFromLinearRing(const LinearRing *geom, PathAdapter &pathAdapter)
{
  if (geom == nullptr || geom->isEmpty()) return;

  for (unsigned long i = 0, n = geom->getNumPoints(); i < n - 1; ++i)
  {
    if (i == 0)
      pathAdapter.moveto(geom->getCoordinateN(boost::numeric_cast<int>(i)));
    else
      pathAdapter.lineto(geom->getCoordinateN(boost::numeric_cast<int>(i)));
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Handle LineString
 */
// ----------------------------------------------------------------------

void contourFromLineString(const LineString *geom, PathAdapter &pathAdapter)
{
  if (geom == nullptr || geom->isEmpty()) return;

  unsigned long n = geom->getNumPoints();

  for (unsigned long i = 0; i < n; ++i)
  {
    if (i == 0)
      pathAdapter.moveto(geom->getCoordinateN(boost::numeric_cast<int>(i)));
    else
      pathAdapter.lineto(geom->getCoordinateN(boost::numeric_cast<int>(i)));
  }

  //	if ((!(geom->isClosed())) && (n > 2))
  //		pathAdapter.lineto(geom->getCoordinateN(boost::numeric_cast<int>(0)));
}

// ----------------------------------------------------------------------
/*!
 * \brief Handle Polygon
 */
// ----------------------------------------------------------------------

void contoursFromPolygon(const Polygon *geom, PathAdapter &pathAdapter)
{
  if (geom == nullptr || geom->isEmpty()) return;

  contourFromLineString(geom->getExteriorRing(), pathAdapter);

  for (size_t i = 0, n = geom->getNumInteriorRing(); i < n; ++i)
    contourFromLineString(geom->getInteriorRingN(i), pathAdapter);
}

// ----------------------------------------------------------------------
/*!
 * \brief Handle MultiLineString
 */
// ----------------------------------------------------------------------

void contoursFromMultiLineString(const MultiLineString *geom, PathAdapter &pathAdapter)
{
  if (geom == nullptr || geom->isEmpty()) return;

  for (size_t i = 0, n = geom->getNumGeometries(); i < n; ++i)
    contourFromLineString(dynamic_cast<const LineString *>(geom->getGeometryN(i)), pathAdapter);
}

// ----------------------------------------------------------------------
/*!
 * \brief Handle MultiPolygon
 */
// ----------------------------------------------------------------------

void contoursFromMultiPolygon(const MultiPolygon *geom, PathAdapter &pathAdapter)
{
  if (geom == nullptr || geom->isEmpty()) return;

  for (size_t i = 0, n = geom->getNumGeometries(); i < n; ++i)
    contoursFromPolygon(dynamic_cast<const Polygon *>(geom->getGeometryN(i)), pathAdapter);
}

// ----------------------------------------------------------------------
/*!
 * \brief Handle GeometryCollection
 */
// ----------------------------------------------------------------------

void contoursFromGeometryCollection(const GeometryCollection *geom, PathAdapter &pathAdapter)
{
  if (geom == nullptr || geom->isEmpty()) return;

  for (size_t i = 0, n = geom->getNumGeometries(); i < n; ++i)
    frontier::GeosTools::getContours(geom->getGeometryN(i), pathAdapter);
}
}  // namespace

namespace frontier
{
namespace GeosTools
{
// ----------------------------------------------------------------------
/*!
 * \brief Extract contours from geos geometry
 */
// ----------------------------------------------------------------------

const Path &getContours(const Geometry *geom, PathAdapter &pathAdapter)
{
  if (const LinearRing *lr = dynamic_cast<const LinearRing *>(geom))
    contourFromLinearRing(lr, pathAdapter);
  else if (const LineString *ls = dynamic_cast<const LineString *>(geom))
    contourFromLineString(ls, pathAdapter);
  else if (const Polygon *p = dynamic_cast<const Polygon *>(geom))
    contoursFromPolygon(p, pathAdapter);
  else if (const MultiLineString *ml = dynamic_cast<const MultiLineString *>(geom))
    contoursFromMultiLineString(ml, pathAdapter);
  else if (const MultiPolygon *mpg = dynamic_cast<const MultiPolygon *>(geom))
    contoursFromMultiPolygon(mpg, pathAdapter);
  else if (const GeometryCollection *g = dynamic_cast<const GeometryCollection *>(geom))
    contoursFromGeometryCollection(g, pathAdapter);
  else if (dynamic_cast<const Point *>(geom))
    ;
  else if (dynamic_cast<const MultiPoint *>(geom))
    ;
  else
    throw std::runtime_error("Encountered an unsupported GEOS geometry component");

  return pathAdapter.getPath();
}
}  // namespace GeosTools
}  // namespace frontier


/*
 * Copyright (c) 2011, Mattia Penati <mattia.penati@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *     * 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 Politecnico di Milano 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.
 */

#ifndef AMA_TENSOR_IEXP_IEXP_CWISE_BINARY_HPP
#define AMA_TENSOR_IEXP_IEXP_CWISE_BINARY_HPP 1

#include <ama/tensor/iexp/iexp_base.hpp>
#include <ama/tensor/iexp/is_same_iexp.hpp>
#include <boost/mpl/assert.hpp>

namespace ama
{
  namespace tensor_
  {

    /* this class represent a unary component-wise operation */
    template <typename LEFT, typename RIGHT, typename OPERATOR> class iexp_cwise_binary;

    /* specialization of iexp_traits */
    template <typename LEFT, typename RIGHT, typename OPERATOR>
    struct iexp_traits< iexp_cwise_binary<LEFT, RIGHT, OPERATOR> >
    {
      typedef typename OPERATOR::result_type value_type;

      typedef typename LEFT::dimension_type dimension_type;

      typedef typename LEFT::controvariant_list controvariant_list;
      typedef typename LEFT::covariant_list covariant_list;

      typedef ::boost::mpl::false_ is_assignable;
    };

    /* class declaration */
    template <typename LEFT, typename RIGHT, typename OPERATOR>
    class iexp_cwise_binary:
        public iexp_base< iexp_cwise_binary<LEFT, RIGHT, OPERATOR> >
    {
      BOOST_MPL_ASSERT_MSG(
            (is_same_iexp<LEFT,RIGHT>::value)
          , COMPONENT_WISE_BINARY_OPERATION_BETWEEN_DIFFERENT_EXPRESSION_ARE_NOT_ALLOWED
          , (LEFT, RIGHT));

    protected:
      typedef iexp_base< iexp_cwise_binary<LEFT, RIGHT, OPERATOR> > base_type;
      typedef iexp_cwise_binary<LEFT, RIGHT, OPERATOR> derived_type;

    protected:
      typedef LEFT left_operand_type;
      typedef RIGHT right_operand_type;
      typedef OPERATOR operator_type;

    public:
      typedef typename base_type::value_type value_type;

    public:
      /* constructor */
      iexp_cwise_binary(left_operand_type const & left_operand,
                        right_operand_type const & right_operand,
                        operator_type const & op = operator_type())
          : m_left_operand(left_operand),
            m_right_operand(right_operand),
            m_operator(op) { }

    public:
      /* retrieve the value */
      template <typename IMAP>
      value_type at() const
      {
        return m_operator(
                 m_left_operand.template at<IMAP>(),
                 m_right_operand.template at<IMAP>());
      }

    protected:
      /* members */
      left_operand_type m_left_operand;
      right_operand_type m_right_operand;
      operator_type const m_operator;
    };

  }
}

#endif /* AMA_TENSOR_IEXP_IEXP_CWISE_BINARY_HPP */


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

#include <vector>
#include <algorithm>
#include <stdexcept>
#include <map>
#include <utility>
#include <algorithm>
#include <string>
#include <iostream>
#include <time.h>

#include <boost/lexical_cast.hpp>

#include <hashclash/saveload_bz2.hpp>
#include <hashclash/sha1detail.hpp>
#include <hashclash/rng.hpp>
#include <hashclash/sha1differentialpath.hpp>
#include <hashclash/progress_display.hpp>

#include "main.hpp"

void random_permutation(vector<sha1differentialpath>& paths, path_container_autobalance& container)
{
	// use a pseudo-random permutation fixed by inputs
	seed(paths.size()); addseed(container.t); addseed(container.modn);
	for (unsigned i = 0; i < paths.size(); ++i)
	{
		unsigned k = xrng64() % paths.size();
		paths[i].swap(paths[k]);
	}
	addseed(time(NULL));
}

inline std::string pathsstring(const std::string& basepath, unsigned modi, unsigned modn)
{
	return workdir +  "/"  + basepath
		+ "_" + boost::lexical_cast<std::string>(modi)
		+ "of" + boost::lexical_cast<std::string>(modn);
}

bool beginpathless(const sha1differentialpath& l, const sha1differentialpath& r)
{
	for (int i = 0; i < 3; ++i)
		if (l[l.tbegin()+i] != r[r.tbegin()+i])
			return l[l.tbegin()+i] < r[r.tbegin()+i];
	for (int i = 3; i < 5; ++i)
		if (l[l.tbegin()+i].getsdr() != r[r.tbegin()+i].getsdr())
			return l[l.tbegin()+i].getsdr() < r[r.tbegin()+i].getsdr();
	return false;
}

bool beginpatheq(const sha1differentialpath& l, const sha1differentialpath& r)
{
	for (int i = 0; i < 3; ++i)
		if (l[l.tbegin()+i] != r[r.tbegin()+i])
			return false;
	for (int i = 3; i < 5; ++i)
		if (l[l.tbegin()+i].getsdr() != r[r.tbegin()+i].getsdr())
			return false;
	return true;
}

void best_filter(vector<sha1differentialpath>& pathsout)
{
	vector<sha1differentialpath> ret;
	std::cout << "Filtering best paths (insize=" << pathsout.size() << " sorting..." << std::flush;
	std::sort(pathsout.begin(), pathsout.end(), beginpathless);
	std::cout << "filtering..." << std::flush;
	std::size_t i = 0;
	while (i < pathsout.size())
	{
		unsigned nrcondi = pathsout[i].nrcond();
		std::size_t e = i + 1;
		while (e < pathsout.size() && beginpatheq(pathsout[i],pathsout[e]))
		{
			unsigned nrconde = pathsout[e].nrcond();
			if (nrconde < nrcondi)
			{
				std::swap(pathsout[i], pathsout[e]);
				nrcondi = nrconde;
			}
			++e;
		}
#ifdef BOOST_NO_CXX11_RVALUE_REFERENCES
		ret.push_back(pathsout[i]);
#else
		ret.push_back(std::move(pathsout[i]));
#endif
		i = e;
	}
	pathsout = std::move(ret);
	std::cout << " outsize=" << pathsout.size() << ")" << std::endl;
}

vector<sha1differentialpath> pathscache;
void dostep(path_container_autobalance& container, bool savetocache)
{
	const unsigned t = container.t;
	const unsigned modn = container.modn;
	const unsigned modi = container.modi;

	vector< sha1differentialpath > pathsin, pathstmp, pathsout;
	if (pathscache.size() != 0) {
		pathsin.swap(pathscache);
		random_permutation(pathsin, container);
	} else if (container.newinputpath) {
		sha1differentialpath path;
		path.offset = - int(t) + 4;
		path.path.resize(6);
		path.me.resize(6);
		pathsin.push_back( path );
		cout << "Generated 1 new path." << endl;
	} else if (container.inputfile.size() == 0) {
		for (unsigned k = 0; k < modn; ++k)
		{
			try {
				std::string filename = pathsstring("paths" + boost::lexical_cast<std::string>(t+1), k, modn);
				cout << "Loading " << filename << "..." << flush;
				load_bz2(pathstmp, filename, binary_archive);
				random_permutation(pathstmp, container);
				for (unsigned j = modi; j < pathstmp.size(); j += modn)
					pathsin.push_back(pathstmp[j]);
				cout << "done: " << pathstmp.size() << " (work:" << pathsin.size() << ")." << endl;
			} catch(...) {
				cout << "failed." << endl;
			}
		}
	} else {
		try {
			cout << "Loading " << container.inputfile << "..." << flush;
			load_bz2(pathstmp, binary_archive, container.inputfile);
			random_permutation(pathstmp, container);
			for (unsigned j = modi; j < pathstmp.size(); j += modn)
				pathsin.push_back(pathstmp[j]);
			cout << "done: " << pathsin.size() << "." << endl;
		} catch(...) {
			cout << "failed." << endl;
		}
	}
	if (container.showinputpaths)
		for (unsigned r = 0; r < pathsin.size(); ++r)
			show_path(pathsin[r]);

	std::string tstring = "t=" + boost::lexical_cast<std::string>(t) + ": ";
	if (tstring.size() == 5) tstring += " ";

	if (container.estimatefactor != 0) {
		cout << "Estimating maxcond for upper bound " << unsigned(double(container.ubound)*container.estimatefactor)
			<< " (=" << container.ubound << " * " << container.estimatefactor << ")..." << endl;
		progress_display show_progress(pathsin.size(), true, cout, tstring, "      ", "e     ");
		for (unsigned k = 0; k < pathsin.size(); ++k,++show_progress) {
			if (container.t < 79 && container.expandprevmessagediff) {
				sdr mmask = pathsin[k].getme(t+1);
				uint32 deltam = mmask.adddiff();
				uint32 dqtm3 = pathsin[k][t-3].getsdr().rotate_left(30).adddiff();

				uint32 addmask = (~mmask.mask)+1;
				uint32 andmask = mmask.mask & 0x7FFFFFF;
				mmask.sign = 0;
				do {
					mmask.sign += addmask; mmask.sign &= andmask;
					pathsin[k].getme(t+1) = mmask;
					pathsin[k][t-3] = naf(dqtm3 + deltam - mmask.adddiff()).rotate_right(30);
					sha1_backward_differential_step(pathsin[k], container);
				} while (mmask.sign != 0);
			} else
				sha1_backward_differential_step(pathsin[k], container);
		}
		container.finish_estimate();
		cout << "Found maxcond = " << container.maxcond << endl;
	}

	progress_display show_progress(pathsin.size(), true, cout, tstring, "      ", "      ");
	for (unsigned k = 0; k < pathsin.size(); ++k,++show_progress) {
		if (container.t < 79 && container.expandprevmessagediff) {
			sdr mmask = pathsin[k].getme(t+1);
			uint32 deltam = mmask.adddiff();
			uint32 dqtm3 = pathsin[k][t-3].getsdr().rotate_left(30).adddiff();

			uint32 addmask = (~mmask.mask)+1;
			uint32 andmask = mmask.mask & 0x7FFFFFF;
			mmask.sign = 0;
			do {
				mmask.sign += addmask; mmask.sign &= andmask;
				pathsin[k].getme(t+1) = mmask;
				pathsin[k][t-3] = naf(dqtm3 + deltam - mmask.adddiff()).rotate_right(30);
				sha1_backward_differential_step(pathsin[k], container);
			} while (mmask.sign != 0);
		} else
			sha1_backward_differential_step(pathsin[k], container);
	}

	pathstmp.swap(pathsout);
	container.export_results(pathsout);

	best_filter(pathsout);

	if (pathsout.size() > 0)
		show_path(pathsout[0]);
	std::string filenameout = pathsstring("paths" + boost::lexical_cast<std::string>(t), modi, modn);
	cout << "Saving " << pathsout.size() << " paths..." << flush;
	if (savetocache)
		pathsout.swap(pathscache);
	else
		save_bz2(pathsout, filenameout, binary_archive);
	cout << "done." << endl;
}


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

#ifndef MTL_PROPERTY_MAP_IMPL_INCLUDE
#define MTL_PROPERTY_MAP_IMPL_INCLUDE

#include <boost/numeric/mtl/mtl_fwd.hpp>
#include <boost/numeric/mtl/utility/is_row_major.hpp>
#include <boost/numeric/mtl/utility/static_assert.hpp>

namespace mtl { namespace detail {

// functor with matrix reference to access rows
template <class Matrix> struct indexer_row_ref
{
    typedef Matrix                      matrix_type;
    typedef typename Matrix::key_type   key_type;
    indexer_row_ref(const matrix_type& ma) : ma(ma) {}

    typename Matrix::size_type operator() (key_type const& key) const
    {
	return ma.indexer.row(ma, key);
    }
    const matrix_type& ma;
};

// functor to access rows using the key itself
template <class Matrix> struct row_in_key
{
    typedef Matrix                      matrix_type;
    typedef typename Matrix::key_type   key_type;
    row_in_key(const matrix_type&) {}

    typename Matrix::size_type operator() (key_type const& key) const
    {
	return key.row();
    }
};

// functor to access rows using the key itself
template <class Matrix> struct row_in_element_key
{
    typedef Matrix                        matrix_type;
    row_in_element_key(const matrix_type&) {}

    template <typename Key>
    typename Matrix::size_type operator() (Key const& key) const
    {
	return key.indices[0];
    }
};

// functor access the major dimension in key itself
template <class Matrix> struct major_in_key
{
    typedef Matrix                      matrix_type;
    typedef typename Matrix::key_type   key_type;
    major_in_key(const matrix_type&) {}

    typename Matrix::size_type operator() (key_type const& key) const
    {
	return key.major;
    }
};

// functor to access rows using the key itself
template <class Matrix> struct major_in_element_key
{
    typedef Matrix                      matrix_type;
    major_in_element_key(const matrix_type&) {}

    template <typename Key>
    typename Matrix::size_type operator() (Key const& key) const
    {
	return key.indices[0];
    }
};

template <class Matrix> struct indexer_minor_ref
{
    typedef Matrix                      matrix_type;
    typedef typename Matrix::key_type   key_type;
    indexer_minor_ref(const matrix_type& ma) : ma(ma) {}

    typename Matrix::size_type operator() (key_type const& key) const
    {
	return ma.indexer.minor_from_offset(ma, key.offset);
    }
    const matrix_type& ma;
};

template <class Matrix> struct indexer_col_ref
{
    typedef Matrix                      matrix_type;
    typedef typename Matrix::key_type   key_type;
    indexer_col_ref(const matrix_type& ma) : ma(ma) {}

    typename Matrix::size_type operator() (key_type const& key) const
    {
	return ma.indexer.col(ma, key);
    }
    const matrix_type& ma;
};

template <class Matrix> struct col_in_key
{
    typedef Matrix                      matrix_type;
    typedef typename Matrix::key_type   key_type;
    col_in_key(const matrix_type&) {}

    typename Matrix::size_type operator() (key_type const& key) const
    {
	return key.col();
    }
};

// functor to access columns using the key itself
template <class Matrix> struct col_in_element_key
{
    typedef Matrix                      matrix_type;
    col_in_element_key(const matrix_type&) {}

    template <typename Key>
    typename Matrix::size_type operator() (Key const& key) const
    {
	return key.indices[1];
    }
};

// Collection must be derived from contiguous_memory_block
// key must contain pointer
template <class Collection> struct index_from_offset
{
    typedef Collection                      collection_type;

    index_from_offset(const collection_type& coll) : coll(coll) {}

    template <typename Key>
    typename Collection::size_type operator() (Key const& key) const
    {
	return coll.offset(&*key);
    }
private:
    const collection_type& coll;
};

template <typename Matrix>
struct const_value_from_other
{
    typedef typename Matrix::other     other;
    typedef typename other::key_type   key_type;
    typedef typename other::value_type value_type;

    explicit const_value_from_other(Matrix const& matrix)
	: its_const_value(matrix.ref) {}

    const value_type operator() (key_type const& key) const
    {
	return its_const_value(key);
    }

  protected:
    typename traits::const_value<typename boost::remove_const<other>::type>::type  its_const_value;
};

template <typename Matrix>
struct value_from_other
{
    typedef typename Matrix::other     other;
    typedef typename other::key_type   key_type;
    typedef typename other::value_type value_type;

    explicit value_from_other(Matrix const& matrix)
	: its_value(matrix.ref) {}

    const value_type operator() (key_type const& key) const
    {
	return its_value(key);
    }

    void operator() (key_type const& key, value_type value) const
    {
	its_value(key, value);
    }

  protected:
    typename traits::value<other>::type  its_value;
};

// property map to read value if key is referring to value, e.g. pointer
template <class Matrix> struct direct_const_value
{
    direct_const_value(const Matrix&) {} // for compatibility
    typename Matrix::value_type const operator() (const typename Matrix::key_type key) const
    {
	return *key;
    }
};

// same with writing
template <class Matrix> struct direct_value
  : public direct_const_value<Matrix>
{
    typedef typename Matrix::value_type value_type;

    direct_value(const Matrix& ma)
      : direct_const_value<Matrix>(ma)
    {} // for compatibility

    // May be to be replaced by inserter
    void operator() (typename Matrix::key_type const& key, value_type value)
    {
	* const_cast<value_type *>(key) = value;
    }

    // should be inherited
    typename Matrix::value_type operator() (typename Matrix::key_type const& key) const
    {
	return *key;
    }
};

template <class Matrix> struct matrix_const_value_ref
{
    typedef Matrix                      matrix_type;
    typedef typename Matrix::key_type   key_type;
    matrix_const_value_ref(const matrix_type& ma) : ma(ma) {}

    typename Matrix::value_type operator() (key_type const& key) const
    {
	return ma(key);
    }
    const matrix_type& ma;
};

template <class Matrix> struct matrix_value_ref
{
    typedef Matrix                      matrix_type;
    typedef typename Matrix::key_type   key_type;
    typedef typename Matrix::value_type value_type;
    matrix_value_ref(matrix_type& ma) : ma(ma) {}

    typename Matrix::value_type operator() (key_type const& key) const
    {
	return ma(key);
    }

    // Much better with inserters
    void operator() (typename Matrix::key_type const& key, value_type const& value)
    {
	ma(key, value);
    }

    matrix_type& ma;
};

template <class Matrix> struct matrix_offset_const_value
{
    typedef Matrix                      matrix_type;
    typedef typename Matrix::key_type   key_type;
    matrix_offset_const_value(const matrix_type& ma) : ma(ma) {}

    typename Matrix::value_type operator() (key_type const& key) const
    {
	return ma.value_from_offset(key.offset);
    }
    const matrix_type& ma;
};

template <class Matrix> struct matrix_offset_value
{
    typedef Matrix                      matrix_type;
    typedef typename Matrix::key_type   key_type;
    typedef typename Matrix::value_type value_type;
    matrix_offset_value(matrix_type& ma) : ma(ma) {}

    typename Matrix::value_type operator() (key_type const& key) const
    {
	return ma.value_from_offset(key.offset);
    }

    // Much better with inserters
    void operator() (typename Matrix::key_type const& key, value_type const& value)
    {
	ma.value_from_offset(key.offset) = value;
    }

    matrix_type& ma;
};

template <class Matrix> struct offset_from_key
{
    typedef Matrix                      matrix_type;
    typedef typename Matrix::key_type   key_type;
    typedef typename Matrix::size_type  size_type;
    offset_from_key(const matrix_type& ) {}

    size_type operator() (key_type const& key) const
    {
	return key.offset;
    }
};

// functor to access columns using the key itself
template <class Matrix> struct const_value_in_element_key
{
    typedef Matrix                      matrix_type;
    const_value_in_element_key(const matrix_type&) {}

    template <typename Key>
    typename Matrix::value_type operator() (Key const& key) const
    {
	return key.ref[key.indices[0]][key.indices[1]];
    }
};

template <class Value, class Parameters>
struct coordinate2D_row
{
    typedef const mtl::mat::coordinate2D<Value, Parameters>&       matrix_ref_type;
    explicit coordinate2D_row(matrix_ref_type A) : A(A) {}

    template <typename Key>
    typename Parameters::size_type operator() (Key key) const
    { return A.row_index_array()[key.offset]; }

    matrix_ref_type A;
};

template <class Value, class Parameters>
struct coordinate2D_col
{
    typedef const mtl::mat::coordinate2D<Value, Parameters>&       matrix_ref_type;
    explicit coordinate2D_col(matrix_ref_type A) : A(A) {}

    template <typename Key>
    typename Parameters::size_type operator() (Key key) const
    { return A.column_index_array()[key.offset]; }

    matrix_ref_type A;
};

template <class Value, class Parameters>
struct coordinate2D_const_value
{
    typedef const mtl::mat::coordinate2D<Value, Parameters>&       matrix_ref_type;
    explicit coordinate2D_const_value(matrix_ref_type A) : A(A) {}

    template <typename Key>
    Value operator() (Key key) const
    { return A.value_array()[key.offset]; }

    matrix_ref_type A;
};

template <class Value, class Parameters>
struct sparse_banded_row  // maybe refactor into sparse_banded_major
{
    MTL_STATIC_ASSERT((mtl::traits::is_row_major<Parameters>::value), "Only row-major sparse banded matrices supported so far.");
    typedef const mtl::mat::sparse_banded<Value, Parameters>&  matrix_ref_type;
    typedef typename Parameters::size_type                        size_type;
    explicit sparse_banded_row(matrix_ref_type A) : A(A) {}

    template <typename Key>
    size_type operator() (Key key) const
    { return key.offset / A.ref_bands().size(); }

    matrix_ref_type A;
};

template <class Value, class Parameters>
struct sparse_banded_col // maybe refactor into sparse_banded_minor
{
    MTL_STATIC_ASSERT((mtl::traits::is_row_major<Parameters>::value), "Only row-major sparse banded matrices supported so far.");
    typedef const mtl::mat::sparse_banded<Value, Parameters>&  matrix_ref_type;
    typedef typename Parameters::size_type                        size_type;

    explicit sparse_banded_col(matrix_ref_type A) : A(A) {}

    template <typename Key>
    size_type operator() (Key key) const
    {
	size_type bs= A.ref_bands().size(), major= key.offset / bs, b= key.offset % bs;
	return major + A.ref_bands()[b];
    }

    matrix_ref_type A;
};

template <class Value, class Parameters>
struct sparse_banded_const_value
{
    typedef const mtl::mat::sparse_banded<Value, Parameters>&       matrix_ref_type;
    explicit sparse_banded_const_value(matrix_ref_type A) : A(A) {}

    template <typename Key>
    Value operator() (Key key) const
    { return A.ref_data()[key.offset]; }

    matrix_ref_type A;
};

}} // namespace mtl::detail

#endif // MTL_PROPERTY_MAP_IMPL_INCLUDE


/****************************************************************************/
/* Copyright 2005-2006, Francis Russell                                     */
/*                                                                          */
/* Licensed under the Apache License, Version 2.0 (the License);            */
/* you may not use this file except in compliance with the License.         */
/* You may obtain a copy of the License at                                  */
/*                                                                          */
/*     http://www.apache.org/licenses/LICENSE-2.0                           */
/*                                                                          */
/* Unless required by applicable law or agreed to in writing, software      */
/* distributed under the License is distributed on an AS IS BASIS,          */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
/* See the License for the specific language governing permissions and      */
/* limitations under the License.                                           */
/*                                                                          */
/****************************************************************************/

#ifndef DESOLA_TG_CODE_GENERATOR_HPP
#define DESOLA_TG_CODE_GENERATOR_HPP

#include <cstddef>
#include <utility>
#include <string>
#include <map>
#include <algorithm>
#include <boost/bind.hpp>
#include <boost/ref.hpp>
#include <boost/tuple/tuple.hpp>
#include <TaskGraph>
#include "Desola_tg_fwd.hpp"

//NOTE: The storage for the results is uninitialized, so make sure that
//      setExpression is used before calling addExpression on any result
//      element.

namespace desola
{

namespace detail
{

template<typename T_element>
class TGCodeGenerator : public TGExpressionNodeVisitor<T_element>
{
private:
  const std::string prefix;
  NameGenerator& generator;

  inline std::string getIndexName()
  {
    return generator.getName(prefix);
  }

  static void setMatrixElement(NameGenerator& generator, TGMatrix<T_element>& matrix, const std::pair<const TGElementIndex<tg_matrix>, TGOutputReference<tg_scalar, T_element> >& pair)
  {
    matrix.setExpression(generator, tg::TaskExpression(pair.first.getRow()), tg::TaskExpression(pair.first.getCol()), pair.second.getInternal().getExpression());
  }

  static void setVectorElement(NameGenerator& generator, TGVector<T_element>& vector, const std::pair<const TGElementIndex<tg_vector>, TGOutputReference<tg_scalar, T_element> >& pair)
  {
    vector.setExpression(tg::TaskExpression(pair.first.getRow()), pair.second.getInternal().getExpression());
  }

  static void matrixVectorMultKernel(NameGenerator& generator,
    TGVector<T_element>& lhs, TGVector<T_element>& rhs,
    const tg::TaskExpression& row, const tg::TaskExpression& col,
    const TGScalarExpr<T_element>& value, const bool transpose)
  {
    if (!transpose)
      rhs.addExpression(row, value.mul(lhs.getExpression(col)));
    else
      rhs.addExpression(col, value.mul(lhs.getExpression(row)));

  }

  static void matrixMultiVectorMultKernel(NameGenerator& generator,
    const std::vector<boost::tuple<TGVector<T_element>*, TGVector<T_element>*, bool> >& vectors,
    const tg::TaskExpression& row, const tg::TaskExpression& col,
    const TGScalarExpr<T_element>& value)
  {
    for(std::size_t index=0; index<vectors.size(); ++index)
    {
      if (!boost::get<2>(vectors[index]))
        boost::get<1>(vectors[index])->addExpression(row, value.mul(boost::get<0>(vectors[index])->getExpression(col)));
      else
        boost::get<1>(vectors[index])->addExpression(col, value.mul(boost::get<0>(vectors[index])->getExpression(row)));
    }
  }

  static void matrixMultKernel(NameGenerator& generator, TGMatrix<T_element>& b, TGMatrix<T_element>& c, const tg::TaskExpression& row, const tg::TaskExpression& col, const TGScalarExpr<T_element>& value)
  {
    using namespace tg;

    tVarNamed(unsigned, j, generator.getName("mat_mult_inner").c_str());
    tFor(j, 0u, c.getCols()-1)
    {
      c.addExpression(generator, row, j, value.mul(b.getExpression(generator, col, j)));
    }
  }

public:
  TGCodeGenerator(TGExpressionGraph<T_element>& g) : prefix("index"), generator(g.getNameGenerator())
  {
  }

  virtual void visit(TGElementSet<tg_vector, T_element>& e)
  {
    using namespace tg;

    TGVector<T_element>&  newVector(e.getInternal());
    TGVector<T_element>&  vector(e.getOperand().getInternal());

    tVarNamed(unsigned, i, getIndexName().c_str());
    tFor(i, 0u, vector.getRows()-1)
    {
      newVector.setExpression(i, vector.getExpression(i));
    }

    const std::map<TGElementIndex<tg_vector>, TGOutputReference<tg_scalar, T_element> > assignments(e.getAssignments());
    std::for_each(assignments.begin(), assignments.end(), boost::bind(setVectorElement, boost::ref(generator), boost::ref(newVector), _1));
  }

  virtual void visit(TGElementSet<tg_matrix, T_element>& e)
  {
    using namespace tg;

    TGMatrix<T_element>& newMatrix(e.getInternal());
    TGMatrix<T_element>& matrix(e.getOperand().getInternal());

    tVarNamed(unsigned, i, getIndexName().c_str());
    tVarNamed(unsigned, j, getIndexName().c_str());

    tFor(i, 0u, matrix.getRows()-1)
    {
      tFor(j, 0u, matrix.getCols()-1)
      {
        newMatrix.setExpression(generator, i, j, matrix.getExpression(generator, i, j));
      }
    }

    const std::map<TGElementIndex<tg_matrix>, TGOutputReference<tg_scalar, T_element> > assignments(e.getAssignments());
    std::for_each(assignments.begin(), assignments.end(), boost::bind(setMatrixElement, boost::ref(generator), boost::ref(newMatrix), _1));
  }

  virtual void visit(TGElementGet<tg_vector, T_element>& e)
  {
    using namespace tg;

    TGScalar<T_element>& result(e.getInternal());
    TGVector<T_element>& vector(e.getOperand().getInternal());

    result.setExpression(vector.getExpression(TaskExpression(e.getIndex().getRow())));
  }

  virtual void visit(TGElementGet<tg_matrix, T_element>& e)
  {
    using namespace tg;

    TGScalar<T_element>& result(e.getInternal());
    TGMatrix<T_element>& matrix(e.getOperand().getInternal());

    result.setExpression(matrix.getExpression(generator, TaskExpression(e.getIndex().getRow()), TaskExpression(e.getIndex().getCol())));
  }

  virtual void visit(TGLiteral<tg_scalar, T_element>& e)
  {
  }
  virtual void visit(TGLiteral<tg_vector, T_element>& e)
  {
  }

  virtual void visit(TGLiteral<tg_matrix, T_element>& e)
  {
  }

  //NOTE: These loops have been structured to try to obtain good cache usage assuming
  //      row major storage.  If IR optimisation works, a more naive implementation
  //      of matrix multiply can be used instead and it will be automatically
  //      optimised for best cache locality.

  virtual void visit(TGMatrixMult<T_element>& e)
  {
    using namespace tg;

    TGMatrix<T_element>& a(e.getLeft().getInternal());
    TGMatrix<T_element>& b(e.getRight().getInternal());
    TGMatrix<T_element>& c(e.getInternal());

    tVarNamed(unsigned, i, getIndexName().c_str());
    tVarNamed(unsigned, j, getIndexName().c_str());
    tVarNamed(unsigned, k, getIndexName().c_str());

    tFor(i, 0u, c.getRows()-1)
    {
      tFor(j, 0u, c.getCols()-1)
      {
        c.setExpression(generator, i, j, TGScalarExpr<T_element>());
      }
    }

    typename TGMatrix<T_element>::MatrixIterationCallback kernel;
    kernel = boost::bind(matrixMultKernel, _1, boost::ref(b), boost::ref(c), _2, _3, _4);
    a.iterateSparse(generator, kernel);
  }

  virtual void visit(TGMatrixVectorMult<T_element>& e)
  {
    using namespace tg;

    TGVector<T_element>& result(e.getInternal());
    TGMatrix<T_element>& matrix(e.getLeft().getInternal());
    TGVector<T_element>& vector(e.getRight().getInternal());

    tVarNamed(unsigned, i, getIndexName().c_str());
    tVarNamed(unsigned, j, getIndexName().c_str());

    tFor(i, 0u, matrix.getRows()-1)
    {
      result.setExpression(i, TGScalarExpr<T_element>());
    }

    typename TGMatrix<T_element>::MatrixIterationCallback kernel =
      boost::bind(matrixVectorMultKernel, _1, boost::ref(vector), boost::ref(result), _2, _3, _4, e.isTranspose());

    matrix.iterateSparse(generator, kernel);
  }

  virtual void visit(TGMatrixMultiVectorMult<T_element>& e)
  {
    using namespace tg;

    TGMatrix<T_element>& matrix(e.getMatrix().getInternal());
    std::vector< boost::tuple<TGVector<T_element>*, TGVector<T_element>*, bool> > vectors;

    const std::size_t numVectors = e.getNumVectors();

    for(std::size_t index = 0; index < numVectors; ++index)
      vectors.push_back(boost::make_tuple(&e.getVector(index).getInternal(),
                        boost::get<TGVector<T_element>*>(e.getInternal(index)),
                        e.isTranspose(index)));

    tVarNamed(unsigned, i, getIndexName().c_str());
    tVarNamed(unsigned, j, getIndexName().c_str());

    tFor(i, 0u, matrix.getRows()-1)
    {
      for(std::size_t index = 0; index < numVectors; ++index)
        boost::get<1>(vectors[index])->setExpression(i, TGScalarExpr<T_element>());
    }

    typename TGMatrix<T_element>::MatrixIterationCallback kernel =
      boost::bind(matrixMultiVectorMultKernel, _1, boost::ref(vectors), _2, _3, _4);

    matrix.iterateSparse(generator, kernel);
  }

  virtual void visit(TGVectorDot<T_element>& e)
  {
    using namespace tg;

    TGScalar<T_element>& result(e.getInternal());
    TGVector<T_element>& left(e.getLeft().getInternal());
    TGVector<T_element>& right(e.getRight().getInternal());

    tVarNamed(unsigned, i, getIndexName().c_str());

    result.setExpression(TGScalarExpr<T_element>());
    tFor(i, 0u, left.getRows()-1)
    {
      result.addExpression(left.getExpression(i).mul(right.getExpression(i)));
    }
  }

  virtual void visit(TGVectorCross<T_element>& e)
  {
    using namespace tg;

    TGVector<T_element>& result(e.getInternal());
    TGVector<T_element>& left(e.getLeft().getInternal());
    TGVector<T_element>& right(e.getRight().getInternal());

    result.setExpression(TaskExpression(0), left.getExpression(1).mul(right.getExpression(2)).sub(right.getExpression(1).mul(left.getExpression(2))));
    result.setExpression(TaskExpression(1), left.getExpression(2).mul(right.getExpression(0)).sub(right.getExpression(2).mul(left.getExpression(0))));
    result.setExpression(TaskExpression(2), left.getExpression(0).mul(right.getExpression(1)).sub(right.getExpression(0).mul(left.getExpression(1))));
  }

  virtual void visit(TGVectorTwoNorm<T_element>& e)
  {
    using namespace tg;

    TGScalar<T_element>& result(e.getInternal());
    TGVector<T_element>& vector(e.getOperand().getInternal());

    tVarNamed(unsigned, i, getIndexName().c_str());

    result.setExpression(TGScalarExpr<T_element>());
    tFor(i, 0u, vector.getRows()-1)
    {
      result.addExpression(vector.getExpression(i).mul(vector.getExpression(i)));
    }
    result.setExpression(result.getExpression().sqrt());
  }

  virtual void visit(TGMatrixTranspose<T_element>& e)
  {
    using namespace tg;

    TGMatrix<T_element>& result(e.getInternal());
    TGMatrix<T_element>& matrix(e.getOperand().getInternal());

    tVarNamed(unsigned, i, getIndexName().c_str());
    tVarNamed(unsigned, j, getIndexName().c_str());

    tFor(i, 0u, matrix.getRows()-1)
    {
      tFor(j, 0u, matrix.getCols()-1)
      {
        result.setExpression(generator, j, i, matrix.getExpression(generator, i, j));
      }
    }
  }

  virtual void visit(TGPairwise<tg_scalar, T_element>& e)
  {
    using namespace tg;

    TGScalar<T_element>& result(e.getInternal());
    TGScalar<T_element>& left(e.getLeft().getInternal());
    TGScalar<T_element>& right(e.getRight().getInternal());

    result.setExpression(performOp(e.getOperation(), left.getExpression(), right.getExpression()));
  }

  virtual void visit(TGPairwise<tg_vector, T_element>& e)
  {
     using namespace tg;

     TGVector<T_element>& result(e.getInternal());
     TGVector<T_element>& left(e.getLeft().getInternal());
     TGVector<T_element>& right(e.getRight().getInternal());

     tVarNamed(unsigned, i, getIndexName().c_str());
     tFor(i, 0u, result.getRows()-1)
     {
       result.setExpression(i, performOp(e.getOperation(), left.getExpression(i), right.getExpression(i)));
     }
  }

  virtual void visit(TGPairwise<tg_matrix, T_element>& e)
  {
    using namespace tg;

    TGMatrix<T_element>& result(e.getInternal());
    TGMatrix<T_element>& left(e.getLeft().getInternal());
    TGMatrix<T_element>& right(e.getRight().getInternal());

    tVarNamed(unsigned, i, getIndexName().c_str());
    tVarNamed(unsigned, j, getIndexName().c_str());
    tFor(i, 0u, result.getRows()-1)
    {
      tFor(j, 0u, result.getCols()-1)
      {
        result.setExpression(generator, i, j, performOp(e.getOperation(), left.getExpression(generator, i, j), right.getExpression(generator, i, j)));
      }
    }
  }

  virtual void visit(TGScalarPiecewise<tg_scalar, T_element>& e)
  {
    using namespace tg;

    TGScalar<T_element>& result(e.getInternal());
    TGScalar<T_element>& left(e.getLeft().getInternal());
    TGScalar<T_element>& right(e.getRight().getInternal());

    result.setExpression(performOp(e.getOperation(), left.getExpression(), right.getExpression()));
  }

  virtual void visit(TGScalarPiecewise<tg_vector, T_element>& e)
  {
     using namespace tg;

     TGVector<T_element>& result(e.getInternal());
     TGVector<T_element>& left(e.getLeft().getInternal());
     TGScalar<T_element>& right(e.getRight().getInternal());

     tVarNamed(unsigned, i, getIndexName().c_str());
     tFor(i, 0u, result.getRows()-1)
     {
       result.setExpression(i, performOp(e.getOperation(), left.getExpression(i), right.getExpression()));
     }
  }

  virtual void visit(TGScalarPiecewise<tg_matrix, T_element>& e)
  {
    using namespace tg;

    TGMatrix<T_element>& result(e.getInternal());
    TGMatrix<T_element>& left(e.getLeft().getInternal());
    TGScalar<T_element>& right(e.getRight().getInternal());

    tVarNamed(unsigned, i, getIndexName().c_str());
    tVarNamed(unsigned, j, getIndexName().c_str());
    tFor(i, 0u, result.getRows()-1)
    {
      tFor(j, 0u, result.getCols()-1)
      {
        result.setExpression(generator, i, j, performOp(e.getOperation(), left.getExpression(generator, i, j), right.getExpression()));
      }
    }
  }

  virtual void visit(TGNegate<tg_scalar, T_element>& e)
  {
    using namespace tg;

    TGScalar<T_element>& result(e.getInternal());
    TGScalar<T_element>& operand(e.getOperand().getInternal());

    result.setExpression(operand.getExpression().negate());
  }

  virtual void visit(TGNegate<tg_vector, T_element>& e)
  {
    using namespace tg;

    TGVector<T_element>& result(e.getInternal());
    TGVector<T_element>& operand(e.getOperand().getInternal());

    tVarNamed(unsigned, i, getIndexName().c_str());
    tFor(i, 0u, result.getRows()-1)
    {
      result.setExpression(i, operand.getExpression(i).negate());
    }
  }

  virtual void visit(TGNegate<tg_matrix, T_element>& e)
  {
    using namespace tg;

    TGMatrix<T_element>& result(e.getInternal());
    TGMatrix<T_element>& operand(e.getOperand().getInternal());

    tVarNamed(unsigned, i, getIndexName().c_str());
    tVarNamed(unsigned, j, getIndexName().c_str());
    tFor(i, 0u, result.getRows()-1)
    {
      tFor(j, 0u, result.getCols()-1)
      {
        result.setExpression(generator, i, j, operand.getExpression(generator, i, j).negate());
      }
    }
  }

  virtual void visit(TGAbsolute<T_element>& e)
  {
    using namespace tg;

    TGScalar<T_element>& result(e.getInternal());
    TGScalar<T_element>& operand(e.getOperand().getInternal());

    result.setExpression(operand.getExpression().abs());
  }

  virtual void visit(TGSquareRoot<T_element>& e)
  {
    using namespace tg;

    TGScalar<T_element>& result(e.getInternal());
    TGScalar<T_element>& operand(e.getOperand().getInternal());

    result.setExpression(operand.getExpression().sqrt());
  }

private:
  static inline TGScalarExpr<T_element> performOp(const TGPairwiseOp op, const TGScalarExpr<T_element>& left, const TGScalarExpr<T_element>& right)
  {
    switch(op)
    {
      case tg_pair_add: return left.add(right);
      case tg_pair_sub: return left.sub(right);
      case tg_pair_mul: return left.mul(right);
      case tg_pair_div: return left.div(right);
      default: throw TGInvalidOperationError("Unrecognised TaskGraph Evaluator Pairwise Operation");
    }
  }

  static inline TGScalarExpr<T_element> performOp(const TGScalarPiecewiseOp op, const TGScalarExpr<T_element>& left, const TGScalarExpr<T_element>& right)
  {
    switch(op)
    {
      case tg_piecewise_multiply: return left.mul(right);
      case tg_piecewise_divide: return left.div(right);
      case tg_piecewise_assign: return right;
      default: throw TGInvalidOperationError("Unrecognised TaskGraph Evaluator ScalarPiecewise Operation");
    }
  }
};

}

}
#endif


// Copyright (C) 2009-2011 Anders Logg
//
// This file is part of DOLFIN.
//
// DOLFIN is free software: you can 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 3 of the License, or
// (at your option) any later version.
//
// DOLFIN 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
//
// Modified by Marie E. Rognes, 2010
// Modified by Garth N. Wells, 2010
//
// First added:  2009-12-08
// Last changed: 2011-11-12
//

#include <vector>
#include <armadillo>

#include <dolfin/common/Array.h>
#include <dolfin/common/Timer.h>
#include <dolfin/fem/BasisFunction.h>
#include <dolfin/fem/DofMap.h>
#include <dolfin/fem/DirichletBC.h>
#include <dolfin/function/Function.h>
#include <dolfin/function/FunctionSpace.h>
#include <dolfin/la/GenericVector.h>
#include <dolfin/log/log.h>
#include <dolfin/mesh/BoundaryMesh.h>
#include <dolfin/mesh/Cell.h>
#include <dolfin/mesh/FacetCell.h>
#include "Extrapolation.h"

using namespace dolfin;

//-----------------------------------------------------------------------------
void Extrapolation::extrapolate(Function& w, const Function& v)
{
  // Using set_local for simplicity here
  not_working_in_parallel("Extrapolation");

  // Too verbose
  //info("Extrapolating function: %s --> %s",
  //     v.function_space().element().signature().c_str(),
  //     w.function_space().element().signature().c_str());

  // Check that the meshes are the same
  if (w.function_space()->mesh() != v.function_space()->mesh())
  {
    dolfin_error("Extrapolation.cpp",
                 "compute extrapolation",
                 "Extrapolation must be computed on the same mesh");
  }

  // Extract mesh and function spaces
  const FunctionSpace& V = *v.function_space();
  const FunctionSpace& W = *w.function_space();
  dolfin_assert(V.mesh());
  const Mesh& mesh = *V.mesh();

  // Initialize cell-cell connectivity
  const uint D = mesh.topology().dim();
  mesh.init(D, D);

  // UFC cell view of center cell
  UFCCell c0(mesh);

  // List of values for each dof of w (multivalued until we average)
  std::vector<std::vector<double> > coefficients;
  coefficients.resize(W.dim());

  // Local array for dof indices
  dolfin_assert(W.dofmap());
  std::vector<uint> dofs(W.dofmap()->max_cell_dimension());

  // Iterate over cells in mesh
  for (CellIterator cell0(mesh); !cell0.end(); ++cell0)
  {
    // Update UFC view
    c0.update(*cell0);

    // Tabulate dofs for w on cell and store values
    W.dofmap()->tabulate_dofs(&dofs[0], *cell0);

    // Compute coefficients on this cell
    uint offset = 0;
    compute_coefficients(coefficients, v, V, W, *cell0, c0, dofs, offset);
  }

  // Average coefficients
  average_coefficients(w, coefficients);
}
//-----------------------------------------------------------------------------
void Extrapolation::compute_coefficients(std::vector<std::vector<double> >& coefficients,
                                         const Function& v,
                                         const FunctionSpace& V,
                                         const FunctionSpace& W,
                                         const Cell& cell0,
                                         const ufc::cell& c0,
                                         const std::vector<uint>& dofs,
                                         uint& offset)
{
  // Call recursively for mixed elements
  dolfin_assert(V.element());
  const uint num_sub_spaces = V.element()->num_sub_elements();
  if (num_sub_spaces > 0)
  {
    for (uint k = 0; k < num_sub_spaces; k++)
    {
      compute_coefficients(coefficients, v[k], *V[k], *W[k], cell0, c0,
                           dofs, offset);
    }
    return;
  }

  // Build data structures for keeping track of unique dofs
  std::map<uint, std::map<uint, uint> > cell2dof2row;
  std::set<uint> unique_dofs;
  build_unique_dofs(unique_dofs, cell2dof2row, cell0, c0, V);

  // Compute size of linear system
  dolfin_assert(W.element());
  const uint N = W.element()->space_dimension();
  const uint M = unique_dofs.size();

  // Check size of system
  if (M < N)
  {
    dolfin_error("Extrapolation.cpp",
                 "compute extrapolation",
                 "Not enough degrees of freedom on local patch to build extrapolation");
  }

  // Create matrix and vector for linear system
  arma::mat A(M, N);
  arma::vec b(M);

  // Add equations on cell and neighboring cells
  add_cell_equations(A, b, cell0, cell0, c0, c0, V, W, v, cell2dof2row[cell0.index()]);
  dolfin_assert(V.mesh());
  UFCCell c1(*V.mesh());
  for (CellIterator cell1(cell0); !cell1.end(); ++cell1)
  {
    if (cell2dof2row[cell1->index()].size() == 0)
      continue;

    c1.update(*cell1);
    add_cell_equations(A, b, cell0, *cell1, c0, c1, V, W, v, cell2dof2row[cell1->index()]);
  }

  // Solve least squares system
  arma::Col<double> x = arma::solve(A, b);

  // Insert resulting coefficients into global coefficient vector
  dolfin_assert(W.dofmap());
  for (uint i = 0; i < W.dofmap()->cell_dimension(cell0.index()); ++i)
    coefficients[dofs[i + offset]].push_back(x[i]);

  // Increase offset
  offset += W.dofmap()->cell_dimension(cell0.index());
}
//-----------------------------------------------------------------------------
void Extrapolation::build_unique_dofs(std::set<uint>& unique_dofs,
                                      std::map<uint, std::map<uint, uint> >& cell2dof2row,
                                      const Cell& cell0,
                                      const ufc::cell& c0,
                                      const FunctionSpace& V)
{
  // Counter for matrix row index
  uint row = 0;
  dolfin_assert(V.mesh());
  UFCCell c1(*V.mesh());

  // Compute unique dofs on center cell
  cell2dof2row[cell0.index()] = compute_unique_dofs(cell0, c0, V, row, unique_dofs);

  // Compute unique dofs on neighbouring cells
  for (CellIterator cell1(cell0); !cell1.end(); ++cell1)
  {
    c1.update(*cell1);
    cell2dof2row[cell1->index()] = compute_unique_dofs(*cell1, c1, V, row, unique_dofs);
  }
}
//-----------------------------------------------------------------------------
void Extrapolation::add_cell_equations(arma::Mat<double>& A,
                                       arma::Col<double>& b,
                                       const Cell& cell0,
                                       const Cell& cell1,
                                       const ufc::cell& c0,
                                       const ufc::cell& c1,
                                       const FunctionSpace& V,
                                       const FunctionSpace& W,
                                       const Function& v,
                                       std::map<uint, uint>& dof2row)
{
  // Extract coefficents for v on patch cell
  dolfin_assert(V.element());
  std::vector<double> dof_values(V.element()->space_dimension());
  v.restrict(&dof_values[0], *V.element(), cell1, c1);

  // Iterate over given local dofs for V on patch cell
  dolfin_assert(W.element());
  for (std::map<uint, uint>::iterator it = dof2row.begin(); it!= dof2row.end(); it++)
  {
    const uint i = it->first;
    const uint row = it->second;

    // Iterate over basis functions for W on center cell
    for (uint j = 0; j < W.element()->space_dimension(); ++j)
    {

      // Create basis function
      const BasisFunction phi(j, *W.element(), c0);

      // Evaluate dof on basis function
      const double dof_value = V.element()->evaluate_dof(i, phi, c1);

      // Insert dof_value into matrix
      A(row, j) = dof_value;
    }

    // Insert coefficient into vector
    b[row] = dof_values[i];
  }
}
//-----------------------------------------------------------------------------
std::map<dolfin::uint, dolfin::uint>
Extrapolation::compute_unique_dofs(const Cell& cell, const ufc::cell& c,
                                   const FunctionSpace& V,
                                   uint& row,
                                   std::set<uint>& unique_dofs)
{
  dolfin_assert(V.dofmap());

  std::vector<uint> dofs(V.dofmap()->cell_dimension(cell.index()));
  V.dofmap()->tabulate_dofs(&dofs[0], cell);

  // Data structure for current cell
  std::map<uint, uint> dof2row;

  for (uint i = 0; i < V.dofmap()->cell_dimension(cell.index()); ++i)
  {
    // Ignore if this degree of freedom is already considered
    if (unique_dofs.find(dofs[i]) != unique_dofs.end())
      continue;

    // Put global index into unique_dofs
    unique_dofs.insert(dofs[i]);

    // Map local dof index to current matrix row-index
    dof2row[i] = row;

    // Increase row index
    row++;
  }

  return dof2row;
}
//-----------------------------------------------------------------------------
void Extrapolation::average_coefficients(Function& w,
                               std::vector<std::vector<double> >& coefficients)
{
  const FunctionSpace& W = *w.function_space();
  Array<double> dof_values(W.dim());

  for (uint i = 0; i < W.dim(); i++)
  {
    double s = 0.0;
    for (uint j = 0; j < coefficients[i].size(); ++j)
      s += coefficients[i][j];
    s /= static_cast<double>(coefficients[i].size());
    dof_values[i] = s;
  }

  // Update dofs for w
  dolfin_assert(w.vector());
  w.vector()->set_local(dof_values);
}
//-----------------------------------------------------------------------------


// This is an advanced implementation of the algorithm described in the
// following paper:
//    C. Hertzberg,  R.  Wagner,  U.  Frese,  and  L.  Schroder.  Integratinggeneric   sensor   fusion   algorithms with
//    sound   state   representationsthrough  encapsulation  of  manifolds. CoRR,  vol.  abs/1107.1119,  2011.[Online].
//    Available: http://arxiv.org/abs/1107.1119

/*
 *  Copyright (c) 2019--2023, The University of Hong Kong
 *  All rights reserved.
 *
 *  Modifier: Dongjiao HE <hdj65822@connect.hku.hk>
 *
 *  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 Universitaet Bremen 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 OWNER 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.
 */

/*
 *  Copyright (c) 2008--2011, Universitaet Bremen
 *  All rights reserved.
 *
 *  Author: Christoph Hertzberg <chtz@informatik.uni-bremen.de>
 *
 *  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 Universitaet Bremen 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 OWNER 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.
 */
/**
 * @file mtk/build_manifold.hpp
 * @brief Macro to automatically construct compound manifolds.
 *
 */
#ifndef MTK_AUTOCONSTRUCT_HPP_
#define MTK_AUTOCONSTRUCT_HPP_

#include <vector>

#include <Eigen/Core>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/seq.hpp>

#include "src/SubManifold.hpp"
#include "startIdx.hpp"

#ifndef PARSED_BY_DOXYGEN
//////// internals //////

#define MTK_APPLY_MACRO_ON_TUPLE(r, macro, tuple) macro tuple

#define MTK_TRANSFORM_COMMA(macro, entries) \
    BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM_S(1, MTK_APPLY_MACRO_ON_TUPLE, macro, entries))

#define MTK_TRANSFORM(macro, entries) BOOST_PP_SEQ_FOR_EACH_R(1, MTK_APPLY_MACRO_ON_TUPLE, macro, entries)

#define MTK_CONSTRUCTOR_ARG(type, id) const type& id = type()
#define MTK_CONSTRUCTOR_COPY(type, id) id(id)
#define MTK_BOXPLUS(type, id) id.boxplus(MTK::subvector(__vec, &self::id), __scale);
#define MTK_OPLUS(type, id) id.oplus(MTK::subvector_(__vec, &self::id), __scale);
#define MTK_BOXMINUS(type, id) id.boxminus(MTK::subvector(__res, &self::id), __oth.id);
#define MTK_S2_hat(type, id) \
    if (id.IDX == idx) {     \
        id.S2_hat(res);      \
    }
#define MTK_S2_Nx_yy(type, id) \
    if (id.IDX == idx) {       \
        id.S2_Nx_yy(res);      \
    }
#define MTK_S2_Mx(type, id) \
    if (id.IDX == idx) {    \
        id.S2_Mx(res, dx);  \
    }
#define MTK_OSTREAM(type, id) << __var.id << " "
#define MTK_ISTREAM(type, id) >> __var.id
#define MTK_S2_state(type, id)                              \
    if (id.TYP == 1) {                                      \
        S2_state.push_back(std::make_pair(id.IDX, id.DIM)); \
    }
#define MTK_SO3_state(type, id)                                \
    if (id.TYP == 2) {                                         \
        (SO3_state).push_back(std::make_pair(id.IDX, id.DIM)); \
    }
#define MTK_vect_state(type, id)                                                           \
    if (id.TYP == 0) {                                                                     \
        (vect_state).push_back(std::make_pair(std::make_pair(id.IDX, id.DIM), type::DOF)); \
    }

#define MTK_SUBVARLIST(seq, S2state, SO3state)                                                                \
    BOOST_PP_FOR_1(                                                                                \
        (BOOST_PP_SEQ_SIZE(seq), BOOST_PP_SEQ_HEAD(seq), BOOST_PP_SEQ_TAIL(seq)(~), 0, 0, S2state, SO3state), \
        MTK_ENTRIES_TEST, MTK_ENTRIES_NEXT, MTK_ENTRIES_OUTPUT)

#define MTK_PUT_TYPE(type, id, dof, dim, S2state, SO3state) MTK::SubManifold<type, dof, dim> id;
#define MTK_PUT_TYPE_AND_ENUM(type, id, dof, dim, S2state, SO3state) \
    MTK_PUT_TYPE(type, id, dof, dim, S2state, SO3state)              \
    enum { DOF = type::DOF + dof };                                  \
    enum { DIM = type::DIM + dim };                                  \
    typedef type::scalar scalar;

#define MTK_ENTRIES_OUTPUT(r, state) MTK_ENTRIES_OUTPUT_I state
#define MTK_ENTRIES_OUTPUT_I(s, head, seq, dof, dim, S2state, SO3state)                            \
    MTK_APPLY_MACRO_ON_TUPLE(~, BOOST_PP_IF(BOOST_PP_DEC(s), MTK_PUT_TYPE, MTK_PUT_TYPE_AND_ENUM), \
                             (BOOST_PP_TUPLE_REM_2 head, dof, dim, S2state, SO3state))

#define MTK_ENTRIES_TEST(r, state) MTK_TUPLE_ELEM_4_0 state

//! this used to be BOOST_PP_TUPLE_ELEM_4_0:
#define MTK_TUPLE_ELEM_4_0(a, b, c, d, e, f, g) a

#define MTK_ENTRIES_NEXT(r, state) MTK_ENTRIES_NEXT_I state
#define MTK_ENTRIES_NEXT_I(len, head, seq, dof, dim, S2state, SO3state)                                          \
    (BOOST_PP_DEC(len), BOOST_PP_SEQ_HEAD(seq), BOOST_PP_SEQ_TAIL(seq), dof + BOOST_PP_TUPLE_ELEM_2_0 head::DOF, \
     dim + BOOST_PP_TUPLE_ELEM_2_0 head::DIM, S2state, SO3state)

#endif /* not PARSED_BY_DOXYGEN */

/**
 * Construct a manifold.
 * @param name is the class-name of the manifold,
 * @param entries is the list of sub manifolds
 *
 * Entries must be given in a list like this:
 * @code
 * typedef MTK::trafo<MTK::SO3<double> > Pose;
 * typedef MTK::vect<double, 3> Vec3;
 * MTK_BUILD_MANIFOLD(imu_state,
 *    ((Pose, pose))
 *    ((Vec3, vel))
 *    ((Vec3, acc_bias))
 * )
 * @endcode
 * Whitespace is optional, but the double parentheses are necessary.
 * Construction is done entirely in preprocessor.
 * After construction @a name is also a manifold. Its members can be
 * accessed by names given in @a entries.
 *
 * @note Variable types are not allowed to have commas, thus types like
 *       @c vect<double, 3> need to be typedef'ed ahead.
 */
#define MTK_BUILD_MANIFOLD(name, entries)                                                                 \
    struct name {                                                                                \
        typedef name self;                                                                                \
        std::vector<std::pair<int, int> > S2_state;                                                       \
        std::vector<std::pair<int, int> > SO3_state;                                                      \
        std::vector<std::pair<std::pair<int, int>, int> > vect_state;                                     \
        MTK_SUBVARLIST(entries, S2_state, SO3_state)                                                      \
        name(MTK_TRANSFORM_COMMA(MTK_CONSTRUCTOR_ARG, entries))                                           \
            : MTK_TRANSFORM_COMMA(MTK_CONSTRUCTOR_COPY, entries) {}                                       \
        int getDOF() const { return DOF; }                                                                \
        void boxplus(const MTK::vectview<const scalar, DOF>& __vec, scalar __scale = 1) {                 \
            MTK_TRANSFORM(MTK_BOXPLUS, entries)                                                           \
        }                                                                                \
        void oplus(const MTK::vectview<const scalar, DIM>& __vec, scalar __scale = 1) {                   \
            MTK_TRANSFORM(MTK_OPLUS, entries)                                                             \
        }                                                                                \
        void boxminus(MTK::vectview<scalar, DOF> __res, const name& __oth) const {                        \
            MTK_TRANSFORM(MTK_BOXMINUS, entries)                                                          \
        }                                                                                \
        friend std::ostream& operator<<(std::ostream& __os, const name& __var) {                          \
            return __os MTK_TRANSFORM(MTK_OSTREAM, entries);                                              \
        }                                                                                \
        void build_S2_state() { MTK_TRANSFORM(MTK_S2_state, entries) }                                    \
        void build_vect_state() { MTK_TRANSFORM(MTK_vect_state, entries) }                                \
        void build_SO3_state() { MTK_TRANSFORM(MTK_SO3_state, entries) }                                  \
        void S2_hat(Eigen::Matrix<scalar, 3, 3>& res, int idx) { MTK_TRANSFORM(MTK_S2_hat, entries) }     \
        void S2_Nx_yy(Eigen::Matrix<scalar, 2, 3>& res, int idx) { MTK_TRANSFORM(MTK_S2_Nx_yy, entries) } \
        void S2_Mx(Eigen::Matrix<scalar, 3, 2>& res, Eigen::Matrix<scalar, 2, 1> dx, int idx) {           \
            MTK_TRANSFORM(MTK_S2_Mx, entries)                                                             \
        }                                                                                \
        friend std::istream& operator>>(std::istream& __is, name& __var) {                                \
            return __is MTK_TRANSFORM(MTK_ISTREAM, entries);                                              \
        }                                                                                \
    };

#endif /*MTK_AUTOCONSTRUCT_HPP_*/