#include <numeric>
#include <algorithm>

#include <boost/range/adaptors.hpp>
namespace ba = boost::adaptors;

template<class P>
template<class Filtration, class ReportPair>
void
dionysus::ClearingReduction<P>::
operator()(const Filtration& filtration, const ReportPair& report_pair)
{
    using Cell = typename Filtration::Cell;
    (*this)(filtration, [](const Cell&) { return false; }, report_pair, &no_progress);
}

template<class P>
template<class Filtration, class Relative, class ReportPair, class Progress>
void
dionysus::ClearingReduction<P>::
operator()(const Filtration& filtration, const Relative& relative, const ReportPair& report_pair, const Progress& progress)
{
    persistence_.resize(filtration.size());

    // sort indices by decreasing dimension
    std::vector<size_t> indices(filtration.size());
    std::iota(indices.begin(), indices.end(), 0);
    std::stable_sort(indices.begin(), indices.end(),
                     [&filtration](size_t x, size_t y)
                     { return filtration[x].dimension() > filtration[y].dimension(); });

    typedef     typename Filtration::Cell                       Cell;
    typedef     ChainEntry<Field, Cell>                         CellChainEntry;
    typedef     ChainEntry<Field, Index>                        ChainEntry;

    for(size_t i : indices)
    {
        progress();
        const auto& c = filtration[i];

        if (relative(c))
        {
            persistence_.set_skip(i);
            continue;
        }

        if (persistence_.pair(i) != persistence_.unpaired())
            continue;

        persistence_.set(i, c.boundary(persistence_.field()) |
                                       ba::filtered([relative](const CellChainEntry& e) { return !relative(e.index()); }) |
                                       ba::transformed([this,&filtration](const CellChainEntry& e)
                                       { return ChainEntry(e.element(), filtration.index(e.index())); }));

        Index pair = persistence_.reduce(i);
        if (pair != persistence_.unpaired())
            report_pair(c.dimension(), pair, i);
    }
}


/*!
 * @file
 * Defines unit tests for left fold algorithms.
 *
 *
 * @copyright Louis Dionne 2014
 * Distributed under the Boost Software License, Version 1.0.
 *         (See accompanying file LICENSE.md or copy at
 *             http://www.boost.org/LICENSE_1_0.txt)
 */

//////////////////////////////////////////////////////////////////////////
// GENERATED HEADER: DO NOT EDIT.
//////////////////////////////////////////////////////////////////////////

#ifndef BOOST_MPL11_DETAIL_TEST_FOLDL_HPP
#define BOOST_MPL11_DETAIL_TEST_FOLDL_HPP

#include <boost/mpl11/detail/test/expect.hpp>
#include <boost/mpl11/functional.hpp>

namespace boost { namespace mpl11 { namespace detail {
    namespace foldl_detail {
        template <int> struct x { using type = x; };

        template <typename ...x>
        struct f { using type = f<typename x::type...>; };

        <%
            def xs(range)
              range.map(&:to_s).map{|n| "x<#{n}>"}
            end
        %>

        template <template <typename ...> class fold>
        struct test_foldl :
            <% for n in 0..20 %>
                <%= ',' unless n == 0 -%>
                expect<fold<lift<f>, x<0> <%= comma(xs(1..n)).join %>>
                >::template to_eq<
                    <%= xs(1..n).foldl('x<0>'){|x,y| "f<#{x}, #{y}>" } %>
                >
            <% end %>
        { };
    } // end namespace foldl_detail

    using foldl_detail::test_foldl;
}}} // end namespace boost::mpl11::detail

#endif // !BOOST_MPL11_DETAIL_TEST_FOLDL_HPP


#include <algorithm>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/program_options.hpp>
#include <cmath>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <mpi.h>
#ifdef LIKWID
#include <likwid.h>
#endif

// own includes ---------------------------------------------------------------
#include "bte_config.h"
#include "collision_tensor/collision_tensor_galerkin.hpp"
#include "collision_tensor/dense/multi_slices_factory.hpp"
#include "aux/rdtsc_timer.hpp"
#include "aux/timer.hpp"
#include "collision_tensor/dense/storage/vbcrs_sparsity.hpp"
#include "spectral/basis/spectral_basis_factory_ks.hpp"

namespace po = boost::program_options;
namespace bf = boost::filesystem;

#include <yaml-cpp/yaml.h>

using namespace std;
using namespace boltzmann;

typedef SpectralBasisFactoryKS basis_factory_t;
typedef SpectralBasisFactoryKS::basis_type basis_type;

int main(int argc, char* argv[])
{
  MPI_Init(&argc, &argv);

  std::string version_id = GIT_SHA1;
  cout << "VersionID: " << version_id << "@" << GIT_BNAME << std::endl;

  int nrep;
  if (argc < 2) {
    cerr << "usage: " << argv[0] << " nrep"
         << "\n";
    return 1;
  } else {
    nrep = atoi(argv[1]);
    cerr << "nrep: " << nrep << "\n";
  }

  // ----------------------------------------------------------------------
  // Load config
  if (!boost::filesystem::is_regular_file("config.yaml")) {
    cout << "config file not found\n";
    return 1;
  }
  YAML::Node config = YAML::LoadFile("config.yaml");
  const size_t K = config["SpectralBasis"]["deg"].as<size_t>();

  // ----------------------------------------------------------------------
  // create basis
  basis_type basis;
  SpectralBasisFactoryKS::create(basis, K);

  unsigned int n_velo_dofs = basis.n_dofs();

  Timer<> stimer;
  stimer.start();
  CollisionTensorGalerkin Q(basis);
  stimer.print(std::cout, stimer.stop(), "tensor constructor");

  stimer.start();
  Q.read_hdf5(config["Scattering"]["file"].as<std::string>().c_str());
  stimer.print(std::cout, stimer.stop(), "loading tensor");

  Eigen::VectorXd x(n_velo_dofs);
  x.setOnes();

#ifdef LIKWID
  LIKWID_MARKER_INIT;
  LIKWID_MARKER_REGISTER("CT");
  LIKWID_MARKER_START("CT");
#else
  RDTSCTimer timer;
#endif

  MPI_Barrier(MPI_COMM_WORLD);
  Eigen::VectorXd y2(n_velo_dofs);
  for (int i = 0; i < nrep; ++i) {
#ifndef LIKWID
    timer.start();
#endif
    Q.apply(y2.data(), x.data());
#ifndef LIKWID
    auto tlap = timer.stop();
    timer.print(cout, tlap, "sparse");
#endif
  }
#ifdef LIKWID
  LIKWID_MARKER_STOP("CT");
  LIKWID_MARKER_CLOSE;
#endif

  MPI_Finalize();
  return 0;
}


#ifndef TENSOR_H_
#define TENSOR_H_

#include <Eigen/Dense>

class TensorXd {
public:
  TensorXd(int nrow, int ncol, int nslice);
  Eigen::Block<Eigen::MatrixXd> operator[](int slice);

private:
  int nrow_, ncol_, nslice_;
  Eigen::MatrixXd value_;
};

#endif


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

// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2008-2012 Bruno Lalande, Paris, France.
// Copyright (c) 2009-2012 Mateusz Loskot, London, UK.
// Copyright (c) 2013 Adam Wulkiewicz, Lodz, Poland.

// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.

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

#ifndef BOOST_GEOMETRY_EXTENSIONS_ALGEBRA_GEOMETRIES_ROTATION_MATRIX_HPP
#define BOOST_GEOMETRY_EXTENSIONS_ALGEBRA_GEOMETRIES_ROTATION_MATRIX_HPP

#include <cstddef>

#include <boost/geometry/extensions/algebra/core/tags.hpp>
#include <boost/geometry/extensions/algebra/geometries/concepts/rotation_matrix_concept.hpp>

namespace boost { namespace geometry {

namespace model {

template <typename T, std::size_t Dimension>
class rotation_matrix
{
    BOOST_CONCEPT_ASSERT( (concepts::RotationMatrix<rotation_matrix>) );

public:

    /// @brief Default constructor, no initialization
    inline rotation_matrix()
    {}

    /// @brief Get a coordinate
    /// @tparam I row index
    /// @tparam J col index
    /// @return the cell value
    template <std::size_t I, std::size_t J>
    inline T const& get() const
    {
        BOOST_STATIC_ASSERT(I < Dimension);
        BOOST_STATIC_ASSERT(J < Dimension);
        return m_values[I * Dimension + J];
    }

    /// @brief Set a coordinate
    /// @tparam I row index
    /// @tparam J col index
    /// @param value value to set
    template <std::size_t I, std::size_t J>
    inline void set(T const& value)
    {
        BOOST_STATIC_ASSERT(I < Dimension);
        BOOST_STATIC_ASSERT(J < Dimension);
        m_values[I * Dimension + J] = value;
    }

private:

    T m_values[Dimension * Dimension];
};

} // namespace model

#ifndef DOXYGEN_NO_TRAITS_SPECIALIZATIONS
namespace traits
{

template <typename CoordinateType, std::size_t Dimension>
struct tag<model::rotation_matrix<CoordinateType, Dimension> >
{
    typedef rotation_matrix_tag type;
};

template <typename CoordinateType, std::size_t Dimension>
struct coordinate_type<model::rotation_matrix<CoordinateType, Dimension> >
{
    typedef CoordinateType type;
};

template <typename CoordinateType, std::size_t Dimension>
struct coordinate_system<model::rotation_matrix<CoordinateType, Dimension> >
{
    typedef cs::cartesian type;
};

template <typename CoordinateType, std::size_t Dimension>
struct dimension<model::rotation_matrix<CoordinateType, Dimension> >
    : boost::mpl::int_<Dimension>
{};

template <typename CoordinateType, std::size_t Dimension, std::size_t I, std::size_t J>
struct indexed_access<model::rotation_matrix<CoordinateType, Dimension>, I, J>
{
    typedef CoordinateType coordinate_type;

    static inline coordinate_type get(model::rotation_matrix<CoordinateType, Dimension> const& m)
    {
        return m.template get<I, J>();
    }

    static inline void set(model::rotation_matrix<CoordinateType, Dimension> & m, coordinate_type const& value)
    {
        m.template set<I, J>(value);
    }
};

} // namespace traits
#endif // DOXYGEN_NO_TRAITS_SPECIALIZATIONS

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_EXTENSIONS_ALGEBRA_GEOMETRIES_ROTATION_MATRIX_HPP


#include <unordered_map>
#include <string>
#include <climits>

#include <gtest/gtest.h>
#include <Eigen/Dense>

#include <BingoCpp/agraph.h>
#include <BingoCpp/backend.h>

#include "test_fixtures.h"
#include "testing_utils.h"

using namespace bingo;

typedef std::unordered_map<std::string, std::string> GraphString;
typedef std::unordered_map<std::string , GraphString> StringMap;

namespace {

struct AGraphTestVals {
  Eigen::ArrayXXd x;
  Eigen::ArrayXXd f_of_x;
  Eigen::ArrayXXd grad_x;
  Eigen::ArrayXXd grad_c;

  AGraphTestVals() {}
  AGraphTestVals(Eigen::ArrayXXd x_,
                 Eigen::ArrayXXd f_of_x_,
                 Eigen::ArrayXXd grad_x_,
                 Eigen::ArrayXXd grad_c_) :
      x(x_),
      f_of_x(f_of_x_),
      grad_x(grad_x_),
      grad_c(grad_c_) {}
};

class AGraphTest : public ::testing::TestWithParam<std::string> {
 public:
  AGraph invalid_graph;
  AGraph sample_agraph_1;
  AGraph all_funcs_graph;
  StringMap map_to_graph_string;
  std::unordered_map<std::string, AGraph> map_to_graph;
  AGraphTestVals sample_agraph_1_values;

  void SetUp() {
    sample_agraph_1 = testutils::init_sample_agraph_1();
    invalid_graph = init_invalid_graph(sample_agraph_1);
    all_funcs_graph = init_all_funcs_graph();
    map_to_graph_string = init_hash_map();
    map_to_graph = {
      {"all_funcs_graph", all_funcs_graph},
      {"sample_agraph_1", sample_agraph_1},
      {"invalid_graph", invalid_graph}
    };
    sample_agraph_1_values = init_sample_agraph_1_values();
  }

  void TearDown() {}

  AGraph init_invalid_graph(AGraph &agraph) {
    AGraph return_val = AGraph();
    return_val.SetCommandArray(agraph.GetCommandArray());
    return return_val;
  }

