#ifndef GUNEROTRANSACTIONSENDCIRCUIT_H_
#define GUNEROTRANSACTIONSENDCIRCUIT_H_

#include <deque>
#include <boost/optional.hpp>
#include <boost/static_assert.hpp>
#include <libff/common/utils.hpp>
#include <libsnark/common/default_types/r1cs_gg_ppzksnark_pp.hpp>
#include <libsnark/common/default_types/r1cs_gg_ppzksnark_pp.hpp>
#include <libsnark/relations/constraint_satisfaction_problems/r1cs/examples/r1cs_examples.hpp>
#include <libsnark/zk_proof_systems/ppzksnark/r1cs_gg_ppzksnark/r1cs_gg_ppzksnark.hpp>
#include <libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp>
#include <libsnark/zk_proof_systems/ppzksnark/uscs_ppzksnark/uscs_ppzksnark.hpp>
#include <libff/algebra/fields/field_utils.hpp>
#include <libff/algebra/scalar_multiplication/multiexp.hpp>
#include <libsnark/common/default_types/r1cs_ppzksnark_pp.hpp>
#include <libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp>
#include <libff/algebra/curves/edwards/edwards_pp.hpp>
#include <libff/algebra/curves/mnt/mnt4/mnt4_pp.hpp>
#include <libff/algebra/curves/mnt/mnt6/mnt6_pp.hpp>
#include <libff/common/utils.hpp>

#include <libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_read_gadget.hpp>
#include <libsnark/gadgetlib1/gadgets/merkle_tree/merkle_tree_check_update_gadget.hpp>

#include "gunerotransactionsend_gadget.hpp"
#include "GuneroProof.hpp"

using namespace libsnark;

namespace gunero {

class GuneroTransactionSendWitness{
public:
    uint256 W;
    uint256 T;
    uint256 V_S;
    uint256 V_R;
    uint256 L_P;

    GuneroTransactionSendWitness() {}
    GuneroTransactionSendWitness(
        const uint256& pW,
        const uint256& pT,
        const uint256& pV_S,
        const uint256& pV_R,
        const uint256& pL_P
    ) : W(pW),
        T(pT),
        V_S(pV_S),
        V_R(pV_R),
        L_P(pL_P)
    {
    }
    ~GuneroTransactionSendWitness() {}

    ADD_SERIALIZE_METHODS;

    template <typename Stream, typename Operation>
    inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
        READWRITE(W);
        READWRITE(T);
        READWRITE(V_S);
        READWRITE(V_R);
        READWRITE(L_P);
    }

    friend std::ostream& operator<<(std::ostream &out, const GuneroTransactionSendWitness &witness)
    {
        ::Serialize(out, witness, 1, 1);

        return out;
    }

    friend std::istream& operator>>(std::istream &in, GuneroTransactionSendWitness &witness)
    {
        ::Unserialize(in, witness, 1, 1);

        return in;
    }
};

///// TRANSACTION SEND PROOF /////
// With this proof, we are validating that the sender of the token is accepting that this token's ownership should
// be transferred to the new transaction hash given. The "account view hash" validates that this proof is consistent
// with the others generated, and also serves as an additional precaution for others using this proof to validate an
// unauthorized release of the token to a party not covered in the transaction.
// Public Parameters:
// Current Authorization Root Hash (W)
// Token UID (T)
// Sender Account View Hash (V_S)
// Receiver Account View Hash (V_R)
// Previous Transaction Hash (L_P)

// Private Parameters:
// Sender Private Key (s_S)
// Sender Account View Randomizer (r_S)
// Receiver Account View Randomizer (r_R)
// Previous Sender Account Address (A_PS)
// Previous Authorization Root Hash (W_P)
// alt: Receiver Proof Public Key (P_proof_R)

//1) Obtain A_S from s_S through EDCSA operations
//1 alt) Obtain P_proof_S from s_S through PRF operations
//2) Validate V_S == hash(A_S, hash(W, r_S)) (View Hash is consistent for Sender)
//2 alt) Validate V_S == hash(P_proof_S, hash(W, r_S)) (View Hash is consistent for Sender)
//3) Validate V_R == hash(A_R, hash(W, r_R)) (View Hash is consistent for Receiver)
//3 alt) Validate V_R == hash(P_proof_R, hash(W, r_R)) (View Hash is consistent for Receiver)
//4) Validate L_P == hash(A_PS, hash(s_S, hash(T, W_P))) (The send proof is valid, sender owns token)
template<typename FieldT, typename BaseT, typename HashT>
class GuneroTransactionSendCircuit
{
public:
    GuneroTransactionSendCircuit()
    {}
    ~GuneroTransactionSendCircuit() {}

    void generate(
        const std::string& r1csPath,
        const std::string& pkPath,
        const std::string& vkPath
    ) {
        protoboard<FieldT> pb;
        gunerotransactionsend_gadget<FieldT, BaseT, HashT> gunero(pb);

        gunero.generate_r1cs_constraints(r1csPath, pkPath, vkPath);
    }

    bool prove(
        const uint256& pW,
        const uint256& pT,
        const uint256& pV_S,
        const uint256& pV_R,
        const uint256& pL_P,
        const uint252& ps_S,
        const uint256& pr_S,
        const uint256& pr_R,
        const uint160& pA_PS,
        const uint256& pW_P,
        const uint256& pP_proof_R,
        const r1cs_ppzksnark_proving_key<BaseT>& pk,
        const r1cs_ppzksnark_verification_key<BaseT>& vk,
        GuneroProof& proof
    )
    {
#ifdef DEBUG
        libff::print_header("Gunero witness (proof)");
#endif

        {
            r1cs_primary_input<FieldT> primary_input;
            r1cs_auxiliary_input<FieldT> aux_input;
            {
                protoboard<FieldT> pb;
                {
#ifdef DEBUG
                    libff::print_header("Gunero gunerotransactionsend_gadget.load_r1cs_constraints()");
#endif

                    gunerotransactionsend_gadget<FieldT, BaseT, HashT> gunero(pb);

                    gunero.generate_r1cs_witness(
                        pW,
                        pT,
                        pV_S,
                        pV_R,
                        pL_P,
                        ps_S,
                        pr_S,
                        pr_R,
                        pA_PS,
                        pW_P,
                        pP_proof_R
                    );

#ifdef DEBUG
                    printf("\n"); libff::print_indent(); libff::print_mem("after gunerotransactionsend_gadget.load_r1cs_constraints()"); libff::print_time("after gunerotransactionsend_gadget.load_r1cs_constraints()");
#endif
                }

                // The constraint system must be satisfied or there is an unimplemented
                // or incorrect sanity check above. Or the constraint system is broken!
                assert(pb.is_satisfied());

                // TODO: These are copies, which is not strictly necessary.
                primary_input = pb.primary_input();
                aux_input = pb.auxiliary_input();

                // Swap A and B if it's beneficial (less arithmetic in G2)
                // In our circuit, we already know that it's beneficial
                // to swap, but it takes so little time to perform this
                // estimate that it doesn't matter if we check every time.
                // pb.constraint_system.swap_AB_if_beneficial();

                //Test witness_map()
                {
                    r1cs_primary_input<FieldT> primary_input_test = gunerotransactionsend_gadget<FieldT, BaseT, HashT>::witness_map(
                        pW,
                        pT,
                        pV_S,
                        pV_R,
                        pL_P
                    );
                    assert(primary_input == primary_input_test);
                }
            }

            r1cs_ppzksnark_proof<BaseT> r1cs_proof = r1cs_ppzksnark_prover<BaseT>(
                pk,
                primary_input,
                aux_input
            );

            proof = GuneroProof(r1cs_proof);

#ifdef DEBUG
            printf("\n"); libff::print_indent(); libff::print_mem("after witness (proof)"); libff::print_time("after witness (proof)");
#endif
        }

        //Verify
        {
            r1cs_primary_input<FieldT> primary_input = gunerotransactionsend_gadget<FieldT, BaseT, HashT>::witness_map(
                pW,
                pT,
                pV_S,
                pV_R,
                pL_P
            );

            return r1cs_ppzksnark_verifier_strong_IC<BaseT>(vk, primary_input, proof.to_libsnark_proof<r1cs_ppzksnark_proof<BaseT>>());
        }
    }

    bool verify(
        const uint256& pW,
        const uint256& pT,
        const uint256& pV_S,
        const uint256& pV_R,
        const uint256& pL_P,
        const GuneroProof& proof,
        const r1cs_ppzksnark_verification_key<BaseT>& vk,
        const r1cs_ppzksnark_processed_verification_key<BaseT>& vk_precomp
        )
    {
        try
        {
            r1cs_primary_input<FieldT> primary_input = gunerotransactionsend_gadget<FieldT, BaseT, HashT>::witness_map(
                pW,
                pT,
                pV_S,
                pV_R,
                pL_P
            );

            r1cs_ppzksnark_proof<BaseT> r1cs_proof = proof.to_libsnark_proof<r1cs_ppzksnark_proof<BaseT>>();

            ProofVerifier<BaseT> verifierEnabled = ProofVerifier<BaseT>::Strict();

            bool verified = verifierEnabled.check(
                vk,
                vk_precomp,
                primary_input,
                r1cs_proof
            );

            if (verified)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        catch (...)
        {
            return false;
        }
    }
};

} // end namespace `gunero`

#endif /* GUNEROTRANSACTIONSENDCIRCUIT_H_ */


#include <cctbx/boost_python/flex_fwd.h>

#include <boost/python/module.hpp>
#include <boost/python/class.hpp>
#include <boost/python/args.hpp>
#include <boost/python/return_value_policy.hpp>
#include <boost/python/return_by_value.hpp>
#include <boost/python/def.hpp>
#include "atom_mask.h"
#include "util.h"

namespace mmtbx { namespace masks {
namespace {

  scitbx::af::shared<std::string> generate_groups_p(const std::string &s, int n)
  {
    std::set<std::string> g;
    generate_groups(g, s,n);
    scitbx::af::shared<std::string> result;
    for(std::set<std::string>::const_iterator it=g.begin(); it!=g.end(); ++it)
      result.push_back(*it);
    return result;
  }

  void wrap_atom_mask()
  {
    typedef atom_mask w_t;
    using namespace boost::python;
    typedef return_value_policy<return_by_value> rbv;

    class_<w_t>("atom_mask", no_init)
      .def(init<
          const cctbx::uctbx::unit_cell &,
          const cctbx::sgtbx::space_group &,
          double,
          optional<
            double,
            double,
            double >
          > ((
              arg("unit_cell"),
              arg("group"),
              arg("resolution"),
              arg("grid_step_factor"),
              arg("solvent_radius"),
              arg("shrink_truncation_radius")
              )))
      .def(init<
          const cctbx::uctbx::unit_cell &,
          const cctbx::sgtbx::space_group &,
          const grid_t::index_type &,
          double,
          double
          > ((
              arg("unit_cell"),
              arg("space_group"),
              arg("gridding_n_real"),
              arg("solvent_radius"),
              arg("shrink_truncation_radius")
              )))
      .def("compute", &w_t::compute, (arg("sites_frac"), arg("atom_radii"),
         arg("shells") = shells_array_t() ) )
      .def("structure_factors", &w_t::structure_factors, (arg("indices"),
         arg("layer")=0) )
      .def("mask_data_whole_uc", &w_t::mask_data_whole_uc, (
         arg("layer")=0) )
      .def("grid_size", &w_t::grid_size)
      .def("n_asu_atoms", &w_t::n_asu_atoms)
      .def("n_solvent_layers", &w_t::n_solvent_layers)
      .def("xplor_write_map", &w_t::xplor_write_map, (arg("file_name"),
         arg("layer")=0, arg("invert")=false) )
      .def("layer_volume_fractions", &w_t::layer_volume_fractions)
      .def_readonly("solvent_radius",
               &w_t::solvent_radius)
      .def_readonly("shrink_truncation_radius",
               &w_t::shrink_truncation_radius)
      .def_readonly("contact_surface_fraction",
               &w_t::contact_surface_fraction)
      .def_readonly("accessible_surface_fraction",
               &w_t::accessible_surface_fraction)
      // DO NOTE USE, these are temporarily here
      .def_readonly("debug_mask_asu_time", &w_t::debug_mask_asu_time)
      .def_readonly("debug_atoms_to_asu_time", &w_t::debug_atoms_to_asu_time)
      .def_readonly("debug_accessible_time", &w_t::debug_accessible_time)
      .def_readonly("debug_contact_time", &w_t::debug_contact_time)
      .def_readonly("debug_fft_time", &w_t::debug_fft_time)
      .def_readonly("debug_has_enclosed_box", &w_t::debug_has_enclosed_box)
     ;

    // DO NOT USE
    boost::python::def("generate_groups", generate_groups_p);
  }

