//==================================================================================================
/*!
  @file

  @copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_CONSTANT_DEFINITION_INVEPS_HPP_INCLUDED
#define BOOST_SIMD_CONSTANT_DEFINITION_INVEPS_HPP_INCLUDED

#include <boost/simd/config.hpp>
#include <boost/simd/detail/nsm.hpp>
#include <boost/simd/detail/dispatch.hpp>
#include <boost/simd/detail/constant_traits.hpp>
#include <boost/simd/detail/dispatch/function/make_callable.hpp>
#include <boost/simd/detail/dispatch/hierarchy/functions.hpp>
#include <boost/simd/detail/dispatch/as.hpp>

namespace boost { namespace simd
{
  namespace tag
  {
    struct inveps_ : boost::dispatch::constant_value_<inveps_>
    {
      BOOST_DISPATCH_MAKE_CALLABLE(ext,inveps_,boost::dispatch::constant_value_<inveps_>);
      BOOST_SIMD_REGISTER_CONSTANT(1, 0x4b000000UL, 0x4330000000000000ULL);
    };
  }

  namespace ext
  {
    BOOST_DISPATCH_FUNCTION_DECLARATION(tag,inveps_)
  }

  namespace detail
  {
    BOOST_DISPATCH_CALLABLE_DEFINITION(tag::inveps_,inveps);
  }

  template<typename T> BOOST_FORCEINLINE auto Inveps()
  BOOST_NOEXCEPT_DECLTYPE(detail::inveps( boost::dispatch::as_<T>{}))
  {
    return detail::inveps( boost::dispatch::as_<T>{} );
  }

  template<typename T> BOOST_FORCEINLINE
  auto Inveps(boost::dispatch::as_<T> const&) BOOST_NOEXCEPT_DECLTYPE(Inveps<T>())
  {
    return Inveps<T>();
  }
} }

#endif


#include <boost/accumulators/statistics/p_square_cumul_dist.hpp>


#define EIGEN_RUNTIME_NO_MALLOC
#include <Eigen/Core>
#include <catch2/catch.hpp>
#include "ear/dsp/ptr_adapter.hpp"
#include "ear/dsp/variable_block_size.hpp"

using namespace ear;
using namespace ear::dsp;

TEST_CASE("test") {
  // dummy process function and parameters
  Eigen::Index inner_block_size = 512;
  Eigen::Index channels_in = 2;
  Eigen::Index channels_out = 4;

  auto do_process = [&](size_t block_size, const float *const *in,
                        float *const *out) {
    for (size_t i = 0; i < block_size; i++) {
      out[0][i] = in[0][i] * 2.0f;
      out[1][i] = in[1][i] * 3.0f;
      out[2][i] = in[0][i] * 4.0f;
      out[3][i] = in[1][i] * 5.0f;
    }
  };

  auto process = [&](const float *const *in, float *const *out) {
    do_process(inner_block_size, in, out);
  };

  // wrap this in an adapter
  VariableBlockSizeAdapter adapter(inner_block_size, channels_in, channels_out,
                                   process);
  PtrAdapter input_ptrs(channels_in);
  PtrAdapter output_ptrs(channels_out);

  // the test structure
  Eigen::VectorXi block_sizes(5);
  block_sizes << 0, 512, 1024, 300, 500;
  int test_len = block_sizes.sum();

  // generate an input and the expected output, which is shifted by
  // inner_block_size
  Eigen::MatrixXf input = Eigen::MatrixXf::Random(test_len, channels_in);

  Eigen::MatrixXf expected_output =
      Eigen::MatrixXf::Zero(test_len, channels_out);
  input_ptrs.set_eigen(input);
  output_ptrs.set_eigen(expected_output, inner_block_size);
  do_process(test_len - inner_block_size, input_ptrs.ptrs(),
             output_ptrs.ptrs());

  // run with the defined block sizes
  Eigen::MatrixXf output = Eigen::MatrixXf::Random(test_len, channels_out);
  Eigen::Index offset = 0;
  for (int block_size : block_sizes) {
    Eigen::internal::set_is_malloc_allowed(false);
    input_ptrs.set_eigen(input, offset);
    output_ptrs.set_eigen(output, offset);
    adapter.process(block_size, input_ptrs.ptrs(), output_ptrs.ptrs());
    Eigen::internal::set_is_malloc_allowed(true);
    offset += block_size;
  }

  // check the output
  REQUIRE(expected_output == output);
}


/** \file UmfPackSolver.h */

#pragma once

#if defined HAVE_UMFPACK && defined MTL_HAS_UMFPACK

// std c++ headers
#include <iostream>

// MTL4 includes
#include <boost/numeric/mtl/operation/two_norm.hpp>
#include <boost/numeric/mtl/interface/umfpack_solve.hpp>

// AMDiS includes
#include "solver/LinearSolver.hpp"

namespace AMDiS
{
  template <class MatrixType, class VectorType>
  struct Umfpack_Runner : public RunnerBase<MatrixType, VectorType>
  {
    using Super = RunnerBase<MatrixType, VectorType>;
    using Self  = Umfpack_Runner<MatrixType, VectorType>;

    /// Constructor.
    /**
     * Reads parameters for solver with name 'NAME':
     *   NAME->store symbolic       \ref store_symbolic
     *   NAME->symmetric strategy   \ref symmetric_strategy
     *   NAME->alloc init           \ref alloc_init
     **/
    Umfpack_Runner(LinearSolverInterface* oem_)
      : oem(*oem_),
        solver(NULL),
        store_symbolic(0),
        symmetric_strategy(0),
        alloc_init(0.7)
    {
      Parameters::get(oem.getName() + "->store symbolic", store_symbolic); // ?
      Parameters::get(oem.getName() + "->symmetric strategy", symmetric_strategy);
      Parameters::get(oem.getName() + "->alloc init", alloc_init);
    }

    /// Destructor.
    ~Umfpack_Runner()
    {
      if (solver != NULL)
      {
        delete solver;
        solver = NULL;
      }
    }

    /// Implementation of \ref RunnerBase::init()
    virtual void init(SolverMatrix<Matrix<DOFMatrix*>> const& /*A*/,
                      MatrixType const& fullMatrix) override
    {
      namespace umfpack = mtl::matrix::umfpack;
      if (solver != NULL)
      {
        delete solver;
        solver = NULL;
      }

      try
      {
        solver = new umfpack::solver<MatrixType>(fullMatrix, symmetric_strategy,
						 alloc_init);
      }
      catch (umfpack::error& e)
      {
        ERROR_EXIT("UMFPACK_ERROR(factorize, %d) = %s\n", e.code, e.what());
      }
    }

    /// Implementation of \ref RunnerBase::solve()
    virtual int solve(MatrixType const& A, VectorType& x,
		      VectorType const& b) override
    {
      FUNCNAME("Umfpack_Runner::solve()");
      TEST_EXIT(solver != NULL)("The umfpack solver was not initialized\n");
      namespace umfpack = mtl::matrix::umfpack;

      int code = 0;
      try
      {
        code = (*solver)(x, b);
      }
      catch (umfpack::error& e)
      {
        ERROR_EXIT("UMFPACK_ERROR(solve, %d) = %s\n", e.code, e.what());
      }

      VectorType r(b);
      r -= A * x;
      double residual = two_norm(r);
      oem.setResidual(residual);
      oem.setErrorCode(code);

      return code;
    }

    /// Implementation of \ref RunnerInterface::adjoint_solve()
    virtual void exit() override {}

  public:
    LinearSolverInterface& oem;

  private:
    mtl::matrix::umfpack::solver<MatrixType>* solver;

    int store_symbolic;

    int symmetric_strategy;

    double alloc_init;
  };

  /**
   * \ingroup Solver
   * \class AMDiS::UmfPackSolver
   * \brief \implements LinearSolver
   * Wrapper for the external UMFPACK solver:
   *   http://www.cise.ufl.edu/research/sparse/umfpack/
   *
   * This is a direct solver for large sparse matrices.
   */
  using UmfPackSolver = LinearSolver< MTLTypes::MTLMatrix, MTLTypes::MTLVector,
				      Umfpack_Runner< MTLTypes::MTLMatrix,
						      MTLTypes::MTLVector > >;

}

#endif // HAVE_UMFPACK


#ifndef ILP_JANSEN_ROHWEDDER_HPP
#define ILP_JANSEN_ROHWEDDER_HPP

#include "ilp_task.hpp"
#include "digraph.hpp"

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/adj_list_serialize.hpp>

#include <unordered_set>

namespace ilp
{
    int compute_H(const matrix<int>& A);

    int compute_K(const ilp_task& ilpTask);

    struct DynamicTable
    {
        explicit DynamicTable(const ilp_task& ilpTask);

        struct Path
        {
            Path() = default;
            Path(cvector<int> x, int distance) : x{std::move(x)}, distance{distance} { };

            cvector<int> x;
            int distance = std::numeric_limits<int>::min();
        };

        using PathsBlock = std::unordered_map<cvector<int>, Path, detail::VectorHash<int>>;

        struct Entry
        {
            PathsBlock paths_from;
        };

        using EntriesBlock = std::unordered_map<cvector<int>, Entry, detail::VectorHash<int>>;

        template <typename PathType>
        bool upd_from(int entry_index,
                      const cvector<int>& from,
                      const cvector<int>& to,
                      PathType&& new_path)
        {
            bool has_updated = false;

            auto& current_path = data[entry_index][from].paths_from[to];
            if (current_path.distance < new_path.distance)
            {
                has_updated = true;
                current_path = std::forward<PathType>(new_path);
            }

            return has_updated;
        }

        [[nodiscard]] bool entry_condition(const cvector<int>& p,
                                           int entry_index) const;

        [[nodiscard]] bool bound_condition(const cvector<int>& p,
                                           int entry_index) const;

        void populate_entry_from(int entry_index, const cvector<int>& from);

        void populate();

        template <typename PointType>
        bool add_entry_point(PointType&& p, int entry_index)
        {
            bool has_inserted = false;
            auto p_it = this->data[entry_index].find(p);
            if (p_it == this->data[entry_index].end())
            {
                this->data[entry_index][std::forward<PointType>(p)] = Entry{ };
                has_inserted = true;
            }

            return has_inserted;
        }

    public:
        const ilp_task& ilpTask;

        std::vector<EntriesBlock> data;
        std::vector<cvector<int>> b_cuts;
        std::vector<double> bounds;
        int K;
        int H;
    };

    ilp_solution jansen_rohwedder(const ilp_task& ilpTask);

} // namespace ilp

#endif // ILP_JANSEN_ROHWEDDER_HPP


#ifndef ALGORITHM_Distance_Map_HPP_VG3U4S7CO1
#define ALGORITHM_Distance_Map_HPP_VG3U4S7CO1
#include <stdlib.h>
#include <fstream>
#include <iomanip>
#include <log4cxx/logger.h>
#include <log4cxx/basicconfigurator.h>
#include <log4cxx/helpers/exception.h>
#include <boost/foreach.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/unordered_map.hpp>
#include <boost/math/special_functions/round.hpp>
#include <boost/multi_array.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <Eigen/Core>
#include <Eigen/Eigen>
#include <Eigen/Geometry>

//namespace std {using namespace __gnu_cxx;}
using namespace std;

namespace prhlt {
    class Algorithm_Distance_Map{
        public:
            Algorithm_Distance_Map(cv::Mat &ex_image);
            ~Algorithm_Distance_Map();
            void run(int ex_curvature_ratio, int ex_threshold);
            void run(int ex_curvature_ratio, cv::Mat ex_frontier_mat);
            cv::Mat get_distance_matrix_as_greyscale_image();
            Eigen::MatrixXd get_distance_matrix();
            void save_distance_matrix_to_file(string ex_filename);
            void save_image_matrix_to_file(string ex_filename);
        protected:
          float min_change;
          float scale;
          cv::Mat image;
          log4cxx::LoggerPtr logger;
          int curvature_ratio;
          Eigen::MatrixXd distance_matrix;
          void initialize_border();
          bool distance_matrix_changed_flag;
          void activate_distance_matrix_changed_flag();
          void reset_distance_matrix_changed_flag();
          void calculate_distance_map();
          void initialize_distance_matrix(int threshold);
          void initialize_distance_matrix(cv::Mat ex_frontier_mat);
          void forward_raster_sequence();
          void update_value_forward(int r, int c);
          void backward_raster_sequence();
          void update_value_backward(int r, int c);
          float get_minimum_for_update(int r1, int c1, int r2, int c2);
          virtual float calculate_neighbour_value(int r1, int c1, int r2, int c2);
          bool solution_converged();
    };
}