  AGraphTestVals init_sample_agraph_1_values() {
    int num_points = 11;
    Eigen::ArrayXXd x(num_points, 2);
    x.col(0) = Eigen::ArrayXd::LinSpaced(num_points, -1, 0);
    x.col(1) = Eigen::ArrayXd::LinSpaced(num_points, 0, 1);
    Eigen::ArrayXXd f_of_x = (x + 1.0).sin() + 1.0;
    Eigen::ArrayXXd grad_x = Eigen::ArrayXXd::Zero(x.rows(), x.cols());
    grad_x.col(0) = (x.col(0) + 1.0).cos();
    Eigen::ArrayXXd grad_c = (x + 1.0).cos() + 1.0;
    return AGraphTestVals(x, f_of_x.col(0), grad_x, grad_c.col(0));
  }

  AGraph init_all_funcs_graph() {
    AGraph test_graph = AGraph();
    test_graph.SetGeneticAge(10);
    Eigen::ArrayX3i command_array(13, 3);
    command_array << 0, 0, 0,
                     1, 0, 0,
                     2, 1, 0,
                     3, 2, 0,
                     4, 3, 0,
                     5, 4, 0,
                     6, 5, 0,
                     7, 6, 0,
                     8, 7, 0,
                     9, 8, 0,
                     10, 9, 0,
                     11, 10, 0,
                     12, 11, 0;
    test_graph.SetCommandArray(command_array);
    Eigen::VectorXd local_opt_params(2);
    local_opt_params << 1.0, 1.0;
    test_graph.SetLocalOptimizationParams(local_opt_params);
    return test_graph;
  }

  StringMap init_hash_map() {
    StringMap return_val;
    GraphString all_funcs_graph_map {
      {"latex string", "\\sqrt{ |(log{ exp{ cos{ sin{ \\frac{ (1.000000"
                       " + X_0 - (X_0))(X_0) }{ X_0 } } } } })^{ ("
                       "X_0) }| }"},
      {"console string", "sqrt(|(log(exp(cos(sin(((1.000000 + X_0 - (X_"
                         "0))(X_0))/(X_0))))))^(X_0)|)"},
      {"complexity", "13"}
    };
    GraphString sample_agraph_1_map {
      {"latex string", "sin{ X_0 + 1.000000 } + 1.000000"},
      {"console string", "sin(X_0 + 1.000000) + 1.000000"},
      {"complexity", "5"}
    };
    GraphString invalid_graph_map {
      {"latex string", "sin{ X_0 + ? } + ?"},
      {"console string", "sin(X_0 + ?) + ?"},
      {"complexity", "5"}
    };
    return_val.insert({"all_funcs_graph", all_funcs_graph_map});
    return_val.insert({"sample_agraph_1", sample_agraph_1_map});
    return_val.insert({"invalid_graph", invalid_graph_map});
    return return_val;
  }
};

TEST_F(AGraphTest, copy) {
  AGraph agraph_copy = sample_agraph_1.Copy();

  Eigen::ArrayX3i command_array = sample_agraph_1.GetCommandArray();
  command_array(1, 1) = 100;
  sample_agraph_1.SetCommandArray(command_array);
  Eigen::VectorXd constants = sample_agraph_1.GetLocalOptimizationParams();
  constants[0] = 100;
  sample_agraph_1.SetLocalOptimizationParams(constants);

  ASSERT_EQ(agraph_copy.GetGeneticAge(), 10);
  ASSERT_EQ(agraph_copy.GetCommandArray()(1,1), 0);
  ASSERT_DOUBLE_EQ(agraph_copy.GetLocalOptimizationParams()[0], 1.0);
}

TEST_P(AGraphTest, latex_print) {
  std::string agraph_name = GetParam();
  std::string string_rep = map_to_graph_string.at(agraph_name).at("latex string");
  AGraph agraph = map_to_graph.at(agraph_name);
  ASSERT_STREQ(string_rep.c_str(), agraph.GetLatexString().c_str());
}

TEST_P(AGraphTest, console_print) {
  std::string agraph_name = GetParam();
  std::string string_rep = map_to_graph_string.at(agraph_name).at("console string");
  AGraph agraph = map_to_graph.at(agraph_name);
  std::stringstream ss;
  ss << agraph;
  ASSERT_STREQ(string_rep.c_str(), ss.str().c_str());
}

TEST_P(AGraphTest, complexity_print) {
  std::string agraph_name = GetParam();
  int complexity_val = std::stoi(map_to_graph_string.at(agraph_name).at("complexity"));
  AGraph agraph = map_to_graph.at(agraph_name);
  ASSERT_EQ(complexity_val, agraph.GetComplexity());
}

INSTANTIATE_TEST_CASE_P(,AGraphTest, ::testing::Values(
    "all_funcs_graph", "sample_agraph_1", "invalid_graph"));

TEST_F(AGraphTest, stack_print) {
  std::stringstream expected_str;
  expected_str << "---full stack---\n"
                   "(0) <= X_0\n"
                   "(1) <= C_0 = 1.000000\n"
                   "(2) <= (0) + (1)\n"
                   "(3) <= sin (2)\n"
                   "(4) <= (0) + (1)\n"
                   "(5) <= (3) + (1)\n"
                   "---small stack---\n"
                   "(0) <= X_0\n"
                   "(1) <= C_0 = 1.000000\n"
                   "(2) <= (0) + (1)\n"
                   "(3) <= sin (2)\n"
                   "(4) <= (3) + (1)\n";
  ASSERT_STREQ(expected_str.str().c_str(), sample_agraph_1.GetStackString().c_str());
}

TEST_F(AGraphTest, invalid_stack_print) {
  std::stringstream expected_str;
  expected_str << "---full stack---\n"
                   "(0) <= X_0\n"
                   "(1) <= C\n"
                   "(2) <= (0) + (1)\n"
                   "(3) <= sin (2)\n"
                   "(4) <= (0) + (1)\n"
                   "(5) <= (3) + (1)\n"
                   "---small stack---\n"
                   "(0) <= X_0\n"
                   "(1) <= C\n"
                   "(2) <= (0) + (1)\n"
                   "(3) <= sin (2)\n"
                   "(4) <= (3) + (1)\n";
  ASSERT_STREQ(expected_str.str().c_str(), invalid_graph.GetStackString().c_str());
}

TEST_F(AGraphTest, evaluateAt) {
  ASSERT_TRUE(testutils::almost_equal(
    sample_agraph_1_values.f_of_x,
    sample_agraph_1.EvaluateEquationAt(sample_agraph_1_values.x)
  ));
}

TEST_F(AGraphTest, evaluatWithXDerivative) {
  Eigen::ArrayXXd x = sample_agraph_1_values.x;
  EvalAndDerivative result = sample_agraph_1.EvaluateEquationWithXGradientAt(x);
  Eigen::ArrayXXd f_of_x = result.first;
  Eigen::ArrayXXd df_dx = result.second;
  ASSERT_TRUE(testutils::almost_equal(sample_agraph_1_values.f_of_x, f_of_x));
  ASSERT_TRUE(testutils::almost_equal(sample_agraph_1_values.grad_x, df_dx));
}

TEST_F(AGraphTest, evaluatWithCDerivative) {
  Eigen::ArrayXXd x = sample_agraph_1_values.x;
  EvalAndDerivative result = sample_agraph_1.EvaluateEquationWithLocalOptGradientAt(x);
  Eigen::ArrayXXd f_of_x = result.first;
  Eigen::ArrayXXd df_dc = result.second;
  ASSERT_TRUE(testutils::almost_equal(sample_agraph_1_values.f_of_x, f_of_x));
  ASSERT_TRUE(testutils::almost_equal(sample_agraph_1_values.grad_c, df_dc));
}

TEST_F(AGraphTest, NeedsLocalOptimization) {
  ASSERT_TRUE(invalid_graph.NeedsLocalOptimization());
}

TEST_F(AGraphTest, getNumberOfOptimizationParams) {
  ASSERT_EQ(invalid_graph.GetNumberLocalOptimizationParams(), 1);
}

TEST_F(AGraphTest, setOptimizationParams) {
  Eigen::VectorXd opt_params(1);
  opt_params << 1.0;
  invalid_graph.SetLocalOptimizationParams(opt_params);
  ASSERT_FALSE(invalid_graph.NeedsLocalOptimization());
  ASSERT_TRUE(testutils::almost_equal(
    invalid_graph.EvaluateEquationAt(sample_agraph_1_values.x),
    sample_agraph_1.EvaluateEquationAt(sample_agraph_1_values.x))
  );
}

TEST_F(AGraphTest, setting_fitness_updates_fit_set) {
  AGraph new_graph = AGraph();
  ASSERT_FALSE(new_graph.IsFitnessSet());
  new_graph.SetFitness(2.0);
  ASSERT_TRUE(new_graph.IsFitnessSet());
}

TEST_F(AGraphTest, notify_command_array_mod) {
  ASSERT_TRUE(sample_agraph_1.IsFitnessSet());
  sample_agraph_1.NotifyCommandArrayModificiation();
  ASSERT_FALSE(sample_agraph_1.IsFitnessSet());
}

TEST_F(AGraphTest, setting_command_array_unsets_fitness) {
  ASSERT_TRUE(sample_agraph_1.IsFitnessSet());
  sample_agraph_1.SetCommandArray(Eigen::ArrayX3i::Ones(1, 3));
  ASSERT_FALSE(sample_agraph_1.IsFitnessSet());
}

TEST_F(AGraphTest, TestDistanceBetweenGraphs) {
  ASSERT_EQ(sample_agraph_1.Distance(sample_agraph_1), 0);
  AGraph other_agraph = sample_agraph_1.Copy();
  other_agraph.GetCommandArrayModifiable().row(2) << 6, 1, 0;
  ASSERT_EQ(sample_agraph_1.Distance(other_agraph), 3);

}

// class AGraphExceptionTest : public ::testing::Test {
//  public:
//   AGraph x_squared;
//   Eigen::ArrayXXd large_x;

//   void SetUp() {
//     x_squared = init_exception_graph();
//     large_x = init_large_x_vals();
//   }

//   void TearDown() {}

//   AGraph init_exception_graph() {
//     Eigen::ArrayX3i command_array(2, 3);
//     command_array << 0, 0, 0,
//                      4, 0, 0;
//     AGraph test_stack = AGraph();
//     test_stack.setCommandArray(command_array);
//     return test_stack;
//   }

//   Eigen::ArrayXXd init_large_x_vals() {
//     int num_points = 100;
//     double begin = 2;
//     double end = 12;
//     Eigen::ArrayXXd result(num_points, 1);
//     result = Eigen::ArrayXd::LinSpaced(num_points, begin, end);
//     return result;
//   }
// };

// TEST_F(AGraphExceptionTest, evaluate_overflow_returns_nan_array) {
//   std::cout << large_x << std::endl;
//   Eigen::ArrayXXd return_vals = x_squared.evaluateEquationAt(large_x);
// }
} // namespace


//  Copyright (c) 2018-2019 Cem Bassoy
//
//  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)
//
//  The authors gratefully acknowledge the support of
//  Fraunhofer and Google in producing this work
//  which started as a Google Summer of Code project.
//

#include <iostream>
#include <algorithm>
#include <vector>

#include <boost/numeric/ublas/tensor/multiplication.hpp>
#include <boost/numeric/ublas/tensor/extents.hpp>
#include <boost/numeric/ublas/tensor/strides.hpp>
#include "utility.hpp"

#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_SUITE (test_tensor_contraction)

using test_types = zip<int,long,float,double,std::complex<float>>::with_t<boost::numeric::ublas::first_order, boost::numeric::ublas::last_order>;

//using test_types = zip<int>::with_t<boost::numeric::ublas::first_order>;

struct fixture
{
	using extents_type = boost::numeric::ublas::shape;
	fixture()
	  : extents {
	      extents_type{1,1}, // 1
	      extents_type{1,2}, // 2
	      extents_type{2,1}, // 3
	      extents_type{2,3}, // 4
	      extents_type{5,4}, // 5
	      extents_type{2,3,1}, // 6
	      extents_type{4,1,3}, // 7
	      extents_type{1,2,3}, // 8
	      extents_type{4,2,3}, // 9
	      extents_type{4,2,3,5}} // 10
	{
	}
	std::vector<extents_type> extents;
};

BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_mtv, value,  test_types, fixture )
{
	using namespace boost::numeric;
	using value_type   = typename value::first_type;
	using layout_type  = typename value::second_type;
	using strides_type = ublas::strides<layout_type>;
	using vector_type  = std::vector<value_type>;
	using extents_type = ublas::shape;
	using extents_type_base = typename extents_type::base_type;
	using size_type = typename extents_type_base::value_type;

	for(auto const& na : extents) {

		if(na.size() > 2)
			continue;

		auto a = vector_type(na.product(), value_type{2});
		auto wa = strides_type(na);
		for(auto m = 0u; m < na.size(); ++m){
			auto nb = extents_type {na[m],1};
			auto wb = strides_type (nb);
			auto b  = vector_type  (nb.product(), value_type{1} );

			auto nc_base = extents_type_base(std::max(na.size()-1, size_type{2}), 1);

			for(auto i = 0u, j = 0u; i < na.size(); ++i)
				if(i != m)
					nc_base[j++] = na[i];

			auto nc = extents_type (nc_base);
			auto wc = strides_type (nc);
			auto c  = vector_type  (nc.product(), value_type{0});

			ublas::detail::recursive::mtv(
			      size_type(m),
			      c.data(), nc.data(), wc.data(),
			      a.data(), na.data(), wa.data(),
			      b.data());

			for(auto i = 0u; i < c.size(); ++i)
				BOOST_CHECK_EQUAL( c[i] , value_type(na[m]) * a[i] );

		}
	}
}

BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_mtm, value,  test_types, fixture )
{
	using namespace boost::numeric;
	using value_type   = typename value::first_type;
	using layout_type  = typename value::second_type;
	using strides_type = ublas::strides<layout_type>;
	using vector_type  = std::vector<value_type>;
	using extents_type = ublas::shape;
	//	using extents_type_base = typename extents_type::base_type;

	for(auto const& na : extents) {

		if(na.size() != 2)
			continue;

		auto a  = vector_type  (na.product(), value_type{2});
		auto wa = strides_type (na);

		auto nb = extents_type {na[1],na[0]};
	auto wb = strides_type (nb);
	auto b  = vector_type  (nb.product(), value_type{1} );

	auto nc = extents_type {na[0],nb[1]};
auto wc = strides_type (nc);
auto c  = vector_type  (nc.product());

ublas::detail::recursive::mtm(
    c.data(), nc.data(), wc.data(),
    a.data(), na.data(), wa.data(),
    b.data(), nb.data(), wb.data());

for(auto i = 0u; i < c.size(); ++i)
BOOST_CHECK_EQUAL( c[i] , value_type(na[1]) * a[0] );

}
}

BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_ttv, value,  test_types, fixture )
{
	using namespace boost::numeric;
	using value_type   = typename value::first_type;
	using layout_type  = typename value::second_type;
	using strides_type = ublas::strides<layout_type>;
	using vector_type  = std::vector<value_type>;
	using extents_type = ublas::shape;
	using extents_type_base = typename extents_type::base_type;
	using size_type = typename extents_type_base::value_type;

	for(auto const& na : extents) {

		auto a = vector_type(na.product(), value_type{2});
		auto wa = strides_type(na);
		for(auto m = 0u; m < na.size(); ++m){
			auto b  = vector_type  (na[m], value_type{1} );
			auto nb = extents_type {na[m],1};
			auto wb = strides_type (nb);

			auto nc_base = extents_type_base(std::max(na.size()-1, size_type(2)),1);

			for(auto i = 0ul, j = 0ul; i < na.size(); ++i)
				if(i != m)
					nc_base[j++] = na[i];

			auto nc = extents_type (nc_base);
			auto wc = strides_type (nc);
			auto c  = vector_type  (nc.product(), value_type{0});

			ublas::ttv(size_type(m+1), na.size(),
			           c.data(), nc.data(), wc.data(),
			           a.data(), na.data(), wa.data(),
			           b.data(), nb.data(), wb.data());

			for(auto i = 0u; i < c.size(); ++i)
				BOOST_CHECK_EQUAL( c[i] , value_type(na[m]) * a[i] );

		}
	}
}

BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_ttm, value,  test_types, fixture )
{
	using namespace boost::numeric;
	using value_type   = typename value::first_type;
	using layout_type  = typename value::second_type;
	using strides_type = ublas::strides<layout_type>;
	using vector_type  = std::vector<value_type>;
	using extents_type = ublas::shape;
	using size_type = typename extents_type::value_type;

	for(auto const& na : extents) {

		auto a = vector_type(na.product(), value_type{2});
		auto wa = strides_type(na);
		for(auto m = 0u; m < na.size(); ++m){
			auto nb = extents_type {na[m], na[m] };
			auto b  = vector_type  (nb.product(), value_type{1} );
			auto wb = strides_type (nb);

			auto nc = na;
			auto wc = strides_type (nc);
			auto c  = vector_type  (nc.product(), value_type{0});

			ublas::ttm(size_type(m+1), na.size(),
			           c.data(), nc.data(), wc.data(),
			           a.data(), na.data(), wa.data(),
			           b.data(), nb.data(), wb.data());

			for(auto i = 0u; i < c.size(); ++i)
				BOOST_CHECK_EQUAL( c[i] , value_type(na[m]) * a[i] );

		}
	}
}

BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_ttt_permutation, value,  test_types, fixture )
{
	using namespace boost::numeric;
	using value_type   = typename value::first_type;
	using layout_type  = typename value::second_type;
	using strides_type = ublas::strides<layout_type>;
	using vector_type  = std::vector<value_type>;
	using extents_type = ublas::shape;
	using size_type    = typename strides_type::value_type;

	auto compute_factorial = [](auto const& p){
		auto f = 1ul;
		for(auto i = 1u; i <= p; ++i)
			f *= i;
		return f;
	};

	auto compute_inverse_permutation = [](auto const& pi){
		auto pi_inv = pi;
		for(auto j = 0u; j < pi.size(); ++j)
			pi_inv[pi[j]-1] = j+1;
		return pi_inv;
	};

	auto permute_extents = [](auto const& pi, auto const& na){
		auto nb = na;
		assert(pi.size() == na.size());
		for(auto j = 0u; j < pi.size(); ++j)
			nb[j] = na[pi[j]-1];
		return nb;
	};

	// left-hand and right-hand side have the
	// the same number of elements

	// computing the inner product with
	// different permutation tuples for
	// right-hand side

	for(auto const& na : extents) {

		auto wa = strides_type(na);
		auto a  = vector_type(na.product(), value_type{2});
		auto pa  = na.size();
		auto pia = std::vector<size_type>(pa);
		std::iota( pia.begin(), pia.end(), 1 );

		auto pib     = pia;
		auto pib_inv = compute_inverse_permutation(pib);

		auto f = compute_factorial(pa);

		// for the number of possible permutations
		// only permutation tuple pib is changed.
		for(auto i = 0u; i < f; ++i) {

			auto nb = permute_extents( pib, na  );
			auto wb = strides_type(nb);
			auto b  = vector_type(nb.product(), value_type{3});
			auto pb = nb.size();

			// the number of contractions is changed.
			for( auto q = size_type(0); q <= pa; ++q) {

				auto r  = pa - q;
				auto s  = pb - q;

				auto pc = r+s > 0 ? std::max(r+s,size_type(2)) : size_type(2);

				auto nc_base = std::vector<size_type>( pc , 1 );

				for(auto i = 0u; i < r; ++i)
					nc_base[ i ] = na[ pia[i]-1 ];

				for(auto i = 0u; i < s; ++i)
					nc_base[ r + i ] = nb[ pib_inv[i]-1 ];

				auto nc = extents_type ( nc_base );
				auto wc = strides_type ( nc );
				auto c  = vector_type  ( nc.product(), value_type(0) );

				ublas::ttt(pa,pb,q,
				           pia.data(), pib_inv.data(),
				           c.data(), nc.data(), wc.data(),
				           a.data(), na.data(), wa.data(),
				           b.data(), nb.data(), wb.data());

				auto acc = value_type(1);
				for(auto i = r; i < pa; ++i)
					acc *= value_type(na[pia[i]-1]);

				for(auto i = 0ul; i < c.size(); ++i)
					BOOST_CHECK_EQUAL( c[i] , acc * a[0] * b[0] );

			}

			std::next_permutation(pib.begin(), pib.end());
			pib_inv = compute_inverse_permutation(pib);
		}
	}
}

BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_ttt, value,  test_types, fixture )
{
	using namespace boost::numeric;
	using value_type   = typename value::first_type;
	using layout_type  = typename value::second_type;
	using strides_type = ublas::strides<layout_type>;
	using vector_type  = std::vector<value_type>;
	using extents_type = ublas::shape;
	using size_type    = typename strides_type::value_type;

	// left-hand and right-hand side have the
	// the same number of elements

	// computing the inner product with
	// different permutation tuples for
	// right-hand side

	for(auto const& na : extents) {

		auto wa = strides_type(na);
		auto a  = vector_type(na.product(), value_type{2});
		auto pa  = na.size();

		auto nb = na;
		auto wb = strides_type(nb);
		auto b  = vector_type(nb.product(), value_type{3});
		auto pb = nb.size();

		//		std::cout << "na = ";
		//		std::copy(na.begin(), na.end(), std::ostream_iterator<size_type>(std::cout, " "));
		//		std::cout << std::endl;

		//		std::cout << "nb = ";
		//		std::copy(nb.begin(), nb.end(), std::ostream_iterator<size_type>(std::cout, " "));
		//		std::cout << std::endl;

		// the number of contractions is changed.
		for( auto q = size_type(0); q <= pa; ++q) { // pa

			auto r  = pa - q;
			auto s  = pb - q;

			auto pc = r+s > 0 ? std::max(r+s, size_type(2)) : size_type(2);

			auto nc_base = std::vector<size_type>( pc , 1 );

			for(auto i = 0u; i < r; ++i)
				nc_base[ i ] = na[ i ];

			for(auto i = 0u; i < s; ++i)
				nc_base[ r + i ] = nb[ i ];

			auto nc = extents_type ( nc_base );
			auto wc = strides_type ( nc );
			auto c  = vector_type  ( nc.product(), value_type{0} );

			//			std::cout << "nc = ";
			//			std::copy(nc.begin(), nc.end(), std::ostream_iterator<size_type>(std::cout, " "));
			//			std::cout << std::endl;

			ublas::ttt(pa,pb,q,
			           c.data(), nc.data(), wc.data(),
			           a.data(), na.data(), wa.data(),
			           b.data(), nb.data(), wb.data());

			auto acc = value_type(1);
			for(auto i = r; i < pa; ++i)
				acc *= value_type(na[i]);

			for(auto i = 0u; i < c.size(); ++i)
				BOOST_CHECK_EQUAL( c[i] , acc * a[0] * b[0] );

		}

	}
}

BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_inner, value,  test_types, fixture )
{
	using namespace boost::numeric;
	using value_type   = typename value::first_type;
	using layout_type  = typename value::second_type;
	using strides_type = ublas::strides<layout_type>;
	using vector_type  = std::vector<value_type>;

	for(auto const& n : extents) {

		auto a = vector_type(n.product(), value_type{2});
		auto b = vector_type(n.product(), value_type{3});
		auto w = strides_type(n);

		auto c = ublas::inner(n.size(), n.data(), a.data(), w.data(), b.data(), w.data(), value_type(0));
		auto cref = std::inner_product(a.begin(), a.end(), b.begin(), value_type(0));

		BOOST_CHECK_EQUAL( c , cref );

	}

}

BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_outer, value,  test_types, fixture )
{
	using namespace boost::numeric;
	using value_type   = typename value::first_type;
	using layout_type  = typename value::second_type;
	using extents_type = ublas::shape;
	using strides_type = ublas::strides<layout_type>;
	using vector_type  = std::vector<value_type>;

	for(auto const& na : extents) {

		auto a = vector_type(na.product(), value_type{2});
		auto wa = strides_type(na);

		for(auto const& nb : extents) {

			auto b = vector_type(nb.product(), value_type{3});
			auto wb = strides_type(nb);

			auto c = vector_type(nb.product()*na.product());
			auto nc = typename extents_type::base_type(na.size()+nb.size());

			for(auto i = 0u; i < na.size(); ++i)
				nc[i] = na[i];
			for(auto i = 0u; i < nb.size(); ++i)
				nc[i+na.size()] = nb[i];

			auto wc = strides_type(extents_type(nc));

			ublas::outer(c.data(), nc.size(), nc.data(), wc.data(),
			             a.data(), na.size(), na.data(), wa.data(),
			             b.data(), nb.size(), nb.data(), wb.data());

			for(auto const& cc : c)
				BOOST_CHECK_EQUAL( cc , a[0]*b[0] );
		}

	}

}