  void init_module()
  {
    wrap_atom_mask();
  }

} // namespace <anonymous>
}} // namespace mmtbx::masks

BOOST_PYTHON_MODULE(mmtbx_masks_ext)
{
  mmtbx::masks::init_module();
}


#include <aslam/backend/Optimizer.hpp>
#include <aslam/backend/Optimizer2.hpp>
#include <boost/shared_ptr.hpp>
#include <numpy_eigen/boost_python_headers.hpp>

// some wrappers:
Eigen::VectorXd b(const aslam::backend::Optimizer* o) { return o->b(); }
Eigen::VectorXd dx(const aslam::backend::Optimizer* o) { return o->dx(); }
Eigen::VectorXd rhs(const aslam::backend::Optimizer* o) { return o->rhs(); }

void exportOptimizer() {
    using namespace boost::python;
    using namespace aslam::backend;

    class_<SolutionReturnValue>("SolutionReturnValue")
        .def_readwrite("JStart", &SolutionReturnValue::JStart)
        .def_readwrite("JFinal", &SolutionReturnValue::JFinal)
        .def_readwrite("iterations", &SolutionReturnValue::iterations)
        .def_readwrite("failedIterations", &SolutionReturnValue::failedIterations)
        .def_readwrite("lmLambdaFinal", &SolutionReturnValue::lmLambdaFinal)
        .def_readwrite("dXFinal", &SolutionReturnValue::dXFinal)
        .def_readwrite("dJFinal", &SolutionReturnValue::dJFinal)
        .def_readwrite("linearSolverFailure", &SolutionReturnValue::linearSolverFailure);

    class_<Optimizer, boost::shared_ptr<Optimizer> >("Optimizer", init<>())
        .def(init<OptimizerOptions>())
        .def("setProblem",
             static_cast<void (Optimizer::*)(boost::shared_ptr<OptimizationProblemBase>)>(&Optimizer::setProblem))

        /// \brief initialize the optimizer to run on an optimization problem.
        ///        This should be called before calling optimize()
        .def("initialize", &Optimizer::initialize)

        /// \brief initialize the linear solver specified in the optimizer options.
        .def("initializeLinearSolver", &Optimizer::initializeLinearSolver)

        /// \brief Run the optimization
        .def("optimize", &Optimizer::optimize)
        .def("optimizeDogLeg", &Optimizer::optimizeDogLeg)

        .def("buildGnMatrices", &Optimizer::buildGnMatrices)
        /// \brief Get the optimizer options.
        .add_property("options", make_function(&Optimizer::options, return_internal_reference<>()))

        /// \brief return the full Hessian
        .def("H", &Optimizer::H, return_internal_reference<>())

        /// \brief return the full rhs
        .def("rhs", &rhs)

        /// \brief return the reduced system lhs
        .def("A", &Optimizer::A, return_internal_reference<>())

        /// \brief return the reduced system rhs
        .def("b", &b)

        /// \brief return the reduced system dx
        .def("dx", &dx)

        /// The value of the objective function.
        .def("J", &Optimizer::J)

        // \todo Covariance calculations
        // void computeCovariances();
        // const Eigen::MatrixXd & getDenseBlockCovariance(int di1, int di2);
        // Eigen::MatrixXd getSparseSparseCovariance(int si1, int si2);
        // Eigen::MatrixXd getDenseSparseCovariance(int di, int si);

        /// \brief Evaluate the error at the current state.
        .def("evaluateError", &Optimizer::evaluateError)

        /// \brief Get dense design variable i.
        .def("denseVariable", &Optimizer::denseVariable, return_internal_reference<>())
        /// \brief Get sparse design variable i
        .def("sparseVariable", &Optimizer::sparseVariable, return_internal_reference<>())

        /// \brief how many dense design variables are involved in the problem
        .def("numDenseDesignVariables", &Optimizer::numDenseDesignVariables)

        /// \brief how many sparse design variables are involved in the problem
        .def("numSparseDesignVariables", &Optimizer::numSparseDesignVariables)

        .def("printTiming", &Optimizer::printTiming)

        .def("computeCovariances", &Optimizer::computeCovariances)
        .def("computeDiagonalCovariances", &Optimizer::computeDiagonalCovariances)
        // \todo Think of a nice way to expose this to Python
        //.def("computeCovarianceBlocks", &Optimizer::computeCovarianceBlocks)
        .def("getCovarianceBlock", &Optimizer::getCovarianceBlock, return_internal_reference<>())
        .def("P", &Optimizer::P, return_internal_reference<>())
        .def("getCovariance", &Optimizer::getCovariance, return_internal_reference<>())

        ;

    class_<Optimizer2, boost::shared_ptr<Optimizer2> >("Optimizer2", init<>())
        .def(init<Optimizer2Options>())
        .def("setProblem", &Optimizer2::setProblem)

        /// \brief initialize the optimizer to run on an optimization problem.
        ///        This should be called before calling optimize()
        .def("initialize", &Optimizer2::initialize)

        /// \brief initialize the linear solver specified in the optimizer options.
        .def("initializeLinearSolver", &Optimizer2::initializeLinearSolver)

        /// \brief Run the optimization
        .def("optimize", &Optimizer2::optimize)
        //.def("optimizeDogLeg", &Optimizer2::optimizeDogLeg)

        /// \brief Get the optimizer options.
        .add_property("options", make_function(&Optimizer2::options, return_internal_reference<>()))

        /// \brief return the full Hessian
        //.def("H", &Optimizer2::H, return_internal_reference<>())

        /// \brief return the full rhs
        //.def("rhs", &rhs)

        /// \brief return the reduced system lhs
        //.def("A", &Optimizer2::A, return_internal_reference<>())

        /// \brief return the reduced system rhs
        //.def("b", &b)

        /// \brief return the reduced system dx
        //.def("dx", &dx)

        /// The value of the objective function.
        .def("J", &Optimizer2::J)

        // \todo Covariance calculations
        // void computeCovariances();
        // const Eigen::MatrixXd & getDenseBlockCovariance(int di1, int di2);
        // Eigen::MatrixXd getSparseSparseCovariance(int si1, int si2);
        // Eigen::MatrixXd getDenseSparseCovariance(int di, int si);
        .def("computeCovariances", &Optimizer2::computeCovariances)
        .def("computeDiagonalCovariances", &Optimizer2::computeDiagonalCovariances)

        /// \brief Evaluate the error at the current state.
        .def("evaluateError", &Optimizer2::evaluateError)

        /// \brief Get dense design variable i.
        .def("densignVariable", &Optimizer2::designVariable, return_internal_reference<>())

        /// \brief how many dense design variables are involved in the problem
        .def("numDesignVariables", &Optimizer2::numDesignVariables)

        .def("printTiming", &Optimizer2::printTiming)
        .def("computeHessian", &Optimizer2::computeHessian)

        ;
}


/* CirKit: A circuit toolkit
 * Copyright (C) 2009-2015  University of Bremen
 * Copyright (C) 2015-2017  EPFL
 *
 * 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.
 */

#include "exact_toffoli_synthesis.hpp"

#include <array>
#include <iostream>

#include <boost/format.hpp>

#include <core/utils/timer.hpp>
#include <classical/abc/abc_api.hpp>
#include <reversible/functions/add_gates.hpp>
#include <reversible/utils/truth_table_helpers.hpp>

#include <sat/bsat/satSolver.h>

namespace cirkit
{

/******************************************************************************
 * Types                                                                      *
 ******************************************************************************/

class exact_toffoli_synthesis_manager
{
public:
  exact_toffoli_synthesis_manager( const binary_truth_table& spec, const properties::ptr& settings );

  void run( circuit& circ );

private:
  /* indexes:
   *   i    : target, line
   *   j, k : controls
   *   l    : gate
   *   t    : truth table row
   */
  int var_offset( int l ) const;
  int sim_var( int l, int i, int t ) const;
  int not_var( int l, int i ) const;
  int cnot_var( int l, int i, int j ) const;
  int tof_var( int l, int i, int j, int k ) const;

  int equal_other_clauses( int g, int l, int t, int i );
  int not_clauses( int l, int i, int t );
  int cnot_clauses( int l, int i, int j, int t );
  int tof_clauses( int l, int i, int j, int k, int t );
  int gate_clauses( int l );
  std::vector<int> spec_assumptions() const;
  void create_circuit( circuit& circ ) const;

  int symmetry_breaking_ordering( int l );
  int symmetry_breaking_not_same( int l );

  template<typename ...Lits>
  int add_clause( Lits... literals )
  {
    std::array<int, sizeof...( Lits )> cls = {{ literals... }};
    int * p = &cls.front();
    return abc::sat_solver_addclause( solver.get(), p, p + sizeof...( Lits ) );
  }

  int make_lit( int var, int c ) const;

  /* range helpers */
  template<typename Fn>
  void for_each_not( Fn&& f )
  {
    for ( auto i = 0u; i < n; ++i )
    {
      f( i );
    }
  }

  template<typename Fn>
  void for_each_cnot( Fn&& f )
  {
    for ( auto i = 0u; i < n; ++i )
    {
      for ( auto j = 1u; j < n; ++j )
      {
        f( i, j );
      }
    }
  }

  template<typename Fn>
  void for_each_tof( Fn&& f )
  {
    for ( auto i = 0u; i < n; ++i )
    {
      for ( auto j = 2u; j < n; ++j )
      {
        for ( auto k = 1u; k < j; ++k )
        {
          f( i, j, k );
        }
      }
    }
  }

  void debug_vars();

private:
  const binary_truth_table& spec;
  unsigned                  n, r;
  int                       num_vars_per_gate;
  int                       sim_offset;

  std::vector<boost::dynamic_bitset<>> spec_vec;

  /* solver */
  std::unique_ptr<abc::sat_solver, void(*)(abc::sat_solver*)> solver;