#endif /* end of include guard*/


//
// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed
// under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#include "boost/mpl/int.hpp"
#include "boost/mpl/fold.hpp"
#include "boost/mpl/prior.hpp"
#include "boost/mpl/count.hpp"
#include "boost/mpl/insert.hpp"
#include <boost/mpl/greater.hpp>
#include <boost/mpl/for_each.hpp>
#include <boost/mpl/filter_view.hpp>
#include "boost/mpl/vector/vector20.hpp"
#include "boost/assert.hpp"
#include <boost/type_traits/is_same.hpp>

#include <vector>
#include <ctime>
#include <iostream>

#if defined(BOOST_DINKUMWARE_STDLIB) && BOOST_DINKUMWARE_STDLIB < 310
namespace std { using ::clock_t; }
#endif

namespace mpl = boost::mpl;
using namespace mpl::placeholders;

// A metafunction that returns the Event associated with a transition.
template <class Transition>
struct transition_event
{
    typedef typename Transition::event type;
};

// A metafunction computing the maximum of a transition's source and
// end states.
template <class Transition>
struct transition_max_state
{
    typedef typename mpl::int_<
        (Transition::current_state > Transition::next_state)
            ? Transition::current_state
            : Transition::next_state
    > type;
};

template<class Derived>
class state_machine;

// Generates a singleton runtime lookup table that maps current state
// to a function that makes the FSM take its transition on the given
// Event type.
template <class Fsm, int initial_state, class Stt, class Event>
struct dispatch_table
{
 private:
    // This is a table of these function pointers.
    typedef int (*cell)(Fsm&, int, Event const&);

    // Compute the maximum state value in the Fsm so we know how big
    // to make the table
    BOOST_STATIC_CONSTANT(
        int, max_state = (
            mpl::fold<Stt
              , mpl::int_<initial_state>
              , mpl::if_<
                    mpl::greater<transition_max_state<_2>,_1>
                  , transition_max_state<_2>
                  , _1
                >
            >::type::value
        )
    );

    // A function object for use with mpl::for_each that stuffs
    // transitions into cells.
    struct init_cell
    {
        init_cell(dispatch_table* self_)
          : self(self_)
        {}

        // Cell initializer function object, used with mpl::for_each
        template <class Transition>
        void operator()(Transition const&) const
        {
            self->entries[Transition::current_state] = &Transition::execute;
        }

        dispatch_table* self;
    };

 public:
    // initialize the dispatch table for a given Event and Fsm
    dispatch_table()
    {
        // Initialize cells for no transition
        for (int i = 0; i <= max_state; ++i)
        {
            // VC7.1 seems to need the two-phase assignment.
            cell call_no_transition = &state_machine<Fsm>::call_no_transition;
            entries[i] = call_no_transition;
        }

        // Go back and fill in cells for matching transitions.
        mpl::for_each<
            mpl::filter_view<
                Stt
              , boost::is_same<transition_event<_>, Event>
            >
        >(init_cell(this));
    }

    // The singleton instance.
    static const dispatch_table instance;

 public: // data members
    cell entries[max_state + 1];
};

// This declares the statically-initialized dispatch_table instance.
template <class Fsm, int initial_state, class Stt, class Event>
const dispatch_table<Fsm, initial_state, Stt, Event>
dispatch_table<Fsm, initial_state, Stt, Event>::instance;

// CRTP base class for state machines.  Pass the actual FSM class as
// the Derived parameter.
template<class Derived>
class state_machine
{
 public: // Member functions

    // Main function used by clients of the derived FSM to make
    // transitions.
    template<class Event>
    int process_event(Event const& evt)
    {
        typedef typename Derived::transition_table stt;
        typedef dispatch_table<Derived, Derived::initial_state,stt,Event> table;

        // Call the action
        return this->m_state
            = table::instance.entries[this->m_state](
                *static_cast<Derived*>(this), this->m_state, evt);
    }

    // Getter that returns the current state of the FSM
    int current_state() const
    {
        return this->m_state;
    }

 private:
    template <class Fsm, int initial_state, class Stt, class Event>
    friend class dispatch_table;

    template <class Event>
    static int call_no_transition(Derived& fsm, int state, Event const& e)
    {
        return fsm.no_transition(state, e);
    }

    // Default no-transition handler.  Can be replaced in the Derived
    // FSM class.
    template <class Event>
    int no_transition(int state, Event const& e)
    {
        BOOST_ASSERT(false);
        return state;
    }

 protected:    // interface for the derived class

    template<class State>
    state_machine(State state)                  // Construct with an initial state
        : m_state(state)
    {
    }

    state_machine()
      : m_state(Derived::initial_state)         // Construct with the default initial_state
    {
    }

    // Template used to form rows in the transition table
    template<
        int CurrentState
      , class Event
      , int NextState
      , void (Derived::*action)(Event const&)
    >
    struct row
    {
        BOOST_STATIC_CONSTANT(int, current_state = CurrentState);
        BOOST_STATIC_CONSTANT(int, next_state = NextState);
        typedef Event event;

        // Take the transition action and return the next state.
        static int execute(Derived& fsm, int state, Event const& evt)
        {
            BOOST_ASSERT(state == current_state);
            (fsm.*action)(evt);
            return next_state;
        }
    };

 private: // data members
    int m_state;
};

namespace  // Concrete FSM implementation
{
  // events
  struct play {};
  struct stop {};
  struct pause {};
  struct open_close {};

  // A "complicated" event type that carries some data.
  struct cd_detected
  {
      cd_detected(std::string name, std::vector<std::clock_t> durations)
        : name(name)
        , track_durations(durations)
      {}

      std::string name;
      std::vector<std::clock_t> track_durations;
  };

  // Concrete FSM implementation
  class player : public state_machine<player>
  {
      // The list of FSM states
      enum states {
          Empty, Open, Stopped, Playing, Paused
        , initial_state = Empty
      };

#ifdef __MWERKS__
   public: // Codewarrior bug workaround.  Tested at 0x3202
#endif
      // transition actions
      void start_playback(play const&)       { std::cout << "player::start_playback\n"; }
      void open_drawer(open_close const&)    { std::cout << "player::open_drawer\n"; }
      void close_drawer(open_close const&)   { std::cout << "player::close_drawer\n"; }
      void store_cd_info(cd_detected const&) { std::cout << "player::store_cd_info\n"; }
      void stop_playback(stop const&)        { std::cout << "player::stop_playback\n"; }
      void pause_playback(pause const&)      { std::cout << "player::pause_playback\n"; }
      void resume_playback(play const&)      { std::cout << "player::resume_playback\n"; }
      void stop_and_open(open_close const&)  { std::cout << "player::stop_and_open\n"; }
#ifdef __MWERKS__
   private:
#endif
      friend class state_machine<player>;
      typedef player p; // makes transition table cleaner

      // Transition table
      struct transition_table : mpl::vector11<
          //    Start     Event         Next      Action
          //  +---------+-------------+---------+---------------------+
          row < Stopped , play        , Playing , &p::start_playback  >,
          row < Stopped , open_close  , Open    , &p::open_drawer     >,
          //  +---------+-------------+---------+---------------------+
          row < Open    , open_close  , Empty   , &p::close_drawer    >,
          //  +---------+-------------+---------+---------------------+
          row < Empty   , open_close  , Open    , &p::open_drawer     >,
          row < Empty   , cd_detected , Stopped , &p::store_cd_info   >,
          //  +---------+-------------+---------+---------------------+
          row < Playing , stop        , Stopped , &p::stop_playback   >,
          row < Playing , pause       , Paused  , &p::pause_playback  >,
          row < Playing , open_close  , Open    , &p::stop_and_open   >,
          //  +---------+-------------+---------+---------------------+
          row < Paused  , play        , Playing , &p::resume_playback >,
          row < Paused  , stop        , Stopped , &p::stop_playback   >,
          row < Paused  , open_close  , Open    , &p::stop_and_open   >
          //  +---------+-------------+---------+---------------------+
      > {};

      // Replaces the default no-transition response.
      template <class Event>
      int no_transition(int state, Event const& e)
      {
          std::cout << "no transition from state " << state
                    << " on event " << typeid(e).name() << std::endl;
          return state;
      }
  };

  //
  // Testing utilities.
  //
  static char const* const state_names[] = { "Empty", "Open", "Stopped", "Playing", "Paused" };

  void pstate(player const& p)
  {
      std::cout << " -> " << state_names[p.current_state()] << std::endl;
  }

  void test()
  {
      player p;
      p.process_event(open_close()); pstate(p);
      p.process_event(open_close()); pstate(p);
      p.process_event(
          cd_detected(
              "louie, louie"
            , std::vector<std::clock_t>( /* track lengths */ )
          )
      );
      pstate(p);

      p.process_event(play());  pstate(p);
      p.process_event(pause()); pstate(p);
      p.process_event(play());  pstate(p);
      p.process_event(stop());  pstate(p);
  }
}

int main()
{
    test();
    return 0;
}


/// HEADER
#include "voxel_grid.h"

/// PROJECT
#include <csapex/model/node_modifier.h>
#include <csapex/msg/io.h>
#include <csapex/param/parameter_factory.h>
#include <csapex/utility/register_apex_plugin.h>
#include <csapex_point_cloud/msg/point_cloud_message.h>

/// SYSTEM
// clang-format off
#include <csapex/utility/suppress_warnings_start.h>
#include <boost/mpl/for_each.hpp>
#include <pcl/filters/voxel_grid.h>
#include <pcl_ros/transforms.h>
#include <csapex/utility/suppress_warnings_end.h>
// clang-format on

CSAPEX_REGISTER_CLASS(csapex::VoxelGrid, csapex::Node)

using namespace csapex;
using namespace csapex::connection_types;

VoxelGrid::VoxelGrid()
{
}

void VoxelGrid::setupParameters(Parameterizable& parameters)
{
    parameters.addParameter(csapex::param::factory::declareRange("resolution", 0.01, 1.0, 0.1, 0.005));
    parameters.addParameter(param::factory::declareBool("remove NAN", false), remove_nan_);
}

void VoxelGrid::setup(NodeModifier& node_modifier)
{
    input_cloud_ = node_modifier.addInput<PointCloudMessage>("PointCloud");

    output_ = node_modifier.addOutput<PointCloudMessage>("PointCloud");
}

void VoxelGrid::process()
{
    PointCloudMessage::ConstPtr msg(msg::getMessage<PointCloudMessage>(input_cloud_));

    boost::apply_visitor(PointCloudMessage::Dispatch<VoxelGrid>(this, msg), msg->value);
}

template <class PointT>
void VoxelGrid::inputCloud(typename pcl::PointCloud<PointT>::ConstPtr cloud)
{
    double res = readParameter<double>("resolution");
    Eigen::Vector4f leaf(res, res, res, 0);

    typename pcl::PointCloud<PointT>::Ptr out(new pcl::PointCloud<PointT>);

    typename pcl::PointCloud<PointT>::ConstPtr in;

    if (remove_nan_) {
        typename pcl::PointCloud<PointT>::Ptr tmp(new pcl::PointCloud<PointT>);
        tmp->points.reserve(cloud->points.size());

        for (auto it = cloud->begin(); it != cloud->end(); ++it) {
            if (!std::isnan(it->x)) {
                tmp->points.push_back(*it);
            }
        }
        in = tmp;

    } else {
        in = cloud;
    }

    pcl::VoxelGrid<PointT> voxel_f;
    voxel_f.setInputCloud(in);
    voxel_f.setLeafSize(leaf);
    voxel_f.filter(*out);

    PointCloudMessage::Ptr msg(new PointCloudMessage(cloud->header.frame_id, cloud->header.stamp));
    out->header = cloud->header;
    msg->value = out;

    msg::publish(output_, msg);
}


/**
* @Author: Marcel Ruhf <marcelruhf>
* @Email:  m.ruhf@protonmail.ch
* @Project: SoccerRL
* @Filename: main.cpp
* @License: Licensed under the Apache 2.0 license (see LICENSE.md)
* @Copyright: Copyright (c) 2017 Marcel Ruhf
*/