BOOST_AUTO_TEST_SUITE_END()


//=======================================================================
// Copyright (c) 2013 Piotr Smulewicz
//
// 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 k_cut_test.cpp
 * @brief
 * @author Piotr Smulewicz
 * @version 1.0
 * @date 2013-08-20
 */

#include "test_utils/logger.hpp"
#include "test_utils/test_result_check.hpp"

#include "paal/greedy/k_cut/k_cut.hpp"
#include "paal/utils/irange.hpp"

#include <boost/graph/adjacency_list.hpp>
#include <boost/test/unit_test.hpp>

template <typename Graph>
std::pair<Graph, std::vector<int>> create_instance() {
    std::vector<std::pair<int,int>> edges_p{{1,2},{1,5},{2,3},{2,5},{2,6},
        {3,4},{3,7},{4,7},{4,0},{5,6},{6,7},{7,0}};
    std::vector<int> costs{2,3,3,2,2,4,2,2,2,3,1,3};

    Graph graph(edges_p.begin(), edges_p.end(), costs.begin(), 8);
    std::vector<int> optimal = {4,9,13,16,21,25,29};

    return std::make_pair(graph, optimal);
}

template <typename Graph>
void run_test(const Graph &graph, const std::vector<int> &optimal) {
    using VT = typename boost::graph_traits<Graph>::vertex_descriptor;
    auto index = get(boost::vertex_index, graph);
    auto weight = get(boost::edge_weight, graph);

    for (auto i: paal::irange(2,9)) {
        std::vector<std::pair<VT, int>> vertices_parts;
        int cost_cut = paal::greedy::k_cut(graph, i, back_inserter(vertices_parts),
            boost::weight_map(weight));
        LOGLN("cost cut: " << cost_cut);
        std::vector<int> vertices_to_parts;
        vertices_to_parts.resize(vertices_parts.size());
        for (auto i: vertices_parts){
            LOG(i.first << "(" << i.second << "), ");
            vertices_to_parts[get(index, i.first)] = i.second;
        }
        LOGLN("");

        int cost_cut_verification = 0;
        for (auto v: boost::make_iterator_range(edges(graph))) {
            if (vertices_to_parts[get(index, source(v, graph))]
                    != vertices_to_parts[get(index, target(v, graph))])
                cost_cut_verification += get(weight, v);
        }
        BOOST_CHECK_EQUAL(cost_cut, cost_cut_verification);
        check_result(cost_cut_verification, optimal[i-2], 2.0 - 2.0 / double(i));
    }
}

template <typename VertexList>
using Graph = boost::adjacency_list<boost::vecS, VertexList, boost::undirectedS,
                                    boost::property<boost::vertex_index_t, int>,
                                    boost::property<boost::edge_weight_t, int>>;

BOOST_AUTO_TEST_CASE(KCut) {
    auto instance = create_instance<Graph<boost::vecS>>();
    run_test(instance.first, instance.second);
}

BOOST_AUTO_TEST_CASE(KCut_list) {
    auto instance = create_instance<Graph<boost::listS>>();
    auto graph = instance.first;
    auto optimal = instance.second;

    auto index = get(boost::vertex_index, graph);
    int idx = 0;
    for (auto v : boost::make_iterator_range(vertices(graph))) {
        put(index, v, idx);
        ++idx;
    }

    run_test(graph, optimal);
}


// Copyright Louis Dionne 2013-2016
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)

#include <boost/hana/assert.hpp>
#include <boost/hana/first.hpp>
#include <boost/hana/fold_left.hpp>
#include <boost/hana/insert.hpp>
#include <boost/hana/integral_constant.hpp>
#include <boost/hana/map.hpp>
#include <boost/hana/pair.hpp>
#include <boost/hana/second.hpp>
#include <boost/hana/type.hpp>
namespace hana = boost::hana;

int main() {
    // Given a map of (key, value) pairs, returns a map of (value, key) pairs.
    // This requires both the keys and the values to be compile-time Comparable.
    auto invert = [](auto map) {
        return hana::fold_left(map, hana::make_map(), [](auto map, auto pair) {
            return hana::insert(map, hana::make_pair(hana::second(pair), hana::first(pair)));
        });
    };

    auto m = hana::make_map(
        hana::make_pair(hana::type_c<int>, hana::int_c<1>),
        hana::make_pair(hana::type_c<float>, hana::int_c<2>),
        hana::make_pair(hana::int_c<3>, hana::type_c<void>)
    );

    BOOST_HANA_CONSTANT_CHECK(invert(m) ==
        hana::make_map(
            hana::make_pair(hana::int_c<1>, hana::type_c<int>),
            hana::make_pair(hana::int_c<2>, hana::type_c<float>),
            hana::make_pair(hana::type_c<void>, hana::int_c<3>)
        )
    );
}


#include <scitbx/array_family/boost_python/flex_fwd.h>
#include <scitbx/array_family/selections.h>
#include <scitbx/math/linear_regression.h>
#include <scitbx/math/linear_correlation.h>
#include <scitbx/sym_mat3.h>
#include <scitbx/sym_mat2.h>
#include <scitbx/array_family/shared.h>
#include <scitbx/array_family/tiny_types.h>
#include <scitbx/array_family/accessors/c_grid.h>
#include <scitbx/array_family/boost_python/c_grid_flex_conversions.h>
#include <scitbx/array_family/boost_python/owning_ref_conversions.h>
#include <scitbx/array_family/boost_python/passing_flex_by_reference.h>
#include <scitbx/boost_python/container_conversions.h>
#include <scitbx/boost_python/slice.h>
#include <boost_adaptbx/optional_conversions.h>
#include <boost_adaptbx/type_id_eq.h>
#include <tbxx/optional_copy.hpp>
#include <boost/optional.hpp>
#include <boost/rational.hpp>
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include <boost/python/class.hpp>
#include <boost/python/args.hpp>
#include <boost/python/overloads.hpp>
#include <boost/python/return_value_policy.hpp>
#include <boost/python/copy_const_reference.hpp>
#include <boost/python/slice.hpp>
#include <vector>

#if PY_MAJOR_VERSION >= 3
#define IS_PY3K
#endif

namespace scitbx { namespace af { namespace boost_python {

#ifdef IS_PY3K
  int import_numpy_api_if_available();
#else
  void import_numpy_api_if_available();
#endif
  void wrap_flex_grid();
  void wrap_flex_bool();
  void wrap_flex_size_t();
  void wrap_flex_uint8_t();
  void wrap_flex_uint16_t();
  void wrap_flex_uint32_t();
  // void wrap_flex_uint64_t();
  void wrap_flex_int();
  void wrap_flex_long();
  void wrap_flex_int8_t();
  void wrap_flex_int16_t();
  // void wrap_flex_int32_t();
  #if defined(_MSC_VER)
  void wrap_flex_int64_t();
  #endif
  void wrap_flex_float();
  void wrap_flex_double();
  void wrap_flex_complex_double();
  void wrap_flex_std_string();
  void wrap_flex_vec3_double();
  void wrap_flex_vec2_double();
  void wrap_flex_vec3_int();
  void wrap_flex_sym_mat3_double();
  void wrap_flex_mat3_double();
  void wrap_flex_tiny_size_t_2();

  void wrap_flex_random();
  void wrap_flex_sort();
  void wrap_flex_histogram();
  void wrap_flex_weighted_histogram();
  void wrap_flex_mean_and_variance();
  void wrap_flex_median_statistics();
  void wrap_flex_linear_interpolation();

  void wrap_loops();
  void wrap_empty_container_sizes();

namespace {

  void register_scitbx_tuple_mappings()
  {
    using namespace scitbx::boost_python::container_conversions;

    tuple_mapping_fixed_size<tiny<bool, 3> >();
    tuple_mapping_fixed_size<tiny<int, 2> >();
    tuple_mapping_fixed_size<int3>();
    tuple_mapping_fixed_size<int4>();
    tuple_mapping_fixed_size<int6>();
    tuple_mapping_fixed_size<int9>();
    tuple_mapping_fixed_size<long3>();
    tuple_mapping_fixed_size<double2>();
    tuple_mapping_fixed_size<double3>();
    tuple_mapping_fixed_size<double4>();
    tuple_mapping_fixed_size<double6>();
    tuple_mapping_fixed_size<double9>();
    tuple_mapping_fixed_size<tiny<std::string, 2> >();
    tuple_mapping_fixed_size<tiny<std::string, 3> >();
    tuple_mapping_fixed_size<tiny<std::string, 4> >();

    tuple_mapping_fixed_capacity<small<int, 3> >();
    tuple_mapping_fixed_capacity<small<int, 10> >();
    tuple_mapping_fixed_capacity<small<unsigned, 2> >();
    tuple_mapping_fixed_capacity<small<unsigned, 3> >();
    tuple_mapping_fixed_capacity<small<unsigned, 6> >();
    tuple_mapping_fixed_capacity<small<std::size_t, 5> >();
    tuple_mapping_fixed_capacity<small<double, 3> >();
    tuple_mapping_fixed_capacity<small<double, 5> >();
    tuple_mapping_fixed_capacity<small<double, 6> >();
    // scitbx/math/gaussian/sum.h SCITBX_MATH_GAUSSIAN_SUM_MAX_N_TERMS
    tuple_mapping_fixed_capacity<small<double, 10> >();

    tuple_mapping_fixed_size<tiny<int, 12> >();
    tuple_mapping_fixed_size<tiny<int, 24> >(); // scitbx/math/golay.h

    tuple_mapping_fixed_size<tiny<double, 12> >();

    tuple_mapping_fixed_size<tiny<unsigned, 2> >();
    tuple_mapping_fixed_size<tiny<unsigned, 3> >();
    tuple_mapping_fixed_size<tiny<unsigned, 4> >();
    tuple_mapping_fixed_size<tiny<unsigned, 5> >();
#if !defined(BOOST_ADAPTBX_TYPE_ID_SIZE_T_EQ_UNSIGNED)
    tuple_mapping_fixed_size<tiny<std::size_t, 2> >();
    tuple_mapping_fixed_size<tiny<std::size_t, 3> >();
    tuple_mapping_fixed_size<tiny<std::size_t, 4> >();
#endif

    tuple_mapping_fixed_size<vec3<unsigned int> >();
    tuple_mapping_fixed_size<vec3<unsigned long> >();
    tuple_mapping_fixed_size<vec3<int> >();
    tuple_mapping_fixed_size<mat3<int> >();
    tuple_mapping_fixed_size<vec3<double> >();
    tuple_mapping_fixed_size<vec3<boost::optional<double> > >();
    tuple_mapping_fixed_size<mat3<double> >();
    tuple_mapping_fixed_size<sym_mat3<double> >();
    tuple_mapping_fixed_size<vec2<unsigned int> >();
    tuple_mapping_fixed_size<vec2<unsigned long> >();
    tuple_mapping_fixed_size<vec2<int> >();
    tuple_mapping_fixed_size<mat2<int> >();
    tuple_mapping_fixed_size<vec2<double> >();
    tuple_mapping_fixed_size<mat2<double> >();
    tuple_mapping_fixed_size<sym_mat2<double> >();

    tuple_mapping_fixed_size<vec3<boost::rational<int> > >();
    tuple_mapping_fixed_size<mat3<boost::rational<int> > >();

    tuple_mapping_fixed_size<tiny<vec3<double>, 2> >();
    tuple_mapping_fixed_size<tiny<vec3<double>, 3> >();
    tuple_mapping_fixed_size<tiny<vec3<double>, 4> >();
    tuple_mapping_fixed_size<tiny<vec2<double>, 2> >();
    tuple_mapping_fixed_size<tiny<vec2<double>, 3> >();
    tuple_mapping_fixed_size<tiny<vec2<double>, 4> >();

    tuple_mapping_fixed_capacity<flex_grid_default_index_type>();
    tuple_mapping_fixed_capacity<small<vec3<int>, 3> >();
    tuple_mapping_fixed_capacity<small<vec2<int>, 2> >();
    tuple_mapping_fixed_capacity<small<vec2<double>, 6> >();
    tuple_mapping_fixed_capacity<small<vec2<double>, 7> >();
    tuple_mapping_fixed_capacity<small<vec2<double>, 8> >();
    tuple_mapping_fixed_capacity<small<vec3<double>, 8> >();
  }