  /* settings */
  bool verbose = false;
};

/******************************************************************************
 * Private functions                                                          *
 ******************************************************************************/

exact_toffoli_synthesis_manager::exact_toffoli_synthesis_manager( const binary_truth_table& spec, const properties::ptr& settings )
  : spec( spec ),
    n( spec.num_inputs() ),
    solver( abc::sat_solver_new(), abc::sat_solver_delete ),
    verbose( get( settings, "verbose", verbose ) )
{
  /* we have:
     - 2^n             sim vars
     - n               NOT vars
     - n(n-1)          CNOT vars
     - n(n-1)(n-2) / 2 TOF vars
  */
  num_vars_per_gate = n * ( 1 << n ) + n + n * ( n - 1 ) + n * ( ( n - 1 ) * ( n - 2 ) ) / 2;
  sim_offset = n * ( 1 << n );

  spec_vec = truth_table_to_bitset_vector( spec );
}

void exact_toffoli_synthesis_manager::run( circuit& circ )
{
  r = 0;
  auto nvars = 0;
  abc::sat_solver_restart( solver.get() );

  while ( true )
  {
    ++r;
    nvars += num_vars_per_gate;

    std::cout << "[i] try to find optimum circuit with " << r << " gates" << std::endl;

    abc::sat_solver_setnvars( solver.get(), nvars );

    gate_clauses( r - 1 );

    if ( r > 1 )
    {
      symmetry_breaking_ordering( r - 1 );
      symmetry_breaking_not_same( r - 1 );
    }
    auto assump = spec_assumptions();
    int * p = &assump.front();

    const auto result = abc::sat_solver_solve( solver.get(), p, p + assump.size(), 0, 0, 0, 0 );

    if ( result == 1 ) /* SAT */
    {
      std::cout << "[i] found circuit with " << r << " gates" << std::endl;
      create_circuit( circ );
      return;
    }
  }

  assert( false );
}

inline int exact_toffoli_synthesis_manager::var_offset( int l ) const
{
  assert( l < static_cast<int>( r ) );

  return l * num_vars_per_gate;
}

inline int exact_toffoli_synthesis_manager::sim_var( int l, int i, int t ) const
{
  assert( l < static_cast<int>( r ) );
  assert( i < static_cast<int>( n ) );
  assert( t < static_cast<int>( 1u << n ) );

  return var_offset( l ) + i * ( 1 << n ) + t;
}

inline int exact_toffoli_synthesis_manager::not_var( int l, int i ) const
{
  assert( l < static_cast<int>( r ) );
  assert( i < static_cast<int>( n ) );

  return var_offset( l ) + sim_offset + i;
}

inline int exact_toffoli_synthesis_manager::cnot_var( int l, int i, int j ) const
{
  assert( l < static_cast<int>( r ) );
  assert( i < static_cast<int>( n ) );
  assert( j > 0 && j < static_cast<int>( n ) );

  return var_offset( l ) + sim_offset + n + i * ( n - 1 ) + j - 1;
}

inline int exact_toffoli_synthesis_manager::tof_var( int l, int i, int j, int k ) const
{
  assert( l < static_cast<int>( r ) );
  assert( i < static_cast<int>( n ) );
  assert( j > 1 && j < static_cast<int>( n ) );
  assert( k > 0 && k < static_cast<int>( j ) );

  auto offset = var_offset( l ) + sim_offset + n + n * ( n - 1 );
  offset += i * ( ( n - 2 ) * ( n - 1 ) ) / 2; /* offset based on target */
  offset += ( ( j - 2 ) * ( j - 1 ) ) / 2; /* right j entry */

  return offset + k - 1;
}

inline int exact_toffoli_synthesis_manager::equal_other_clauses( int g, int l, int t, int i )
{
  for ( auto ii = 0u; ii < n; ++ii )
  {
    if ( static_cast<int>( ii ) == i ) continue;

    if ( l == 0 )
    {
      const auto bit = ( t >> ii ) & 1;
      add_clause( make_lit( g, 1 ), make_lit( sim_var( l, ii, t ), 1 - bit ) );
    }
    else
    {
      add_clause( make_lit( g, 1 ), make_lit( sim_var( l - 1, ii, t ), 0 ), make_lit( sim_var( l, ii, t ), 1 ) );
      add_clause( make_lit( g, 1 ), make_lit( sim_var( l - 1, ii, t ), 1 ), make_lit( sim_var( l, ii, t ), 0 ) );
    }
  }

  return 1;
}

int exact_toffoli_synthesis_manager::not_clauses( int l, int i, int t )
{
  const auto g = not_var( l, i );
  equal_other_clauses( g, l, t, i );

  if ( l == 0 )
  {
    const auto bit = ( t >> i ) & 1;
    add_clause( make_lit( g, 1 ), make_lit( sim_var( l, i, t ), bit ) );
  }
  else
  {
    add_clause( make_lit( g, 1 ), make_lit( sim_var( l - 1, i, t ), 0 ), make_lit( sim_var( l, i, t ), 0 ) );
    add_clause( make_lit( g, 1 ), make_lit( sim_var( l - 1, i, t ), 1 ), make_lit( sim_var( l, i, t ), 1 ) );
  }

  return 1;
}

int exact_toffoli_synthesis_manager::cnot_clauses( int l, int i, int j, int t )
{
  const auto g = cnot_var( l, i, j );
  equal_other_clauses( g, l, t, i );

  const auto c1 = ( i + j ) % n;

  if ( l == 0 )
  {
    const auto tbit = ( t >> i ) & 1;
    const auto cbit = ( t >> c1 ) & 1;
    add_clause( make_lit( g, 1 ), make_lit( sim_var( l, i, t ), tbit == cbit ) );
  }
  else
  {
    add_clause( make_lit( g, 1 ), make_lit( sim_var( l - 1, i, t ), 0 ), make_lit( sim_var( l - 1, c1, t ), 0 ), make_lit( sim_var( l, i, t ), 1 ) );
    add_clause( make_lit( g, 1 ), make_lit( sim_var( l - 1, i, t ), 0 ), make_lit( sim_var( l - 1, c1, t ), 1 ), make_lit( sim_var( l, i, t ), 0 ) );
    add_clause( make_lit( g, 1 ), make_lit( sim_var( l - 1, i, t ), 1 ), make_lit( sim_var( l - 1, c1, t ), 0 ), make_lit( sim_var( l, i, t ), 0 ) );
    add_clause( make_lit( g, 1 ), make_lit( sim_var( l - 1, i, t ), 1 ), make_lit( sim_var( l - 1, c1, t ), 1 ), make_lit( sim_var( l, i, t ), 1 ) );
  }

  return 1;
}

int exact_toffoli_synthesis_manager::tof_clauses( int l, int i, int j, int k, int t )
{
  const auto g = tof_var( l, i, j, k );
  equal_other_clauses( g, l, t, i );

  const auto c1 = ( i + j ) % n;
  const auto c2 = ( i + k ) % n;

  if ( l == 0 )
  {
    const auto tbit = ( t >> i ) & 1;
    const auto cbit1 = ( t >> c1 ) & 1;
    const auto cbit2 = ( t >> c2 ) & 1;
    add_clause( make_lit( g, 1 ), make_lit( sim_var( l, i, t ), tbit == ( cbit1 && cbit2 ) ) );
  }
  else
  {
    add_clause( make_lit( g, 1 ), make_lit( sim_var( l - 1, i, t ), 1 ), make_lit( sim_var( l - 1, c1, t ), 0 ), make_lit( sim_var( l, i, t ), 0 ) );
    add_clause( make_lit( g, 1 ), make_lit( sim_var( l - 1, i, t ), 0 ), make_lit( sim_var( l - 1, c1, t ), 0 ), make_lit( sim_var( l, i, t ), 1 ) );
    add_clause( make_lit( g, 1 ), make_lit( sim_var( l - 1, i, t ), 1 ), make_lit( sim_var( l - 1, c2, t ), 0 ), make_lit( sim_var( l, i, t ), 0 ) );
    add_clause( make_lit( g, 1 ), make_lit( sim_var( l - 1, i, t ), 0 ), make_lit( sim_var( l - 1, c2, t ), 0 ), make_lit( sim_var( l, i, t ), 1 ) );
    add_clause( make_lit( g, 1 ), make_lit( sim_var( l - 1, i, t ), 1 ), make_lit( sim_var( l - 1, c1, t ), 1 ), make_lit( sim_var( l - 1, c2, t ), 1 ), make_lit( sim_var( l, i, t ), 1 ) );
    add_clause( make_lit( g, 1 ), make_lit( sim_var( l - 1, i, t ), 0 ), make_lit( sim_var( l - 1, c1, t ), 1 ), make_lit( sim_var( l - 1, c2, t ), 1 ), make_lit( sim_var( l, i, t ), 0 ) );
  }

  return 1;
}

int exact_toffoli_synthesis_manager::symmetry_breaking_ordering( int l )
{
  /* no CNOT( i, j ) before NOT( i ) */
  for_each_cnot( [this, l]( int i, int j ) {
      add_clause( make_lit( not_var( l, i ), 1 ), make_lit( cnot_var( l - 1, i, j ), 1 ) );
    } );
  /* no TOF( i, j, k ) before NOT( i ) */
  for_each_tof( [this, l]( int i, int j, int k ) {
      add_clause( make_lit( not_var( l, i ), 1 ), make_lit( tof_var( l - 1, i, j, k ), 1 ) );
    } );
  /* order CNOTs with same target */
  for_each_cnot( [this, l]( int i, int j ) {
      for ( auto jj = 1; jj < j; ++jj )
      {
        add_clause( make_lit( cnot_var( l, i, j ), 1 ), make_lit( cnot_var( l - 1, i, jj ), 1 ) );
      }
    } );
  /* order of TOFs with same target */
  for_each_tof( [this, l]( int i, int j, int k ) {
      for ( auto jj = 2; jj < j; ++jj )
      {
        for ( auto kk = 1; kk < jj; ++kk )
        {
          add_clause( make_lit( tof_var( l, i, j, k ), 1 ), make_lit( tof_var( l - 1, i, jj, kk ), 1 ) );
        }
      }
      for ( auto kk = 1; kk < j; ++kk )
      {
        add_clause( make_lit( tof_var( l, i, j, k ), 1 ), make_lit( tof_var( l - 1, i, j, kk ), 1 ) );
      }
    } );
  /* no CNOT( i, j ) before NOT( i' ) where i' not in {i, j} */
  for_each_cnot( [this, l]( int i, int j ) {
      for ( auto ii = 0u; ii < n; ++ii )
      {
        if ( static_cast<int>( ii ) == i || ii == ( ( i + j ) % n ) ) { continue; }
        add_clause( make_lit( not_var( l, ii ), 1 ), make_lit( cnot_var( l - 1, i, j ), 1 ) );
      }
    } );
  /* no TOF( i, j, k ) before NOT( i' ) where i' not in {i, j, k} */
  for_each_tof( [this, l]( int i, int j, int k ) {
      for ( auto ii = 0u; ii < n; ++ii )
      {
        if ( static_cast<int>( ii ) == i || ii == ( ( i + j ) % n ) || ii == ( ( i + k ) % n ) ) { continue; }
        add_clause( make_lit( not_var( l, ii ), 1 ), make_lit( tof_var( l - 1, i, j, k ), 1 ) );
      }
    } );

  return 1;
}

int exact_toffoli_synthesis_manager::symmetry_breaking_not_same( int l )
{
  for_each_not( [this, l]( int i ) {
      add_clause( make_lit( not_var( l, i ), 1 ), make_lit( not_var( l - 1, i ), 1 ) );
    } );
  for_each_cnot( [this, l]( int i, int j ) {
      add_clause( make_lit( cnot_var( l, i, j ), 1 ), make_lit( cnot_var( l - 1, i, j ), 1 ) );
    } );
  for_each_tof( [this, l]( int i, int j, int k ) {
      add_clause( make_lit( tof_var( l, i, j, k ), 1 ), make_lit( tof_var( l - 1, i, j, k ), 1 ) );
    } );

  return 1;
}

void exact_toffoli_synthesis_manager::debug_vars()
{
  for ( auto l = 0u; l < r; ++l )
  {
    for ( auto i = 0u; i < n; ++i )
    {
      for ( auto t = 0u; t < ( 1u << n ); ++t )
      {
        std::cout << boost::format( "%6d sim_var( %d, %d, %d )" ) % sim_var( l, i, t ) % l % i % t << std::endl;
      }
    }

    for ( auto i = 0u; i < n; ++i )
    {
      std::cout << boost::format( "%6d not_var( %d, %d )" ) % not_var( l, i ) % l % i << std::endl;
    }

    for ( auto i = 0u; i < n; ++i )
    {
      for ( auto j = 1u; j < n; ++j )
      {
        std::cout << boost::format( "%6d cnot_var( %d, %d, %d )" ) % cnot_var( l, i, j ) % l % i % j << std::endl;
      }
    }

    for ( auto i = 0u; i < n; ++i )
    {
      for ( auto j = 2u; j < n; ++j )
      {
        for ( auto k = 1u; k < j; ++k )
        {
          std::cout << boost::format( "%6d tof_var( %d, %d, %d, %d )" ) % tof_var( l, i, j, k ) % l % i % j % k << std::endl;
        }
      }
    }
  }
}

int exact_toffoli_synthesis_manager::gate_clauses( int l )
{
  for ( auto t = 0u; t < ( 1u << n ); ++t )
  {
    for_each_not( [this, l, t]( int i ) { not_clauses( l, i, t ); } );
    for_each_cnot( [this, l, t]( int i, int j ) { cnot_clauses( l, i, j, t ); } );
    for_each_tof( [this, l, t]( int i, int j, int k ) { tof_clauses( l, i, j, k, t ); } );
  }

  /* at least one gate */
  std::vector<int> gates;

  for ( auto i = 0u; i < n; ++i )
  {
    gates.push_back( make_lit( not_var( l, i ), 0 ) );

    for ( auto j = 1u; j < n; ++j )
    {
      gates.push_back( make_lit( cnot_var( l, i, j ), 0 ) );

      for ( auto k = 1u; k < j; ++k )
      {
        gates.push_back( make_lit( tof_var( l, i, j, k ), 0 ) );
      }
    }
  }

  int * p = &gates.front();
  abc::sat_solver_addclause( solver.get(), p, p + gates.size() );

  return 1;
}

std::vector<int> exact_toffoli_synthesis_manager::spec_assumptions() const
{
  std::vector<int> assumptions;
  assumptions.reserve(  sim_offset );

  for ( auto i = 0u; i < n; ++i )
  {
    for ( auto t = 0u; t < ( 1u << n ); ++t )
    {
      assumptions.push_back( make_lit( sim_var( r - 1, i, t ), spec_vec[t][i] ? 0 : 1 ) );
    }
  }

  return assumptions;
}

void exact_toffoli_synthesis_manager::create_circuit( circuit& circ ) const
{
  circ.set_lines( n );

  for ( auto l = 0u; l < r; ++l )
  {
    auto added = false;

    for ( auto i = 0u; i < n; ++i )
    {
      if ( abc::sat_solver_var_value( solver.get(), not_var( l, i ) ) )
      {
        if ( !added )
        {
          added = true;
          append_not( circ, n - i - 1 );
        }
        else { std::cout << "[w] alternative gate found" << std::endl; }
      }

      for ( auto j = 1u; j < n; ++j )
      {
        if ( abc::sat_solver_var_value( solver.get(), cnot_var( l, i, j ) ) )
        {
          if ( !added )
          {
            added = true;
            append_cnot( circ, n - ( ( i + j ) % n ) - 1, n - i - 1 );
          }
          else { std::cout << "[w] alternative gate found" << std::endl; }
        }

        for ( auto k = 1u; k < j; ++k )
        {
          if ( abc::sat_solver_var_value( solver.get(), tof_var( l, i, j, k ) ) )
          {
            if ( !added )
            {
              added = true;
              append_toffoli( circ )( n - ( ( i + k ) % n ) - 1, n - ( ( i + j ) % n ) - 1 )( n - i - 1 );
            }
            else { std::cout << "[w] alternative gate found" << std::endl; }
          }
        }
      }
    }
  }
}

inline int exact_toffoli_synthesis_manager::make_lit( int var, int c ) const
{
  return abc::Abc_Var2Lit( var, c );
}

/******************************************************************************
 * Public functions                                                           *
 ******************************************************************************/

bool exact_toffoli_synthesis( circuit& circ, const binary_truth_table& spec, const properties::ptr& settings, const properties::ptr& statistics )
{
  properties_timer t( statistics );

  exact_toffoli_synthesis_manager mgr( spec, settings );

  mgr.run( circ );

  return true;
}

}