#include <iostream>
#include <fstream>
#include <cassert>
#include <vector>
#include <string>
#include <valarray>
#include <chrono>
#include <cmath>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_real_distribution.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/optional.hpp>
#include <Simulator.hpp>
#include <Functions.hpp>
#include <Constants.hpp>
#include <Globals.hpp>
#include <SarsaParams.hpp>
#include <TrueOnlineSarsaUpdate.hpp>
#include <ActionValueFunction.hpp>
#include <Tiles.hpp>

namespace mr
{
    double EPSILON = 0.2;
}

std::valarray<float> features(float distance, float velocity) {
    const int num_tiles = 64;
    const int tilings1 = 64;

    const int mem_size = 4096;

    std::valarray<int> tiles_array(num_tiles);

    const int num_of_variables = 2;
    float scaled[num_of_variables];
    scaled[0] = distance / 4.f;
    scaled[1] = velocity / 4.f;

    tiles(&tiles_array[0],tilings1,mem_size,scaled,num_of_variables);

    std::valarray<float> feat(mem_size);

    for (int i=0, size = tiles_array.size(); i<size; ++i) {
        feat[tiles_array[i]] = 1;
    }

    /*
    std::cout << "Tiles array size: " << tiles_array.size() << std::endl;

    std::cout << "Tiles index: ";
    for (int i = 0; i < tiles_array.size(); ++i)
    {
        std::cout << tiles_array[i] << " ";
    }
    std::cout << std::endl;
     */

    return feat;
}

int main(int argc, char **argv)
{
    mr::Simulator simulator;
    mr::SarsaParams params;
    params.alpha = 0.3/4096;
    params.gamma = 0.9;
    params.lambda = 0.99;
    mr::ActionValueFunction<int> vf(params);

    //---------------------
    //initialization of the tiling code

    int dummy_tiles[1];
    float dummy_vars[1];

    srand(0);
    tiles(dummy_tiles,1,1,dummy_vars,0); // initialize the tiling hashing
    // consistently before srand is set

    //---------------------

    //initializing the seed of the random number generator
    srand((unsigned) time(NULL) * getpid());

    try
    {
        std::ofstream datafile;

        boost::mt19937 gen((unsigned) time(NULL) * getpid());
        boost::random::uniform_real_distribution<double> rDist(0, 1);
        boost::random::uniform_int_distribution<> iDist(-20, 20);

        datafile.open("/users/marcelruhf/Documents/new4_data11.csv");

        datafile << "Episode,Accumulative Reward" << std::endl;

        for (int i = 0; i < mr::NUM_EPISODES; ++i)
        {
            boost::optional<int> a, aPrime;
            double r;

            //std::cout << "================================" << std::endl;
            //std::cout << "Episode " << i+1 << " started..." << std::endl;
            //std::cout << "Press ENTER to start the next episode: ";
            //std::cin.get();
            //std::cout << "================================" << std::endl;

            //auto start = std::chrono::high_resolution_clock::now();

            long accumulative_reward = 0;

            for (;;)
            {
                //auto rightnow = std::chrono::high_resolution_clock::now();
                //std::chrono::duration<float> duration = rightnow - start;

                if (simulator.getDistX() == 0 && simulator.getDistX() == 0)
                {  // The robot blocked the ball...
                    //std::cout << "Goal state reached, end of episode." << std::endl;
                    break;
                }
                else if (simulator.getDistY() == 0)
                {  // The ball reached the goal and the robot didn't catch it...
                    //std::cout << "No, they scored!" << std::endl;
                    break;
                }

                // Set S In SARS'A'
                float distX = simulator.getDistX();
                float velocity = simulator.getVelocity();
                std::valarray<float> s = features(distX, velocity);

                // START: Set A in SARS'A'
                if (!aPrime)
                {
                    if (rDist(gen) < mr::EPSILON)
                    {
                        a = iDist(gen);
                    }
                    else
                    {
                        if (!vf.allEqual(s))
                        {
                            a = vf.bestAction(s);
                        }
                        else
                        {
                            a = iDist(gen);
                        }
                        //std::cout << "Taking non-random action: " << *a << std::endl;
                    }
                }
                else
                {
                    a = aPrime;
                }
                // END: Set A in SARS'A'

                //std::cout << "Current distance: " << simulator.getDistX() << ", " << "ball velocity: " << simulator.getVelocity() << ", move " << *a << "... ";
                simulator.move(*a);  // Executes A in SARS'A'
                //std::cout << "new distance: " << simulator.getDistX() << std::endl;

                // Set S' in SARS'A'
                float distXPrime = simulator.getDistX();
                float velocityPrime = simulator.getVelocity();
                std::valarray<float> sPrime = features(distXPrime, velocityPrime);

                // START: Set A' in SARS'A'
                if (rDist(gen) < mr::EPSILON)
                {
                    //std::cout << "Taking random action (epsilon-greedy): ";
                    aPrime = iDist(gen);
                    //std::cout << aPrime << std::endl;
                }
                else
                {
                    if (!vf.allEqual(sPrime))
                    {
                        aPrime = vf.bestAction(sPrime);
                    }
                    else
                    {
                        aPrime = iDist(gen);
                    }
                }
                // END: Set A' in SARS'A'

                // Set R in SARS'A'
                r = -std::abs(simulator.getDistX());
                accumulative_reward += r;  // Add reward to episode total

                vf.update(s, *a, r, sPrime, *aPrime);  // updates action value function, end of iteration...
            }

            if (i < 500)
            {
                mr::EPSILON -= (mr::EPSILON/1500);
            }
            else
            {
                mr::EPSILON = 0;
            }

            datafile << i+1 << "," << accumulative_reward << std::endl;
            //std::cout << "Episode " << i+1 << " complete" << std::endl;
            //std::cout << "Accumulative reward: " << accumulative_reward << std::endl;
            simulator.nextEpisode();
            vf.clearE();
        }

        datafile.close();

        return 0;
    }
    catch (const std::runtime_error* &re)
    {
        std::cout << re->what() << std::endl;
    }
    catch (const std::exception &e)
    {
        std::cout << "An exception has occurred!" << std::endl;
        std::cout << e.what() << std::endl;
    }
}


/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/*
 Copyright (C) 2007 Ferdinando Ametrano

 This file is part of QuantLib, a free-software/open-source library
 for financial quantitative analysts and developers - http://quantlib.org/

 QuantLib is free software: you can redistribute it and/or modify it
 under the terms of the QuantLib license.  You should have received a
 copy of the license along with this program; if not, please email
 <quantlib-dev@lists.sf.net>. The license is also available online at
 <http://quantlib.org/license.shtml>.

 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 license for more details.
*/

/*! \file volcube.hpp
    \brief Interest rate (optionlet/swaption) volatility cube
*/

#ifndef quantlib_volatility_cube_h
#define quantlib_volatility_cube_h

#include <ql/handle.hpp>
#include <boost/shared_ptr.hpp>
#include <vector>

namespace QuantLib {

    class Period;
    class AbcdAtmVolCurve;
    class InterestRateVolSurface;
    class InterestRateIndex;

    class VolatilityCube {
      public:
        VolatilityCube(const std::vector<Handle<InterestRateVolSurface> >&,
                       const std::vector<Handle<AbcdAtmVolCurve> >&);
        const Period& minIndexTenor() const;
        const Period& maxIndexTenor() const;
        const std::vector<Handle<InterestRateVolSurface> >& surfaces() const;
        const std::vector<Handle<AbcdAtmVolCurve> >& curves() const;
      protected:
        std::vector<Handle<InterestRateVolSurface> > surfaces_;
        std::vector<Handle<AbcdAtmVolCurve> > curves_;
    };

    // inline

    inline const std::vector<Handle<InterestRateVolSurface> >&
    VolatilityCube::surfaces() const {
        return surfaces_;
    }

    inline const std::vector<Handle<AbcdAtmVolCurve> >&
    VolatilityCube::curves() const {
        return curves_;
    }

}

#endif


#include <cmath>
#include <functional>
#include <memory>
#include <stdexcept>
#include <vector>
#include <ipps.h>

// Eigen dense matrices
#include <Eigen/Dense>

namespace signal_processing {

std::function<std::vector<std::vector<double>>(int, double)> hp_butterworth() {
  return [](int filter_order,
            double cutoff_freq) -> std::vector<std::vector<double>> {
    // Allocated memory for coefficients
    std::vector<Ipp64f> taps(2 * (filter_order + 1));
    IppStatus status = ippStsNoErr;
    int internal_buffer_size;

    // Calculate required buffer size for internal calculations
    status = ippsIIRGenGetBufferSize(filter_order, &internal_buffer_size);
    if (status != ippStsNoErr) {
      throw std::runtime_error(
          "\nERROR: in signal_processing::hp_butterworth: Error in buffer size "
          "calculations\n");
    }

    // Divide by 2 to make cutoff frequency match the definition given in MATLAB
    Ipp8u* internal_calcs = ippsMalloc_8u(internal_buffer_size);
    status =
        ippsIIRGenHighpass_64f(cutoff_freq / 2.0, 0, filter_order, taps.data(),
                               ippButterworth, internal_calcs);

    // Check if filter computation succeeded
    if (status != ippStsNoErr) {
      throw std::runtime_error(
          "\nERROR: in signal_processing::hp_butterworth: Error in coefficient "
          "calculations\n");
    }

    std::vector<double> numerator(filter_order + 1);
    std::vector<double> denominator(filter_order + 1);

    for (int i = 0; i < filter_order + 1; ++i) {
      numerator[i] = taps[i];
      denominator[i] = taps[i + filter_order + 1];
    }

    // Free memory associated with internal calcs
    ippsFree(internal_calcs);

    return std::vector<std::vector<double>>{numerator, denominator};
  };
}

std::function<std::vector<double>(std::vector<double>, std::vector<double>, int,
                                  int)>
    impulse_response() {
  return [](const std::vector<double>& numerator_coeffs,
            const std::vector<double>& denominator_coeffs, int order,
            int num_samples) -> std::vector<double> {
    if (numerator_coeffs.size() != denominator_coeffs.size()) {
      throw std::runtime_error(
          "\nERROR: in signal_processing::impulse_response: Inputs for "
          "numerator "
          "and denominator coefficients not same length\n");
    }

    IppStatus status = ippStsNoErr;
    int internal_buffer_size;
    IppsIIRState_64f* filter_state = nullptr;
    Ipp64f *samples = ippsMalloc_64f(num_samples),
           *impulse = ippsMalloc_64f(num_samples);
    std::vector<double> taps(numerator_coeffs.size() +
                             denominator_coeffs.size());

    // Set all values to zero except first one for impulse
    impulse[0] = 1.0;
    for (int i = 1; i < num_samples; ++i) {
      impulse[i] = 0.0;
    }

    // Put filter coefficients into single stack array
    for (unsigned int i = 0; i < numerator_coeffs.size(); ++i) {
      taps[i] = numerator_coeffs[i];
      taps[i + numerator_coeffs.size()] = denominator_coeffs[i];
    }

    // Get buffer size required for internal calcs
    status = ippsIIRGetStateSize_64f(order, &internal_buffer_size);
    if (status != ippStsNoErr) {
      throw std::runtime_error(
          "\nERROR: in signal_processing::impulse_response: Error in buffer "
          "size "
          "calculations\n");
    }

    // Allocate memory for internal calcs
    Ipp8u* internal_calcs = ippsMalloc_8u(internal_buffer_size);

    // Initialize filter state
    status = ippsIIRInit_64f(&filter_state, taps.data(), order, nullptr,
                             internal_calcs);
    if (status != ippStsNoErr) {
      throw std::runtime_error(
          "\nERROR: in signal_processing::impulse_response: Error in filter "
          "initialization\n");
    }

    // Apply filter to impulse
    status = ippsIIR_64f(impulse, samples, num_samples, filter_state);
    if (status != ippStsNoErr) {
      throw std::runtime_error(
          "\nERROR: in signal_processing::impulse_response: Error in filter "
          "application\n");
    }

    std::vector<double> sample_vec(num_samples);
    for (int i = 0; i < num_samples; ++i) {
      sample_vec[i] = samples[i];
    }

    // Free memory used for filtering
    ippsFree(samples);
    ippsFree(impulse);
    ippsFree(internal_calcs);

    return sample_vec;
  };
}

std::function<std::vector<double>(double, double, unsigned int, unsigned int)>
    acausal_highpass_filter() {
  return [](double freq_corner, double time_step, unsigned int order,
            unsigned int num_samples) -> std::vector<double> {

    // Calculate normalized frequency
    double freq_cutoff_norm = 1.0 / (2.0 * time_step);

    // Initialize filter and frequencies
    Eigen::VectorXd filter(num_samples);
    std::vector<double> freq_steps(static_cast<unsigned int>(num_samples / 2) +
                                   1);
    double step_freq = freq_cutoff_norm / static_cast<double>(num_samples / 2);

    // Create vector of frequencies ranging from 0 to normalized cutoff
    // frequency
    for (unsigned int i = 0; i < freq_steps.size(); ++i) {
      freq_steps[i] = static_cast<double>(i) * step_freq;
    }

    // Calculate first half of filter coefficients
    for (unsigned int i = 0; i < freq_steps.size(); ++i) {
      filter(i) =
          std::sqrt(1.0 / (1.0 + std::pow(freq_corner / freq_steps[i],
                                          2.0 * order)));
    }

    // Mirror coefficients
    Eigen::VectorXd highpass_filter(2 * filter.size() - 2);
    highpass_filter.head(filter.size()) = filter;
    highpass_filter.segment(filter.size(), filter.size() - 2) =
        filter.segment(1, filter.size() - 2).reverse();

    // Place filter coefficients in STL vector
    std::vector<double> filter_vector(highpass_filter.size());
    Eigen::VectorXd::Map(&filter_vector[0], highpass_filter.size()) =
        highpass_filter;

    return filter_vector;
  };
}
}  // namespace signal_processing


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

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

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