  af::shared<std::size_t>
  slice_indices(
    std::size_t array_size,
    boost::python::slice const& python_slice)
  {
    scitbx::boost_python::adapted_slice a_sl(python_slice, array_size);
    af::shared<std::size_t> result(af::reserve(a_sl.size));
    for(long i=a_sl.start;i!=a_sl.stop;i+=a_sl.step) {
      result.push_back(static_cast<std::size_t>(i));
    }
    return result;
  }

  struct linear_regression_core_wrappers
  {
    typedef math::linear_regression_core<> w_t;

    static void
    wrap()
    {
      using namespace boost::python;
      using boost::python::arg;
      class_<w_t>("linear_regression_core", no_init)
        .def("is_well_defined", &w_t::is_well_defined)
        .def("y_intercept", &w_t::y_intercept)
        .def("slope", &w_t::slope)
      ;
    }
  };

  struct linear_regression_wrappers
  {
    typedef math::linear_regression<> w_t;
    typedef w_t::float_type float_t;

    static void
    wrap()
    {
      using namespace boost::python;
      using boost::python::arg;
      class_<w_t, bases<math::linear_regression_core<> > >(
            "linear_regression", no_init)
        .def(init<
          af::const_ref<float_t> const&,
          af::const_ref<float_t> const&,
          float_t const&>((
            arg("x"),
            arg("y"),
            arg("epsilon")=1e-15)))
        .def(init<
          af::const_ref<float_t> const&,
          af::const_ref<float_t> const&,
          af::const_ref<float_t> const&,
          float_t const&>((
            arg("x"),
            arg("y"),
            arg("weights"),
            arg("epsilon")=1e-15)))
      ;
    }
  };

  struct linear_correlation_wrappers
  {
    typedef math::linear_correlation<> w_t;
    typedef w_t::float_type float_t;

    static void
    wrap()
    {
      using namespace boost::python;
      using boost::python::arg;
      class_<w_t>("linear_correlation", no_init)
        .def(init<
          af::const_ref<float_t> const&,
          af::const_ref<float_t> const&,
          float_t const&,
          bool const&>(
            (arg("x"),
             arg("y"),
             arg("epsilon")=1.e-15,
             arg("subtract_mean")=true)))
        .def("is_well_defined", &w_t::is_well_defined)
        .def("n", &w_t::n)
        .def("mean_x", &w_t::mean_x)
        .def("mean_y", &w_t::mean_y)
        .def("numerator", &w_t::numerator)
        .def("sum_denominator_x", &w_t::sum_denominator_x)
        .def("sum_denominator_y", &w_t::sum_denominator_y)
        .def("denominator", &w_t::denominator)
        .def("coefficient", &w_t::coefficient)
      ;
    }
  };

  boost::python::tuple
  integer_offsets_vs_pointers(
    af::ref<double> const& data,
    af::ref<std::size_t> const& permutation,
    unsigned n_repeats,
    bool use_pointers,
    int use_iterators)
  {
    SCITBX_ASSERT(permutation.size() == data.size());
    std::vector<double*> data_pointers;
    data_pointers.reserve(data.size());
    for(unsigned i=0;i<data.size();i++) {
      data_pointers.push_back(&data[permutation[i]]);
    }
    const char* result_type = 0;
    double result_value = 0;
    if (!use_pointers) {
      if (use_iterators == 0) {
        result_type = "d[p[i]]";
        for(unsigned i_repeat=0;i_repeat<n_repeats;i_repeat++) {
          for(unsigned i=0;i<data.size();i++) {
            result_value += data[permutation[i]];
          }
          for(unsigned i=0;i<data.size();i++) {
            result_value -= data[permutation[i]];
          }
        }
      }
      else if (use_iterators == 1) {
        result_type = "d[*p++]";
        for(unsigned i_repeat=0;i_repeat<n_repeats;i_repeat++) {
          std::size_t* p_end = permutation.end();
          std::size_t* p = permutation.begin();
          while (p != p_end) {
            result_value += data[*p++];
          }
          p = permutation.begin();
          while (p != p_end) {
            result_value -= data[*p++];
          }
        }
      }
      else {
        throw std::runtime_error("use_iterators: value error");
      }
    }
    else {
      if (use_iterators == 0) {
        result_type = "*dp[i]";
        for(unsigned i_repeat=0;i_repeat<n_repeats;i_repeat++) {
          for(unsigned i=0;i<data.size();i++) {
            result_value += *data_pointers[i];
          }
          for(unsigned i=0;i<data.size();i++) {
            result_value -= *data_pointers[i];
          }
        }
      }
      else if (use_iterators == 1) {
        result_type = "**dpi++";
        for(unsigned i_repeat=0;i_repeat<n_repeats;i_repeat++) {
          std::vector<double*>::const_iterator dp_end = data_pointers.end();
          std::vector<double*>::const_iterator dp = data_pointers.begin();
          while (dp != dp_end) {
            result_value += **dp++;
          }
          dp = data_pointers.begin();
          while (dp != dp_end) {
            result_value -= **dp++;
          }
        }
      }
      else if (use_iterators == 2) {
        result_type = "**dpp++";
        for(unsigned i_repeat=0;i_repeat<n_repeats;i_repeat++) {
          double** dp = &*data_pointers.begin();
          double** dp_end = dp + data_pointers.size();
          while (dp != dp_end) {
            result_value += **dp++;
          }
          dp = &*data_pointers.begin();
          while (dp != dp_end) {
            result_value -= **dp++;
          }
        }
      }
      else {
        throw std::runtime_error("use_iterators: value error");
      }
    }
    return boost::python::make_tuple(result_type, result_value);
  }

} // namespace <anonymous>

  struct cost_of_m_handle_in_af_shared
  {
    af::shared<double> input, result;

    cost_of_m_handle_in_af_shared(af::shared<double> const &data)
      : input(data),
        result(data.size(), af::init_functor_null<double>())
    {}

    const char*
    operator()(
      unsigned n_repeats,
      unsigned test_id)
    {
      if (test_id == 0) {
        for(int n=0; n < n_repeats; ++n) {
          for(std::size_t i=1; i < input.size(); ++i) {
            result[i] = input[i] - input[i-1];
          }
        }
        return "size+begin inside  loop";
      }
      if (test_id == 1) {
        for(int n=0; n < n_repeats; ++n) {
          double *r = result.begin();
          for(std::size_t i=1; i < input.size(); ++i) {
            r[i] = input[i] - input[i-1];
          }
        }
        return "     begin outside loop";
      }
      for(int n=0; n < n_repeats; ++n) {
        double *r = result.begin();
        std::size_t s = input.size();
        for(std::size_t i=1; i < s; ++i) {
          r[i] = input[i] - input[i-1];
        }
      }
      return "size+begin outside loop";
    }
  };

  // Testing argument passing from Python to C++
  // from flex.double to various scitbx::af array types
  struct flex_argument_passing
  {
    double x[3];

    flex_argument_passing() { x[0] = 1.5; x[1] = 2.5; x[2] = 3.5;}

    // This template pattern ensures that this function is as easy to use
    // from C++ (instantiating it with af::shared<double> for example)
    // as it is easy to wrap in a Boost.Python binding
    // which will transparently pass a flex.double through
    // (instantiating it with af::flex_1d<double>, c.f. below)
    template<template<class> class SharedArray1D>
    void easy_versa_flex_grid_as_reference(SharedArray1D<double> a) {
      a.extend(x, x+3);
      check(a);
      a.push_back(4.5);
      a.insert(&a[1], 0.5);
      SCITBX_ASSERT(a.begin() == &a[0]);
      SCITBX_ASSERT(a.end() == &a[5]);
      SCITBX_ASSERT(a.ref().size() == 5);
      SCITBX_ASSERT(a.ref()[2] == 2.5);
    }

    template<class A>
    void check(A &a) {
      SCITBX_ASSERT(a.size() == 3);
      SCITBX_ASSERT(a[0] == x[0]);
      SCITBX_ASSERT(a[1] == x[1]);
      SCITBX_ASSERT(a[2] == x[2]);
    }

    void shared_as_reference_fails(shared<double> &a) {
      a.extend(x, x+3);
      check(a);
    }

    void shared_as_value_fails(shared<double> a) {
      a.extend(x, x+3);
      check(a);
    }

    void versa_flex_grid_as_reference_succeeds(versa<double, flex_grid<> > &a){
      versa<double, flex_grid<> >::base_array_type b = a.as_base_array();
      b.extend(x, x+3);
      a.resize(flex_grid<>(b.size()));
      check(a);
    }

    void versa_flex_grid_as_value_fails(versa<double, flex_grid<> > a){
      versa<double, flex_grid<> >::base_array_type b = a.as_base_array();
      b.extend(x, x+3);
      a.resize(flex_grid<>(b.size()));
      check(a);
    }
  };

  int tst_c_grid_flex_conversion(
    const_ref<int, c_grid_periodic<3> > const &a,
    int i, int j, int k)
  {
    return a(i,j,k);
  }

namespace {

  void init_module()
  {
    using namespace boost::python;
    using boost::python::arg;

    import_numpy_api_if_available();

    register_scitbx_tuple_mappings();

    wrap_flex_grid();

    wrap_flex_bool();
    wrap_flex_size_t();
    wrap_flex_uint8_t();
    wrap_flex_uint16_t();
    wrap_flex_uint32_t();
    // wrap_flex_uint64_t();
    wrap_flex_int();
    wrap_flex_long();
    wrap_flex_int8_t();
    wrap_flex_int16_t();
    // wrap_flex_int32_t();
    #if defined(_MSC_VER)
    wrap_flex_int64_t();
    #endif
    wrap_flex_float();
    wrap_flex_double();
    wrap_flex_complex_double();
    wrap_flex_std_string();
    wrap_flex_vec3_double();
    wrap_flex_vec2_double();
    wrap_flex_vec3_int();
    wrap_flex_mat3_double();
    wrap_flex_sym_mat3_double();
    wrap_flex_tiny_size_t_2();

    default_c_grid_flex_conversions<bool>();
    default_c_grid_flex_conversions<int>();
    default_c_grid_flex_conversions<long>();
    default_c_grid_flex_conversions<float>();
    default_c_grid_flex_conversions<double>();
    default_c_grid_flex_conversions<std::complex<double> >();
    default_c_grid_flex_conversions< vec2<double> >();
    default_c_grid_flex_conversions< vec3<double> >();
    def("tst_c_grid_flex_conversion", tst_c_grid_flex_conversion);

    ref_owning_shared_conversions<double>();
    ref_owning_shared_conversions< std::complex<double> >();

    {
      namespace oc = boost_adaptbx::optional_conversions;
      oc::to_and_from_python<boost::optional<vec3<double> > >();
      oc::to_and_from_python<boost::optional<af::shared<double> > >();
      oc::to_and_from_python<
        tbxx::optional_container<af::small<int, 10> > >();
      oc::to_and_from_python<
        tbxx::optional_container<af::small<double, 6> > >();
    }

    wrap_flex_random();
    wrap_flex_sort();
    wrap_flex_histogram();
    wrap_flex_weighted_histogram();
    wrap_flex_mean_and_variance();
    wrap_flex_median_statistics();
    wrap_flex_linear_interpolation();

    wrap_loops();
    wrap_empty_container_sizes();

    def("slice_indices", slice_indices, (
      arg("array_size"), arg("python_slice")));

    linear_regression_core_wrappers::wrap();
    linear_regression_wrappers::wrap();
    linear_correlation_wrappers::wrap();

    def("integer_offsets_vs_pointers", integer_offsets_vs_pointers);
    def("reindexing_array",
      (af::shared<int>(*)(
        std::size_t selectee_size,
        const_ref<int> const&  iselection
        ))
      reindexing_array,
      (arg("selectee_size"), arg("iselection")));

    {
      typedef cost_of_m_handle_in_af_shared wt;
      class_<wt>("cost_of_m_handle_in_af_shared", no_init)
        .def(init<af::shared<double> const &>((arg("data"))))
        .add_property("result", &wt::result)
        .def("__call__", &wt::operator(),
             (arg("n_repeats"), arg("test_id")))
        ;
    }
    {
      typedef flex_argument_passing wt;
      typedef void (wt::*easy_t)(flex_1d<double>);
      class_<wt>("flex_argument_passing")
        .def("easy_versa_flex_grid_as_reference",
             (easy_t)&wt::easy_versa_flex_grid_as_reference)
        .def("shared_as_reference_fails", &wt::shared_as_reference_fails)
        .def("shared_as_value_fails", &wt::shared_as_value_fails)
        .def("versa_flex_grid_as_reference_succeeds",
             &wt::versa_flex_grid_as_reference_succeeds)
        .def("versa_flex_grid_as_value_fails",
             &wt::versa_flex_grid_as_value_fails)
        ;
    }
  }

}}}} // namespace scitbx::af::boost_python::<anonymous>