// Local Variables:
// c-basic-offset: 2
// eval: (c-set-offset 'substatement-open 0)
// eval: (c-set-offset 'innamespace 0)
// End:


#include <cctbx/boost_python/flex_fwd.h>
#include <boost/python/module.hpp>
#include <boost/python/class.hpp>
#include <boost/python/def.hpp>
#include <boost/python/list.hpp>
#include <scitbx/array_family/flex_types.h>
#include <scitbx/array_family/shared.h>
#include <scitbx/array_family/versa.h>
#include <scitbx/array_family/accessors/c_grid.h>
#include <cctbx/miller.h>
#include <dials/array_family/reflection_table.h>
#include <algorithm>

typedef cctbx::miller::index<> miller_index_t;

namespace sx_merging {
  void foo2(){
    std::cout <<"HELLO merging foo2"<< std::endl;
  }

  void get_hkl_chunks_cpp(dials::af::reflection_table reflections,
                          const scitbx::af::shared<miller_index_t>& hkl_list,
                          const scitbx::af::shared<int>& chunk_id_list,
                          boost::python::list hkl_chunks_cpp){

    // set up a map hkl:chunk_id
    std::map<miller_index_t, size_t> chunk_lookup;
    for(size_t i = 0UL; i < hkl_list.size(); ++i){
      chunk_lookup[hkl_list[i]] = (size_t)chunk_id_list[i];
    }

    SCITBX_ASSERT(reflections.contains("miller_index_asymmetric"));
    SCITBX_ASSERT(reflections.contains("intensity.sum.value"));
    SCITBX_ASSERT(reflections.contains("intensity.sum.variance"));

    scitbx::af::ref<miller_index_t> miller_index = reflections["miller_index_asymmetric"];
    scitbx::af::ref<double> intensity = reflections["intensity.sum.value"];
    scitbx::af::ref<double> variance = reflections["intensity.sum.variance"];

    miller_index_t* mi_ptr = miller_index.begin();
    double* intensity_ptr = intensity.begin();
    double* variance_ptr = variance.begin();

    int n_chunks = boost::python::len(hkl_chunks_cpp);
    std::vector<dials::af::reflection_table> tables;
    for(size_t i=0UL; i < n_chunks; ++i){
      tables.push_back(boost::python::extract<dials::af::reflection_table>(hkl_chunks_cpp[i]));
      SCITBX_ASSERT(tables.back().contains("miller_index_asymmetric"));
    }

    // cache all columns for all hkl chunks
    std::vector<scitbx::af::shared<miller_index_t> >  mi_cols;
    std::vector<scitbx::af::shared<double> >          intensity_cols;
    std::vector<scitbx::af::shared<double> >          variance_cols;

    for(size_t i=0UL; i < n_chunks; ++i){
      mi_cols.push_back(tables[i]["miller_index_asymmetric"]);
      intensity_cols.push_back(tables[i]["intensity.sum.value"]);
      variance_cols.push_back(tables[i]["intensity.sum.variance"]);
    }

    // distribute reflections over chunks
    for(size_t i=0UL; i < reflections.size(); ++i){
      miller_index_t  hkl       = *mi_ptr++;
      double          intensity = *intensity_ptr++;
      double          variance  = *variance_ptr++;

      if( 0 != chunk_lookup.count(hkl) )
      {
          size_t chunk_id  = chunk_lookup[hkl];
          mi_cols[chunk_id].push_back(hkl);
          intensity_cols[chunk_id].push_back(intensity);
          variance_cols[chunk_id].push_back(variance);
       }
    }
  }
}

using namespace boost::python;
namespace sx_merging{
namespace boost_python { namespace {

  void
  sx_merging_init_module() {
    using namespace boost::python;
    typedef return_value_policy<return_by_value> rbv;
    typedef default_call_policies dcp;

    def("foo2",&sx_merging::foo2);
    def("get_hkl_chunks_cpp",&sx_merging::get_hkl_chunks_cpp);
  }

}
}} // namespace sx_merging::boost_python::<anonymous>

BOOST_PYTHON_MODULE(sx_merging_ext)
{
  sx_merging::boost_python::sx_merging_init_module();
}


//  Copyright (c) 2016 Hartmut Kaiser
//
//  Distributed under the Boost Software License, Version 1.0. (See accompanying
//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#ifndef HISTOGRAM_HPP
#define HISTOGRAM_HPP

#include <hpx/config.hpp>

#include <boost/range/iterator_range.hpp>
#include <boost/parameter/keyword.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/accumulators/framework/accumulator_base.hpp>
#include <boost/accumulators/framework/extractor.hpp>
#include <boost/accumulators/numeric/functional.hpp>
#include <boost/accumulators/framework/parameters/sample.hpp>
#include <boost/accumulators/framework/depends_on.hpp>
#include <boost/accumulators/statistics_fwd.hpp>
#include <boost/accumulators/statistics/count.hpp>
#include <boost/accumulators/statistics/max.hpp>
#include <boost/accumulators/statistics/min.hpp>

#include <algorithm>
#include <cstddef>
#include <functional>
#include <limits>
#include <utility>
#include <vector>

////////////////////////////////////////////////////////////////////////////////
namespace boost { namespace accumulators
{
    ///////////////////////////////////////////////////////////////////////////
    // cache_size and num_bins named parameters
    BOOST_PARAMETER_NESTED_KEYWORD(tag, histogram_num_bins, num_bins)
    BOOST_PARAMETER_NESTED_KEYWORD(tag, histogram_min_range, min_range)
    BOOST_PARAMETER_NESTED_KEYWORD(tag, histogram_max_range, max_range)
}}

namespace hpx { namespace util
{
    using boost::accumulators::histogram_num_bins;
    using boost::accumulators::histogram_min_range;
    using boost::accumulators::histogram_max_range;

    namespace detail
    {
        ///////////////////////////////////////////////////////////////////////////
        // The histogram histogram estimator returns a histogram of the sample
        // distribution. The positions and sizes of the bins are determined using a
        // specifiable number of cached samples (cache_size). The range between the
        // minimum and the maximum of the cached samples is subdivided into a
        // specifiable number of bins (num_bins) of same size. Additionally,
        // an under- and an overflow bin is added to capture future under- and
        // overflow samples. Once the bins are determined, the cached samples and
        // all subsequent samples are added to the correct bins. At the end, a
        // range of std::pair is return, where each pair contains the position of
        // the bin (lower bound) and the samples count (normalized with the
        // total number of samples).
        template <typename Sample>
        struct histogram_impl : boost::accumulators::accumulator_base
        {
#if BOOST_VERSION > 105400
            typedef typename boost::numeric::functional::fdiv_base<
                    Sample, std::size_t
                >::result_type float_type;
#else
            typedef typename boost::numeric::functional::average<
                    Sample, std::size_t
                >::result_type float_type;
#endif
            typedef std::vector<std::pair<float_type, float_type> > histogram_type;
            typedef std::vector<float_type> array_type;

            // for boost::result_of
            typedef boost::iterator_range<
                    typename histogram_type::iterator
                > result_type;

// conversion from 'const __int64' to 'const double', possible loss of data
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable: 4244)
#endif
            template <typename Args>
            histogram_impl(Args const& args)
             :  num_bins_(args[histogram_num_bins]),
                minimum_(args[histogram_min_range]),
                maximum_(args[histogram_max_range]),
                bin_size_ (boost::numeric::average(
                    args[histogram_max_range] - args[histogram_min_range],
                    args[histogram_num_bins])
                ),
                samples_in_bin_(std::size_t(args[histogram_num_bins] + 2), 0.),
                bin_positions_(args[histogram_num_bins] + 2),
                histogram_(
                   std::size_t(args[histogram_num_bins] + 2),
                   std::make_pair(0, 1)
                ),
                is_dirty_(true)
            {
                // determine bin positions (their lower bounds)
                for (std::size_t i = 0; i < this->num_bins_ + 2; ++i)
                {
                    this->bin_positions_[i] = minimum_ + (i - 1.0) * bin_size_;
                }
            }
#if defined(_MSC_VER)
#pragma warning(pop)
#endif

            template <typename Args>
            void operator()(Args const &args)
            {
                //std::size_t cnt = count(args);
                {
                    if (args[boost::accumulators::sample] <
                        this->bin_positions_[1])
                    {
                        ++(this->samples_in_bin_[0]);
                    }
                    else if (args[boost::accumulators::sample] >=
                        this->bin_positions_[this->num_bins_ + 1])
                    {
                        ++(this->samples_in_bin_[this->num_bins_ + 1]);
                    }
                    else
                    {
                        typename array_type::iterator it = std::upper_bound(
                            this->bin_positions_.begin()
                          , this->bin_positions_.end()
                          , args[boost::accumulators::sample]
                        );

                        std::size_t d =
                            std::distance(this->bin_positions_.begin(), it);
                        ++(this->samples_in_bin_[d - 1]);
                    }
                }
            }

            template <typename Args>
            result_type result(Args const &args) const
            {
                {
                    // creates a vector of std::pair where each pair i holds
                    // the values bin_positions[i] (x-axis of histogram) and
                    // samples_in_bin[i] / cnt (y-axis of histogram).

                    for (std::size_t i = 0; i < this->num_bins_ + 2; ++i)
                    {
                        this->histogram_[i] = std::make_pair(
                            this->bin_positions_[i],
                            boost::numeric::average(
                                this->samples_in_bin_[i],
                                boost::accumulators::count(args)));
                    }
                }

                // returns a range of pairs
                return boost::make_iterator_range(this->histogram_);
            }

        private:
            std::size_t num_bins_;        // number of bins
            float_type minimum_;
            float_type maximum_;
            float_type bin_size_;
            array_type samples_in_bin_;  // number of samples in each bin
            array_type bin_positions_;   // lower bounds of bins
            mutable histogram_type histogram_;      // histogram
            mutable bool is_dirty_;
        };
    }
}}

///////////////////////////////////////////////////////////////////////////////
// tag::histogram
namespace boost { namespace accumulators
{
    namespace tag
    {
        struct histogram
          : depends_on<count>,
            histogram_num_bins,
            histogram_min_range,
            histogram_max_range
        {
            struct impl
            {
                template<typename Sample, typename Weight>
                struct apply
                {
                    typedef hpx::util::detail::histogram_impl<Sample> type;
                };
            };
        };
    }

    ///////////////////////////////////////////////////////////////////////////////
    // extract::histogram
    namespace extract
    {
        extractor<tag::histogram> const histogram = {};
    }
}} // namespace boost::accumulators

namespace hpx { namespace util
{
    namespace tag
    {
        using boost::accumulators::tag::histogram;
    }

    using boost::accumulators::extract::histogram;
}}

#endif