#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_LINE_LINE_INTERSECTION_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_LINE_LINE_INTERSECTION_HPP

#include <boost/geometry/algorithms/detail/make/make.hpp>
#include <boost/geometry/arithmetic/infinite_line_functions.hpp>
#include <boost/geometry/util/math.hpp>

namespace boost { namespace geometry
{

#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace buffer
{

struct line_line_intersection
{
    template <typename Point>
    static Point between_point(Point const& a, Point const& b)
    {
        Point result;
        geometry::set<0>(result, (geometry::get<0>(a) + geometry::get<0>(b)) / 2.0);
        geometry::set<1>(result, (geometry::get<1>(a) + geometry::get<1>(b)) / 2.0);
        return result;
    }

    template <typename Point>
    static bool
    apply(Point const& pi, Point const& pj, Point const& qi, Point const& qj,
          Point const& vertex, bool equidistant, Point& ip)
    {
        // Calculates ip (below) by either intersecting p (pi, pj)
        // with q (qi, qj) or by taking a point between pj and qi (b) and
        // intersecting r (b, v), where v is the original vertex, with p (or q).
        // The reason for dual approach: p might be nearly collinear with q,
        // and in that case the intersection points can lose precision
        // (or be plainly wrong).
        // Therefore it takes the most precise option (this is usually p, r)
        //
        //             /qj                     |
        //            /                        |
        //           /      /                  |
        //          /      /                   |
        //         /      /                    |
        //        /qi    /                     |
        //              /                      |
        //   ip *  + b * v                     |
        //              \                      |
        //        \pj    \                     |
        //         \      \                    |
        //          \      \                   |
        //           \      \                  |
        //            \pi    \                 |
        //
        // If generated sides along the segments can have an adapted distance,
        // in a custom strategy, then the calculation of the point in between
        // might be incorrect and the optimization is not used.

        using ct = typename coordinate_type<Point>::type;

        auto const p = detail::make::make_infinite_line<ct>(pi, pj);
        auto const q = detail::make::make_infinite_line<ct>(qi, qj);

        using line = decltype(p);
        using arithmetic::determinant;
        using arithmetic::assign_intersection_point;

        // The denominator is the determinant of (a,b) values of lines p q
        // | pa pa |
        // | qb qb |
        auto const denominator_pq = determinant<line, &line::a, &line::b>(p, q);
        constexpr decltype(denominator_pq) const zero = 0;

        if (equidistant)
        {
            auto const between = between_point(pj, qi);
            auto const r = detail::make::make_infinite_line<ct>(vertex, between);
            auto const denominator_pr = determinant<line, &line::a, &line::b>(p, r);

            if (math::equals(denominator_pq, zero)
                && math::equals(denominator_pr, zero))
            {
                // Degenerate case (for example when length results in <inf>)
                return false;
            }

            ip = geometry::math::abs(denominator_pq) > geometry::math::abs(denominator_pr)
                 ? assign_intersection_point<Point>(p, q, denominator_pq)
                 : assign_intersection_point<Point>(p, r, denominator_pr);
        }
        else
        {
            if (math::equals(denominator_pq, zero))
            {
                return false;
            }
            ip = assign_intersection_point<Point>(p, q, denominator_pq);
        }

        return true;
    }
};

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

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_LINE_LINE_INTERSECTION_HPP


#ifndef SBGATTRANSFORMSHAPE_HEADER
#define SBGATTRANSFORMSHAPE_HEADER

#include <vtkSmartPointer.h>
#include <vtkPolyData.h>
#include <armadillo>

class SBGATTransformShape{

public:

	/**
	Shifts origin of provided shape's coordinates to shape's barycenter
	@param shape pointer to shape to align
	*/
	static void ShiftShapeToBarycenter(vtkSmartPointer<vtkPolyData> & shape);

	/**
	Shifts origin of provided shape's coordinates to shape's barycenter
	and rotates coordinates post-translation so that the shape's coordinates are expressed
	in the shape's barycentered principal frame
	@param shape pointer to shape to align
	*/
	static void ShiftShapeToBarycenterAndRotateToPrincipalAxes(vtkSmartPointer<vtkPolyData> & shape);

	/**
	Applies translation to provided shape
	@param x translation vector
	@param shape pointer to shape to align
	*/
	static void Translate(const arma::vec::fixed<3> x,vtkSmartPointer<vtkPolyData> & shape);

	/**
	Applies rotation to provided shape
	@param dcm rotation matrix. If B designates the frame in which the coordinates are expressed BEFORE
	the rotation and B' the frame in in which the coordinates are expressed AFTER
	the rotation, then dcm == [B'B]
	@param shape pointer to shape to align
	*/
	static void Rotate(const arma::mat::fixed<3,3> dcm,vtkSmartPointer<vtkPolyData> & shape);

};

#endif


/* Copyright (c) 2015, Julian Straub <jstraub@csail.mit.edu>
 * Licensed under the MIT license. See the license file LICENSE.
 */

#pragma once

#include <Eigen/Dense>
#include <iostream>

#include <boost/random/gamma_distribution.hpp> // for gamma_distribution.

#include <dpMM/basemeasure.hpp>
#include <dpMM/cat.hpp>
#include <dpMM/niwSphere.hpp>
#include <dpMM/niwSphereFull.hpp>
//#include <dpMM/niwTangent.hpp>

using namespace Eigen;
using std::cout;
using std::endl;

template<class B, typename T>
class LrCluster // : public BaseMeasure<T>
{
protected:
  boost::mt19937 *pRndGen_;
  T alpha_;
  T logLikeSubClSplit_;
  T avgLogLikeData_; // data log likelihood in subclusters
  vector<T> avgLogLikeDataHist_; // data log likelihood in subclusters
  bool splittable_;
  int32_t nDecrease_;

  Matrix<T,Dynamic,1> kernel_;
//  Matrix<T,Dynamic,1> logLikeWindow;

public:
  LrCluster(const shared_ptr<B>& theta, T alpha, boost::mt19937 *pRndGen);
  virtual ~LrCluster();

  virtual LrCluster<B,T>* copy();
  virtual T logLikelihood(const Matrix<T,Dynamic,1>& x) const;

  void posterior(const Matrix<T,Dynamic,Dynamic>& x, const VectorXu& z, uint32_t k);

  Matrix<T,1,2> getSubclusterLogPdf(const Matrix<T,Dynamic,1>& x);
  T dataLogLikelihoodMarginalized();
//  void dataLogLikelihoodMarginalizedMergedWith(
  virtual T logPdfUnderPrior() const;
  virtual T logPdfUnderPriorMarginalized();
  virtual T dataLogLikelihoodMarginalizedMerged(const shared_ptr<LrCluster<B,T> >& other);
  virtual void print() const;

  virtual T& logLikelihoodOfSubclusterSplit(){ return logLikeSubClSplit_;};
  virtual const T& logLikelihoodData(){ return avgLogLikeData_;};

  virtual T qRandomParamProposal() { return 0.0; };
//  virtual T qRandomSplitProposal() { return 0.0; };

//  virtual void sampleUpper();
//  virtual void sampleLR();
  void sample();

  LrCluster<B,T>* merge(const shared_ptr<LrCluster<B,T> >& other);

  bool splittable();
  void setSplittable() { splittable_ = true;};
  void resetSplittable() {
    splittable_ = false;
    nDecrease_ = 0;
    avgLogLikeData_ = -999999999.0;
    avgLogLikeDataHist_.clear(); };
  void updateAvgLogLikeData(T avgLogLikeData);
  void updateAvgLogLikeData();

  shared_ptr<B>& getUpper(){ return theta_;};
  shared_ptr<B>& getR(){ return thetaR_;};
  shared_ptr<B>& getL(){ return thetaL_;};

  T count() const {return theta_->count();};
  T countR(){ return thetaR_->count();};
  T countL(){ return thetaL_->count();};

  T avgLogLikeData(){ return avgLogLikeData_;};

  void posteriorUpper();
  void posteriorLR(const Matrix<T,Dynamic,Dynamic>& x,
    const VectorXu& z, uint32_t k);

  T& alpha(){return alpha_;};
  T& stickL(){return stickL_;};
  T& stickR(){return stickR_;};

  T stickL_, stickR_;
  shared_ptr<B> theta0_; // prior
  shared_ptr<B> theta_; // full cluster
  shared_ptr<B> thetaL_; // left cluster
  shared_ptr<B> thetaR_; // right cluster
private:

  void posteriorSticks();
};

// ---------------------------------- impl -----------------------------------

template<class B, typename T>
LrCluster<B,T>::LrCluster(const shared_ptr<B>& theta, T alpha,
  boost::mt19937 *pRndGen)
  : pRndGen_(pRndGen), alpha_(alpha), avgLogLikeData_(-9999999.), splittable_(false),
  nDecrease_(0),
  stickL_(log(0.5)), stickR_(log(0.5)),
  theta0_(theta),
  theta_(theta->copyNative()),
  thetaL_(theta->copyNative()),
  thetaR_(theta->copyNative())
{
  kernel_.setOnes(8);
  kernel_ /= 8.;
};

template<class B, typename T>
LrCluster<B,T>::~LrCluster()
{};

template<class B, typename T>
LrCluster<B,T>* LrCluster<B,T>::copy()
{
  LrCluster<B,T>* lrCl = new LrCluster<B,T>(theta0_,alpha_, pRndGen_);
  lrCl->stickL_ = stickL_;
  lrCl->stickR_ = stickR_;
  lrCl->theta_.reset(theta_->copyNative());
  lrCl->thetaL_.reset(thetaL_->copyNative());
  lrCl->thetaR_.reset(thetaR_->copyNative());
  return lrCl;
}

template<class B, typename T>
LrCluster<B,T>* LrCluster<B,T>::merge(const shared_ptr<LrCluster<B,T> >& other)
{
  LrCluster<B,T>* merged = new LrCluster<B,T>(
    shared_ptr<B>(theta_->merge(*other->getUpper())),
    alpha_,pRndGen_);
  merged->getL().reset(theta_->copyNative());
  merged->getR().reset(other->getUpper()->copyNative());
  return merged;
};

template<class B, typename T>
T LrCluster<B,T>::logPdfUnderPrior() const
{
//TODO might not need that one
  assert(false);
  return theta_->logPdfUnderPrior();
}

template<class B, typename T>
T LrCluster<B,T>::logPdfUnderPriorMarginalized()
{
//  this->posteriorUpper();
  return theta_->logPdfUnderPriorMarginalized();
}

template<class B, typename T>
T LrCluster<B,T>::dataLogLikelihoodMarginalizedMerged(
    const shared_ptr<LrCluster<B,T> >& other)
{
//  LrCluster<B,T>* merged = new LrCluster<B,T>(
//    shared_ptr<B>(theta_->merge(*other->getUpper())),
//    alpha_,pRndGen_);
//  merged->getL().reset(theta_->copyNative());
//  merged->getR().reset(other->getUpper()->copyNative());
//  return merged;

  return theta_->logPdfUnderPriorMarginalizedMerged(other->getUpper());
};

template<class B, typename T>
void LrCluster<B,T>::sample()
{
  thetaL_->sample();
  thetaR_->sample();
  theta_->sample();
}

template<class B, typename T>
void LrCluster<B,T>::posterior(const Matrix<T,Dynamic,Dynamic>& x,
  const VectorXu& z, uint32_t k)
{
  thetaL_->posterior(x,z,k);
  thetaR_->posterior(x,z,k+1);
  theta_->fromMerge(*thetaL_,*thetaR_);

//  thetaL_->sample();
//  thetaR_->sample();
//  theta_->sample();
  posteriorSticks();
}

//TODO: only to test the scatter correction!
template<>
void LrCluster<NiwSphere<double> ,double>::posterior(
    const Matrix<double,Dynamic,Dynamic>& x, const VectorXu& z, uint32_t k)
{
//  cout<<"::posterior posteriorFromPtsInTpS"<<endl;
  /* assumes that x is already correctly in T_northS */
  thetaL_->posteriorFromPtsInTpS(x,z,k);
  thetaR_->posteriorFromPtsInTpS(x,z,k+1);
  theta_->fromMerge(*thetaL_,*thetaR_);

//  thetaL_->sample();
//  thetaR_->sample();
//  theta_->sample();
  posteriorSticks();
}

template<>
void LrCluster<NiwSphereFull<double> ,double>::posterior(
    const Matrix<double,Dynamic,Dynamic>& x, const VectorXu& z, uint32_t k)
{
//  cout<<"::posterior posteriorFromPtsInTpS"<<endl;
  /* assumes that x is already correctly in T_northS */
  thetaL_->posteriorFromPtsInTpS(x,z,k);
  thetaR_->posteriorFromPtsInTpS(x,z,k+1);
  theta_->fromMerge(*thetaL_,*thetaR_);
//  theta_->posteriorFromPtsInTpS(x,z,k/2,2);

//  thetaL_->sample();
//  thetaR_->sample();
//  theta_->sample();
  posteriorSticks();
}

//template<>
//void LrCluster<NiwTangent<double> ,double>::posterior(
//    const Matrix<double,Dynamic,Dynamic>& x, const VectorXu& z, uint32_t k)
//{
////  cout<<"::posterior posteriorFromPtsInTpS"<<endl;
//  /* assumes that x is already correctly in T_northS */
//  thetaL_->posteriorFromPtsInTpS(x,z,k);
//  thetaR_->posteriorFromPtsInTpS(x,z,k+1);
//  // TODO: make sure that from merge works well if both L and R have exactly
//  // the same linearization point
//  theta_->fromMerge(*thetaL_,*thetaR_);
//
////  thetaL_->sample();
////  thetaR_->sample();
////  theta_->sample();
//  posteriorSticks();
//}

template<>
void LrCluster<NiwSphere<float> , float>::posterior(
    const Matrix<float,Dynamic,Dynamic>& x, const VectorXu& z, uint32_t k)
{
  /* assumes that x is already correctly in T_northS */
  thetaL_->posteriorFromPtsInTpS(x,z,k);
  thetaR_->posteriorFromPtsInTpS(x,z,k+1);
  theta_->fromMerge(*thetaL_,*thetaR_);

//  thetaL_->sample();
//  thetaR_->sample();
//  theta_->sample();
  posteriorSticks();
}

template<class B, typename T>
void LrCluster<B,T>::posteriorSticks()
{
  boost::random::gamma_distribution<> gammaL((thetaL_->count()+alpha_)*0.5);
  boost::random::gamma_distribution<> gammaR((thetaR_->count()+alpha_)*0.5);
  stickL_ = gammaL(*pRndGen_);
  stickR_ = gammaR(*pRndGen_);
  T normalizer = log(stickL_ + stickR_);
  stickL_ = log(stickL_) - normalizer;
  stickR_ = log(stickR_) - normalizer;

#ifndef NDEBUG
  cout<<"sticks "<<stickL_<<", "<<stickR_
    <<" exp: "<<exp(stickL_)<<", "<<exp(stickR_)
    <<" log(sum) = "<<logsumexp(stickL_,stickR_)<<endl;
#endif
  assert(fabs(logsumexp(stickL_,stickR_)) <1e-6);
};

template<class B, typename T>
Matrix<T,1,2> LrCluster<B,T>::getSubclusterLogPdf(const Matrix<T,Dynamic,1>& x)
{
  assert(thetaL_);
  assert(thetaR_);
  Matrix<T,1,2> logPdf;
  logPdf(0) = thetaL_->logLikelihood(x) + stickL_;
  logPdf(1) = thetaR_->logLikelihood(x) + stickR_;
//  cout<<logPdf<<endl;
  return logPdf; //(logPdf.array()-logsumexp(logPdf(0),logPdf(1))).exp().matrix();
}

//template<>
//Matrix<double,1,2> LrCluster<NiwSphered,double>::getSubclusterLogPdf(
//    const Matrix<double,Dynamic,1>& x)
//{
////  cout<<"LrCluster<NiwSphered,double>::getSubclusterLogPdf"<<endl;
//  assert(thetaL_);
//  assert(thetaR_);
//  Matrix<double,1,2> logPdf;
//  logPdf(0) = thetaL_->logLikelihoodNorth(x) + stickL_;
//  logPdf(1) = thetaR_->logLikelihoodNorth(x) + stickR_;
////  cout<<logPdf<<endl;
//  return logPdf; //(logPdf.array()-logsumexp(logPdf(0),logPdf(1))).exp().matrix();
//}
//
//template<>
//Matrix<float,1,2> LrCluster<NiwSpheref,float>::getSubclusterLogPdf(
//    const Matrix<float,Dynamic,1>& x)
//{
//  assert(thetaL_);
//  assert(thetaR_);
//  Matrix<float,1,2> logPdf;
//  logPdf(0) = thetaL_->logLikelihoodNorth(x) + stickL_;
//  logPdf(1) = thetaR_->logLikelihoodNorth(x) + stickR_;
////  cout<<logPdf<<endl;
//  return logPdf; //(logPdf.array()-logsumexp(logPdf(0),logPdf(1))).exp().matrix();
//}

template<>
double LrCluster<NiwSphereFulld,double>::qRandomParamProposal()
{
  return theta_->qRandomMuProposal() ;
}

//template<>
//double LrCluster<NiwSphereFulld,double>::qRandomSplitProposal()
//{
//  return thetaL_->qRandomMuProposal()
//    + thetaR_->qRandomMuProposal() ;
//}

template<class B, typename T>
T LrCluster<B,T>::dataLogLikelihoodMarginalized()
{
  return theta_->logPdfUnderPriorMarginalized();
};

template<class B, typename T>
bool LrCluster<B,T>::splittable()
{
  return splittable_;
};

template<class B, typename T>
void LrCluster<B,T>::updateAvgLogLikeData()
{
  if(thetaL_->count() == 0 || thetaR_->count() == 0)
  {
    splittable_ = true; // to allow reseting or merging this cluster
    updateAvgLogLikeData(99999.0);
  }else{
    T logJoint = 2.*log(alpha_) + boost::math::lgamma(alpha_)
      - boost::math::lgamma(alpha_+ theta_->count());
    logJoint += boost::math::lgamma(thetaL_->count());
    logJoint += boost::math::lgamma(thetaR_->count());
    logJoint += thetaL_->logPdfUnderPriorMarginalized();
    logJoint += thetaR_->logPdfUnderPriorMarginalized();
    updateAvgLogLikeData(logJoint/count());
  }
}

template<class B, typename T>
void LrCluster<B,T>::updateAvgLogLikeData(T avgLogLikeData)
{
#ifndef NDEBUG
  cout<<"\x1b[35m LrCluster<B,T>::updateAvgLogLikeData ";
#endif
  int32_t W = static_cast<int32_t>(kernel_.size());
  avgLogLikeDataHist_.push_back(avgLogLikeData);
  if(static_cast<int32_t>(avgLogLikeDataHist_.size()) >= W)
  {
    // compute convolution to smooth wiggeling
    int32_t N = avgLogLikeDataHist_.size();
    T avgLogLikeDataNow =0.;
    for(int32_t i=0; i< W; ++i)
      avgLogLikeDataNow += kernel_(i)*avgLogLikeDataHist_[N+i-kernel_.size()];

    if(avgLogLikeDataNow - avgLogLikeData_ <= 1e-3)
    {
      // if same or decreased for more thant 2 times we are splittable
      if(nDecrease_ >= 2){
        //TODO
        splittable_ = true;
      }else
        ++ nDecrease_;
    }else{
      //    splittable_ = false;
    }
#ifndef NDEBUG
    cout << (avgLogLikeDataNow-avgLogLikeData_);
#endif
    avgLogLikeData_ = avgLogLikeDataNow;
  }else{
  }
//  avgLogLikeData_ = avgLogLikeData;
#ifndef NDEBUG
    cout<< " -> "<< splittable_<<" history: "<<endl;
  for(int32_t i=0; i<  avgLogLikeDataHist_.size(); ++i)
    cout<<avgLogLikeDataHist_[i]<<" ";
  cout<<"\x1b[0m"<<endl;
#endif
};

//template<class B, typename T>
//void LrCluster<B,T>::dataLogLikelihoodMarginalizedMergedWith(
//    const shared_ptr<B >& other)
//{
//  return
//};

//template<class B, typename T>
//void sampleParamUpper()
//{
//  theta_->
//};

//template<class B, typename T>
//void LrCluster<B,T>::sampleUpper()
//{
//  theta_->sample();
//};
//
//template<class B, typename T>
//void LrCluster<B,T>::sampleLR()
//{
//  thetaL_->sample();
//  thetaR_->sample();
//};

template<class B, typename T>
void LrCluster<B,T>::posteriorUpper()
{
//TODO: hopefully this is a good enough approximation
  theta_->fromMerge(*thetaL_,*thetaR_);
//  theta_->sample();
}

//template<class B, typename T>
//void LrCluster<B,T>::posteriorUpper(const Matrix<T,Dynamic,Dynamic>& x,
//    const VectorXu& z, uint32_t k)
//{
//  //TODO need a way to tell that it should use all labels k and k+1
//  theta_->posterior(x,z,k);
//  assert(false);
//};

template<class B, typename T>
void LrCluster<B,T>::posteriorLR(const Matrix<T,Dynamic,Dynamic>& x,
    const VectorXu& z, uint32_t k)
{
  thetaL_->posterior(x,z,k);
  thetaR_->posterior(x,z,k+1);
//  thetaL_->sample();
//  thetaR_->sample();
};

template<class B, typename T>
T LrCluster<B,T>::logLikelihood(const Matrix<T,Dynamic,1>& x) const
{
//  assert(false);// TODO may not be necessary
  return theta_->logLikelihood(x);
}

/* do not want that, since we only have linearization for a single cluster
template<>
double LrCluster<NiwSphered,double>::logLikelihood(
    const Matrix<double,Dynamic,1>& x) const
{
//  cout<<"LrCluster<NiwSphered,double>::logLikelihood"<<endl;
//  assert(false);// TODO may not be necessary
  return theta_->logLikelihoodNorth(x);
}

template<>
float LrCluster<NiwSpheref,float>::logLikelihood(
    const Matrix<float,Dynamic,1>& x) const
{
//  assert(false);// TODO may not be necessary
  return theta_->logLikelihoodNorth(x);
}
*/

template<class B, typename T>
void LrCluster<B,T>::print() const
{
  cout<<" ---------------- LrCluster ----------------- "<<endl;
  cout<<" upper "<<endl;
  theta_->print();
  cout<<" left "<<endl;
  thetaL_->print();
  cout<<" right "<<endl;
  thetaR_->print();
}


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

#ifndef PAAL_ZELIKOVSKY_11_PER_6_HPP
#define PAAL_ZELIKOVSKY_11_PER_6_HPP

#include <boost/config.hpp>

#include "paal/utils/contract_bgl_adjacency_matrix.hpp"
#include "paal/data_structures/subset_iterator.hpp"
#include "paal/data_structures/metric/metric_to_bgl.hpp"
#include "paal/data_structures/metric/metric_traits.hpp"
#include "paal/data_structures/voronoi/voronoi.hpp"
#include "paal/data_structures/metric/graph_metrics.hpp"
#include "paal/local_search/local_search.hpp"
#include "paal/utils/functors.hpp"
#include "paal/utils/irange.hpp"

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/connected_components.hpp>
#include <boost/graph/subgraph.hpp>
#include <boost/graph/prim_minimum_spanning_tree.hpp>

#include <boost/range/combine.hpp>
#include <boost/range/join.hpp>
#include <boost/range/algorithm/copy.hpp>

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

#include <boost/functional/hash.hpp>

#include <unordered_map>
#include <stack>

namespace paal {

namespace detail {
/**
 * class steiner_tree
 * @brief This is Alexander Zelikovsky 11/6 approximation algorithm for steiner
 tree.
 *
 * Example: <br>
   \snippet zelikovsky_11_per_6_example.cpp Steiner Tree Example
 *
 * example file is steiner_tree_example.cpp
 *
 * @tparam Metric we only use this metric for distances  (Steiner, Terminal) and
 (Terminal, Terminal)
 * @tparam Voronoi models WeakVoronoi (see \ref voronoi). This is a Voronoi
 division where generators are terminals  of the steiner tree.
 */
template <typename Metric, typename Voronoi> class steiner_tree {
    using Idx = int;

  public:
    using MT = data_structures::metric_traits<Metric>;
    using Dist = typename MT::DistanceType;
    using VertexType = typename MT::VertexType;
    static const int SUBSET_SIZE = 3;

    using ThreeTuple = k_tuple_t<Idx, SUBSET_SIZE>;
    using Move = boost::tuple<ThreeTuple, Dist>;
    using ResultSteinerVertices = std::vector<VertexType>;

    /**
     * @brief
     *
     * @param m
     * @param vor
     */
    steiner_tree(const Metric &m, const Voronoi &vor)
        : m_metric(m), m_voronoi(vor),
          N(boost::distance(vor.get_generators())),
          m_save(N) {}

    template <typename OutputIterator>
    void get_result_steiner_vertices(OutputIterator out) {
        ResultSteinerVertices res;

        if (m_voronoi.get_vertices().empty()) {
            return;
        }

        auto ti = irange(N);
        auto subsets = make_three_subset_range(ti.begin(), ti.end());

        auto get_moves = [&](const AMatrix &) {
            return boost::combine(subsets, m_subs_dists | boost::adaptors::transformed(
                                                   utils::remove_reference()));
        };

        auto obj_fun = [&](const AMatrix & m, const Move & t) {
            return gain(t);
        };

        auto commit_move = [&](AMatrix & m, const Move & t) {
            contract(m, get<0>(t));
            res.push_back(m_nearest_vertex[get<0>(t)]);
            return true;
        };

        auto search_components = local_search::make_search_components(
            get_moves, obj_fun, commit_move);

        auto ls_solution = data_structures::metric_to_bgl_with_index(
            m_metric, m_voronoi.get_generators(), m_t_idx);

        fill_sub_dists();

        find_save(ls_solution);
        local_search::local_search(ls_solution,
                                   local_search::best_improving_strategy{},
                                   [ = ](AMatrix & a) {
            find_save(a);
            return true;
        },
                                   utils::always_false(), search_components);

        unique_res(res);
        boost::copy(res, out);
    }

  private:

    // Spanning tree types
    using SpanningTreeEdgeProp = boost::property<boost::edge_index_t, int,
                            boost::property<boost::edge_weight_t, Dist>>;
    using SpanningTree = boost::subgraph<boost::adjacency_list<
        boost::listS, boost::vecS, boost::undirectedS, boost::no_property,
        SpanningTreeEdgeProp>>;
    using GTraits = boost::graph_traits<SpanningTree>;
    using SEdge = typename GTraits::edge_descriptor;

    // Adjacency Matrix types
    using AMatrix = typename data_structures::adjacency_matrix<Metric>::type;
    using MTraits = boost::graph_traits<AMatrix>;
    using MEdge = typename MTraits::edge_descriptor;

    // other types
    using ThreeSubsetsDists = std::vector<Dist>;
    using NearstByThreeSubsets = std::unordered_map<ThreeTuple, VertexType,
        boost::hash<ThreeTuple>>;

    template <typename Iter>
    boost::iterator_range<
        data_structures::subsets_iterator<SUBSET_SIZE, Iter>>
    make_three_subset_range(Iter b, Iter e) {
        return data_structures::make_subsets_iterator_range<SUBSET_SIZE>(b, e);
    }

    void unique_res(ResultSteinerVertices &res) {
        std::sort(res.begin(), res.end());
        auto new_end = std::unique(res.begin(), res.end());
        res.resize(std::distance(res.begin(), new_end));
    }

    void contract(AMatrix &am, const ThreeTuple &t) {
        utils::contract(am, get<0>(t), std::get<1>(t));
        utils::contract(am, get<1>(t), std::get<2>(t));
    }

    Dist gain(const Move &t) {
        auto const &m = m_save;
        Idx a, b, c;
        std::tie(a, b, c) = get<0>(t);

        assert(m(a, b) == m(b, c) || m(b, c) == m(c, a) || m(c, a) == m(a, b));
        return max3(m(a, b), m(b, c), m(c, a)) +
               min3(m(a, b), m(b, c), m(c, a)) - get<1>(t);
    }

    void fill_sub_dists() {
        auto ti = irange(N);

        auto sub_range = make_three_subset_range(ti.begin(), ti.end());
        m_subs_dists.reserve(boost::distance(sub_range));

        // finding nearest vertex to subset
        for (const ThreeTuple &subset : sub_range) {
            // TODO awful coding, need to be changed to loop (using fold
            // form utils/fusion.hpp)
            // TODO There is possible problem, one point could belong to two
            // voronoi regions
            // In our implementation the point will be in exactly one region and
            // there
            // it will not be contained in the range
            auto v_range1 = m_voronoi.get_vertices_for_generator(
                m_t_idx.get_val(std::get<0>(subset)));
            auto v_range2 = m_voronoi.get_vertices_for_generator(
                m_t_idx.get_val(std::get<1>(subset)));
            auto v_range3 = m_voronoi.get_vertices_for_generator(
                m_t_idx.get_val(std::get<2>(subset)));
            auto range = boost::join(boost::join(v_range1, v_range2), v_range3);

            if (boost::empty(range)) {
                m_nearest_vertex[subset] = *m_voronoi.get_vertices().begin();
            } else {
                m_nearest_vertex[subset] = *std::min_element(
                    std::begin(range), std::end(range),
                    utils::make_functor_to_comparator([&](VertexType v) {
                    return dist(v, subset);
                }));
            }
            m_subs_dists.push_back(dist(m_nearest_vertex[subset], subset));
        }
    }

    Dist max3(Dist a, Dist b, Dist c) { return std::max(std::max(a, b), c); }

    Dist min3(Dist a, Dist b, Dist c) { return std::min(std::min(a, b), c); }

    Dist dist(VertexType steiner_point, Idx terminal_idx) {
        return m_metric(steiner_point, m_t_idx.get_val(terminal_idx));
    }

    // minor TODO could by more general somewhere
    Dist dist(VertexType steiner_point, const ThreeTuple &tup) {
        return dist(steiner_point, std::get<0>(tup)) +
               dist(steiner_point, std::get<1>(tup)) +
               dist(steiner_point, std::get<2>(tup));
    }

    /**
     * @brief Constructs spanning tree from current am
     *
     * @return
     */
    SpanningTree get_spanning_tree(const AMatrix &am) {
        // compute spanning tree and write it to  vector
        std::vector<Idx> pm(N);
        boost::prim_minimum_spanning_tree(am, &pm[0]);

        // transform vector into SpanningTree object
        auto const &weight_map = get(boost::edge_weight, am);
        SpanningTree spanning_tree(N);
        for (Idx from = 0; from < N; ++from) {
            if (from != pm[from]) {
                bool succ = add_edge(
                    from, pm[from],
                    SpanningTreeEdgeProp(
                        from, get(weight_map, edge(from, pm[from], am).first)),
                    spanning_tree).second;
                assert(succ);
            }
        }
        return spanning_tree;
    }

    template <typename WeightMap, typename EdgeRange>
    SEdge max_edge(EdgeRange range, const WeightMap &weight_map) const {
        assert(range.first != range.second);
        return *std::max_element(range.first, range.second,
                                 [&](SEdge e, SEdge f) {
            return get(weight_map, e) < get(weight_map, f);
        });
    }

    void create_subgraphs(SpanningTree &g, SpanningTree &g1, SpanningTree &g2) {
        int n = num_vertices(g);
        std::vector<Idx> comps(n);
        boost::connected_components(g, &comps[0]);
        int c1 = comps[0];
        int c2 = -1;

        for (auto i : irange(n)) {
            if (comps[i] == c1) {
                add_vertex(g.local_to_global(i), g1);
            } else {
                assert(c2 == -1 || comps[i] == c2);
                c2 = comps[i];
                add_vertex(g.local_to_global(i), g2);
            }
        }
    }

    // setting m_save(v,w) = max_dist, for each v in g1 and w in g2
    void move_save(const SpanningTree &g1, const SpanningTree &g2,
                   Dist max_dist) {
        auto v1 = vertices(g1);
        auto v2 = vertices(g2);
        for (auto v : boost::make_iterator_range(v1)) {
            for (auto w : boost::make_iterator_range(v2)) {
                auto vg = g1.local_to_global(v);
                auto wg = g2.local_to_global(w);
                m_save(vg, wg) = max_dist;
                m_save(wg, vg) = max_dist;
            }
        }
    }

    // finds the longest edge between each pair of vertices
    // in the spanning tree
    // preforms recursive procedure
    void find_save(const AMatrix &am) {
        auto spanning_tree = get_spanning_tree(am);

        std::stack<SpanningTree *> s;
        s.push(&spanning_tree);

        while (!s.empty()) {
            // TODO delete children at once
            SpanningTree &g = *s.top();
            s.pop();
            int n = num_vertices(g);
            if (n == 1) {
                continue;
            }
            auto e_range = edges(g);
            assert(e_range.first != e_range.second);
            auto const &weight_map = get(boost::edge_weight, g);
            SEdge max_el = max_edge(e_range, weight_map);
            Dist max_dist = get(weight_map, max_el);
            remove_edge(max_el, g);
            SpanningTree &g1 = g.create_subgraph();
            SpanningTree &g2 = g.create_subgraph();
            create_subgraphs(g, g1, g2);

            move_save(g1, g2, max_dist);

            s.push(&g1);
            s.push(&g2);
        }
    }

    const Metric &m_metric;
    const Voronoi &m_voronoi;
    ThreeSubsetsDists m_subs_dists;
    NearstByThreeSubsets m_nearest_vertex;
    int N;
    data_structures::array_metric<Dist> m_save;
    data_structures::bimap<VertexType> m_t_idx;
};
} // !detail

/**
 * @brief 11/6 approximation for steiner_tree problem
 *
 * @tparam Metric
 * @tparam Voronoi
 * @tparam OutputIterator
 * @param m
 * @param v
 * @param out
 */
template <typename Metric, typename Voronoi, typename OutputIterator>
void steiner_tree_zelikovsky11per6approximation(const Metric &m,
                                                const Voronoi &v,
                                                OutputIterator out) {
    detail::steiner_tree<Metric, Voronoi> st(m, v);
    st.get_result_steiner_vertices(out);
}

} // paal

#endif // PAAL_ZELIKOVSKY_11_PER_6_HPP


/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/*
 Copyright (C) 2006 Klaus Spanderen

 This file is part of QuantLib, a free-software/open-source library
 for financial quantitative analysts and developers - http://quantlib.org/

 QuantLib is free software: you can redistribute it and/or modify it
 under the terms of the QuantLib license.  You should have received a
 copy of the license along with this program; if not, please email
 <quantlib-dev@lists.sf.net>. The license is also available online at
 <http://quantlib.org/license.shtml>.

 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 license for more details.
*/

/*! \file longstaffschwartzpathpricer.hpp
    \brief Longstaff-Schwarz path pricer for early exercise options
*/

#ifndef quantlib_longstaff_schwartz_path_pricer_hpp
#define quantlib_longstaff_schwartz_path_pricer_hpp

#include <ql/termstructures/yieldtermstructure.hpp>
#include <ql/math/functional.hpp>
#include <ql/math/generallinearleastsquares.hpp>
#include <ql/methods/montecarlo/pathpricer.hpp>
#include <ql/methods/montecarlo/earlyexercisepathpricer.hpp>
#include <boost/bind.hpp>
#include <boost/function.hpp>

namespace QuantLib {

    //! Longstaff-Schwarz path pricer for early exercise options
    /*! References:

        Francis Longstaff, Eduardo Schwartz, 2001. Valuing American Options
        by Simulation: A Simple Least-Squares Approach, The Review of
        Financial Studies, Volume 14, No. 1, 113-147

        \ingroup mcarlo

        \test the correctness of the returned value is tested by
              reproducing results available in web/literature
    */
    template <class PathType>
    class LongstaffSchwartzPathPricer : public PathPricer<PathType> {
      public:
        typedef typename EarlyExerciseTraits<PathType>::StateType StateType;

        LongstaffSchwartzPathPricer(
            const TimeGrid& times,
            const boost::shared_ptr<EarlyExercisePathPricer<PathType> >& ,
            const boost::shared_ptr<YieldTermStructure>& termStructure);

        Real operator()(const PathType& path) const;
        virtual void calibrate();

      protected:
        bool  calibrationPhase_;
        const boost::shared_ptr<EarlyExercisePathPricer<PathType> >
            pathPricer_;

        boost::scoped_array<Array> coeff_;
        boost::scoped_array<DiscountFactor> dF_;

        mutable std::vector<PathType> paths_;
        const   std::vector<boost::function1<Real, StateType> > v_;
    };

    template <class PathType> inline
    LongstaffSchwartzPathPricer<PathType>::LongstaffSchwartzPathPricer(
        const TimeGrid& times,
        const boost::shared_ptr<EarlyExercisePathPricer<PathType> >&
            pathPricer,
        const boost::shared_ptr<YieldTermStructure>& termStructure)
    : calibrationPhase_(true),
      pathPricer_(pathPricer),
      coeff_     (new Array[times.size()-1]),
      dF_        (new DiscountFactor[times.size()-1]),
      v_         (pathPricer_->basisSystem()) {

        for (Size i=0; i<times.size()-1; ++i) {
            dF_[i] =   termStructure->discount(times[i+1])
                     / termStructure->discount(times[i]);
        }
    }

    template <class PathType> inline
    Real LongstaffSchwartzPathPricer<PathType>::operator()
        (const PathType& path) const {
        if (calibrationPhase_) {
            // store paths for the calibration
            paths_.push_back(path);
            // result doesn't matter
            return 0.0;
        }

        const Size len = EarlyExerciseTraits<PathType>::pathLength(path);
        Real price = (*pathPricer_)(path, len-1);
        for (Size i=len-2; i>0; --i) {
            price*=dF_[i];

            const Real exercise = (*pathPricer_)(path, i);
            if (exercise > 0.0) {
                const StateType regValue = pathPricer_->state(path, i);

                Real continuationValue = 0.0;
                for (Size l=0; l<v_.size(); ++l) {
                    continuationValue += coeff_[i][l] * v_[l](regValue);
                }

                if (continuationValue < exercise) {
                    price = exercise;
                }
            }
        }

        return price*dF_[0];
    }

    template <class PathType> inline
    void LongstaffSchwartzPathPricer<PathType>::calibrate() {
        const Size n = paths_.size();
        Array prices(n), exercise(n);
        const Size len = EarlyExerciseTraits<PathType>::pathLength(paths_[0]);

        for (Size i=0; i<n; ++i)
            prices[i] = (*pathPricer_)(paths_[i], len-1);

        std::vector<Real>      y;
        std::vector<StateType> x;
        for (Size i=len-2; i>0; --i) {
            y.clear();
            x.clear();

            //roll back step
            for (Size j=0; j<n; ++j) {
                exercise[j]=(*pathPricer_)(paths_[j], i);

                if (exercise[j]>0.0) {
                    x.push_back(pathPricer_->state(paths_[j], i));
                    y.push_back(dF_[i]*prices[j]);
                }
            }

            if (v_.size() <=  x.size()) {
                coeff_[i] = GeneralLinearLeastSquares(x, y, v_).coefficients();
            }
            else {
            // if number of itm paths is smaller then the number of
            // calibration functions then early exercise if exerciseValue > 0
                coeff_[i] = Array(v_.size(), 0.0);
            }

            for (Size j=0, k=0; j<n; ++j) {
                prices[j]*=dF_[i];
                if (exercise[j]>0.0) {
                    Real continuationValue = 0.0;
                    for (Size l=0; l<v_.size(); ++l) {
                        continuationValue += coeff_[i][l] * v_[l](x[k]);
                    }
                    if (continuationValue < exercise[j]) {
                        prices[j] = exercise[j];
                    }
                    ++k;
                }
            }
        }

        // remove calibration paths and release memory
        std::vector<PathType> empty;
        paths_.swap(empty);
        // entering the calculation phase
        calibrationPhase_ = false;
    }
}

#endif


#include "pipeline.hpp"
#include <boost/filesystem.hpp>
#include <CGAL/Polygon_mesh_processing/clip.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/Polygon_mesh_processing/transform.h>

#include <CGAL/Polygon_mesh_processing/remesh.h>
#include <CGAL/Polygon_mesh_processing/border.h>
#include <boost/function_output_iterator.hpp>

namespace PMP = CGAL::Polygon_mesh_processing;
namespace params = PMP::parameters;

typedef boost::graph_traits<Mesh>::halfedge_descriptor halfedge_descriptor;
typedef boost::graph_traits<Mesh>::edge_descriptor     edge_descriptor;

using namespace boost::filesystem;
namespace PMP = CGAL::Polygon_mesh_processing;
namespace params = PMP::parameters;

struct halfedge2edge   {
        halfedge2edge(const Mesh& m, std::vector<edge_descriptor>& edges)
        : m_mesh(m), m_edges(edges)
        {}
        void operator()(const halfedge_descriptor& h) const
            {  m_edges.push_back(edge(h, m_mesh)); }
        const Mesh& m_mesh;
        std::vector<edge_descriptor>& m_edges;
    };

int generate_subdomain_meshes(
    std::vector<Tissue> &tissues,
    std::vector<Tissue> &subdomain_meshes,
    vector<Mesh> &patch_vec,
    vector<std::pair<int, int> >  &pair_vec,
    std::string out_dir
){
    cout << "\nout_dir = " << out_dir << "\n";
    path p (out_dir.c_str());                                                                                // Move directory checking to prog_opts.cpp //#########
    try {
        if (!is_directory(p)){cout << p << "is not a directory\n";}
        else {  cout << p << " is a directory \n";}
    }catch (const filesystem_error& ex)  {cout << ex.what() << '\n';}
/*
//   cout << "tissues.begin()->mesh_vec.begin()->is_empty()  = "
//   << tissues.begin()->mesh_vec.begin()->is_empty() << "\n ";*/

    // store new meshes in std::vector<Tissue> subdomain_meshes
    // nb "if any" applies to each step
    // For each tissue, store
    //i)Union - senior tissues
    //ii)remaining non-contact surface
    //iii)each contact patch - named by tissues. 1 for each tissue type, make union, nb preserve features

    // build tissue unions
    for(std::vector<Tissue>::iterator tissue_it = tissues.begin(); tissue_it != tissues.end(); ++tissue_it) {
        if (  !(tissue_it->mesh_vec.size()) ) continue;
        Mesh union_msh = *tissue_it->mesh_vec.begin();

        for(std::vector<Mesh>::iterator mesh_it = tissue_it->mesh_vec.begin() +1; mesh_it != tissue_it->mesh_vec.end(); ++mesh_it) {                // i) for each tissue
            PMP::corefine_and_compute_union( union_msh, *mesh_it,  union_msh );
        }//mesh_it
        Tissue new_tissue;
        new_tissue.name = tissue_it->name;
        new_tissue.YoungsModulus = tissue_it->YoungsModulus;
        new_tissue.mesh_vec.push_back(union_msh);
        new_tissue.mesh_vec.push_back(union_msh);
        subdomain_meshes.push_back(new_tissue);
    }//tissue_it

/*
 *    // Save tissue unions for inspection
    for(std::vector<Tissue>::iterator tissue_it_a = subdomain_meshes.begin(); tissue_it_a != subdomain_meshes.end(); ++tissue_it_a) {
        if (!tissue_it_a->mesh_vec.at(1).is_empty() )   {
            int iter_num_a = tissue_it_a - subdomain_meshes.begin() +1;
            std::string filename = p.c_str();
            filename += "/tissue_union_" + to_string(iter_num_a) + tissue_it_a->name + ".off";
            cout << filename << endl;
            std::ofstream output(filename);
            output << tissue_it_a->mesh_vec.at(1);
            output.close();
        }
    }*///tissue_it_a

    cout << "subdomain_meshes.begin()->mesh_vec.begin()->is_empty()  = " << subdomain_meshes.begin()->mesh_vec.begin()->is_empty() << endl;
    cout << flush;
    // for each tissue ## generate patches ##
    for(std::vector<Tissue>::iterator tissue_it_a = subdomain_meshes.begin(); tissue_it_a != subdomain_meshes.end(); ++tissue_it_a) {
        cout << "\n\ntissue_it_a = " << tissue_it_a - subdomain_meshes.begin()<< std::endl;

        for(std::vector<Tissue>::iterator tissue_it_b = subdomain_meshes.begin(); tissue_it_b != tissue_it_a; ++tissue_it_b) {                      // for each senior tissue ## generate interface patches ##
            cout << "\ntissue_it_b = " << tissue_it_b - subdomain_meshes.begin() << "\t" << std::flush;
            Tissue target = *tissue_it_a;

            for(std::vector<Tissue>::iterator tissue_it_c = subdomain_meshes.begin(); tissue_it_c != tissue_it_a; ++tissue_it_c) {                  // form union of all other senior tissues - may be the wrong approach.
            // instead, subtract each other tissue from the target.
                cout << "\ntissue_it_c = " << tissue_it_c - subdomain_meshes.begin()<< std::flush;
                if (tissue_it_c == tissue_it_b){
                    cout << "\tcontinue\n";
                    continue;
                }
                PMP::corefine_and_compute_difference(
                    target.mesh_vec.at(1),                           // target
                    tissue_it_c->mesh_vec.at(1),                     // clipper
                    target.mesh_vec.at(1)
                );
            }//tissue_it_c
            // 'target' now _should_ contain 'tissue_it_a', cropped by every tissue senior to 'a' except 'b'.
            cout << "target.mesh_vec.at(1).is_empty() = " << target.mesh_vec.at(1).is_empty() << endl;
/*
            if (!target.mesh_vec.at(1).is_empty() )   {
                int iter_num_a = tissue_it_a - subdomain_meshes.begin() +1;
                int iter_num_b = tissue_it_b - subdomain_meshes.begin() +1;
                std::string filename = p.c_str();
                filename += "/target_" + to_string(iter_num_a) + tissue_it_a->name + to_string(iter_num_b) + tissue_it_b->name + ".off";
                cout << filename << endl;
                std::ofstream output(filename);
                output << target.mesh_vec.at(1);
                output.close();
            }*/
            Mesh target_mesh = tissue_it_b->mesh_vec.at(1);//target.mesh_vec.at(1);
            Mesh clipper_mesh = target.mesh_vec.at(1);
/*
             if (!target_mesh.is_empty() )   {         //   target.mesh_vec.at(1)                                                                                // name and save the patch
                int iter_num_a = tissue_it_a - subdomain_meshes.begin() +1;
                int iter_num_b = tissue_it_b - subdomain_meshes.begin() +1;
                const std::pair<int, int> pair(iter_num_b, iter_num_a);
                cout << "\n target_mesh : " << iter_num_b << "_" << iter_num_a;

                std::string filename_t = p.c_str();
                filename_t += "/" + to_string(patch_vec.size()) + "target_mesh_" + to_string(iter_num_b) + tissue_it_b->name + "_" + to_string(iter_num_a) + tissue_it_a->name + ".off";
                std::ofstream output(filename_t);
                output << target_mesh;
                output.close();
             }*/
/*
             if (!clipper_mesh.is_empty() )   {      // target.mesh_vec.at(1)                                                                                // name and save the patch
                int iter_num_a = tissue_it_a - subdomain_meshes.begin() +1;
                int iter_num_b = tissue_it_b - subdomain_meshes.begin() +1;
                const std::pair<int, int> pair(iter_num_b, iter_num_a);
                cout << "\n clipper_mesh : " << iter_num_b << "_" << iter_num_a << endl;

                std::string filename_c = p.c_str();
                filename_c += "/" + to_string(patch_vec.size()) + "clipper_mesh_" + to_string(iter_num_b) + tissue_it_b->name + "_" + to_string(iter_num_a) + tissue_it_a->name + ".off";
                std::ofstream output(filename_c);
                output << clipper_mesh;
                output.close();
             }*/

            PMP::corefine_and_compute_intersection(
                target_mesh,//target.mesh_vec.at(1),                           // target
                clipper_mesh,//tissue_it_b->mesh_vec.at(1),                     // clipper
                target_mesh,//target.mesh_vec.at(1),

                params::clip_volume(false).face_index_map( get(CGAL::dynamic_face_property_t<std::size_t>(), target_mesh) ).use_bool_op_to_clip_surface(true),

                params::face_index_map( get(CGAL::dynamic_face_property_t<std::size_t>(), clipper_mesh)  )
            );
            // 'target' now _should_ contain only the contact patch between 'a' and 'b'

            if (!target_mesh/*target.mesh_vec.at(1)*/.is_empty() )   {                                                                                // name and save the patch
                int iter_num_a = tissue_it_a - subdomain_meshes.begin() +1;
                int iter_num_b = tissue_it_b - subdomain_meshes.begin() +1;
                const std::pair<int, int> pair(iter_num_b, iter_num_a);
                cout << "\n target patch : " << iter_num_b << "_" << iter_num_a;
/* */           patch_vec.push_back(target_mesh);
                pair_vec.push_back(pair);

                std::string filename = p.c_str();
                filename += "/" + to_string(patch_vec.size()) + "patch_" + to_string(iter_num_b) + tissue_it_b->name + "_" + to_string(iter_num_a) + tissue_it_a->name + ".off";
                std::ofstream output(filename);
                output << target_mesh;
                output.close();
             }
        }//tissue_it_b  // for each senior tissue ## generate interface patches ##

                                                                                // ## generate freespace patches ##
        Mesh target = tissue_it_a->mesh_vec.at(1);
        for(std::vector<Tissue>::iterator tissue_it_d = subdomain_meshes.begin(); tissue_it_d != subdomain_meshes.end(); ++tissue_it_d) {           // form union of all other tissues
            if (tissue_it_d == tissue_it_a)continue;
            cout << "\ntissue_it_d = " << tissue_it_d - subdomain_meshes.begin() << std::endl;

            Mesh Clipper = tissue_it_d->mesh_vec.at(1);
            CGAL::Polygon_mesh_processing::reverse_face_orientations(Clipper);   // invert the clipper, leave what is outside all other subdomains.
            PMP::corefine_and_compute_intersection(
                target,
                Clipper,
                target,

                params::clip_volume(false).face_index_map( get(                                                       CGAL::dynamic_face_property_t<std::size_t>(), target ) ).use_bool_op_to_clip_surface(true),

                params::face_index_map( get(CGAL::dynamic_face_property_t<std::size_t>(), Clipper))
                );
        }//tissue_it_d  // ## generate freespace patches ##
        // target should contain only the freespace patch of 'tissue_it_a'

        if (!target.is_empty() )   {
            int iter_num_a = tissue_it_a - subdomain_meshes.begin() +1;
            int iter_num_b = 0;                                                                                // 0, i.e. free surface
            const std::pair<int, int> pair(iter_num_a, iter_num_b);
            cout << "\n target patch : " << iter_num_a << "_" << iter_num_b;
/* */       patch_vec.push_back(target);
            pair_vec.push_back(pair);

            std::string filename = p.c_str();
            filename += "/" + to_string(patch_vec.size()) + "patch_freespace_" + to_string(iter_num_a) + tissue_it_a->name + "_" + to_string(iter_num_b) + ".off";
            std::ofstream output(filename);
            output << target;
            output.close();
        }//if (!target.is_empty() )
    }//tissue_it_a

/*
    // write original patches to file for comparison
    const std::size_t nb_patches = patch_vec.size();
    for (std::size_t i = 0; i < nb_patches; ++i){
      std::string filename = out_dir;
      filename += "/original_patch" + to_string(i) + ".off";
      std::ofstream outfile(filename);
      outfile << patch_vec.at(i);
      outfile.close();
      cout << "\n Wrote filename :" << filename ;
    }

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

    return patch_vec.size();
}


#include <cctbx/boost_python/flex_fwd.h>

#include <cctbx/hendrickson_lattman.h>
#include <cctbx/miller/merge_equivalents.h>
#include <boost/python/class.hpp>
#include <boost/python/return_value_policy.hpp>
#include <boost/python/return_by_value.hpp>
#include <string>

namespace cctbx { namespace miller { namespace boost_python {

namespace {

  struct merge_equivalents_real_wrappers
  {
    typedef merge_equivalents_real<> w_t;

    static void
    wrap()
    {
      using namespace boost::python;
      typedef return_value_policy<return_by_value> rbv;
      class_<w_t>("merge_equivalents_real", no_init)
        .def(init<af::const_ref<index<> > const&,
                  af::const_ref<double> const&>())
        .add_property("indices", make_getter(&w_t::indices, rbv()))
        .add_property("data", make_getter(&w_t::data, rbv()))
        .add_property("redundancies", make_getter(&w_t::redundancies, rbv()))
        .add_property("r_linear", make_getter(&w_t::r_linear, rbv()))
        .add_property("r_square", make_getter(&w_t::r_square, rbv()))
        .add_property("r_int", &w_t::r_int)
        .add_property("r_merge", &w_t::r_merge)
        .add_property("r_meas", &w_t::r_meas)
        .add_property("r_pim", &w_t::r_pim)
      ;
    }
  };

  struct merge_equivalents_string_wrappers
  {
    typedef merge_equivalents_string<std::string, double> w_t;

    static void
      wrap()
    {
      using namespace boost::python;
      typedef return_value_policy<return_by_value> rbv;
      class_<w_t>("merge_equivalents_string", no_init)
        .def(init<af::const_ref<index<> > const&,
          af::const_ref<std::string > const&>())
        .add_property("indices", make_getter(&w_t::indices, rbv()))
        .add_property("data", make_getter(&w_t::data, rbv()))
        .add_property("redundancies", make_getter(&w_t::redundancies, rbv()))
        ;
    }
  };

  struct merge_equivalents_complex_wrappers
  {
    typedef merge_equivalents_generic<std::complex<double>, double> w_t;

    static void
      wrap()
    {
      using namespace boost::python;
      typedef return_value_policy<return_by_value> rbv;
      class_<w_t>("merge_equivalents_complex", no_init)
        .def(init<af::const_ref<index<> > const&,
          af::const_ref<std::complex<double> > const&>())
        .add_property("indices", make_getter(&w_t::indices, rbv()))
        .add_property("data", make_getter(&w_t::data, rbv()))
        .add_property("redundancies", make_getter(&w_t::redundancies, rbv()))
        ;
    }
  };

  struct merge_equivalents_hl_wrappers
  {
    typedef merge_equivalents_generic<hendrickson_lattman<>, double> w_t;

    static void
    wrap()
    {
      using namespace boost::python;
      typedef return_value_policy<return_by_value> rbv;
      class_<w_t>("merge_equivalents_hl", no_init)
        .def(init<af::const_ref<index<> > const&,
                  af::const_ref<hendrickson_lattman<> > const&>())
        .add_property("indices", make_getter(&w_t::indices, rbv()))
        .add_property("data", make_getter(&w_t::data, rbv()))
        .add_property("redundancies", make_getter(&w_t::redundancies, rbv()))
      ;
    }
  };

  template <typename IntegralType>
  struct merge_equivalents_exact_wrappers
  {
    typedef merge_equivalents_exact<IntegralType> w_t;

    static void
    wrap(const char* python_name)
    {
      using namespace boost::python;
      typedef return_value_policy<return_by_value> rbv;
      class_<w_t>(python_name, no_init)
        .def(init<af::const_ref<index<> > const&,
                  af::const_ref<IntegralType> const&,
                  boost::optional<IntegralType> >
                  //())
                  ((arg("unmerged_indices"), arg("unmerged_data"),
                  arg("incompatible_flags_replacement")=boost::optional<IntegralType>())))
        .add_property("indices", make_getter(&w_t::indices, rbv()))
        .add_property("data", make_getter(&w_t::data, rbv()))
        .add_property("redundancies", make_getter(&w_t::redundancies, rbv()))
        .add_property("n_incompatible_flags", make_getter(&w_t::n_incompatible_flags, rbv()))
      ;
    }
  };

  struct merge_equivalents_obs_wrappers
  {
    typedef merge_equivalents_obs<> w_t;

    static void
    wrap()
    {
      using namespace boost::python;
      typedef return_value_policy<return_by_value> rbv;
      class_<w_t>("merge_equivalents_obs", no_init)
        .def(init<
          af::const_ref<index<> > const&,
          af::const_ref<double> const&,
          af::const_ref<double> const&,
          double, bool>((
            arg("unmerged_indices"),
            arg("unmerged_data"),
            arg("unmerged_sigmas"),
            arg("sigma_dynamic_range")=1e-6,
            arg("use_internal_variance")=true)))
        .add_property("indices", make_getter(&w_t::indices, rbv()))
        .add_property("data", make_getter(&w_t::data, rbv()))
        .add_property("sigmas", make_getter(&w_t::sigmas, rbv()))
        .def_readonly("sigma_dynamic_range", &w_t::sigma_dynamic_range)
        .add_property("redundancies", make_getter(&w_t::redundancies, rbv()))
        .add_property("r_linear", make_getter(&w_t::r_linear, rbv()))
        .add_property("r_square", make_getter(&w_t::r_square, rbv()))
        .add_property("r_int", &w_t::r_int)
        .add_property("r_merge", &w_t::r_merge)
        .add_property("r_meas", &w_t::r_meas)
        .add_property("r_pim", &w_t::r_pim)
      ;
    }
  };

  struct merge_equivalents_shelx_wrappers
  {
    typedef merge_equivalents_shelx<> w_t;

    static void
    wrap()
    {
      using namespace boost::python;
      typedef return_value_policy<return_by_value> rbv;
      class_<w_t>("merge_equivalents_shelx", no_init)
        .def(init<af::const_ref<index<> > const&,
                  af::const_ref<double> const&,
                  af::const_ref<double> const&>())
        .add_property("indices", make_getter(&w_t::indices, rbv()))
        .add_property("data", make_getter(&w_t::data, rbv()))
        .add_property("sigmas", make_getter(&w_t::sigmas, rbv()))
        .add_property("redundancies", make_getter(&w_t::redundancies, rbv()))
        .add_property("r_linear", make_getter(&w_t::r_linear, rbv()))
        .add_property("r_square", make_getter(&w_t::r_square, rbv()))
        .add_property("r_int", &w_t::r_int)
        .add_property("r_merge", &w_t::r_merge)
        .add_property("r_meas", &w_t::r_meas)
        .add_property("r_pim", &w_t::r_pim)
        .add_property("inconsistent_equivalents", &w_t::inconsistent_equivalents)
      ;
    }
  };

  struct split_unmerged_wrappers
  {
    typedef split_unmerged<> w_t;

    static void
    wrap()
    {
      using namespace boost::python;
      typedef return_value_policy<return_by_value> rbv;
      class_<w_t>("split_unmerged", no_init)
        .def(init<af::const_ref<index<> > const&,
                  af::const_ref<double> const&,
                  af::const_ref<double> const&, bool, unsigned>((
            arg("unmerged_indices"),
            arg("unmerged_data"),
            arg("unmerged_sigmas"),
            arg("weighted")=true,
            arg("seed")=0)))
        .add_property("data_1", make_getter(&w_t::data_1, rbv()))
        .add_property("data_2", make_getter(&w_t::data_2, rbv()))
        .add_property("indices", make_getter(&w_t::indices, rbv()));

//        .add_property("coefficient", &w_t::coefficient);
    }
  };

} // namespace <anoymous>

  void wrap_merge_equivalents()
  {
    merge_equivalents_real_wrappers::wrap();
    merge_equivalents_complex_wrappers::wrap();
    merge_equivalents_string_wrappers::wrap();
    merge_equivalents_hl_wrappers::wrap();
    merge_equivalents_exact_wrappers<bool>::wrap(
      "merge_equivalents_exact_bool");
    merge_equivalents_exact_wrappers<int>::wrap(
      "merge_equivalents_exact_int");
    merge_equivalents_obs_wrappers::wrap();
    merge_equivalents_shelx_wrappers::wrap();
    split_unmerged_wrappers::wrap();
  }

}}} // namespace cctbx::miller::boost_python