BOOST_PYTHON_MODULE(scitbx_array_family_flex_ext)
{
  scitbx::af::boost_python::init_module();
}


#include <boost/test/unit_test.hpp>

#include <array>

#include "convert.hpp"

struct ConvertFixture
{
    ConvertFixture() = default;
    ~ConvertFixture() = default;

    int positiveValue {42};
    int zeroValue {0};
    int negativeValue {-13};

    // Value is 101 decimal, or 0++ 0+-
    std::array<int, 6> positiveTritArray { -1, 1, -1, 1, 1, 0};

    // Value is -444 decimal or 00- ++- --0
    std::array<int, 9> negativeTritArray { 0, -1, -1, -1, 1, 1, -1, 0, 0};
};

BOOST_FIXTURE_TEST_SUITE(convert, ConvertFixture)

BOOST_AUTO_TEST_CASE(convert_to_decimal)
{
    BOOST_TEST(to_decimal(positiveTritArray) == 101);
    BOOST_TEST(to_decimal(negativeTritArray) == -444);
}

BOOST_AUTO_TEST_CASE(convert_to_trits)
{
    auto ptrits { to_trits<int, 6>(positiveValue) };
    auto ztrits { to_trits<int, 6>(zeroValue) };
    auto ntrits { to_trits<int, 6>(negativeValue) };

    BOOST_TEST(ptrits == (std::array<int, 6> { 0, -1, -1, -1, 1, 0 }));
    BOOST_TEST(ztrits == (std::array<int, 6> { 0, 0, 0, 0, 0, 0}));
    BOOST_TEST(ntrits == (std::array<int, 6> { -1, -1, -1, 0, 0, 0}));
}

BOOST_AUTO_TEST_CASE(convert_to_triad_string)
{
    BOOST_TEST(triad_to_string(negativeValue) == "z");
}

BOOST_AUTO_TEST_SUITE_END()


//
//  curry.hpp
//  solver
//
//  Created by Alexandre Martin on 11/05/2016.
//  Copyright © 2016 scalexm. All rights reserved.
//

#ifndef CURRY_HPP
#define CURRY_HPP

#include "cnf.hpp"
#include "expr/theory_expr.hpp"
#include <boost/variant.hpp>
#include <ostream>

constexpr char CURRY_SYMBOL = '$';

struct const_equality {
    int left, right;
};

struct curry_equality {
    int arg1, arg2, right;
};

inline std::ostream & operator <<(std::ostream & stream, const const_equality & eq) {
    return stream << eq.left << " = " << eq.right;
}

inline std::ostream & operator <<(std::ostream & stream, const curry_equality & eq) {
    return stream << CURRY_SYMBOL << '(' << eq.arg1 << ", " << eq.arg2 << ") = " << eq.right;
}

using curry_atom = boost::variant<const_equality, curry_equality>;

struct curry_env {
    cnf clauses;
    int max_constant;
    int tseitin_offset;
    std::vector<curry_atom> atoms; // offset + 1 <-> curry_atom
};

curry_env curry_transform(const expr::equality_expr &);

#endif


#include "xrandom.h"
#include "watch.h"

#include <boost/random.hpp>
#include <ctime>
#include <cstdlib>

namespace global
{

	float64 random::float64_0_1()
	{
		initial();
		return ((float64)rand_int32() / (float64)(GLOBAL_RAND_MAX) );
	}

	float64 random::float64_M1_P1()
	{
		initial();
		return ((float64)(rand_int32() - 0.5 * GLOBAL_RAND_MAX) / (float64)(0.5 * GLOBAL_RAND_MAX) );
	}

	float64 random::float64_range(float64 min_, float64 max_)
	{
		initial();
		if (max_ < min_)
		{
			max_ = min_;
			LErrStr(boost::format("random::float64_range : max < min"));
		}
		return (float64_0_1()* (max_ - min_)) + min_;
	}

	bool random::bool_T_F()
	{
		initial();
		return (rand() % 2 == 0);
	}

	bool random::bool_T_F(float64 true_prob)
	{
		if (float64_0_1() <= true_prob)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	int32 random::int32_0_1()
	{
		initial();
		return (rand() % 2);
	}

	int32 random::int32_range(int32 min_, int32 max_)
	{
		initial();
		if (max_ < min_)
		{
			max_ = min_;
			LErrStr(boost::format("random::int32_range : max < min"));
		}
		return (rand_int32() % (max_ - min_ + 1)) + min_;
	}

	uint32 random::uint32_0_1()
	{
		initial();
		return (uint32)(rand() % 2);
	}

	uint32 random::uint32_range(uint32 min_, uint32 max_)
	{
		initial();
		if (min_ > (uint32)GLOBAL_RAND_MAX)
		{
			min_ = (uint32)GLOBAL_RAND_MAX;
			LErrStr(boost::format("random::uint32_range : min > GLOBAL_RAND_MAX"));
		}
		if (max_ > (uint32)GLOBAL_RAND_MAX)
		{
			max_ = (uint32)GLOBAL_RAND_MAX;
			LErrStr(boost::format("random::uint32_range : max > GLOBAL_RAND_MAX"));
		}
		if (max_ < min_)
		{
			max_ = min_;
			LErrStr(boost::format("random::uint32_range : max < min"));
		}
		return (uint32)(rand_int32() % (max_ - min_ + 1)) + min_;
	}

	int32 random::rand_int32()
	{
		initial();
		if (RAND_MAX == 0x7FFF)	//	windows
		{
			int32 i = ((rand() % 2) << 30);
			i |= (rand() << 15);
			i |= rand();
			return i;
		}
		else if (RAND_MAX == GLOBAL_RAND_MAX)	// linux
		{
			return rand();
		}
		else
		{
			LErrStr(boost::format("random::rand_int32 unexpected RAND_MAX: %d") % RAND_MAX);
			return rand();
		}
	}

	bool random::unique_assign(int32* buf, int32 size, int32 min, int32 max)
	{
		if (min > max || size > (max - min + 1))
		{
			return false;
		}
		for(int32 i = 0; i < size ; ++ i)
		{
			while (true)
			{
				buf[i] = int32_range(min, max);
				bool repeat_flag = false;
				for (int32 j = 0; j < i; ++ j)
				{
					if (buf[i] == buf[j])
					{
						repeat_flag = true;
						break;
					}
				}
				if (!repeat_flag)
				{
					break;
				}
			}
		}
		return true;
	}

	void random::initial()
	{
		if (!initial_flag)
		{
			srand((unsigned)time(NULL));
			initial_flag = true;
		}
	}

	bool random::initial_flag = false;

	int32 const random::GLOBAL_RAND_MAX = 0x7FFFFFFF;

}


// Copyright 2005 The Trustees of Indiana University.

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

//  Authors: Ben Martin
//           Douglas Gregor
//           Andrew Lumsdaine
#include <boost/graph/minimum_degree_ordering.hpp>
#include "graph_types.hpp"
#include <boost/python.hpp>
#include <vector>
#include <boost/graph/iteration_macros.hpp>

namespace boost { namespace graph { namespace python {

template<typename Graph>
boost::python::list
minimum_degree_ordering
(Graph& g,
 vector_property_map<
   int, typename property_map<Graph, vertex_index_t>::const_type
 >* in_supernode_size,
 int delta)
{
  typedef std::vector<int> InversePermutationMap;
  typedef std::vector<int> PermutationMap;
  typedef typename property_map<Graph, vertex_index_t>::const_type
    VertexIndexMap;
  typedef vector_property_map<int, VertexIndexMap> OutDegreeMap;
  typedef vector_property_map<int, VertexIndexMap> SuperNodeSizeMap;

  typedef typename graph_traits<Graph>::vertices_size_type vertices_size_type;

  vertices_size_type n = num_vertices(g);

  // Out-degree map
  OutDegreeMap out_degree_map(num_vertices(g), get(vertex_index, g));
  BGL_FORALL_VERTICES_T(v, g, Graph)
    put(out_degree_map, v, out_degree(v, g));

  InversePermutationMap inv_perm_map(n, 0);
  PermutationMap perm_map(n, 0);
  SuperNodeSizeMap supernode_size =
    in_supernode_size ? *in_supernode_size
    : SuperNodeSizeMap(num_vertices(g), get(vertex_index, g));
  BGL_FORALL_VERTICES_T(v, g, Graph)
    supernode_size[v] = 1;

  boost::minimum_degree_ordering(g, out_degree_map, inv_perm_map, perm_map,
				 supernode_size, delta,
                                 get(vertex_index, g));

  // Build the new ordering
  typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
  std::vector<vertex_descriptor> new_ordering(num_vertices(g));
  BGL_FORALL_VERTICES_T(v, g, Graph)
    new_ordering[perm_map[get(vertex_index, g, v)]] = v;

  // Build resulting list for Python
  boost::python::list result;
  typedef typename std::vector<vertex_descriptor>::iterator iterator;
  for (iterator i = new_ordering.begin(); i != new_ordering.end(); ++i)
    result.append(*i);

  return result;
}

void export_minimum_degree_ordering()
{
  using boost::python::arg;
  using boost::python::def;

#define UNDIRECTED_GRAPH(Name,Type)
#define DIRECTED_GRAPH(Name,Type)                                       \
  {                                                                     \
    typedef vector_property_map<int,                                    \
                                property_map<Type, vertex_index_t>::const_type>\
      SuperNodeSizeMap;                                                 \
                                                                        \
    def("minimum_degree_ordering",                                      \
        &minimum_degree_ordering<Type>,                                 \
        (arg("graph"),                                                  \
         arg("supernode_size") = static_cast<SuperNodeSizeMap*>(0),     \
         arg("delta") = 0));                                            \
  }
#include "graphs.hpp"
}

} } } // end namespace boost::graph::python


#pragma once

#include <memory>
#include <boost/numeric/ublas/matrix.hpp>
#include "core/Agent.hpp"

// IWYU pragma: no_include <boost/numeric/ublas/functional.hpp>
// IWYU pragma: no_include <boost/numeric/ublas/storage.hpp>
// IWYU pragma: no_include "core/Action.hpp"

namespace aima::core { class Percept; }

namespace aima::vacuum {
    class BasicVacuumEnvironment;

    class StatefulVacuumAgent : public core::Agent {
    public:
        StatefulVacuumAgent( unsigned long x, unsigned long y );

        /**
         * Takes a reference to the environment that the agent will operate in.
         * The reference is not saved, it is only used to get the environment's dimensions.
         * @param environment
         */
        explicit StatefulVacuumAgent( const vacuum::BasicVacuumEnvironment& environment );

        std::unique_ptr<Agent> clone() const override;

        std::unique_ptr<core::Action> execute( const core::Percept& percept ) override;

    protected:
        StatefulVacuumAgent( const StatefulVacuumAgent& ) = default;

    private:
        bool allClean();

        boost::numeric::ublas::matrix<bool> state;
    };
}


/*
 MIT License

 Copyright (c) 2018 Yuki Koyama

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in all
 copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */

#ifndef NLOPTUTIL_HPP
#define NLOPTUTIL_HPP

#include <Eigen/Core>
#include <chrono>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <nlopt.hpp>
#include <sstream>
#include <string>

namespace nloptutil
{
    namespace internal
    {
        constexpr double constraint_tol = 1e-10;
    }