#include "ros/ros.h"
#include <hdmap_msgs/imu.h>
#include <nav_msgs/Odometry.h>
#include <sensor_msgs/Imu.h>
#include <tf/transform_broadcaster.h>
#include <tf/transform_datatypes.h>

#include "common/math_utils.h"
#include "fusion/transPointCLoud.h"
#include "fusion/utmProjection.h"
#include <exception>
#include <iostream>
#include <signal.h>
#include <sstream>
#include <stdlib.h>
#include <vector>
//#include "loadExtrinsic.hpp"

#include <Eigen/Core>
#include <Eigen/Geometry>

using namespace lidar_slam;

class IMUReceiver {
public:
  struct GPIMU {
    int nGPSWeek;
    double dGPSTime;

    double dVX;
    double dVY;
    double dVZ;
    double dAX;
    double dAY;
    double dAZ;

    float fTemperature;
    char cStatus[4];
  };

  IMUReceiver(ros::NodeHandle &node) {

    subSensorImu = node.subscribe<hdmap_msgs::imu>(
        "/sensor/imu", 2, &IMUReceiver::imuHandler, this);
    pubImu = node.advertise<sensor_msgs::Imu>("/imu/data_raw", 1);
  }
  void imuHandler(hdmap_msgs::imu imu) {
    sensor_msgs::Imu imu_raw;
    imu_raw.header = imu.header;
    imu_raw.header.frame_id = "imu";
    imu_raw.angular_velocity.x = deg2rad(imu.dVX);
    imu_raw.angular_velocity.y = deg2rad(imu.dVY);
    imu_raw.angular_velocity.z = deg2rad(imu.dVZ);
    imu_raw.linear_acceleration.x = imu.dAX * 9.81;
    imu_raw.linear_acceleration.y = imu.dAY * 9.81;
    imu_raw.linear_acceleration.z = imu.dAZ * 9.81;
    pubImu.publish(imu_raw);
  }

private:
  ros::Publisher pubImu;
  ros::Subscriber subSensorImu;
  tf::TransformBroadcaster _tfBroadcaster;
  nav_msgs::Odometry Omg;
  tf::StampedTransform Tmg;

  ros::Publisher pubInitOml;
  nav_msgs::Odometry initOml;
  tf::StampedTransform initTml;

  bool initialize;
  Eigen::Vector3d Vml_init;
  Eigen::Quaterniond Qml_init;

  Eigen::Isometry3d Tmw;
  Eigen::Vector3d Vwm_offset;
  Eigen::Isometry3d Tli;
};

int main(int argc, char **argv) {

  ros::init(argc, argv, "imu_receiver");
  ros::NodeHandle n;
  IMUReceiver imu_receiver(n);
  ros::spin();
  return 0;
}


// Copyright (c) 2013 Vasili Baranau
// Distributed under the MIT software license
// See the accompanying file License.txt or http://opensource.org/licenses/MIT

#include "GenerationManager.h"

#include <algorithm>
#include <boost/shared_ptr.hpp>
#include "Core/Headers/Path.h"
#include "Core/Headers/StlUtilities.h"
#include "Core/Headers/Math.h"
#include "Model/Headers/Config.h"

#include "PackingServices/Headers/MathService.h"
#include "PackingServices/Headers/PackingSerializer.h"
#include "PackingServices/Headers/GeometryService.h"
#include "PackingServices/Headers/ImmobileParticlesService.h"

#include "PackingServices/PostProcessing/Headers/InsertionRadiiGenerator.h"
#include "PackingServices/PostProcessing/Headers/HessianService.h"
#include "PackingServices/PostProcessing/Headers/PressureService.h"
#include "PackingServices/PostProcessing/Headers/RattlerRemovalService.h"
#include "PackingServices/PostProcessing/Headers/MolecularDynamicsService.h"

#include "PackingServices/EnergyServices/Headers/HarmonicPotential.h"
#include "PackingServices/EnergyServices/Headers/IEnergyService.h"

#include "PackingServices/DistanceServices/Headers/DistanceService.h"

#include "Geometries/Headers/BulkGeometry.h"
#include "Geometries/Headers/CircleGeometry.h"
#include "Geometries/Headers/RectangleGeometry.h"
#include "Geometries/Headers/TrapezoidGeometry.h"

#include "PackingGenerators/InitialGenerators/Headers/BulkPoissonGenerator.h"
#include "PackingGenerators/InitialGenerators/Headers/BulkPoissonInCellsGenerator.h"
#include "PackingGenerators/InitialGenerators/Headers/HcpGenerator.h"

#include "PackingGenerators/Headers/IPackingGenerator.h"

using namespace std;
using namespace Geometries;
using namespace PackingServices;
using namespace PackingGenerators;
using namespace Model;
using namespace Core;

namespace Generation
{
    GenerationManager::GenerationManager(PackingSerializer* packingSerializer,
            IPackingGenerator* packingGenerator,
            InsertionRadiiGenerator* insertionRadiiGenerator,
            DistanceService* distanceService,
            OrderService* orderService,
            IEnergyService* contractionEnergyService,
            HessianService* hessianService,
            PressureService* pressureService,
            MolecularDynamicsService* molecularDynamicsService,
            RattlerRemovalService* rattlerRemovalService,
            ImmobileParticlesService* immobileParticlesService)
    {
        this->packingSerializer = packingSerializer;
        this->packingGenerator = packingGenerator;
        this->insertionRadiiGenerator = insertionRadiiGenerator;
        this->distanceService = distanceService;
        this->orderService = orderService;
        this->contractionEnergyService = contractionEnergyService;
        this->hessianService = hessianService;
        this->pressureService = pressureService;
        this->molecularDynamicsService = molecularDynamicsService;
        this->rattlerRemovalService = rattlerRemovalService;
        this->immobileParticlesService = immobileParticlesService;

        innerDiameterRatio = 1.0;
    }

    void GenerationManager::GeneratePacking(const ExecutionConfig& userConfig)
    {
        ExecuteAlgorithm(userConfig, PACKING_FILE_NAME, false, false, &GenerationManager::GeneratePacking);
    }

    void GenerationManager::GenerateInsertionRadii(const ExecutionConfig& userConfig)
    {
        ExecuteAlgorithm(userConfig, INSERTION_RADII_FILE_NAME, true, true, &GenerationManager::GenerateInsertionRadii);
    }

    void GenerationManager::CalculateDistancesToClosestSurfaces(const ExecutionConfig& userConfig)
    {
        ExecuteAlgorithm(userConfig, DISTANCES_TO_CLOSEST_SURFACES_FOLDER_NAME, true, true, &GenerationManager::CalculateDistancesToClosestSurfaces);
    }

    void GenerationManager::CalculateContactNumberDistribution(const ExecutionConfig& userConfig)
    {
        ExecuteAlgorithm(userConfig, CONTACT_NUMBER_DISTRIBUTION_FILE_NAME, true, true, &GenerationManager::CalculateContactNumberDistribution);
    }

    void GenerationManager::CalculateEntropy(const ExecutionConfig& userConfig)
    {
        ExecuteAlgorithm(userConfig, ENTROPY_FILE_NAME, true, true, &GenerationManager::CalculateEntropy);
    }

    void GenerationManager::CalculateDirections(const ExecutionConfig& userConfig)
    {
        ExecuteAlgorithm(userConfig, PARTICLE_DIRECTIONS_FILE_NAME, true, true, &GenerationManager::CalculateDirections);
    }

    void GenerationManager::CalculateContractionEnergies(const ExecutionConfig& userConfig)
    {
        ExecuteAlgorithm(userConfig, CONTRACTION_ENERGIES_FILE_NAME, true, true, &GenerationManager::CalculateContractionEnergies);
    }

    void GenerationManager::GenerateOrder(const ExecutionConfig& userConfig)
    {
        ExecuteAlgorithm(userConfig, ORDER_FILE_NAME, true, true, &GenerationManager::GenerateOrder);
    }

    void GenerationManager::CalculateHessianEigenvalues(const ExecutionConfig& userConfig)
    {
        ExecuteAlgorithm(userConfig, HESSIAN_EIGENVALUES_FILE_NAME, true, true, &GenerationManager::CalculateHessianEigenvalues);
    }

    void GenerationManager::CalculatePressures(const ExecutionConfig& userConfig)
    {
        ExecuteAlgorithm(userConfig, PRESSURES_FILE_NAME, true, true, &GenerationManager::CalculatePressures);
    }

    void GenerationManager::CalculateMolecularDynamicsStatistics(const ExecutionConfig& userConfig)
    {
        ExecuteAlgorithm(userConfig, MOLECULAR_DYNAMICS_STATISTICS_FILE_NAME, true, true, &GenerationManager::CalculateMolecularDynamicsStatistics);
    }

    void GenerationManager::RemoveRattlers(const ExecutionConfig& userConfig)
    {
        ExecuteAlgorithm(userConfig, "", false, true, &GenerationManager::RemoveRattlers);
    }

    void GenerationManager::CalculatePairCorrelationFunction(const Model::ExecutionConfig& userConfig)
    {
        ExecuteAlgorithm(userConfig, PAIR_CORRELATION_FUNCTION_FILE_NAME, true, true, &GenerationManager::CalculatePairCorrelationFunction);
    }

    void GenerationManager::CalculateStructureFactor(const Model::ExecutionConfig& userConfig)
    {
        ExecuteAlgorithm(userConfig, STRUCTURE_FACTOR_FILE_NAME, true, true, &GenerationManager::CalculateStructureFactor);
    }

    void GenerationManager::GenerateLocalOrientationalDisorder(const ExecutionConfig& userConfig)
    {
        ExecuteAlgorithm(userConfig, LOCAL_ORIENTATIONAL_DISORDER_FILE_NAME, true, true, &GenerationManager::GenerateLocalOrientationalDisorder);
    }

    void GenerationManager::CalculateImmediateMolecularDynamicsStatistics(const ExecutionConfig& userConfig)
    {
        ExecuteAlgorithm(userConfig, IMMEDIATE_MOLECULAR_DYNAMICS_STATISTICS_FILE_NAME, true, true, &GenerationManager::CalculateImmediateMolecularDynamicsStatistics);
    }

    void GenerationManager::CalculateNearestNeighbors(const ExecutionConfig& userConfig)
    {
        ExecuteAlgorithm(userConfig, NEAREST_NEIGHBORS_FILE_NAME, true, true, &GenerationManager::CalculateNearestNeighbors);
    }

    void GenerationManager::CalculateActiveGeometry(const Model::ExecutionConfig& userConfig)
    {
        ExecuteAlgorithm(userConfig, ACTIVE_GEOMETRY_FILE_NAME, true, true, &GenerationManager::CalculateActiveGeometry);
    }

    void GenerationManager::CalculateSuccessfulPermutationProbability(const Model::ExecutionConfig& userConfig)
    {
        ExecuteAlgorithm(userConfig, SUCCESSFUL_PERMUTATION_PROBABILITY_FILE_NAME, true, true, &GenerationManager::CalculateSuccessfulPermutationProbability);
    }

    void GenerationManager::ExecuteAlgorithm(const ExecutionConfig& userConfig, string targetFileName,
            bool shouldExitIfTargetFileExists, bool shouldAlwaysReadPacking, Action algorithm)
    {
        string targetFilePath = Path::Append(userConfig.generationConfig.baseFolder, targetFileName);
        if (shouldExitIfTargetFileExists && Path::Exists(targetFilePath))
        {
            return;
        }

        ExecutionConfig fullConfig;
        FillFullConfig(userConfig, &fullConfig);

        Math::SetSeed(fullConfig.generationConfig.seed);
        boost::shared_ptr<IGeometry> geometry = CreateGeometry(fullConfig.systemConfig);

        SystemConfig activeConfig;
        boost::shared_ptr<IGeometry> activeGeometry;
        string activeConfigPath = Path::Append(fullConfig.generationConfig.baseFolder, ACTIVE_GEOMETRY_FILE_NAME);
        if (Path::Exists(activeConfigPath))
        {
            SpatialVector shift;
            packingSerializer->ReadActiveConfig(activeConfigPath, &activeConfig, &shift);
            activeConfig.MergeWith(fullConfig.systemConfig);

            activeGeometry = CreateGeometry(activeConfig, shift);
        }

        ModellingContext context(&fullConfig.systemConfig, geometry.get(), activeGeometry.get());

        packingGenerator->SetContext(context);
        insertionRadiiGenerator->SetContext(context);
        distanceService->SetContext(context);
        orderService->SetContext(context);
        contractionEnergyService->SetContext(context);
        hessianService->SetContext(context);
        pressureService->SetContext(context);
        molecularDynamicsService->SetContext(context);
        rattlerRemovalService->SetContext(context);

        Packing particles;
        ReadOrCreatePacking(fullConfig, context, shouldAlwaysReadPacking, &particles);

        (this->*algorithm)(fullConfig, context, targetFilePath, &particles);
    }

    // Actions for corresponding algorithms

    void GenerationManager::GeneratePacking(const ExecutionConfig& fullConfig, const ModellingContext& context, string targetFilePath, Packing* particles)
    {
        string infoFile = Path::Append(fullConfig.generationConfig.baseFolder, PACKING_FILE_NAME_NFO);
        if (Path::Exists(infoFile))
        {
            return;
        }

//        // A quick fix for Matthias packings
//        Packing& particlesRef = *particles;
//        string nearestNeighbotsPath = Path::Append(fullConfig.generationConfig.baseFolder, NEAREST_NEIGHBORS_FILE_NAME);
//        if (Path::Exists(nearestNeighbotsPath))
//        {
//            vector<ParticlePair> closestPairs;
//            packingSerializer->ReadNearestNeighbors(nearestNeighbotsPath, &closestPairs);
//            for (size_t i = 0; i < closestPairs.size(); ++i)
//            {
//                bool radiusCorrect = closestPairs[i].normalizedDistanceSquare > 0.95;
//                particlesRef[closestPairs[i].firstParticleIndex].isImmobile = radiusCorrect;
//                particlesRef[closestPairs[i].secondParticleIndex].isImmobile = radiusCorrect;
//            }
//        }

        packingGenerator->SetGenerationConfig(fullConfig.generationConfig);
        packingGenerator->ArrangePacking(particles);

        // TODO: Think about writing info file here.
        // TODO: Think on renaming the folder, updating the generation config, rescaling the packing, updating the info file according to the innerDiameterRatio here.
        packingSerializer->SerializePacking(targetFilePath, *particles);

        innerDiameterRatio = packingGenerator->GetFinalInnerDiameterRatio();
        string contractionEnergiesFilePath = Path::Append(fullConfig.generationConfig.baseFolder, CONTRACTION_ENERGIES_FILE_NAME);
        CalculateContractionEnergies(fullConfig, context, contractionEnergiesFilePath, particles);

        innerDiameterRatio = 1.0;
    }

    void GenerationManager::GenerateInsertionRadii(const ExecutionConfig& fullConfig, const ModellingContext& context, string targetFilePath, Packing* particles)
    {
        vector<FLOAT_TYPE> insertionRadii;
        insertionRadiiGenerator->FillInsertionRadii(*particles, fullConfig.generationConfig.insertionRadiiCount, &insertionRadii);
        packingSerializer->SerializeInsertionRadii(targetFilePath, insertionRadii);
    }

    void GenerationManager::CalculateDistancesToClosestSurfaces(const ExecutionConfig& fullConfig, const ModellingContext& context, string targetFolderPath, Packing* particles)
    {
        vector<int> surfaceIndexes;
        vector<vector<FLOAT_TYPE> > distancesToSurfaces;

        int minNeighborsCount = 2; // Now: some number to ensure that we have all the numbers consecutively. Before: contractionEnergyService->GetMinNeighborsCount(); // average neighbors count in mechanically stable packings with infinite friction
        int maxNeighborsCount = 14; // max number of contacts in monodisperse packings
        int minSurfaceIndex = minNeighborsCount - 1;
        int surfaceIndexesCount = maxNeighborsCount - minNeighborsCount + 1;
        surfaceIndexes.resize(surfaceIndexesCount);
        VectorUtilities::FillLinearScale(minSurfaceIndex, &surfaceIndexes);
        surfaceIndexes.insert(surfaceIndexes.begin(), 0);

        insertionRadiiGenerator->FillDistancesToSurfaces(*particles, fullConfig.generationConfig.insertionRadiiCount, surfaceIndexes, targetFolderPath, *packingSerializer);
    }

    void GenerationManager::CalculateContactNumberDistribution(const ExecutionConfig& fullConfig, const ModellingContext& context, string targetFilePath, Packing* particles)
    {
        printf("Calculating contact number distributions\n");
        Packing& packingToUse = *particles;

        // just make sure that the closest pair touches
        //ClosestPairProvider
        distanceService->SetParticles(*particles);
        ParticlePair closestPair = distanceService->FindClosestPair();
        FLOAT_TYPE closestNormalizedDistance = std::sqrt(closestPair.normalizedDistanceSquare);
        printf("Normalized distance of a closest pair is %f\n", closestNormalizedDistance);
        if (closestNormalizedDistance > 1.0001)
        {
            printf("Min normalized distance is too high, parcking was probably not rescaled to the final density. Rescaling before contacts calculation...\n");
            Packing rescaledParticles = *particles;
            for (Particle& p : rescaledParticles)
            {
                p.diameter *= closestNormalizedDistance;
            }
            packingToUse = rescaledParticles;
        }

        FLOAT_TYPE contractionRate = 1.0 - 1e-4;
        vector<int> neighborCounts;
        vector<int> neighborCountFrequencies;
        vector<vector<int>> touchingParticleIndexes;
        contractionEnergyService->SetParticles(packingToUse);
        FLOAT_TYPE estimatedCoordinationNumber = insertionRadiiGenerator->GetContactNumberDistribution(packingToUse, contractionEnergyService,
                contractionRate, &neighborCounts, &neighborCountFrequencies, &touchingParticleIndexes);

        printf("Estimated coordination number is %f\n", estimatedCoordinationNumber);

        packingSerializer->SerializeContactNumberDistribution(targetFilePath, neighborCounts, neighborCountFrequencies);

        string targetFolder = Core::Path::GetParentPath(targetFilePath);
        string contactNumbersFilePath = Core::Path::Append(targetFolder, CONTACTING_NEIGHBORS_FILE_NAME);
        packingSerializer->SerializeContactingNeighborIndexes(contactNumbersFilePath, touchingParticleIndexes);

        vector<FLOAT_TYPE> normalizedContactingNeighborDistances;
        insertionRadiiGenerator->FillNormalizedContactingNeighborDistances(packingToUse, touchingParticleIndexes, &normalizedContactingNeighborDistances);

        string contactingNeighborDistancesFilePath = Core::Path::Append(targetFolder, CONTACTING_NEIGHBOR_DISTANCES_FILE_NAME);
        packingSerializer->SerializeContactingNeighborDistances(contactingNeighborDistancesFilePath, normalizedContactingNeighborDistances);

//        FLOAT_TYPE expectedCoordinationNumber = GetExpectedCoordinationNumber(fullConfig, context, targetFilePath, particles);
//        // Find the best contraction rate to get this coordination number
//        FLOAT_TYPE contractionRate = insertionRadiiGenerator->GetContractionRateForCoordinationNumber(contractionEnergyService, expectedCoordinationNumber);
//
//        // Find coordination numbers distribution
//        vector<int> neighborCounts;
//        vector<int> neighborCountFrequencies;
//        FLOAT_TYPE estimatedCoordinationNumber = insertionRadiiGenerator->GetContactNumberDistribution(*particles, contractionEnergyService, contractionRate, &neighborCounts, &neighborCountFrequencies);
//        if (std::abs(estimatedCoordinationNumber - expectedCoordinationNumber) > 1e-2)
//        {
//            printf("Coordination number estimated incorrectly\n");
//        }
//
//        packingSerializer->SerializeContactNumberDistribution(targetFilePath, neighborCounts, neighborCountFrequencies);
    }

    FLOAT_TYPE GenerationManager::GetExpectedCoordinationNumber(const Model::ExecutionConfig& fullConfig, const Model::ModellingContext& context, std::string targetFilePath, Model::Packing* particles) const
    {
        string energyFilePath = Path::Append(Path::GetParentPath(targetFilePath), CONTRACTION_ENERGIES_FILE_NAME);
        if (!Path::Exists(energyFilePath))
        {
            throw InvalidOperationException("Contraction energy file does not exist.");
        }
        contractionEnergyService->SetParticles(*particles);

        // Read coordination numbers from file
        vector<FLOAT_TYPE> contractionRatios;
        vector<FLOAT_TYPE> energyPowers;
        vector<FLOAT_TYPE> contractionEnergies;
        vector<int> nonRattlersCounts;
        packingSerializer->ReadContractionEnergies(energyFilePath, &contractionRatios, &energyPowers, &contractionEnergies, &nonRattlersCounts);

        // Find expected coordination number
        FLOAT_TYPE expectedCoordinationNumber = 0;
        for (size_t i = 0; i < contractionRatios.size(); ++i)
        {
            if (energyPowers[i] > 0)
            {
                expectedCoordinationNumber = contractionEnergies[i - 1] / nonRattlersCounts[i - 1];
                break;
            }
        }

        return expectedCoordinationNumber;
    }

    void GenerationManager::CalculateEntropy(const ExecutionConfig& fullConfig, const ModellingContext& context, string targetFilePath, Packing* particles)
    {
        FLOAT_TYPE entropy = insertionRadiiGenerator->CalculateEntropy(*particles, fullConfig.generationConfig.insertionRadiiCount);
        packingSerializer->SerializeEntropy(targetFilePath, entropy);

//        vector<FLOAT_TYPE> localEntropies;
//        FLOAT_TYPE entropy = insertionRadiiGenerator->CalculateLocalEntropies(particles, fullConfig->generationConfig.insertionRadiiCount, &localEntropies);
//        packingSerializer->SerializeLocalEntropy(targetFilePath, entropy, &localEntropies);
    }

    void GenerationManager::CalculateDirections(const ExecutionConfig& fullConfig, const ModellingContext& context, string targetFilePath, Packing* particles)
    {
        vector<OrderService::NeighborDirections> particleDirections;
        orderService->SetParticles(*particles);
        orderService->FillParticleDirections(&particleDirections);
        packingSerializer->SerializeParticleDirections(targetFilePath, fullConfig.systemConfig.particlesCount, particleDirections);
    }

    void GenerationManager::CalculateContractionEnergies(const ExecutionConfig& fullConfig, const ModellingContext& context, string targetFilePath, Packing* particles)
    {
        printf("Calculating contraction energies\n");

        vector<FLOAT_TYPE> contractionRatios;
        FillContractionRatios(&contractionRatios);
        int count = contractionRatios.size();
        vector<FLOAT_TYPE> fullContractionRatios;

        // Copy contraction rates twice
        StlUtilities::Append(contractionRatios, &fullContractionRatios);
        StlUtilities::Append(contractionRatios, &fullContractionRatios);

        const HarmonicPotential zeroPotential(0.0);
        const HarmonicPotential secondPotential(2.0);

        vector<const IPairPotential*> fullPotentials(count, &zeroPotential);
        vector<const IPairPotential*> potentials(count, &secondPotential);
        StlUtilities::Append(potentials, &fullPotentials);

        // Rescale the contraction ratios according to the final innerDiameterRatio (this method may be called after packing generation).
        vector<FLOAT_TYPE> rescaledContractionRatios;
        for (vector<FLOAT_TYPE>::iterator it = fullContractionRatios.begin(); it != fullContractionRatios.end(); ++it)
        {
            FLOAT_TYPE contractionRatio = *it;
            rescaledContractionRatios.push_back(contractionRatio / innerDiameterRatio);
        }

        contractionEnergyService->SetParticles(*particles);
        IEnergyService::EnergiesResult result = contractionEnergyService->GetContractionEnergies(rescaledContractionRatios, fullPotentials);

        // Use non-rescaled contraction ratios.
        vector<FLOAT_TYPE> fullEnergyPowers(count, 0.0);
        vector<FLOAT_TYPE> energyPowers(count, 2.0);
        StlUtilities::Append(energyPowers, &fullEnergyPowers);

        packingSerializer->SerializeContractionEnergies(targetFilePath, fullContractionRatios, fullEnergyPowers, result.contractionEnergies, result.nonRattlersCounts);
    }

    void GenerationManager::GenerateOrder(const ExecutionConfig& fullConfig, const ModellingContext& context, string targetFilePath, Packing* particles)
    {
        orderService->SetParticles(*particles);
        OrderService::Order order = orderService->GetOrder(6); // 6 is the best order, as is maximum for FCC packings
        packingSerializer->SerializeOrder(targetFilePath, order);
    }