    inline Eigen::VectorXd solve(const Eigen::VectorXd&           x_initial,
                                 const Eigen::VectorXd&           upper,
                                 const Eigen::VectorXd&           lower,
                                 const nlopt::vfunc               objective_function,
                                 const std::vector<nlopt::vfunc>& equality_constraints,
                                 const std::vector<nlopt::vfunc>& inequality_constraints,
                                 nlopt::algorithm                 algorithm          = nlopt::LD_TNEWTON,
                                 void*                            data               = nullptr,
                                 bool                             is_maximization    = false,
                                 int                              max_evaluations    = 1000,
                                 double                           relative_func_tol  = 1e-06,
                                 double                           relative_param_tol = 1e-06,
                                 bool                             verbose            = false,
                                 double                           initial_step_scale = 1.0)
    {
        const unsigned M = static_cast<unsigned>(x_initial.rows());

        nlopt::opt solver(algorithm, M);

        if (upper.rows() != 0)
        {
            const std::vector<double> u(upper.data(), upper.data() + upper.rows());
            solver.set_upper_bounds(u);
        }

        if (lower.rows() != 0)
        {
            const std::vector<double> l(lower.data(), lower.data() + lower.rows());
            solver.set_lower_bounds(l);
        }

        solver.set_maxeval(max_evaluations);
        solver.set_ftol_rel(relative_func_tol);
        solver.set_xtol_rel(relative_param_tol);

        if (is_maximization)
        {
            solver.set_max_objective(objective_function, data);
        }
        else
        {
            solver.set_min_objective(objective_function, data);
        }

        for (auto func : equality_constraints)
        {
            solver.add_equality_constraint(func, data, internal::constraint_tol);
        }

        for (auto func : inequality_constraints)
        {
            solver.add_inequality_constraint(func, data, internal::constraint_tol);
        }

        std::vector<double> x_star(x_initial.data(), x_initial.data() + x_initial.rows());

        // Record the cost value for the initial solution
        double initial_cost_value;
        if (verbose)
        {
            std::vector<double> dummy(M);
            initial_cost_value = objective_function(x_star, dummy, data);
        }

        // Scale the initial step size (only for derivative-free algorithms such as nlopt::LN_COBYLA)
        const std::vector<double> step = [&]() {
            std::vector<double> step(M, 0.0);
            solver.get_initial_step(x_star, step);
            for (auto& d : step)
            {
                d *= initial_step_scale;
            }
            return step;
        }();
        solver.set_initial_step(step);

        // Start timing measurement
        const auto t_start = std::chrono::system_clock::now();

        // Run the optimization
        double final_cost_value;
        try
        {
            solver.optimize(x_star, final_cost_value);
        }
        catch (nlopt::roundoff_limited)
        {
            // Do nothing when this exception is thrown
        }
        catch (std::invalid_argument e)
        {
            if (verbose)
            {
                std::cerr << e.what() << std::endl;
            }
            assert(false);
        }
        catch (nlopt::forced_stop e)
        {
            // Do nothing when this exception is thrown
            if (verbose)
            {
                std::cerr << e.what() << std::endl;
            }
        }
        catch (std::runtime_error e)
        {
            if (verbose)
            {
                std::cerr << e.what() << std::endl;
            }
            return x_initial;
        }

        // Stop timing measurement
        const auto t_end = std::chrono::system_clock::now();

        // Show statistics if "verbose" is set as true
        if (verbose)
        {
            const std::string elapsed_time_message = [&]() {
                const auto t_elapsed_in_microsec =
                    std::chrono::duration_cast<std::chrono::microseconds>(t_end - t_start).count();
                const auto t_elapsed_in_millisec = t_elapsed_in_microsec / 1000.0;
                const auto t_elapsed_in_sec      = t_elapsed_in_millisec / 1000.0;

                std::ostringstream sstream;
                if (t_elapsed_in_sec > 10.0)
                {
                    sstream << std::fixed << std::setprecision(3) << t_elapsed_in_sec << " [s]";
                }
                else if (t_elapsed_in_millisec > 10.0)
                {
                    sstream << std::fixed << std::setprecision(3) << t_elapsed_in_millisec << " [ms]";
                }
                else
                {
                    sstream << t_elapsed_in_microsec << " [us]";
                }
                return sstream.str();
            }();

            std::cout << "---- nlopt-util ----" << std::endl;
            std::cout << "Dimensions     : " << M << std::endl;
            std::cout << "Function value : " << initial_cost_value << " => " << final_cost_value << std::endl;
            std::cout << "Elapsed time   : " << elapsed_time_message << std::endl;
            std::cout << "Function evals : " << solver.get_numevals() << std::endl;
            std::cout << "--------------------" << std::endl;
        }

        return Eigen::Map<Eigen::VectorXd>(&x_star[0], M);
    }

    inline Eigen::VectorXd solve(const Eigen::VectorXd& x_initial,
                                 const Eigen::VectorXd& upper,
                                 const Eigen::VectorXd& lower,
                                 const nlopt::vfunc     objective_function,
                                 nlopt::algorithm       algorithm          = nlopt::LD_TNEWTON,
                                 void*                  data               = nullptr,
                                 bool                   is_maximization    = false,
                                 int                    max_evaluations    = 1000,
                                 double                 relative_func_tol  = 1e-06,
                                 double                 relative_param_tol = 1e-06,
                                 bool                   verbose            = false,
                                 double                 initial_step_scale = 1.0)
    {
        return solve(x_initial,
                     upper,
                     lower,
                     objective_function,
                     {},
                     {},
                     algorithm,
                     data,
                     is_maximization,
                     max_evaluations,
                     relative_func_tol,
                     relative_param_tol,
                     verbose,
                     initial_step_scale);
    }

    namespace unconstrained
    {
        namespace grad_based
        {
            struct Func
            {
                const std::function<double(const Eigen::VectorXd&)>          objective;
                const std::function<Eigen::VectorXd(const Eigen::VectorXd&)> gradient;
            };

            inline double objective_wrapper(const std::vector<double>& x, std::vector<double>& grad, void* data)
            {
                const Eigen::VectorXd g =
                    static_cast<Func*>(data)->gradient(Eigen::Map<const Eigen::VectorXd>(x.data(), x.size()));
                std::memcpy(grad.data(), g.data(), sizeof(double) * g.size());
                return static_cast<Func*>(data)->objective(Eigen::Map<const Eigen::VectorXd>(x.data(), x.size()));
            }

            namespace bounded
            {
                inline Eigen::VectorXd solve(const Eigen::VectorXd&                                        x_initial,
                                             const Eigen::VectorXd&                                        upper,
                                             const Eigen::VectorXd&                                        lower,
                                             const std::function<double(const Eigen::VectorXd&)>&          objective,
                                             const std::function<Eigen::VectorXd(const Eigen::VectorXd&)>& gradient,
                                             nlopt::algorithm algorithm          = nlopt::LD_TNEWTON,
                                             bool             is_maximization    = false,
                                             int              max_evaluations    = 1000,
                                             double           relative_func_tol  = 1e-06,
                                             double           relative_param_tol = 1e-06,
                                             bool             verbose            = false)
                {
                    nloptutil::unconstrained::grad_based::Func func{objective, gradient};

                    return nloptutil::solve(x_initial,
                                            upper,
                                            lower,
                                            nloptutil::unconstrained::grad_based::objective_wrapper,
                                            algorithm,
                                            static_cast<void*>(&func),
                                            is_maximization,
                                            max_evaluations,
                                            relative_func_tol,
                                            relative_param_tol,
                                            verbose);
                }
            } // namespace bounded
        }     // namespace grad_based

        namespace derivative_free
        {
            inline double objective_wrapper(const std::vector<double>& x, std::vector<double>&, void* data)
            {
                return (*static_cast<std::function<double(const Eigen::VectorXd&)>*>(data))(
                    Eigen::Map<const Eigen::VectorXd>(x.data(), x.size()));
            }

            namespace bounded
            {
                inline Eigen::VectorXd solve(const Eigen::VectorXd&                               x_initial,
                                             const Eigen::VectorXd&                               upper,
                                             const Eigen::VectorXd&                               lower,
                                             const std::function<double(const Eigen::VectorXd&)>& objective,
                                             const nlopt::algorithm algorithm       = nlopt::GN_DIRECT,
                                             const bool             is_maximization = false,
                                             const int              max_evaluations = 1000,
                                             const bool             verbose         = false)
                {
                    auto objective_local_copy = objective;

                    return nloptutil::solve(x_initial,
                                            upper,
                                            lower,
                                            nloptutil::unconstrained::derivative_free::objective_wrapper,
                                            algorithm,
                                            static_cast<void*>(&objective_local_copy),
                                            is_maximization,
                                            max_evaluations,
                                            0.0,
                                            0.0,
                                            verbose);
                }
            } // namespace bounded
        }     // namespace derivative_free
    }         // namespace unconstrained
} // namespace nloptutil

#endif // NLOPTUTIL_HPP


/* Compute_Morse_Graph.hpp */
#ifndef _CMDP_COMPUTE_MORSE_GRAPH_HPP_
#define _CMDP_COMPUTE_MORSE_GRAPH_HPP_

#ifdef CMG_VISUALIZE
#include <boost/unordered_map.hpp>
#include "CImg.h"
using namespace cimg_library;
#endif

#include <map>
#include <stack>
#include <vector>
#include <exception>
#include "boost/foreach.hpp"
#include <memory>

#include "GraphTheory.h"
#include "join.h"
#include "MapGraph.h"

#include <ctime>

#ifdef MEMORYBOOKKEEPING
uint64_t max_grid_internal_memory = 0;
uint64_t max_grid_external_memory = 0;
#endif

#ifdef DO_CONLEY_INDEX
#include "chomp/ConleyIndex.h"
#endif
template < class Toplex, class CellContainer >
void subdivide ( Toplex & phase_space, CellContainer & morse_set );

// Some macros for verbose output.
#ifdef CMG_VERBOSE

#define CMG_VERBOSE_PRINT(x) std::cout << x;

#define CMG_VERBOSE_START_CLOCK \
clock_t start = clock ();

#define CMG_VERBOSE_REPORT_CLOCK \
std::cout << "   Time Elapsed: " << (double)(clock() - start)/(double)CLOCKS_PER_SEC << "\n";

#define CMG_VERBOSE_REPORT_MORSE_SETS \
std::cout << "   Number of Morse Sets: " << morse_sets . size () << "\n";\
std::cout << "   Sizes of Morse Sets: ";\
BOOST_FOREACH ( CellContainer & morse_set, morse_sets ) \
std::cout << " " << morse_set . size ();\
std::cout << ".\n";

#endif

#ifndef CMG_VERBOSE
#define CMG_VERBOSE_PRINT(x)  if(0){std::cout << x;}
#define CMG_VERBOSE_START_CLOCK
#define CMG_VERBOSE_REPORT_CLOCK
#define CMG_VERBOSE_REPORT_MORSE_SETS
#endif

class MorseDecomposition {
public:

  // Constructor
  template < class GridPtr >
  MorseDecomposition ( GridPtr grid, int depth )
  : grid_ ( grid ), spurious_(false), depth_(depth) {
    if ( grid_ . get () == NULL ) {
      throw std::logic_error ( "Bad Initialization of MorseDecomposition Object\n" );
    }
#ifdef MEMORYBOOKKEEPING
    max_grid_external_memory += grid -> memory ();
    max_grid_internal_memory = std::max( max_grid_internal_memory, grid -> memory () );
#endif
  }

  // Deconstructor
  ~MorseDecomposition ( void ) {
    BOOST_FOREACH ( MorseDecomposition * child, children_ ) {
      delete child;
    }
  }

  /// MorseDecomposition::size
  /// return size of grid
  size_t size ( void ) const { return grid_ -> size (); }

  /// MorseDecomposition::depth
  /// tell how deep in hierarchical decomposition we are
  size_t depth ( void ) const { return depth_; }

  /// MorseDecomposition::decomposition
  const std::vector< std::shared_ptr<Grid> > & decomposition ( void ) const {
    return decomposition_;
  }

  /// MorseDecomposition::children
  /// accessor method to return vector of MorseDecomposition * pointing to hierarchical children.
  /// note: empty until "decompose" is called.
  const std::vector < MorseDecomposition * > & children ( void ) const {
    return children_;
  }

  /// MorseDecomposition::reachability
  /// accessor method to obtain reachability_ private data member
  const std::vector < std::vector < unsigned int > > & reachability ( void ) const {
    return reachability_;
  }