    void GenerationManager::CalculateHessianEigenvalues(const ExecutionConfig& fullConfig, const ModellingContext& context, string targetFilePath, Packing* particles)
    {
        hessianService->SetParticles(*particles);
        vector<FLOAT_TYPE> hessianEigenvalues;
        hessianService->FillHessianEigenvalues(&hessianEigenvalues);
        packingSerializer->SerializeHessianEigenvalues(targetFilePath, hessianEigenvalues);
    }

    void GenerationManager::CalculatePressures(const ExecutionConfig& fullConfig, const ModellingContext& context, string targetFilePath, Packing* particles)
    {
        vector<FLOAT_TYPE> contractionRatios;
        FillContractionRatios(&contractionRatios);
        int count = contractionRatios.size();
        vector<FLOAT_TYPE> fullContractionRatios;

        // No sense in calculating pressure for power = 0
        StlUtilities::Append(contractionRatios, &fullContractionRatios);
        vector<FLOAT_TYPE> fullEnergyPowers(count, 2.0);

        vector<FLOAT_TYPE> pressures;
        pressureService->SetParticles(*particles);
        pressureService->FillPressures(fullContractionRatios, fullEnergyPowers, &pressures);
        packingSerializer->SerializePressures(targetFilePath, fullContractionRatios, fullEnergyPowers, pressures);
    }

    void GenerationManager::CalculateMolecularDynamicsStatistics(const ExecutionConfig& fullConfig, const ModellingContext& context, string targetFilePath, Packing* particles)
    {
        printf("Calculating molecular dynamics statistics\n");

        molecularDynamicsService->SetGenerationConfig(fullConfig.generationConfig);
        molecularDynamicsService->SetParticles(*particles);

        MolecularDynamicsStatistics statistics = molecularDynamicsService->CalculateStationaryStatistics();
        packingSerializer->SerializeMolecularDynamicsStatistics(targetFilePath, statistics);
    }

    void GenerationManager::RemoveRattlers(const ExecutionConfig& fullConfig, const ModellingContext& context, string targetFilePath, Packing* particles)
    {
        vector<bool> rattlerMask(context.config->particlesCount);
        rattlerRemovalService->SetParticles(*particles);
//        const FLOAT_TYPE contractionRate = 1.0 - 1.0e-7;
//        const FLOAT_TYPE contractionRate = 1.0 - 1.0e-5;
        const FLOAT_TYPE contractionRate = 1.0 / 1.5; //particles that have one radius between surfaces will become in contact
        rattlerRemovalService->FillRattlerMask(contractionRate, &rattlerMask);
        int nonRattlersCount = rattlerRemovalService->FindNonRattlersCount(rattlerMask);
        Packing nonRattlerParticles;
        nonRattlerParticles.resize(nonRattlersCount);
        rattlerRemovalService->FillNonRattlerPacking(rattlerMask, &nonRattlerParticles);

        printf("Removing rattlers. Non-rattlers count: %d\n", nonRattlersCount);

        PackingInfo oldInfo;
        PackingInfo nonRattlerInfo;

        ExecutionConfig nonRattlerConfig;

        string packingInfoFilePath = Path::Append(fullConfig.generationConfig.baseFolder, PACKING_FILE_NAME_NFO);
        packingSerializer->ReadPackingInfo(packingInfoFilePath, &oldInfo);

        rattlerRemovalService->FillNonRattlerConfig(nonRattlersCount, fullConfig, &nonRattlerConfig);
        rattlerRemovalService->FillNonRattlerPackingInfo(nonRattlersCount, nonRattlerParticles, nonRattlerConfig, oldInfo, &nonRattlerInfo);

        // Serialize non-rattler packing
        packingSerializer->SerializePackingInfo(packingInfoFilePath, nonRattlerConfig.systemConfig, nonRattlerInfo);

        string packingFilePath = Path::Append(fullConfig.generationConfig.baseFolder, PACKING_FILE_NAME);
        packingSerializer->SerializePacking(packingFilePath, nonRattlerParticles);

        string configFilePath = Path::Append(fullConfig.generationConfig.baseFolder, CONFIG_FILE_NAME);
        packingSerializer->SerializeConfig(configFilePath, nonRattlerConfig);
    }

    void GenerationManager::CalculatePairCorrelationFunction(const ExecutionConfig& fullConfig, const ModellingContext& context, string targetFilePath, Packing* particles)
    {
        printf("Calculating pair correlation function\n");

        distanceService->SetParticles(*particles);
        PairCorrelationFunction pairCorrelationFunction;
        distanceService->FillPairCorrelationFunction(&pairCorrelationFunction);

        packingSerializer->SerializePairCorrelationFunction(targetFilePath, pairCorrelationFunction);
    }

    void GenerationManager::CalculateStructureFactor(const ExecutionConfig& fullConfig, const ModellingContext& context, string targetFilePath, Packing* particles)
    {
        printf("Calculating structure factor\n");

        Packing& particlesRef = *particles;

        std::vector<int> particleIndexesOfInterest;
        if (fullConfig.generationConfig.particlesToKeepForStructureFactor > 0)
        {
            int particlesToChooseCount = fullConfig.generationConfig.particlesToKeepForStructureFactor;
            bool keepSmallest = fullConfig.generationConfig.keepSmallParticlesForStructureFactor.value;

            // choosing that many largest particles
            // NOTE: maybe use nth element
            std::vector<double> diameters;
            for (const DomainParticle& particle : particlesRef)
            {
                diameters.push_back(particle.diameter);
            }
            std::vector<int> permutation;
            StlUtilities::SortPermutation(diameters, &permutation);

            if (keepSmallest)
            {
                permutation.erase(permutation.begin() + particlesToChooseCount, permutation.end());
            }
            else
            {
                int particlesToRemove = permutation.size() - particlesToChooseCount;
                permutation.erase(permutation.begin(), permutation.begin() + particlesToRemove);
            }

            particleIndexesOfInterest.swap(permutation);

            double minSelectedDiameter = 1000; // TODO: use numeric_limits
            double maxSelectedDiameter = 0;
            for (int i : particleIndexesOfInterest)
            {
                double diameter = particlesRef[i].diameter;
                if (diameter > maxSelectedDiameter)
                {
                    maxSelectedDiameter = diameter;
                }
                if (diameter < minSelectedDiameter)
                {
                    minSelectedDiameter = diameter;
                }
            }

            if (keepSmallest)
            {
                printf("Expected to calculate structure factor for %d smallest particles. Selected %d smallest particles. Max selected diameter: %f\n",
                    particlesToChooseCount,
                    particleIndexesOfInterest.size(),
                    maxSelectedDiameter);
            }
            else
            {
                printf("Expected to calculate structure factor for %d largest particles. Selected %d largest particles. Min selected diameter: %f\n",
                    particlesToChooseCount,
                    particleIndexesOfInterest.size(),
                    minSelectedDiameter);
            }
        }

        distanceService->SetParticles(*particles);
        StructureFactor structureFactor;
        distanceService->FillStructureFactor(particleIndexesOfInterest, &structureFactor);

        packingSerializer->SerializeStructureFactor(targetFilePath, structureFactor);
    }

    void GenerationManager::GenerateLocalOrientationalDisorder(const ExecutionConfig& fullConfig, const ModellingContext& context, string targetFilePath, Packing* particles)
    {
        printf("Calculating local orientational disorder\n");

        orderService->SetParticles(*particles);
        OrderService::LocalOrientationalDisorder localOrientationalDisorder;
        orderService->FillLocalOrientationalDisorder(&localOrientationalDisorder);
        packingSerializer->SerializeLocalOrientationalDisorder(targetFilePath, localOrientationalDisorder);
        packingSerializer->SerializeCloseNeighbors(Path::Append(fullConfig.generationConfig.baseFolder, "close_neighbors.txt"), localOrientationalDisorder);
    }

    void GenerationManager::CalculateImmediateMolecularDynamicsStatistics(const ExecutionConfig& fullConfig, const ModellingContext& context, string targetFilePath, Packing* particles)
    {
        printf("Calculating immediate molecular dynamics statistics\n");

        molecularDynamicsService->SetGenerationConfig(fullConfig.generationConfig);
        molecularDynamicsService->SetParticles(*particles);

        MolecularDynamicsStatistics statistics = molecularDynamicsService->CalculateImmediateStatistics();
        packingSerializer->SerializeMolecularDynamicsStatistics(targetFilePath, statistics);
    }

    void GenerationManager::CalculateNearestNeighbors(const Model::ExecutionConfig& fullConfig, const Model::ModellingContext& context, std::string targetFilePath, Model::Packing* particles)
    {
        printf("Calculating nearest neighbors\n");

        distanceService->SetParticles(*particles);
        vector<ParticlePair> closestPairs;
        distanceService->FillClosestPairs(&closestPairs);

        vector<bool> isImmobileMask(fullConfig.systemConfig.particlesCount);
        vector<int> isImmobileIntMask(fullConfig.systemConfig.particlesCount);

        VectorUtilities::InitializeWith(&isImmobileMask, false);
        VectorUtilities::InitializeWith(&isImmobileIntMask, 0);

        Model::Packing& particlesRef = *particles;
        for (ParticleIndex i = 0; i < fullConfig.systemConfig.particlesCount; ++i)
        {
            isImmobileMask[i] = particlesRef[i].isImmobile;
            isImmobileIntMask[i] = particlesRef[i].isImmobile ? 1 : 0;
        }

        vector<int> permutation;
        StlUtilities::SortPermutation(isImmobileIntMask, &permutation);

        vector<ParticlePair> sortedClosestPairs;
        StlUtilities::Permute(closestPairs, permutation, &sortedClosestPairs);

        vector<bool> sortedIsImmobileMask;
        StlUtilities::Permute(isImmobileMask, permutation, &sortedIsImmobileMask);

        packingSerializer->SerializeNearestNeighbors(targetFilePath, closestPairs, sortedIsImmobileMask);
    }

    void GenerationManager::CreateActiveConfig(const ExecutionConfig& fullConfig, FLOAT_TYPE contractionFactorByParticleCenters, SystemConfig* activeConfig, SpatialVector* shift) const
    {
        SystemConfig& activeConfigRef = *activeConfig;
        activeConfigRef.Reset();
        activeConfigRef.MergeWith(fullConfig.systemConfig);

        FLOAT_TYPE meanParticleRadius = 0.5; // TODO: determine better
        // contractionFactor is measured by the true radius of the bounding cylinder (i.e., by particle surfaces)
        FLOAT_TYPE contractionFactor = ((activeConfigRef.packingSize[Axis::X] - 2.0 * meanParticleRadius) * contractionFactorByParticleCenters + 2.0 * meanParticleRadius) / activeConfigRef.packingSize[Axis::X];
        SpatialVector contractionFactors;
        VectorUtilities::InitializeWith(&contractionFactors, contractionFactor);

//            contractionFactors[DIMENSIONS - 1] = 1.0; // For packings that are periodic by z. TODO: periodicity shall be specified in generation.conf. IGeometry shall include this information (as done in LatticeGeoetry)
        contractionFactors[DIMENSIONS - 1] = (activeConfigRef.packingSize[DIMENSIONS - 1] - 8.0 * meanParticleRadius) / activeConfigRef.packingSize[DIMENSIONS - 1]; // Remove 2 diameters from each side
        VectorUtilities::Multiply(activeConfigRef.packingSize, contractionFactors, &activeConfigRef.packingSize);

        SpatialVector margins;
        VectorUtilities::Subtract(fullConfig.systemConfig.packingSize, activeConfigRef.packingSize, &margins);
        VectorUtilities::MultiplyByValue(margins, 0.5, shift);
    }

    void GenerationManager::GetActiveConfigWithParticlesCount(const ExecutionConfig& fullConfig, FLOAT_TYPE contractionFactorByParticleCenters, Packing* particles, SystemConfig* activeConfig, SpatialVector* shift) const
    {
        SystemConfig& activeConfigRef = *activeConfig;
        // the default active area is 0.5 of the cylinder, if cylinder radius is measured by particle centers
        CreateActiveConfig(fullConfig, contractionFactorByParticleCenters, activeConfig, shift);
        boost::shared_ptr<IGeometry> activeGeometry = CreateGeometry(activeConfigRef, *shift);

        ////////////////////
        // Set active geometry

        // NOTE: here I use a dirty hack and do not call SetContext for all the services, as I know, that non of the services caches the active geometry.
        // TODO: extract setting context from ExecuteAlgorithm to a separate method, call here.

        // Set correct isImmobile mask
        SetActiveParticlesByActiveGeometry(fullConfig.systemConfig, *activeGeometry.get(), particles);

        ////////////////////
        // Determine the amount of active non-rattler particles

        // Find the best contraction rate to get the expected coordination number
//        FLOAT_TYPE expectedCoordinationNumber = GetExpectedCoordinationNumber(fullConfig, context, targetFilePath, particles);
//        FLOAT_TYPE contractionRate = insertionRadiiGenerator->GetContractionRateForCoordinationNumber(contractionEnergyService, expectedCoordinationNumber);
        FLOAT_TYPE contractionRate = 1.0 - 1e-7;

        vector<bool> rattlerMask;
        rattlerRemovalService->SetParticles(*particles);
        rattlerRemovalService->FillRattlerMask(contractionRate, &rattlerMask);

        int activeNonRattlersCount = 0;
        Packing& particlesRef = *particles;
        for (ParticleIndex i = 0; i < fullConfig.systemConfig.particlesCount; ++i)
        {
//            if (!particlesRef[i].isImmobile && !rattlerMask[i])
            if (!particlesRef[i].isImmobile)
            {
                activeNonRattlersCount++;
            }
        }

        activeConfigRef.particlesCount = activeNonRattlersCount;
    }

    void GenerationManager::FindActiveConfigForActiveParticlesCount(const ExecutionConfig& fullConfig, int activeParticlesCount, Packing* particles, SystemConfig* activeConfig, SpatialVector* shift) const
    {
        GetActiveParticlesCountFunctor getActiveParticlesCountFunctor(*this, fullConfig, particles, activeConfig, shift);
        StlUtilities::DoBinarySearch(0.0, 1.0, activeParticlesCount, 0, 1e-7, getActiveParticlesCountFunctor);
    }

    void GenerationManager::CalculateActiveGeometry(const ExecutionConfig& fullConfig, const ModellingContext& context, string targetFilePath, Packing* particles)
    {
        printf("Calculating active geometry\n");

        SpatialVector shift;
        SystemConfig activeConfig;

        // NOTE: this is a dirty hack. I'm using insertionRadiiCount parameter to store expectedActiveParticlesCount
        // TODO: add a separate config parameter
        int expectedActiveParticlesCount = fullConfig.generationConfig.insertionRadiiCount;

        // The usual workflow is the following:
        // 1. run CalculateActiveGeometry without an expectedActiveParticlesCount to determine approximate amount of particles inside the active geometry.
        // 2. select the min amount of activeParticlesCount
        // 3. rename the old active configs
        // 4. run CalculateActiveGeometry with expectedActiveParticlesCount = this min number
        // It ensures that all the active geometries have equal amount of active particles.
        // It will make the entropy and compactivity calculations easier
        // (actually, i'm not sure how to calculate compactivity and how to account for rattlers if the amount of particles varies).
        // TODO: extract this entire procedure to a separate method (it's hard currently,
        // as requires processing of several packings at once, which is completely unsupported by the architecture)
        if (expectedActiveParticlesCount == -1)
        {
            // the default active area is 0.5 of the cylinder, if cylinder radius is measured by particle centers
            FLOAT_TYPE contractionFactorByParticleCenters = 0.5;
            GetActiveConfigWithParticlesCount(fullConfig, contractionFactorByParticleCenters, particles, &activeConfig, &shift);
        }
        else
        {
            FindActiveConfigForActiveParticlesCount(fullConfig, expectedActiveParticlesCount, particles, &activeConfig, &shift);
        }

        packingSerializer->SerializeActiveConfig(targetFilePath, activeConfig, shift);
    }

    void GenerationManager::CalculateSuccessfulPermutationProbability(const Model::ExecutionConfig& fullConfig, const Model::ModellingContext& context, std::string targetFilePath, Model::Packing* particles)
    {
        printf("Calculating successful permutation probability\n");
        Packing& packingToUse = *particles;

        int maxPermutations = 5000;
        FLOAT_TYPE successfulPermutationProbability = insertionRadiiGenerator->GetSuccessfulPermutationProbability(particles, maxPermutations);

        printf("Estimated successful permutation probability is %f\n", successfulPermutationProbability);

        //packingSerializer->SerializeContactNumberDistribution(targetFilePath, neighborCounts, neighborCountFrequencies);
    }

    void GenerationManager::FillContractionRatios(vector<FLOAT_TYPE>* contractionRatios) const
    {
        contractionRatios->clear();
        AddLogContractionRatios(contractionRatios);
//        AddLinearContractionRatios(contractionRatios);
        contractionRatios->push_back(0.9999999); // predefined value

        StlUtilities::SortAndResizeToUnique(contractionRatios);
    }

    void GenerationManager::AddLogContractionRatios(vector<FLOAT_TYPE>* contractionRatios) const
    {
        const int pointsCount = 15;
        const FLOAT_TYPE minContractionRatio = 0.9999999;
        const FLOAT_TYPE maxContractionRatio = 0.99995;

        vector<FLOAT_TYPE> contractionRatiosArray(pointsCount);

        VectorUtilities::FillLogScale(1. - minContractionRatio, 1. - maxContractionRatio, &contractionRatiosArray);
        VectorUtilities::MultiplyByValue(contractionRatiosArray, -1.0, &contractionRatiosArray);
        VectorUtilities::AddValue(contractionRatiosArray, 1.0, &contractionRatiosArray);

        StlUtilities::Append(contractionRatiosArray, contractionRatios);
    }

    void GenerationManager::AddLinearContractionRatios(vector<FLOAT_TYPE>* contractionRatios) const
    {
        // 200 points per 0.01
        // maxParticleDiameter / maxContractionRatio * innerDiameterRatio <= maxParticleDiameter * VerletListNeighborProvider::MAX_EXPECTED_OUTER_DIAMETER_RATIO + cutoffDistance
        // Approximately 1.0 / maxContractionRatio * innerDiameterRatio <= VerletListNeighborProvider::MAX_EXPECTED_OUTER_DIAMETER_RATIO
        const int pointsCount = 401;
        const FLOAT_TYPE minContractionRatio = 1.0;
        const FLOAT_TYPE maxContractionRatio = 0.98;

        vector<FLOAT_TYPE> contractionRatiosArray(pointsCount);

        VectorUtilities::FillLinearScale(minContractionRatio, maxContractionRatio, &contractionRatiosArray);
        StlUtilities::Append(contractionRatiosArray, contractionRatios);
    }

    void GenerationManager::FillFullConfig(const ExecutionConfig& userConfig, ExecutionConfig* fullConfig) const
    {
        ExecutionConfig fileConfig;
        packingSerializer->ReadConfig(userConfig.generationConfig.baseFolder, &fileConfig);

        fullConfig->MergeWith(userConfig);
        fullConfig->MergeWith(fileConfig);
    }

    boost::shared_ptr<IGeometry> GenerationManager::CreateGeometry(const SystemConfig& config, const Core::SpatialVector& shift) const
    {
        // TODO: use shift in all non-bulk geometries and may be add a template parameter to all of them <TSupportsShift>.
        boost::shared_ptr<IGeometry> geometry;
        switch (config.boundariesMode)
        {
        case BoundariesMode::Bulk:
            geometry.reset(new BulkGeometry(config));
            break;
        case BoundariesMode::Ellipse:
            geometry.reset(new CircleGeometry(config, shift));
            break;
        case BoundariesMode::Rectangle:
            geometry.reset(new RectangleGeometry(config));
            break;
        case BoundariesMode::Trapezoid:
            geometry.reset(new TrapezoidGeometry(config));
            break;
        case BoundariesMode::Unknown:
            break;
        }

        return geometry;
    }

    boost::shared_ptr<IGeometry> GenerationManager::CreateGeometry(const SystemConfig& config) const
    {
        SpatialVector shift;
        VectorUtilities::InitializeWith(&shift, 0.0);
        return CreateGeometry(config, shift);
    }

    void GenerationManager::ReadOrCreatePacking(const ExecutionConfig& fullConfig, const ModellingContext& context, bool shouldAlwaysReadPacking, Packing* particles) const
    {
        Packing& particlesRef = *particles;
        particlesRef.resize(fullConfig.systemConfig.particlesCount);

        for (ParticleIndex i = 0; i < fullConfig.systemConfig.particlesCount; ++i)
        {
            particlesRef[i].index = i;
        }

        if (shouldAlwaysReadPacking)
        {
            // Always read the packing, even if the generation mode is "start"
            packingSerializer->ReadPacking(Path::Append(fullConfig.generationConfig.baseFolder, PACKING_FILE_NAME), particles);
        }
        else
        {
            ReadOrCreatePacking(fullConfig, context, particles);
        }

        // Setting immobile particles in different ways

        Nullable<bool> shouldSuppressCrystallization = fullConfig.generationConfig.shouldSuppressCrystallization;
        if (shouldSuppressCrystallization.hasValue && shouldSuppressCrystallization.value)
        {
            immobileParticlesService->SetContext(context);
            immobileParticlesService->SetAndArrangeImmobileParticles(particles);
        }

        string immobileParticlesPath = Path::Append(fullConfig.generationConfig.baseFolder, IMMOBILE_PARTICLES_FILE_NAME);
        if (Path::Exists(immobileParticlesPath))
        {
            vector<ParticleIndex> immobileParticleIndexes;
            packingSerializer->ReadImmobileParticleIndexes(immobileParticlesPath, &immobileParticleIndexes);
            for (std::size_t immobileParticleIndexIndex = 0; immobileParticleIndexIndex < immobileParticleIndexes.size(); ++immobileParticleIndexIndex)
            {
                ParticleIndex immobileParticleIndex = immobileParticleIndexes[immobileParticleIndexIndex];
                particlesRef[immobileParticleIndex].isImmobile = true;
            }
        }

        if (context.activeGeometry != NULL)
        {
            SetActiveParticlesByActiveGeometry(fullConfig.systemConfig, *context.activeGeometry, particles);
        }
    }

    void GenerationManager::SetActiveParticlesByActiveGeometry(const SystemConfig& systemConfig, const IGeometry& activeGeometry, Packing* particles) const
    {
        Packing& particlesRef = *particles;

        int immobileParticlesCount = 0;
        for (ParticleIndex i = 0; i < systemConfig.particlesCount; ++i)
        {
            particlesRef[i].isImmobile = !activeGeometry.IsSphereInside(particlesRef[i].coordinates, particlesRef[i].diameter * 0.5);
            if (particlesRef[i].isImmobile)
            {
                immobileParticlesCount++;
            }
        }

        if (immobileParticlesCount > 0)
        {
            printf("mobileParticlesCount = %d, immobileParticlesCount = %d\n", systemConfig.particlesCount - immobileParticlesCount, immobileParticlesCount);
        }
    }

    void GenerationManager::ReadOrCreatePacking(const ExecutionConfig& fullConfig, const ModellingContext& context, Packing* particles) const
    {
        string baseFolder = fullConfig.generationConfig.baseFolder;
        int particlesCount = fullConfig.systemConfig.particlesCount;
        particles->resize(particlesCount);

        if (fullConfig.generationConfig.shouldStartGeneration.hasValue && fullConfig.generationConfig.shouldStartGeneration.value)
        {
            packingSerializer->ReadParticleDiameters(Path::Append(baseFolder, DIAMETERS_FILE_NAME), particles);

            if (fullConfig.generationConfig.initialParticleDistribution == InitialParticleDistribution::Poisson)
            {
                // TODO: create PoissonGenerator PoissonInCellsGenerator for all geometries, just call IsSphereInside for the given geometry until the sphere is in the geometry.
                BulkPoissonGenerator initialGenerator;
                CreateInitialPacking(fullConfig, context, particles, &initialGenerator);
            }
            else
            {
                BulkPoissonInCellsGenerator initialGenerator;
                CreateInitialPacking(fullConfig, context, particles, &initialGenerator);
            }

//            HcpGenerator initialGenerator;
//            CreateInitialPacking(fullConfig, context, particles, &initialGenerator);

            packingSerializer->SerializePacking(Path::Append(baseFolder, INIT_PACKING_FILE_NAME), *particles);
        }
        else
        {
            packingSerializer->ReadPacking(Path::Append(baseFolder, PACKING_FILE_NAME), particles);
        }
    }

    void GenerationManager::CreateInitialPacking(const ExecutionConfig& fullConfig, const ModellingContext& context, Packing* particles, IPackingGenerator* initialGenerator) const
    {
        initialGenerator->SetContext(context);
        initialGenerator->SetGenerationConfig(fullConfig.generationConfig);
        initialGenerator->ArrangePacking(particles);
    }

    GenerationManager::~GenerationManager()
    {

    }
}