  /// MorseDecomposition::grid
  /// accessor method to obtain grid_ shared_ptr data member
  std::shared_ptr < Grid > grid ( void ) {
    return grid_;
  }

  /// MorseDecomposition::spurious
  /// accessor method to obtain spurious_ data member
  bool & spurious ( void ) { return spurious_; }

  /// MorseDecomposition::decompose
  ///
  /// Use graph theory to find SCC components, which are stored as type Grid
  /// Fill these into "decomposition_"
  /// Fill children_ with an equal sized vector of pointers to new MorseDecomposition objects seeded with those sets.
  /// Put reachability information obtained in "reachability_"
  void
  decompose ( std::shared_ptr<const Map> f ) {
    //std::cout << "decompose at depth " << depth () << "\n";
    computeMorseSetsAndReachability
      ( &decomposition_,
        &reachability_,
        grid_,
        f );
    //std::cout << "  found " << decomposition_ . size () << " components\n";
  }

  const std::vector < MorseDecomposition * > &
  spawn ( void ) {
    //std::cout << "spawn at depth " << depth () << "\n";
    for ( size_t i = 0; i < decomposition_ . size (); ++ i ) {
      children_ . push_back ( new MorseDecomposition ( decomposition_ [ i ] -> clone (),
                                                       depth() + 1 ) );
    }
    //std::cout << "  spawned " << children_ . size () << " children\n";

    return children_;
  }

private:
  // Member Data
  std::shared_ptr<Grid> grid_;
  std::vector< std::shared_ptr<Grid> > decomposition_;
  std::vector < MorseDecomposition * > children_;
  std::vector < std::vector < unsigned int > > reachability_;
  bool spurious_;
  size_t depth_;
};

class MorseDecompCompare {
public:
  bool operator () ( const MorseDecomposition * lhs, const MorseDecomposition * rhs ) {
    return lhs -> size () < rhs -> size ();
  }
};

// The MorseDecomposition Tree
//  The level of subdivision of the root is whatever the initial level is, which we call 0.
//  The level of subdivision of an internal node in the tree is equal to its depth
//  The level of subdivision of a leaf node in the tree is the same as the level of
//      subdivision of its parent.
//  The children of a node correspond to its Morse Sets (although they may be subdivided).
//  Algorithmically, this means we call decompose whenever the depth <= the number of
//  subdivisions we want.
// ConstructMorseDecomposition
inline void
ConstructMorseDecomposition (MorseDecomposition * root,
                             std::shared_ptr<const Map> f,
                             const unsigned int Min,
                             const unsigned int Max,
                             const unsigned int Limit ) {
  size_t nodes_processed = 0;
  // We use a priority queue in order to do the more difficult computations first.
  std::priority_queue < MorseDecomposition *,
                        std::vector<MorseDecomposition *>,
                        MorseDecompCompare > pq;
  pq . push ( root );
  while ( not pq . empty () ) {
    ++ nodes_processed;
    if ( nodes_processed % 1000 == 0 ) {
      std::cout << nodes_processed
        << " nodes have been encountered on Morse Decomposition Hierarchy.\n";
    }
    MorseDecomposition * work_node = pq . top ();
    pq . pop ();
    //std::cout << "Depth " << work_node -> depth () << ", node " << work_node
    //          << ", size = " << work_node -> size () << "\n";

    // Do not decompose if past Min depth and over the Limit size.
    if ( ( work_node -> depth () > Min )
         && ( work_node -> size () > Limit ) ) {
      //std::cout << "Halting search due to Limit.\n";
      continue;
    }

    work_node -> decompose ( f );

    // Check for spuriousness
    if ( work_node -> decomposition ()  . empty () ) {
      //std::cout << "Empty decomposition for " << work_node << ", marking as spurious.\n";
      work_node -> spurious () = true;
    }

    // Hierarchical Step
    if ( (work_node -> depth () < Max) ) {
      std::vector < MorseDecomposition * > children = work_node -> spawn ();
      BOOST_FOREACH ( MorseDecomposition * child, children ) {
        child -> grid () -> subdivide ();
        pq . push ( child );
      }
    }
    //else {
      //std::cout << "Halting search due to Max.\n";
    //}
  }
}

// ConstructMorseDecomposition
inline void
ConstructMorseGraph (std::shared_ptr<Grid> master_grid,
                     MorseGraph * MG,
                     MorseDecomposition * root,
                     const unsigned int Min ) {
  std::vector < std::shared_ptr < Grid > > grids;
  // Produce Morse Graph
  typedef MorseGraph::Vertex Vertex;
  // "temp" will store which MorseGraph vertices are hierarchically under a given decomposition node
  std::map < MorseDecomposition *, std::vector<Vertex> > temp;
  std::stack < std::pair < MorseDecomposition *, size_t > > eulertourstack;
  // eulertourstack: an item (md, childnum) means "this is node md, explore child childnum if it exists,
  // otherwise do an analysis"
  eulertourstack . push ( std::make_pair( root, 0 ) );
  // The following loop performs an Euler tour and does an operation on each postordering.
  // It is thus a guarantee that the operation has been performed on all descendants in the
  while (  not eulertourstack . empty () ) {
    MorseDecomposition * MD = eulertourstack . top () . first;
    size_t childnum = eulertourstack . top () . second;
    eulertourstack . pop ();
    size_t NC = MD -> children () . size ();
    if ( childnum < NC ) {
        eulertourstack . push ( std::make_pair ( MD, childnum + 1 ) );
        eulertourstack . push ( std::make_pair ( MD -> children () [ childnum ], 0 ) );
    } else {
      // Post-ordering operation

      // Check for Spuriousness
      // If it has children that are all marked spurious, then it is spurious.
      // If it does not have children, it is spurious if and only if it is already marked spurious
      if ( NC > 0 ) {
        MD -> spurious () = true; // by default; we may however switch it back to false
        for ( size_t i = 0; i < NC; ++ i ) {
          if ( not MD -> children () [ i ] -> spurious () ) MD -> spurious () = false;
        }
      }
      temp [ MD ] = std::vector < Vertex > ();

      // If spurious, then change grid to join of all descendants and previous self
      if ( MD -> spurious () && NC > 0) {
        // We alter MD -> grid () so that it is the join of all descendant grids
        std::vector<std::shared_ptr<Grid> > grid_family;
        grid_family . push_back ( MD -> grid () );
        for ( size_t i = 0 ; i < NC; ++ i ) {
          grid_family . push_back ( MD -> children () [ i ] -> grid () );
        }
        join ( MD -> grid (), grid_family . begin(), grid_family . end () );
      }

      if ( MD -> depth () > Min ) continue;
      grids . push_back ( MD -> grid () );
      if ( MD -> spurious () ) continue;

      // Case 1. Min Depth Case
      // Morse Graph Vertex Creation Step
      // (and special case for reachability)
      if ( MD -> depth () == Min ) {
        //std::cout << "Node " << MD << "\n";
        boost::unordered_map < int, Vertex > non_spurious_decomposition;
        size_t ND = MD -> decomposition () . size ();
        for ( size_t i = 0 ; i < ND; ++ i ) {
          //std::cout << "Child " << i << " out of " << ND << "\n";
          if ( (NC == ND) && MD -> children () [ i ] -> spurious () ) {
            //std::cout << "Spurious Rule 2 invoked, skipping child " << i << ".\n";
            grids . push_back ( MD -> children () [ i ] -> grid () );
            continue;
          }
          Vertex v = MG -> AddVertex ();
          //std::cout << "Adding vertex " << v << " for child " << i << "\n";
          MG -> grid ( v ) = MD -> decomposition () [ i ];
          temp [ MD ] . push_back ( v );
          non_spurious_decomposition [ i ] = v;
        }
#ifndef NO_REACHABILITY
        typedef std::pair<int, Vertex> intVertexPair;
        BOOST_FOREACH ( const intVertexPair & ivp, non_spurious_decomposition ) {
          int i = ivp . first;
          Vertex u = ivp . second;
          const std::vector < unsigned int > & reaches = MD -> reachability () [ i ];
          BOOST_FOREACH ( unsigned int j, reaches ) {
            if ( (int) i == (int) j ) continue;
            if ( non_spurious_decomposition . count ( j ) == 0 ) continue;
            Vertex v = non_spurious_decomposition [ j ];
            MG -> AddEdge ( u, v );
            //std::cout << "(A) Adding edge " << u << " " << v << "\n";
          }
        }
#endif
      }
      // Case 2. Less than Min Depth Case
      // temp [MD] creation step
      if ( MD -> depth () < Min ) {
      // Intermediate MD node reachability step
#ifndef NO_REACHABILITY
        for ( unsigned int i = 0; i < NC; ++ i ) {
          const std::vector < unsigned int > & reaches = MD -> reachability () [ i ];
          BOOST_FOREACH ( unsigned int j, reaches ) {
           if ( i == j ) continue;
           BOOST_FOREACH ( Vertex u, temp [ MD -> children () [ i ] ] ) {
              BOOST_FOREACH ( Vertex v, temp [ MD -> children () [ j ] ] ) {
                MG -> AddEdge ( u, v );
                //std::cout << "(B) Adding edge " << u << " " << v << "\n";
              }
            }
          }
        }
#endif
        // Create temp for intermediate
        for ( unsigned int i = 0; i < NC; ++ i ) {
          temp [ MD ] . insert (temp [ MD ] . begin (),
                                temp [ MD -> children () [ i ] ] . begin (),
                                temp [ MD -> children () [ i ] ] . end ());
          temp . erase ( MD -> children () [ i ] );
        }
      }
    }
  }
  join ( master_grid, grids . begin (), grids . end () );
  MG -> phaseSpace () = master_grid;
}

inline void
Compute_Morse_Graph (MorseGraph * MG,
                     std::shared_ptr<Grid> phase_space,
                     std::shared_ptr<const Map> f,
                     const unsigned int Init,
                     const unsigned int Min,
                     const unsigned int Max,
                     const unsigned int Limit) {
  for ( int i = 0; i < (int)Init; ++ i ) phase_space -> subdivide ();
  Compute_Morse_Graph ( MG, phase_space, f, Min - Init, Max - Init, Limit );
}

inline void
Compute_Morse_Graph (MorseGraph * MG,
                     std::shared_ptr<Grid> phase_space,
                     std::shared_ptr<const Map> f,
                     const unsigned int Min,
                     const unsigned int Max,
                     const unsigned int Limit) {
  // Produce Morse Set Decomposition Hierarchy
  std::cout << "Compute_Morse_Graph. Initializing root MorseDecomposition\n";
  std::cout << "Compute_Morse_Graph. A phase_space -> size () == " << phase_space -> size () << "\n";

  std::shared_ptr<Grid> root_space ( (Grid *) (phase_space -> clone ()) );

  //std::cout << "Compute_Morse_Graph. root_space -> size () == " << root_space -> size () << "\n";

  MorseDecomposition * root = new MorseDecomposition ( root_space, 0 );

  //std::cout << "Compute_Morse_Graph. Calling ConstructMorseDecomposition\n";

  ConstructMorseDecomposition (root,
                               f,
                               Min,
                               Max,
                               Limit);
  //std::cout << "Calling ConstructMorseGraph\n";
  // Stitch together Morse Graph from Decomposition Hierarchy
  ConstructMorseGraph ( phase_space, MG, root, Min );

  std::cout << "Compute_Morse_Graph. B phase_space -> size () == " << phase_space -> size () << "\n";

  // Free memory used in decomposition hierarchy
  delete root;
#ifdef MEMORYBOOKKEEPING

  std::cout << "Total Grid Memory (can be external) = " << max_grid_external_memory << "\n";
  std::cout << "Max Memory For Single Grid (must be internal)= " << max_grid_internal_memory << "\n";
  std::cout << "Max SCC Random Access memory use (must be internal)= " << max_scc_memory_internal << "\n";
  std::cout << "Max SCC stack memory use (can be external memory) = " << max_scc_memory_external << "\n";
  std::cout << " ---- SUMMARY ---- \n";
  std::cout << "Internal Memory Requirement = " << max_grid_internal_memory + max_scc_memory_internal << "\n";
  std::cout << "External Memory Requirement = " << max_grid_external_memory + max_scc_memory_external << "\n";
  std::cout << "Max graph memory size (never stored, however) = " << max_graph_memory << "\n";
#endif
  //std::cout << "Returning from COMPUTE MORSE GRAPH\n";
}

#endif