//=======================================================================
// Copyright (c)
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================
/**
 * @file min_cut.hpp
 * @brief
 * @author Piotr Godlewski
 * @version 1.0
 * @date 2014-03-05
 */
#ifndef PAAL_MIN_CUT_HPP
#define PAAL_MIN_CUT_HPP

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

#include <numeric>

namespace paal {
namespace ir {

/**
 * @class min_cut_finder
 * @brief Class for creating and modifying directed graphs with edge capacities
 *  and finding directed minimum cuts between given vertices.
 */
class min_cut_finder {
    using Traits = boost::adjacency_list_traits<boost::vecS, boost::vecS, boost::directedS>;
public:
    using Edge = Traits::edge_descriptor;
    using Vertex = Traits::vertex_descriptor;

    /// Constructor.
    min_cut_finder()
        : m_graph(0), m_cap(get(boost::edge_capacity, m_graph)),
          m_rev(get(boost::edge_reverse, m_graph)),
          m_colors(get(boost::vertex_color, m_graph)) {}

    /**
     * (Re)Initializes the graph.
     */
    void init(int vertices_num) {
        m_graph.clear();
        for (int i = 0; i < vertices_num; ++i) {
            add_vertex_to_graph();
        }
        m_cap = get(boost::edge_capacity, m_graph);
        m_rev = get(boost::edge_reverse, m_graph);
    }

    /**
     * Adds a new vertex to the graph.
     */
    Vertex add_vertex_to_graph() { return add_vertex(m_graph); }

    /**
     * Adds an edge to the graph.
     * @param src source vertex of for the added edge
     * @param trg target vertex of for the added edge
     * @param cap capacity of the added edge
     * @param rev_cap capacity of the reverse edge
     *
     * @return created edge of the graph and the created reverse edge
     */
    std::pair<Edge, Edge>
    add_edge_to_graph(Vertex src, Vertex trg, double cap, double rev_cap = 0.) {
        bool b, b_rev;
        Edge e, e_rev;

        std::tie(e, b) = add_edge(src, trg, m_graph);
        std::tie(e_rev, b_rev) = add_edge(trg, src, m_graph);
        assert(b && b_rev);

        put(m_cap, e, cap);
        put(m_cap, e_rev, rev_cap);

        put(m_rev, e, e_rev);
        put(m_rev, e_rev, e);

        return std::make_pair(e, e_rev);
    }

    /**
     * Finds the min cut between \c src and \c trg.
     *
     * @param src source vertex (belongs to the cut set)
     * @param trg target vertex (does not belong to the cut set)
     *
     * @return min cut value
     */
    double find_min_cut(Vertex src, Vertex trg) {
        assert(src != trg);
        double min_cut_val = boost::boykov_kolmogorov_max_flow(m_graph, src, trg);
        m_colors = get(boost::vertex_color, m_graph);
        m_src_color = get(m_colors, src);
        m_last_cut = std::make_pair(src, trg);
        assert(!is_in_source_set(trg));
        return min_cut_val;
    }

    /**
     * Checks if the given vertex belongs to the source side of the last checked
     * cut.
     */
    bool is_in_source_set(Vertex v) const {
        return (m_src_color == get(m_colors, v));
    }

    /**
     * Returns the number of vertices in the source size of the last checked
     * cut.
     */
    int source_set_size() const {
        auto verts = vertices(m_graph);
        return std::accumulate(verts.first, verts.second, 0,
                               [&](int count, Vertex v) {
            return count + is_in_source_set(v);
        });
    }

    /**
     * Returns the pair of vertices defining the last checked cut.
     */
    std::pair<Vertex, Vertex> get_last_cut() const { return m_last_cut; }

    /**
     * Returns the capacity of a given edge.
     */
    double get_capacity(Edge e) const { return get(m_cap, e); }

    /**
     * Sets the capacity of a given edge.
     */
    void set_capacity(Edge e, double cap) {
        put(m_cap, e, cap);
    }

    /**
     * Sets the capacity of a given edge.
     */
    void set_capacity(Vertex src, Vertex trg, double cap) {
        auto e = edge(src, trg, m_graph);
        assert(e.second);
        set_capacity(e.first, cap);
    }

private:
    using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS,
              boost::property<boost::vertex_color_t, boost::default_color_type,
                  boost::property<boost::vertex_distance_t, long,
                      boost::property<boost::vertex_predecessor_t, Edge>>>,
              boost::property<boost::edge_capacity_t, double,
                  boost::property<boost::edge_residual_capacity_t, double,
                      boost::property<boost::edge_reverse_t, Edge>>>>;
    using EdgeCapacity = boost::property_map<Graph, boost::edge_capacity_t>::type;
    using EdgeReverse = boost::property_map<Graph, boost::edge_reverse_t>::type;
    using VertexColors = boost::property_map<Graph, boost::vertex_color_t>::type;

    Graph m_graph;

    EdgeCapacity m_cap;
    EdgeReverse m_rev;
    VertexColors m_colors;

    boost::default_color_type m_src_color;
    std::pair<Vertex, Vertex> m_last_cut;
};

} //! ir
} //! paal
#endif // PAAL_MIN_CUT_HPP


#pragma once
#ifndef EIGEN_DEFAULT_IO_FORMAT
#define EIGEN_DEFAULT_IO_FORMAT Eigen::IOFormat(12, 0, ", ", ",\n", "", "", "", "")
#endif
#include <fmt/format.h>
#include <fmt/ostream.h>
#include <spdlog/fmt/ostr.h>

#include <FeltElements/Typedefs.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/irange.hpp>

namespace
{
// std::to_string has non-configurable precision of too many decimal places.
auto const to_string = [](auto const f) { return fmt::format("{:f}", f); };
}  // namespace

namespace Fastor
{
template <std::size_t dim0, std::size_t dim1, std::size_t dim2, std::size_t dim3>
std::ostream & operator<<(
	std::ostream & os, FeltElements::Tensor::Multi<dim0, dim1, dim2, dim3> value)
{
	using FeltElements::Tensor::Index;
	using FeltElements::Tensor::Vector;
	using boost::algorithm::join;

	std::array<std::string, value.dimension(0)> is{};
	std::array<std::string, value.dimension(1)> js{};
	std::array<std::string, value.dimension(2)> ks{};
	for (Index i = 0; i < value.dimension(0); i++)
	{
		for (Index j = 0; j < value.dimension(1); j++)
		{
			for (Index k = 0; k < value.dimension(2); k++)
			{
				using FeltElements::Tensor::Func::all;
				using Vec = Vector<value.dimension(3)>;
				Vec const & vec = value(i, j, k, all);

				ks[k] = join(
					boost::make_iterator_range(vec.data(), vec.data() + Vec::size()) |
						boost::adaptors::transformed(to_string),
					", ");
			}
			js[j] = join(ks, "}, {");
		}
		is[i] = join(js, "}},\n\t{{");
	}
	os << "{\n\t{{" << join(is, "}}\n}, {\n\t{{") << "}}\n}\n";
	return os;
}

template <std::size_t dim0, std::size_t dim1, std::size_t dim2>
std::ostream & operator<<(std::ostream & os, FeltElements::Tensor::Multi<dim0, dim1, dim2> value)
{
	using boost::algorithm::join;
	using FeltElements::Tensor::Index;
	using FeltElements::Tensor::Vector;

	std::array<std::string, value.dimension(0)> is{};
	std::array<std::string, value.dimension(1)> js{};
	std::array<std::string, value.dimension(2)> ks{};
	for (Index i = 0; i < value.dimension(0); i++)
	{
		for (Index j = 0; j < value.dimension(1); j++)
		{
			using FeltElements::Tensor::Func::all;
			using Vec = Vector<value.dimension(2)>;
			Vec const & vec = value(i, j, all);

			js[j] = join(
				boost::make_iterator_range(vec.data(), vec.data() + Vec::size()) |
					boost::adaptors::transformed(to_string),
				", ");
		}
		is[i] = join(js, "},\n\t{");
	}
	os << "{\n\t{" << join(is, "}\n}, {\n\t{") << "}\n}\n";
	return os;
}

template <std::size_t dim0, std::size_t dim1>
std::ostream & operator<<(std::ostream & os, FeltElements::Tensor::Matrix<dim0, dim1> value)
{
	using boost::algorithm::join;
	using FeltElements::Tensor::Func::all;
	using FeltElements::Tensor::Index;

	std::array<std::string, value.dimension(0)> is{};
	for (Index i = 0; i < value.dimension(0); i++)
	{
		using Vec = FeltElements::Tensor::Vector<value.dimension(1)>;
		Vec const & vec = value(i, all);
		is[i] = join(
			boost::make_iterator_range(vec.data(), vec.data() + Vec::size()) |
				boost::adaptors::transformed(to_string),
			", ");
	}
	os << "{\n\t{" << join(is, "},\n\t{") << "}\n}";
	return os;
}
}  // namespace Fastor

inline std::ostream & operator<<(
	std::ostream & os, std::vector<OpenVolumeMesh::VertexHandle> const & vtxhs)
{
	using boost::adaptors::transformed;
	using boost::algorithm::join;

	constexpr auto to_idx = [](auto const & vtxh) { return vtxh.idx(); };

	os << join(vtxhs | transformed(to_idx) | transformed(to_string), ", ") << "\n";
	return os;
}

inline std::ostream & operator<<(std::ostream & os, FeltElements::Vtx const & vtx)
{
	os << fmt::format("{{{}, {}, {}}}", vtx[0], vtx[1], vtx[2]);
	return os;
}

template <std::size_t N>
inline std::ostream & operator<<(std::ostream & os, FeltElements::Tensor::Vector<N> const & pos)
{
	const std::string elems = boost::algorithm::join(
		boost::irange(N) |
			boost::adaptors::transformed([&pos](auto i) { return fmt::format("{:.5}", pos(i)); }),
		", ");
	os << fmt::format("{{{}}}", elems);

	return os;
}


/**
 * @file DiscreteData.hpp
 * @brief Implementation of the the functions used for querying the data.
 * @author Ankit Srivastava <asrivast@gatech.edu>
 *
 * Copyright 2020 Georgia Institute of Technology
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef DETAIL_DISCRETEDATA_HPP_
#define DETAIL_DISCRETEDATA_HPP_

#include "common/SetUtils.hpp"
#include "mxx/reduction.hpp"
#include "utils/Logging.hpp"

#include <boost/math/distributions/chi_squared.hpp>

template <typename Counter, typename Var>
/**
 * @brief Default constructor.
 */
DiscreteData<Counter, Var>::DiscreteData(
) : m_counter(),
    m_gsquare(),
    m_varNames()
{
  TIMER_RESET(m_timer);
}

template <typename Counter, typename Var>
/**
 * @brief Constructs the object for querying the given dataset.
 *
 * @param counter Object that executes counting queries.
 * @param varNames Names of all the variables.
 */
DiscreteData<Counter, Var>::DiscreteData(
  const Counter& counter,
  const std::vector<std::string>& varNames
) : m_counter(counter),
    m_varNames(varNames)
{
  LOG_MESSAGE_IF(numVars() != varNames.size(), error, "Number of variables (%d) != Number of variable names", counter.n(), varNames.size());
  TIMER_RESET(m_timer);
}

template <typename Counter, typename Var>
/**
 * @brief Default destructor.
 */
DiscreteData<Counter, Var>::~DiscreteData(
)
{
  if (mxx::comm().is_first()) {
    TIMER_ELAPSED_NONZERO("Time taken in G-square computations: ", m_timer);
  }
}

template <typename Counter, typename Var>
/**
 * @return Number of variables in the given dataset.
 */
Var
DiscreteData<Counter, Var>::numVars(
) const
{
  return static_cast<Var>(m_counter.n());
}

template <typename Counter, typename Var>
/**
 * @return Number of observations in the given dataset.
 */
uint32_t
DiscreteData<Counter, Var>::numObs(
) const
{
  return static_cast<uint32_t>(m_counter.m());
}

template <typename Counter, typename Var>
/**
 * @brief Returns the name of a variable.
 *
 * @param x The index of the query variable.
 *
 * @return The name of the query variable.
 */
const std::string&
DiscreteData<Counter, Var>::varName(
  const Var x
) const
{
  LOG_MESSAGE_IF(x >= m_varNames.size(), error, "Variable index %d out of range.", static_cast<uint32_t>(x));
  return m_varNames[x];
}

template <typename Counter, typename Var>
/**
 * @brief Returns the names of all the variables in the given set.
 *
 * @tparam Set The type of container for the variable indices.
 * @param vars The indices of all the query variable.
 *
 * @return The name of all the query variables.
 */
template <typename Set>
std::vector<std::string>
DiscreteData<Counter, Var>::varNames(
  const Set& vars
) const
{
  std::vector<std::string> names(vars.size());
  auto i = 0u;
  for (const auto var : vars) {
    LOG_MESSAGE_IF(var >= m_varNames.size(), error, "Variable index %d out of range.", static_cast<uint32_t>(var));
    names[i++] = m_varNames[var];
  }
  return names;
}

template <typename Counter, typename Var>
/**
 * @brief Returns the names of all the variables.
 */
const std::vector<std::string>&
DiscreteData<Counter, Var>::varNames(
) const
{
  return m_varNames;
}

template <typename Counter, typename Var>
/**
 * @brief Returns the index of a variable.
 *
 * @param name The name of the query variable.
 *
 * @return The index of the query variable.
 */
Var
DiscreteData<Counter, Var>::varIndex(
  const std::string& name
) const
{
  Var x = 0u;
  for (const auto& var : m_varNames) {
    if (var.compare(name) == 0) {
      break;
    }
    ++x;
  }
  LOG_MESSAGE_IF(x == numVars(), error, "Variable with name %s not found.", name);
  return x;
}

template <typename Counter, typename Var>
/**
 * @brief Returns indices of multiple variables.
 *
 * @tparam Set The type of container to be used for returning indices.
 * @param names The list of names of the query variables.
 *
 * @return The indices of all the queried variables.
 */
template <typename Set>
Set
DiscreteData<Counter, Var>::varIndices(
  const std::vector<std::string>& names
) const
{
  auto indices = set_init(Set(), numVars());
  for (const auto& name : names) {
    indices.insert(indices.end(), this->varIndex(name));
  }
  return indices;
}

template <typename Counter, typename Var>
/**
 * @brief Computes the p-value for the variables, given the conditioning set.
 *
 * @tparam Set The type of the container used for indices of the given variables.
 * @param x The index of the first variable.
 * @param y The index of the second variable.
 * @param given The indices of the variables to be conditioned on.
 */
template <typename Set>
double
DiscreteData<Counter, Var>::pValue(
  const Var x,
  const Var y,
  const Set& given
) const
{
  TIMER_START(m_timer);
  auto ret = m_gsquare.compute(m_counter, x, y, given);
  TIMER_PAUSE(m_timer);
  if (std::fpclassify(ret.second) == FP_ZERO) {
    return 1.0;
  }
  boost::math::chi_squared dist(ret.first);
  double pValue = boost::math::cdf(boost::math::complement(dist, ret.second));
  LOG_MESSAGE(debug, "p-value = %g", pValue);
  return pValue;
}

template <typename Counter, typename Var>
/**
 * @brief Checks if the variables are independent, given the conditioning set.
 *
 * @tparam Set The type of the container used for indices of the given variables.
 * @param alpha Target nominal type I error rate.
 * @param x The index of the first variable.
 * @param y The index of the second variable.
 * @param given The indices of the variables to be conditioned on.
 */
template <typename Set>
bool
DiscreteData<Counter, Var>::isIndependent(
  const double alpha,
  const Var x,
  const Var y,
  const Set& given
) const
{
  return std::isgreater(this->pValue(x, y, given), alpha);
}

template <typename Counter, typename Var>
/**
 * @brief Checks for independence, given the p-value.
 *
 * @param alpha Target nominal type I error rate.
 * @param pv The p-value, expected to be in the range [0.0, 1.0].
 */
bool
DiscreteData<Counter, Var>::isIndependent(
  const double alpha,
  const double pv
) const
{
  return std::isgreater(pv, alpha);
}

template <typename Counter, typename Var>
/**
 * @brief Finds the maximum p-value between the given variables,
 *        conditioned on any subset of the given conditioning set.
 *
 * @tparam SetType The type of the container used for storing indices of the given variables.
 * @tparam Args The trailing args, after the element type, to the container template.
 * @param alpha Target nominal type I error rate.
 * @param x The index of the first variable.
 * @param y The index of the second variable.
 * @param given The indices of the variables to be conditioned on.
 * @param maxSize The maximum size of the subset to be tested.
 * @param minSize The minimum size of the subset to be tested.
 *
 * @return The computed maximum p-value.
 */
template <template <typename...> class SetType, typename... Args>
double
DiscreteData<Counter, Var>::maxPValue(
  const double alpha,
  const Var x,
  const Var y,
  const SetType<Var, Args...>& given,
  const Var maxSize,
  const Var minSize
) const
{
  auto subsetSize = std::min(static_cast<Var>(given.size()), maxSize);
  auto maxPV = std::numeric_limits<double>::lowest();
  for (auto i = minSize; (i <= subsetSize) && !this->isIndependent(alpha, maxPV); ++i) {
    for (auto condition : Subsets<SetType, Var, Args...>(given, i)) {
      auto thisPV = this->pValue(x, y, condition);
      maxPV = std::max(thisPV, maxPV);
      if (this->isIndependent(alpha, maxPV)) {
        break;
      }
    }
  }
  LOG_MESSAGE(debug, "max p-value = %g", maxPV);
  return maxPV;
}

template <typename Counter, typename Var>
/**
 * @brief Finds the maximum p-value between the given variables,
 *        conditioned on any subset of the given conditioning set.
 *
 * @tparam SetType The type of the container used for storing indices of the given variables.
 * @tparam Args The trailing args, after the element type, to the container template.
 * @param alpha Target nominal type I error rate.
 * @param x The index of the first variable.
 * @param y The index of the second variable.
 * @param given The indices of the variables to be conditioned on.
 * @param seed The indices of the variables to be included in every subset.
 * @param maxSize The maximum size of the subset to be tested.
 *
 * @return The computed maximum p-value.
 */
template <template <typename...> class SetType, typename... Args>
double
DiscreteData<Counter, Var>::maxPValue(
  const double alpha,
  const Var x,
  const Var y,
  const SetType<Var, Args...>& given,
  const SetType<Var, Args...>& seed,
  const Var maxSize
) const
{
  auto maxPV = this->pValue(x, y, seed);
  if (!(this->isIndependent(alpha, maxPV) || given.empty())) {
    auto subsetSize = std::min(static_cast<Var>(given.size()), maxSize);
    for (auto i = 1u; (i <= subsetSize) && !this->isIndependent(alpha, maxPV); ++i) {
      for (auto condition : Subsets<SetType, Var, Args...>(given, i)) {
        // Always include the seed set in the conditioning set
        condition = set_union(condition, seed);
        auto thisPV = this->pValue(x, y, condition);
        maxPV = std::max(thisPV, maxPV);
        if (this->isIndependent(alpha, maxPV)) {
          break;
        }
      }
    }
  }
  LOG_MESSAGE(debug, "max p-value = %g", maxPV);
  return maxPV;
}

template <typename Counter, typename Var>
/**
 * @brief Finds the subset of the given conditioning set that maximizes
 *        the p-value between the given variables.
 *
 * @tparam SetType The type of the container used for storing indices of the given variables.
 * @tparam Args The trailing args, after the element type, to the container template.
 * @param alpha Target nominal type I error rate.
 * @param x The index of the first variable.
 * @param y The index of the second variable.
 * @param given The indices of the variables to be conditioned on.
 * @param maxSize The maximum size of the subset to be tested.
 * @param minSize The minimum size of the subset to be tested.
 *
 * @return A pair with the computed maximum p-value and the corresponding subset.
 */
template <template <typename...> class SetType, typename... Args>
std::pair<double, SetType<Var, Args...>>
DiscreteData<Counter, Var>::maxPValueSubset(
  const double alpha,
  const Var x,
  const Var y,
  const SetType<Var, Args...>& given,
  const Var maxSize,
  const Var minSize
) const
{
  auto subsetSize = std::min(static_cast<Var>(given.size()), maxSize);
  auto maxPV = std::numeric_limits<double>::lowest();
  auto z = set_init(SetType<Var, Args...>(), numVars());
  for (auto i = minSize; (i <= subsetSize) && !this->isIndependent(alpha, maxPV); ++i) {
    for (auto condition : Subsets<SetType, Var, Args...>(given, i)) {
      auto thisPV = this->pValue(x, y, condition);
      if (std::isgreater(thisPV, maxPV)) {
        maxPV = thisPV;
        z = condition;
      }
      if (this->isIndependent(alpha, maxPV)) {
        break;
      }
    }
  }
  LOG_MESSAGE(debug, "max p-value = %g", maxPV);
  return std::make_pair(maxPV, z);
}

template <typename Counter, typename Var>
/**
 * @brief Checks if the given variables are independent, given any
 *        subset of the given conditioning subset.
 *
 * @tparam Set The type of the container used for indices of the given variables.
 * @param x The index of the first variable.
 * @param y The index of the second variable.
 * @param given The indices of the variables to be conditioned on.
 * @param maxSize The maximum size of the subset to be tested.
 * @param minSize The minimum size of the subset to be tested.
 */
template <typename Set>
bool
DiscreteData<Counter, Var>::isIndependentAnySubset(
  const double alpha,
  const Var x,
  const Var y,
  const Set& given,
  const Var maxSize,
  const Var minSize
) const
{
  auto maxPV = this->maxPValue(alpha, x, y, given, maxSize, minSize);
  return this->isIndependent(alpha, maxPV);
}

template <typename Counter, typename Var>
/**
 * @return Returns the value of environment variable CSL_TESTS_THRESHOLD
 *         or the default value, if the environment was not set.
 */
uint32_t
DiscreteData<Counter, Var>::testThreshold(
  const uint32_t defaultThreshold
) const
{
  static const char* val = std::getenv("CSL_TESTS_THRESHOLD");
  static uint32_t threshold = (val != nullptr) ? static_cast<uint32_t>(atoi(val)) : defaultThreshold;
  return threshold;
}

template <typename Counter, typename Var>
/**
 * @brief Checks if the given variables are independent, given any
 *        subset of the given conditioning subset. The tests are conducted
 *        in a distributed manner over all the processors in the communicator.
 *
 * @tparam SetType The type of the container used for storing indices of the given variables.
 * @tparam Args The trailing args, after the element type, to the container template.
 * @param alpha Target nominal type I error rate.
 * @param x The index of the first variable.
 * @param y The index of the second variable.
 * @param given The indices of the variables to be conditioned on.
 * @param maxSize The maximum size of the subset to be tested.
 * @param comm The communicator over which the tests are distributed.
 */
template <template <typename...> class SetType, typename... Args>
bool
DiscreteData<Counter, Var>::isIndependentAnySubset(
  const double alpha,
  const Var x,
  const Var y,
  const SetType<Var, Args...>& given,
  const Var maxSize,
  const mxx::comm& comm
) const
{
  static auto findMax = [] (const double& x, const double& y) { return std::isgreaterequal(x, y) ? x : y; };
  static uint32_t threshold = this->testThreshold();
  uint32_t mine = 0u;
  uint32_t others = 0u;
  int r = 0;
  auto maxPV = std::numeric_limits<double>::lowest();
  auto subsetSize = std::min(static_cast<Var>(given.size()), maxSize);
  for (auto i = 0u; i <= subsetSize; ++i) {
    auto subsets = Subsets<SetType, Var, Args...>(given, i);
    for (auto sit = subsets.begin(); sit != subsets.end(); ++sit) {
      if (comm.rank() != r) {
        // This test must be conducted by some other process
        ++others;
      }
      else {
        // I am going to conduct this test
        ++mine;
        // Only conduct more tests if the previous tests did not
        // return independence
        if (!this->isIndependent(alpha, maxPV)) {
          auto thisPV = this->pValue(x, y, *sit);
          maxPV = std::max(maxPV, thisPV);
        }
      }
      // Sync if all the processes have conducted the same number of tests
      if ((mine + others) == (threshold * comm.size())) {
        maxPV = mxx::allreduce(maxPV, findMax, comm);
        if (!this->isIndependent(alpha, maxPV)) {
          mine = 0u;
          others = 0u;
        }
        else {
          return true;
        }
      }
      r = (r + 1) % comm.size();
    }
  }
  return this->isIndependent(alpha, maxPV);
}

template <typename Counter, typename Var>
/**
 * @brief Checks if the given variables are independent, given any
 *        subset of the given conditioning subset.
 *
 * @tparam Set The type of the container used for indices of the given variables.
 * @param x The index of the first variable.
 * @param y The index of the second variable.
 * @param given The indices of the variables to be conditioned on.
 * @param seed The indices of the variables to be included in every subset.
 * @param maxSize The maximum size of the subset to be tested.
 */
template <typename Set>
bool
DiscreteData<Counter, Var>::isIndependentAnySubset(
  const double alpha,
  const Var x,
  const Var y,
  const Set& given,
  const Set& seed,
  const Var maxSize
) const
{
  auto maxPV = this->maxPValue(alpha, x, y, given, seed, maxSize);
  return this->isIndependent(alpha, maxPV);
}

#endif // DETAIL_DISCRETEDATA_HPP_


//
// Created by Xinyu Zhang on 3/26/21.
//
// gcc -I/Users/xinyuzhang/Desktop/Spring2021/c++pattern/project/cosan /Users/xinyuzhang/Desktop/Spring2021/c++pattern/project/cosan/test/preprocessor/NormalizerTest.cpp

#include <iostream>
// #include <Eigen/Dense>
// #include <cosan/io/utils.h>
#include <cosan/preprocessing/normalizer.h>
#include <vector>
//using namespace Eigen;
//using namespace std;

int main() {

	Cosan::Normalizer normalizer;
	Cosan::Normalizer normalizer1(1);
	Cosan::Normalizer normalizerinf(std::numeric_limits<int>::max());
	// Cosan::Normalizer normalizer3(3);
	Cosan::CosanMatrix X1{3,3};

	X1<<3,4,0,
	    2,0,0,
	    5,12,13;

    std::cout << normalizer.normalize(X1) << '\n' << std::endl;

    Cosan::CosanMatrix X2{3, 3};

    X2 << 1, -1, 2,
            2, 0, 0,
            0, 0, 1;

    std::cout << normalizer.normalize(X2) << '\n' << std::endl;

    Cosan::CosanMatrix X3{3, 3};

	X3 << 1, -1, 2,
	            2, 0, 0,
	            0, 0, 0;

    std::cout << normalizer.normalize(X3) << '\n' << std::endl;
    std::cout << normalizer1.normalize(X3) << '\n' << std::endl;
    std::cout << normalizerinf.normalize(X3) << '\n' << std::endl;
    Cosan::CosanMatrix X4{3, 1};

	X4 << 1,
			2,
			0 ;

    std::cout << normalizer.normalize(X4) << '\n' << std::endl;

	std::vector<double> values= {-1, 1, 0};

    std::cout << normalizer.normalize(Eigen::Map<const EigenMatrix>(values.data(), 1, 3)) << '\n' << std::endl;

    return 0;
//
//    m.transposeInPlace();
//    cout<<m<<endl;
//    m.resize(1,4);
//    cout<<n<<endl;
}


// Test for boost/core/bit.hpp (bit_width)
//
// Copyright 2020 Peter Dimov
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#include <boost/core/bit.hpp>
#include <boost/core/lightweight_test.hpp>
#include <boost/core/detail/splitmix64.hpp>
#include <boost/cstdint.hpp>
#include <limits>

template<class T> void test_width( T x )
{
    BOOST_TEST_EQ( boost::core::bit_width( x ), std::numeric_limits<T>::digits - boost::core::countl_zero( x ) );
}

int main()
{
    {
        boost::uint8_t x = 0;

        BOOST_TEST_EQ( boost::core::bit_width( x ), 0 );

        x = 1;

        for( int i = 0; i < 8; ++i, x <<= 1 )
        {
            BOOST_TEST_EQ( boost::core::bit_width( x ), i+1 );
            BOOST_TEST_EQ( boost::core::bit_width( static_cast<boost::uint8_t>( x | ( x >> 1 ) ) ), i+1 );
            BOOST_TEST_EQ( boost::core::bit_width( static_cast<boost::uint8_t>( x | ( x >> 2 ) ) ), i+1 );
        }
    }

    {
        boost::uint16_t x = 0;

        BOOST_TEST_EQ( boost::core::bit_width( x ), 0 );

        x = 1;

        for( int i = 0; i < 16; ++i, x <<= 1 )
        {
            BOOST_TEST_EQ( boost::core::bit_width( x ), i+1 );
            BOOST_TEST_EQ( boost::core::bit_width( static_cast<boost::uint16_t>( x | ( x >> 1 ) ) ), i+1 );
            BOOST_TEST_EQ( boost::core::bit_width( static_cast<boost::uint16_t>( x | ( x >> 2 ) ) ), i+1 );
        }
    }

    {
        boost::uint32_t x = 0;

        BOOST_TEST_EQ( boost::core::bit_width( x ), 0 );

        x = 1;

        for( int i = 0; i < 32; ++i, x <<= 1 )
        {
            BOOST_TEST_EQ( boost::core::bit_width( x ), i+1 );
            BOOST_TEST_EQ( boost::core::bit_width( static_cast<boost::uint32_t>( x | ( x >> 1 ) ) ), i+1 );
            BOOST_TEST_EQ( boost::core::bit_width( static_cast<boost::uint32_t>( x | ( x >> 2 ) ) ), i+1 );
        }
    }

    {
        boost::uint64_t x = 0;

        BOOST_TEST_EQ( boost::core::bit_width( x ), 0 );

        x = 1;

        for( int i = 0; i < 64; ++i, x <<= 1 )
        {
            BOOST_TEST_EQ( boost::core::bit_width( x ), i+1 );
            BOOST_TEST_EQ( boost::core::bit_width( static_cast<boost::uint64_t>( x | ( x >> 1 ) ) ), i+1 );
            BOOST_TEST_EQ( boost::core::bit_width( static_cast<boost::uint64_t>( x | ( x >> 2 ) ) ), i+1 );
        }
    }

    boost::detail::splitmix64 rng;

    for( int i = 0; i < 1000; ++i )
    {
        boost::uint64_t x = rng();

        test_width( static_cast<unsigned char>( x ) );
        test_width( static_cast<unsigned short>( x ) );
        test_width( static_cast<unsigned int>( x ) );
        test_width( static_cast<unsigned long>( x ) );
        test_width( static_cast<unsigned long long>( x ) );
    }

    return boost::report_errors();
}


//
//  main.cpp
//  ArticleDynamicFiniteDomain
//
//  Created by Gabriel Aubut-Lussier on 2017-07-15.
//

#include <boost/icl/interval_map.hpp>
#include <boost/icl/concept/interval.hpp>
#include <boost/icl/type_traits/interval_type_default.hpp>
#include <iostream>
#include <set>

/**
 * In real code, we'd expect some payload to be associated to elements of the domain.
 * This empty struct symbolizes that payload.
 */
struct some_payload
{
};

/**
 * This class represents elements of a dynamic domain. An example domain could be the set
 * of people present inside a room, ordered by a unique number those people were given prior
 * to entering the room. With this image, insertions and removals can be done at any location
 * within the set, as illustrated by people entering or leaving the room with whatever unique
 * number they were holding on to.
 */
class dynamic_domain
{
public:
	using identifier_type = unsigned int;
	using payload_type = some_payload;

public:
	dynamic_domain(identifier_type identifier) : identifier(identifier) {}

	bool operator<(const dynamic_domain& rhs) const
	{
		return identifier < rhs.identifier;
	}

	payload_type& get_payload()
	{
		return payload;
	}

	const payload_type& get_payload() const
	{
		return payload;
	}

	int get_identifier() const
	{
		return identifier;
	}

private:
	identifier_type identifier;
	payload_type payload;
};

using container_type = std::map<dynamic_domain::identifier_type, dynamic_domain>;
using iterator_type = container_type::const_iterator;

/**
 * This iterator class wraps a std::map<K, V>::const_iterator and adds one more
 * operator to it : operator<.
 */
class dynamic_domain_iterator
{
public:
	dynamic_domain_iterator() : it(g_default_container.begin()) {}
	dynamic_domain_iterator(const iterator_type& it) : it(it) {}

	const dynamic_domain& operator*() const
	{
		return it->second;
	}

	const dynamic_domain* operator->() const
	{
		return std::addressof(it->second);
	}

	dynamic_domain_iterator& operator--()
	{
		--it;
		return *this;
	}

	dynamic_domain_iterator& operator++()
	{
		++it;
		return *this;
	}

	bool operator==(const dynamic_domain_iterator& rhs) const
	{
		return it == rhs.it;
	}

	bool operator!=(const dynamic_domain_iterator& rhs) const
	{
		return it != rhs.it;
	}

	bool operator<(const dynamic_domain_iterator& rhs) const
	{
		return it->second < rhs.it->second;
	}

private:
	iterator_type it;

	/**
	 * This default static container makes it possible to default construct an iterator and
	 * allow it to be incremented twice. This is an optimization that makes it possible to
	 * default construct empty intervals which can use the general is_empty algorithm of
	 * Boost.ICL which is : empty = upper < lower. In order for this comparison to be well
	 * defined, both iterators (upper and lower) must be dereferenced, and thus, must point
	 * to valid elements.
	 */
	const static container_type g_default_container;
};

/**
 * For the interval to be empty, the second element of the container must compare less than the first element.
 * That is why the second element has a smaller number than the first.
 */
const container_type dynamic_domain_iterator::g_default_container{{0x9BADBEEF, dynamic_domain{0x9BADBEEF}}, {0x8BADBEEF, dynamic_domain{0x8BADBEEF}}};

namespace std
{
	template <>
	struct iterator_traits<dynamic_domain_iterator>
	{
		using difference_type = iterator_traits<iterator_type>::difference_type;
		using value_type = dynamic_domain_iterator;
		using pointer = const dynamic_domain_iterator*;
		using reference = const dynamic_domain_iterator&;
		using iterator_category = std::bidirectional_iterator_tag;
	};
}

/**
 * This template specialization makes sure that interval containers use
 * boost::icl::closed_interval when the DomainType is dynamic_domain_iterator.
 */
namespace boost { namespace icl {
	template<>
	struct interval_type_default<dynamic_domain_iterator>
	{
		using type = boost::icl::closed_interval<dynamic_domain_iterator>;
	};
}}

/**
 * Thanks to the interval_type_default specialization, we can simply provide
 * the DomainType and CodomainType parameters to the template class.
 */
using MapType = boost::icl::interval_map<dynamic_domain_iterator, int>;

int main(int argc, const char * argv[])
{
	container_type elements;
	elements.insert(std::make_pair(7, dynamic_domain{7}));
	elements.insert(std::make_pair(1, dynamic_domain{1}));
	elements.insert(std::make_pair(2, dynamic_domain{2}));
	elements.insert(std::make_pair(6, dynamic_domain{6}));
	elements.insert(std::make_pair(3, dynamic_domain{3}));
	elements.insert(std::make_pair(4, dynamic_domain{4}));
	elements.insert(std::make_pair(9001, dynamic_domain{9001}));
	elements.insert(std::make_pair(5, dynamic_domain{5}));

	MapType intervalMap;
	auto it = dynamic_domain_iterator{elements.cbegin()};
	intervalMap.add(std::make_pair(MapType::interval_type{it, std::next(it, 1)}, 1));
	intervalMap.add(std::make_pair(MapType::interval_type{std::next(it, 2), std::next(it, 3)}, 1));
	intervalMap.add(std::make_pair(MapType::interval_type{std::next(it, 1), std::next(it, 3)}, 1));
	for (auto element : intervalMap) {
		std::cout << "Domain: [" << element.first.lower()->get_identifier() << ", " << element.first.upper()->get_identifier() << "] = " << element.second << std::endl;
	}
}


/*
 * MTtbarAnalyser.cpp
 *
 *  Created on: 5 Sep 2011
 *      Author: kreczko
 */
//#include "../../interface/TopPairEventCandidate.h"
#include "../../interface/Analysers/MTtbarAnalyser.h"
#include "../../interface/ReconstructionModules/ChiSquaredBasedTopPairReconstruction.h"
#include "../../interface/ReconstructionModules/ChiSquaredThreeJetsTopPairReconstruction.h"
#include "../../interface/ReconstructionModules/TopMassDifferenceTTbarReco.h"
#include "../../interface/BTagWeight.h"

#include <boost/scoped_ptr.hpp>

namespace BAT {

void MTtbarAnalyser::analyse(const EventPtr event) {
	weight_ = event->weight();
	//TODO: add BJet weight_s
	analyseFourJetChi2(event);
	analyseThreeJetChi2(event);
	analyseFourJetTopMassDifference(event);
	analyseFourJetChi2QCDBackground(event);
	analyseThreeJetChi2QCDBackground(event);
	analyseFourJetTopMassDifferenceQCDBackground(event);
}

void MTtbarAnalyser::analyseFourJetChi2(const EventPtr event) {
	if (topElectronPlusJetsRefSelection_->passesFullSelectionExceptLastTwoSteps(event)) {
		LeptonPointer selectedElectron = topElectronPlusJetsRefSelection_->signalLepton(event);
		const ElectronPointer signalElectron(boost::static_pointer_cast<Electron>(selectedElectron));
		double efficiencyCorrection = event->isRealData() ? 1. : signalElectron->getEfficiencyCorrection(true, Globals::ElectronScaleFactorSystematic, event->runnumber());
		METPointer met = event->MET();
		JetCollection jets = topElectronPlusJetsRefSelection_->cleanedJets(event);
		JetCollection bJets = topElectronPlusJetsRefSelection_->cleanedBJets(event);
		unsigned int numberOfBjets(bJets.size());
		vector<double> bjetWeights;
		if (event->isRealData()) {
			for (unsigned int index = 0; index <= numberOfBjets; ++index) {
				if (index == numberOfBjets)
					bjetWeights.push_back(1.);
				else
					bjetWeights.push_back(0);
			}
		} else
			bjetWeights = BjetWeights(jets, numberOfBjets);
		histMan_->setCurrentJetBin(jets.size());
		histMan_->setCurrentBJetBin(numberOfBjets);

		boost::scoped_ptr<ChiSquaredBasedTopPairReconstruction> chi2Reco(
				new ChiSquaredBasedTopPairReconstruction(selectedElectron, met, jets));
		if (!chi2Reco->meetsInitialCriteria()) { //reports details on failure and skips event
			cout << chi2Reco->getDetailsOnFailure();
			return;
		}
		allSolutions = chi2Reco->getAllSolutions();
		for (unsigned int weight_Index = 0; weight_Index < bjetWeights.size(); ++weight_Index) {
			double bjetWeight = bjetWeights.at(weight_Index);
			scale_ = bjetWeight * efficiencyCorrection;
			histMan_->setCurrentBJetBin(weight_Index);
			fillHistograms("ElectronPlusJets/Ref selection/FourJetChi2");
		}
	}

	if (topMuonPlusJetsRefSelection_->passesFullSelectionExceptLastTwoSteps(event)) {
		LeptonPointer selectedMuon = topMuonPlusJetsRefSelection_->signalLepton(event);
		const MuonPointer signalMuon(boost::static_pointer_cast<Muon>(selectedMuon));
		double efficiencyCorrection = event->isRealData() ? 1. : signalMuon->getEfficiencyCorrection(false, Globals::ElectronScaleFactorSystematic, event->runnumber());
		METPointer met = event->MET();
		JetCollection jets = topMuonPlusJetsRefSelection_->cleanedJets(event);
		JetCollection bJets = topMuonPlusJetsRefSelection_->cleanedBJets(event);
		unsigned int numberOfBjets(bJets.size());
		vector<double> bjetWeights;
		if (event->isRealData()) {
			for (unsigned int index = 0; index <= numberOfBjets; ++index) {
				if (index == numberOfBjets)
					bjetWeights.push_back(1.);
				else
					bjetWeights.push_back(0);
			}
		} else
			bjetWeights = BjetWeights(jets, numberOfBjets);
		histMan_->setCurrentJetBin(jets.size());
		histMan_->setCurrentBJetBin(numberOfBjets);

		boost::scoped_ptr<ChiSquaredBasedTopPairReconstruction> chi2Reco(
				new ChiSquaredBasedTopPairReconstruction(selectedMuon, met, jets));
		if (!chi2Reco->meetsInitialCriteria()) { //reports details on failure and skips event
			cout << chi2Reco->getDetailsOnFailure();
			return;
		}
		allSolutions = chi2Reco->getAllSolutions();
		for (unsigned int weight_Index = 0; weight_Index < bjetWeights.size(); ++weight_Index) {
			double bjetWeight = bjetWeights.at(weight_Index);
			scale_ = bjetWeight * efficiencyCorrection;
			histMan_->setCurrentBJetBin(weight_Index);
			fillHistograms("MuonPlusJets/Ref selection/FourJetChi2");
		}
	}
}

void MTtbarAnalyser::analyseThreeJetChi2(const EventPtr event) {
	if (topElectronPlusJetsRefSelection_->passesSelectionUpToStep(event,
			TTbarEPlusJetsReferenceSelection::AtLeastThreeGoodJets)) {
		JetCollection jets = topElectronPlusJetsRefSelection_->cleanedJets(event);
		if (jets.size() != 3) //only consider 3 jet events
			return;

		JetCollection bJets = topElectronPlusJetsRefSelection_->cleanedBJets(event);
		unsigned int numberOfBjets(bJets.size());
		vector<double> bjetWeights;
		if (event->isRealData()) {
			for (unsigned int index = 0; index <= numberOfBjets; ++index) {
				if (index == numberOfBjets)
					bjetWeights.push_back(1.);
				else
					bjetWeights.push_back(0);
			}
		} else
			bjetWeights = BjetWeights(jets, numberOfBjets);
		histMan_->setCurrentJetBin(jets.size());
		histMan_->setCurrentBJetBin(numberOfBjets);

		LeptonPointer selectedElectron = topElectronPlusJetsRefSelection_->signalLepton(event);
		const ElectronPointer signalElectron(boost::static_pointer_cast<Electron>(selectedElectron));
		double efficiencyCorrection = event->isRealData() ? 1. : signalElectron->getEfficiencyCorrection(true, Globals::ElectronScaleFactorSystematic, event->runnumber());

		METPointer met = event->MET();

		boost::scoped_ptr<ChiSquaredThreeJetsTopPairReconstruction> chi2Reco(
				new ChiSquaredThreeJetsTopPairReconstruction(selectedElectron, met, jets));
		if (!chi2Reco->meetsInitialCriteria()) { //reports details on failure and skips event
			cout << chi2Reco->getDetailsOnFailure();
			return;
		}
		allSolutions = chi2Reco->getAllSolutions();
		for (unsigned int weight_Index = 0; weight_Index < bjetWeights.size(); ++weight_Index) {
			double bjetWeight = bjetWeights.at(weight_Index);
			scale_ = bjetWeight * efficiencyCorrection;
			histMan_->setCurrentBJetBin(weight_Index);
			fillHistograms("ElectronPlusJets/Ref selection/ThreeJetChi2");
		}
	}

	if (topMuonPlusJetsRefSelection_->passesSelectionUpToStep(event,
			TTbarMuPlusJetsReferenceSelection::AtLeastThreeGoodJets)) {
		JetCollection jets = topMuonPlusJetsRefSelection_->cleanedJets(event);
		if (jets.size() != 3) //only consider 3 jet events
			return;

		JetCollection bJets = topMuonPlusJetsRefSelection_->cleanedBJets(event);
		unsigned int numberOfBjets(bJets.size());
		vector<double> bjetWeights;
		if (event->isRealData()) {
			for (unsigned int index = 0; index <= numberOfBjets; ++index) {
				if (index == numberOfBjets)
					bjetWeights.push_back(1.);
				else
					bjetWeights.push_back(0);
			}
		} else
			bjetWeights = BjetWeights(jets, numberOfBjets);
		histMan_->setCurrentJetBin(jets.size());
		histMan_->setCurrentBJetBin(numberOfBjets);

		LeptonPointer selectedMuon = topMuonPlusJetsRefSelection_->signalLepton(event);
		const MuonPointer signalMuon(boost::static_pointer_cast<Muon>(selectedMuon));
		double efficiencyCorrection = event->isRealData() ? 1. : signalMuon->getEfficiencyCorrection(false, Globals::MuonScaleFactorSystematic, event->runnumber());
		METPointer met = event->MET();

		boost::scoped_ptr<ChiSquaredThreeJetsTopPairReconstruction> chi2Reco(
				new ChiSquaredThreeJetsTopPairReconstruction(selectedMuon, met, jets));
		if (!chi2Reco->meetsInitialCriteria()) { //reports details on failure and skips event
			cout << chi2Reco->getDetailsOnFailure();
			return;
		}
		allSolutions = chi2Reco->getAllSolutions();
		for (unsigned int weight_Index = 0; weight_Index < bjetWeights.size(); ++weight_Index) {
			double bjetWeight = bjetWeights.at(weight_Index);
			scale_ = bjetWeight * efficiencyCorrection;
			histMan_->setCurrentBJetBin(weight_Index);
			fillHistograms("MuonPlusJets/Ref selection/ThreeJetChi2");
		}
	}
}

void MTtbarAnalyser::analyseFourJetTopMassDifference(const EventPtr event) {
	if (topElectronPlusJetsRefSelection_->passesFullSelectionExceptLastTwoSteps(event)) {
		LeptonPointer selectedElectron = topElectronPlusJetsRefSelection_->signalLepton(event);
		const ElectronPointer signalElectron(boost::static_pointer_cast<Electron>(selectedElectron));
		double efficiencyCorrection = event->isRealData() ? 1. : signalElectron->getEfficiencyCorrection(true, Globals::ElectronScaleFactorSystematic, event->runnumber());

		METPointer met = event->MET();
		JetCollection jets = topElectronPlusJetsRefSelection_->cleanedJets(event);
		JetCollection bJets = topElectronPlusJetsRefSelection_->cleanedBJets(event);
		unsigned int numberOfBjets(bJets.size());
		vector<double> bjetWeights;
		if (event->isRealData()) {
			for (unsigned int index = 0; index <= numberOfBjets; ++index) {
				if (index == numberOfBjets)
					bjetWeights.push_back(1.);
				else
					bjetWeights.push_back(0);
			}
		} else
			bjetWeights = BjetWeights(jets, numberOfBjets);
		histMan_->setCurrentJetBin(jets.size());
		histMan_->setCurrentBJetBin(numberOfBjets);

		boost::scoped_ptr<TopMassDifferenceTTbarReco> topMassDiffReco(
				new TopMassDifferenceTTbarReco(selectedElectron, met, jets));
		if (!topMassDiffReco->meetsInitialCriteria()) { //reports details on failure and skips event
			cout << topMassDiffReco->getDetailsOnFailure();
			return;
		}
		allSolutions = topMassDiffReco->getAllSolutions();
		for (unsigned int weight_Index = 0; weight_Index < bjetWeights.size(); ++weight_Index) {
			double bjetWeight = bjetWeights.at(weight_Index);
			scale_ = bjetWeight * efficiencyCorrection;
			histMan_->setCurrentBJetBin(weight_Index);
			fillHistograms("ElectronPlusJets/Ref selection/FourJetTopMassDifference");
		}
	}

	if (topMuonPlusJetsRefSelection_->passesFullSelectionExceptLastTwoSteps(event)) {
		LeptonPointer selectedMuon = topMuonPlusJetsRefSelection_->signalLepton(event);
		const MuonPointer signalMuon(boost::static_pointer_cast<Muon>(selectedMuon));
		double efficiencyCorrection = event->isRealData() ? 1. : signalMuon->getEfficiencyCorrection(false, Globals::MuonScaleFactorSystematic, event->runnumber());
		METPointer met = event->MET();
		JetCollection jets = topMuonPlusJetsRefSelection_->cleanedJets(event);
		JetCollection bJets = topMuonPlusJetsRefSelection_->cleanedBJets(event);
		unsigned int numberOfBjets(bJets.size());
		vector<double> bjetWeights;
		if (event->isRealData()) {
			for (unsigned int index = 0; index <= numberOfBjets; ++index) {
				if (index == numberOfBjets)
					bjetWeights.push_back(1.);
				else
					bjetWeights.push_back(0);
			}
		} else
			bjetWeights = BjetWeights(jets, numberOfBjets);
		histMan_->setCurrentJetBin(jets.size());
		histMan_->setCurrentBJetBin(numberOfBjets);

		boost::scoped_ptr<TopMassDifferenceTTbarReco> topMassDiffReco(
				new TopMassDifferenceTTbarReco(selectedMuon, met, jets));
		if (!topMassDiffReco->meetsInitialCriteria()) { //reports details on failure and skips event
			cout << topMassDiffReco->getDetailsOnFailure();
			return;
		}
		allSolutions = topMassDiffReco->getAllSolutions();
		for (unsigned int weight_Index = 0; weight_Index < bjetWeights.size(); ++weight_Index) {
			double bjetWeight = bjetWeights.at(weight_Index);
			scale_ = bjetWeight * efficiencyCorrection;
			histMan_->setCurrentBJetBin(weight_Index);
			fillHistograms("MuonPlusJets/Ref selection/FourJetTopMassDifference");
		}
	}
}

void MTtbarAnalyser::analyseFourJetChi2QCDBackground(const EventPtr event) {
	if (qcdElectronConversionSelection_->passesFullSelectionExceptLastTwoSteps(event)) { //passes all except b-tag!
		LeptonPointer selectedElectron = qcdElectronConversionSelection_->signalLepton(event);
		const ElectronPointer signalElectron(boost::static_pointer_cast<Electron>(selectedElectron));
		double efficiencyCorrection = event->isRealData() ? 1. : signalElectron->getEfficiencyCorrection(true, Globals::ElectronScaleFactorSystematic, event->runnumber());
		METPointer met = event->MET();
		JetCollection jets = qcdElectronConversionSelection_->cleanedJets(event);
		JetCollection bJets = qcdElectronConversionSelection_->cleanedBJets(event);
		unsigned int numberOfBjets(bJets.size());
		vector<double> bjetWeights;
		if (event->isRealData()) {
			for (unsigned int index = 0; index <= numberOfBjets; ++index) {
				if (index == numberOfBjets)
					bjetWeights.push_back(1.);
				else
					bjetWeights.push_back(0);
			}
		} else
			bjetWeights = BjetWeights(jets, numberOfBjets);
		histMan_->setCurrentJetBin(jets.size());
		histMan_->setCurrentBJetBin(numberOfBjets);

		boost::scoped_ptr<ChiSquaredBasedTopPairReconstruction> chi2Reco(
				new ChiSquaredBasedTopPairReconstruction(selectedElectron, met, jets));
		if (!chi2Reco->meetsInitialCriteria()) { //reports details on failure and skips event
			cout << chi2Reco->getDetailsOnFailure();
			return;
		}
		allSolutions = chi2Reco->getAllSolutions();
		for (unsigned int weight_Index = 0; weight_Index < bjetWeights.size(); ++weight_Index) {
			double bjetWeight = bjetWeights.at(weight_Index);
			scale_ = bjetWeight * efficiencyCorrection;
			histMan_->setCurrentBJetBin(weight_Index);
			fillHistograms("ElectronPlusJets/QCDConversions/FourJetChi2");
		}
	}

	if (qcdElectronNonIsoSelection_->passesFullSelectionExceptLastTwoSteps(event)) { //passes all except b-tag!
		LeptonPointer selectedElectron = qcdElectronNonIsoSelection_->signalLepton(event);
		const ElectronPointer signalElectron(boost::static_pointer_cast<Electron>(selectedElectron));
		double efficiencyCorrection = event->isRealData() ? 1. : signalElectron->getEfficiencyCorrection(true, Globals::ElectronScaleFactorSystematic, event->runnumber());
		METPointer met = event->MET();
		JetCollection jets = qcdElectronNonIsoSelection_->cleanedJets(event);
		JetCollection bJets = qcdElectronNonIsoSelection_->cleanedBJets(event);
		unsigned int numberOfBjets(bJets.size());
		vector<double> bjetWeights;
		if (event->isRealData()) {
			for (unsigned int index = 0; index <= numberOfBjets; ++index) {
				if (index == numberOfBjets)
					bjetWeights.push_back(1.);
				else
					bjetWeights.push_back(0);
			}
		} else
			bjetWeights = BjetWeights(jets, numberOfBjets);
		histMan_->setCurrentJetBin(jets.size());
		histMan_->setCurrentBJetBin(numberOfBjets);

		boost::scoped_ptr<ChiSquaredBasedTopPairReconstruction> chi2Reco(
				new ChiSquaredBasedTopPairReconstruction(selectedElectron, met, jets));
		if (!chi2Reco->meetsInitialCriteria()) { //reports details on failure and skips event
			cout << chi2Reco->getDetailsOnFailure();
			return;
		}
		allSolutions = chi2Reco->getAllSolutions();
		for (unsigned int weight_Index = 0; weight_Index < bjetWeights.size(); ++weight_Index) {
			double bjetWeight = bjetWeights.at(weight_Index);
			scale_ = bjetWeight * efficiencyCorrection;
			histMan_->setCurrentBJetBin(weight_Index);
			fillHistograms("ElectronPlusJets/QCD non iso e+jets/FourJetChi2");
		}
	}

	if (qcdElectronAntiIDSelection_->passesFullSelectionExceptLastTwoSteps(event)) { //passes all except b-tag!
		LeptonPointer selectedElectron = qcdElectronAntiIDSelection_->signalLepton(event);
		const ElectronPointer signalElectron(boost::static_pointer_cast<Electron>(selectedElectron));
		double efficiencyCorrection = event->isRealData() ? 1. : signalElectron->getEfficiencyCorrection(true, Globals::ElectronScaleFactorSystematic, event->runnumber());
		METPointer met = event->MET();
		JetCollection jets = qcdElectronAntiIDSelection_->cleanedJets(event);
		JetCollection bJets = qcdElectronAntiIDSelection_->cleanedBJets(event);
		unsigned int numberOfBjets(bJets.size());
		vector<double> bjetWeights;
		if (event->isRealData()) {
			for (unsigned int index = 0; index <= numberOfBjets; ++index) {
				if (index == numberOfBjets)
					bjetWeights.push_back(1.);
				else
					bjetWeights.push_back(0);
			}
		} else
			bjetWeights = BjetWeights(jets, numberOfBjets);
		histMan_->setCurrentJetBin(jets.size());
		histMan_->setCurrentBJetBin(numberOfBjets);

		boost::scoped_ptr<ChiSquaredBasedTopPairReconstruction> chi2Reco(
				new ChiSquaredBasedTopPairReconstruction(selectedElectron, met, jets));
		if (!chi2Reco->meetsInitialCriteria()) { //reports details on failure and skips event
			cout << chi2Reco->getDetailsOnFailure();
			return;
		}
		allSolutions = chi2Reco->getAllSolutions();
		for (unsigned int weight_Index = 0; weight_Index < bjetWeights.size(); ++weight_Index) {
			double bjetWeight = bjetWeights.at(weight_Index);
			scale_ = bjetWeight * efficiencyCorrection;
			histMan_->setCurrentBJetBin(weight_Index);
			fillHistograms("ElectronPlusJets/QCDAntiID/FourJetChi2");
		}
	}

	if (qcdMuonNonIsoSelection_->passesFullSelectionExceptLastTwoSteps(event)) { //passes all except b-tag!
		LeptonPointer selectedMuon = qcdMuonNonIsoSelection_->signalLepton(event);
		const MuonPointer signalMuon(boost::static_pointer_cast<Muon>(selectedMuon));
		double efficiencyCorrection = event->isRealData() ? 1. : signalMuon->getEfficiencyCorrection(true, Globals::MuonScaleFactorSystematic, event->runnumber());
		METPointer met = event->MET();
		JetCollection jets = qcdMuonNonIsoSelection_->cleanedJets(event);
		JetCollection bJets = qcdMuonNonIsoSelection_->cleanedBJets(event);
		unsigned int numberOfBjets(bJets.size());
		vector<double> bjetWeights;
		if (event->isRealData()) {
			for (unsigned int index = 0; index <= numberOfBjets; ++index) {
				if (index == numberOfBjets)
					bjetWeights.push_back(1.);
				else
					bjetWeights.push_back(0);
			}
		} else
			bjetWeights = BjetWeights(jets, numberOfBjets);
		histMan_->setCurrentJetBin(jets.size());
		histMan_->setCurrentBJetBin(numberOfBjets);

		boost::scoped_ptr<ChiSquaredBasedTopPairReconstruction> chi2Reco(
				new ChiSquaredBasedTopPairReconstruction(selectedMuon, met, jets));
		if (!chi2Reco->meetsInitialCriteria()) { //reports details on failure and skips event
			cout << chi2Reco->getDetailsOnFailure();
			return;
		}

		allSolutions = chi2Reco->getAllSolutions();
		for (unsigned int weight_Index = 0; weight_Index < bjetWeights.size(); ++weight_Index) {
			double bjetWeight = bjetWeights.at(weight_Index);
			scale_ = bjetWeight * efficiencyCorrection;
			histMan_->setCurrentBJetBin(weight_Index);
			fillHistograms("MuonPlusJets/QCD non iso mu+jets/FourJetChi2");
		}
	}
}

void MTtbarAnalyser::analyseFourJetTopMassDifferenceQCDBackground(const EventPtr event) {
	if (qcdElectronConversionSelection_->passesFullSelectionExceptLastTwoSteps(event)) { //passes all except b-tag!
		LeptonPointer selectedElectron = qcdElectronConversionSelection_->signalLepton(event);
		const ElectronPointer signalElectron(boost::static_pointer_cast<Electron>(selectedElectron));
		double efficiencyCorrection = event->isRealData() ? 1. : signalElectron->getEfficiencyCorrection(true, Globals::ElectronScaleFactorSystematic, event->runnumber());
		METPointer met = event->MET();
		JetCollection jets = qcdElectronConversionSelection_->cleanedJets(event);
		JetCollection bJets = qcdElectronConversionSelection_->cleanedBJets(event);
		unsigned int numberOfBjets(bJets.size());
		vector<double> bjetWeights;
		if (event->isRealData()) {
			for (unsigned int index = 0; index <= numberOfBjets; ++index) {
				if (index == numberOfBjets)
					bjetWeights.push_back(1.);
				else
					bjetWeights.push_back(0);
			}
		} else
			bjetWeights = BjetWeights(jets, numberOfBjets);
		histMan_->setCurrentJetBin(jets.size());
		histMan_->setCurrentBJetBin(numberOfBjets);

		boost::scoped_ptr<TopMassDifferenceTTbarReco> chi2Reco(
				new TopMassDifferenceTTbarReco(selectedElectron, met, jets));
		if (!chi2Reco->meetsInitialCriteria()) { //reports details on failure and skips event
			cout << chi2Reco->getDetailsOnFailure();
			return;
		}
		allSolutions = chi2Reco->getAllSolutions();
		for (unsigned int weight_Index = 0; weight_Index < bjetWeights.size(); ++weight_Index) {
			double bjetWeight = bjetWeights.at(weight_Index);
			scale_ = bjetWeight * efficiencyCorrection;
			histMan_->setCurrentBJetBin(weight_Index);
			fillHistograms("ElectronPlusJets/QCDConversions/FourJetTopMassDifference");
		}
	}

	if (qcdElectronNonIsoSelection_->passesFullSelectionExceptLastTwoSteps(event)) { //passes all except b-tag!
		LeptonPointer selectedElectron = qcdElectronNonIsoSelection_->signalLepton(event);
		const ElectronPointer signalElectron(boost::static_pointer_cast<Electron>(selectedElectron));
		double efficiencyCorrection = event->isRealData() ? 1. : signalElectron->getEfficiencyCorrection(true, Globals::ElectronScaleFactorSystematic, event->runnumber());
		METPointer met = event->MET();
		JetCollection jets = qcdElectronNonIsoSelection_->cleanedJets(event);
		JetCollection bJets = qcdElectronNonIsoSelection_->cleanedBJets(event);
		unsigned int numberOfBjets(bJets.size());
		vector<double> bjetWeights;
		if (event->isRealData()) {
			for (unsigned int index = 0; index <= numberOfBjets; ++index) {
				if (index == numberOfBjets)
					bjetWeights.push_back(1.);
				else
					bjetWeights.push_back(0);
			}
		} else
			bjetWeights = BjetWeights(jets, numberOfBjets);
		histMan_->setCurrentJetBin(jets.size());
		histMan_->setCurrentBJetBin(numberOfBjets);

		boost::scoped_ptr<TopMassDifferenceTTbarReco> chi2Reco(
				new TopMassDifferenceTTbarReco(selectedElectron, met, jets));
		if (!chi2Reco->meetsInitialCriteria()) { //reports details on failure and skips event
			cout << chi2Reco->getDetailsOnFailure();
			return;
		}
		allSolutions = chi2Reco->getAllSolutions();
		for (unsigned int weight_Index = 0; weight_Index < bjetWeights.size(); ++weight_Index) {
			double bjetWeight = bjetWeights.at(weight_Index);
			scale_ = bjetWeight * efficiencyCorrection;
			histMan_->setCurrentBJetBin(weight_Index);
			fillHistograms("ElectronPlusJets/QCD non iso e+jets/FourJetTopMassDifference");
		}
	}

	if (qcdElectronAntiIDSelection_->passesFullSelectionExceptLastTwoSteps(event)) { //passes all except b-tag!
		LeptonPointer selectedElectron = qcdElectronAntiIDSelection_->signalLepton(event);
		const ElectronPointer signalElectron(boost::static_pointer_cast<Electron>(selectedElectron));
		double efficiencyCorrection = event->isRealData() ? 1. : signalElectron->getEfficiencyCorrection(true, Globals::ElectronScaleFactorSystematic, event->runnumber());
		METPointer met = event->MET();
		JetCollection jets = qcdElectronAntiIDSelection_->cleanedJets(event);
		JetCollection bJets = qcdElectronAntiIDSelection_->cleanedBJets(event);
		unsigned int numberOfBjets(bJets.size());
		vector<double> bjetWeights;
		if (event->isRealData()) {
			for (unsigned int index = 0; index <= numberOfBjets; ++index) {
				if (index == numberOfBjets)
					bjetWeights.push_back(1.);
				else
					bjetWeights.push_back(0);
			}
		} else
			bjetWeights = BjetWeights(jets, numberOfBjets);
		histMan_->setCurrentJetBin(jets.size());
		histMan_->setCurrentBJetBin(numberOfBjets);

		boost::scoped_ptr<TopMassDifferenceTTbarReco> chi2Reco(
				new TopMassDifferenceTTbarReco(selectedElectron, met, jets));
		if (!chi2Reco->meetsInitialCriteria()) { //reports details on failure and skips event
			cout << chi2Reco->getDetailsOnFailure();
			return;
		}
		allSolutions = chi2Reco->getAllSolutions();
		for (unsigned int weight_Index = 0; weight_Index < bjetWeights.size(); ++weight_Index) {
			double bjetWeight = bjetWeights.at(weight_Index);
			scale_ = bjetWeight * efficiencyCorrection;
			histMan_->setCurrentBJetBin(weight_Index);
			fillHistograms("ElectronPlusJets/QCDAntiID/FourJetTopMassDifference");
		}
	}

	if (qcdMuonNonIsoSelection_->passesFullSelectionExceptLastTwoSteps(event)) { //passes all except b-tag!
		LeptonPointer selectedMuon = qcdMuonNonIsoSelection_->signalLepton(event);
		const MuonPointer signalMuon(boost::static_pointer_cast<Muon>(selectedMuon));
		double efficiencyCorrection = event->isRealData() ? 1. : signalMuon->getEfficiencyCorrection(true, Globals::MuonScaleFactorSystematic, event->runnumber());
		METPointer met = event->MET();
		JetCollection jets = qcdMuonNonIsoSelection_->cleanedJets(event);
		JetCollection bJets = qcdMuonNonIsoSelection_->cleanedBJets(event);
		unsigned int numberOfBjets(bJets.size());
		vector<double> bjetWeights;
		if (event->isRealData()) {
			for (unsigned int index = 0; index <= numberOfBjets; ++index) {
				if (index == numberOfBjets)
					bjetWeights.push_back(1.);
				else
					bjetWeights.push_back(0);
			}
		} else
			bjetWeights = BjetWeights(jets, numberOfBjets);
		histMan_->setCurrentJetBin(jets.size());
		histMan_->setCurrentBJetBin(numberOfBjets);

		boost::scoped_ptr<TopMassDifferenceTTbarReco> chi2Reco(new TopMassDifferenceTTbarReco(selectedMuon, met, jets));
		if (!chi2Reco->meetsInitialCriteria()) { //reports details on failure and skips event
			cout << chi2Reco->getDetailsOnFailure();
			return;
		}
		allSolutions = chi2Reco->getAllSolutions();
		for (unsigned int weight_Index = 0; weight_Index < bjetWeights.size(); ++weight_Index) {
			double bjetWeight = bjetWeights.at(weight_Index);
			scale_ = bjetWeight * efficiencyCorrection;
			histMan_->setCurrentBJetBin(weight_Index);
			fillHistograms("MuonPlusJets/QCD non iso mu+jets/FourJetTopMassDifference");
		}
	}
}

void MTtbarAnalyser::analyseThreeJetChi2QCDBackground(const EventPtr event) {
	if (qcdElectronConversionSelection_->passesSelectionUpToStep(event,
			TTbarEPlusJetsReferenceSelection::AtLeastThreeGoodJets)) { //passes all except b-tag!
		LeptonPointer selectedElectron = qcdElectronConversionSelection_->signalLepton(event);
		const ElectronPointer signalElectron(boost::static_pointer_cast<Electron>(selectedElectron));
		double efficiencyCorrection = event->isRealData() ? 1. : signalElectron->getEfficiencyCorrection(true, Globals::ElectronScaleFactorSystematic, event->runnumber());
		METPointer met = event->MET();
		JetCollection jets = qcdElectronConversionSelection_->cleanedJets(event);
		if (jets.size() != 3) //only consider 3 jet events
			return;
		JetCollection bJets = qcdElectronConversionSelection_->cleanedBJets(event);
		unsigned int numberOfBjets(bJets.size());
		vector<double> bjetWeights;
		if (event->isRealData()) {
			for (unsigned int index = 0; index <= numberOfBjets; ++index) {
				if (index == numberOfBjets)
					bjetWeights.push_back(1.);
				else
					bjetWeights.push_back(0);
			}
		} else
			bjetWeights = BjetWeights(jets, numberOfBjets);
		histMan_->setCurrentJetBin(jets.size());
		histMan_->setCurrentBJetBin(numberOfBjets);

		boost::scoped_ptr<ChiSquaredThreeJetsTopPairReconstruction> chi2Reco(
				new ChiSquaredThreeJetsTopPairReconstruction(selectedElectron, met, jets));
		if (!chi2Reco->meetsInitialCriteria()) { //reports details on failure and skips event
			cout << chi2Reco->getDetailsOnFailure();
			return;
		}
		allSolutions = chi2Reco->getAllSolutions();
		for (unsigned int weight_Index = 0; weight_Index < bjetWeights.size(); ++weight_Index) {
			double bjetWeight = bjetWeights.at(weight_Index);
			scale_ = bjetWeight * efficiencyCorrection;
			histMan_->setCurrentBJetBin(weight_Index);
			fillHistograms("ElectronPlusJets/QCDConversions/ThreeJetChi2");
		}
	}

	if (qcdElectronNonIsoSelection_->passesSelectionUpToStep(event,
			TTbarEPlusJetsReferenceSelection::AtLeastThreeGoodJets)) { //passes all except b-tag!
		LeptonPointer selectedElectron = qcdElectronNonIsoSelection_->signalLepton(event);
		const ElectronPointer signalElectron(boost::static_pointer_cast<Electron>(selectedElectron));
		double efficiencyCorrection = event->isRealData() ? 1. : signalElectron->getEfficiencyCorrection(true, Globals::ElectronScaleFactorSystematic, event->runnumber());
		METPointer met = event->MET();
		JetCollection jets = qcdElectronNonIsoSelection_->cleanedJets(event);
		if (jets.size() != 3) //only consider 3 jet events
			return;
		JetCollection bJets = qcdElectronNonIsoSelection_->cleanedBJets(event);
		unsigned int numberOfBjets(bJets.size());
		vector<double> bjetWeights;
		if (event->isRealData()) {
			for (unsigned int index = 0; index <= numberOfBjets; ++index) {
				if (index == numberOfBjets)
					bjetWeights.push_back(1.);
				else
					bjetWeights.push_back(0);
			}
		} else
			bjetWeights = BjetWeights(jets, numberOfBjets);
		histMan_->setCurrentJetBin(jets.size());
		histMan_->setCurrentBJetBin(numberOfBjets);

		boost::scoped_ptr<ChiSquaredThreeJetsTopPairReconstruction> chi2Reco(
				new ChiSquaredThreeJetsTopPairReconstruction(selectedElectron, met, jets));
		if (!chi2Reco->meetsInitialCriteria()) { //reports details on failure and skips event
			cout << chi2Reco->getDetailsOnFailure();
			return;
		}
		allSolutions = chi2Reco->getAllSolutions();
		for (unsigned int weight_Index = 0; weight_Index < bjetWeights.size(); ++weight_Index) {
			double bjetWeight = bjetWeights.at(weight_Index);
			scale_ = bjetWeight * efficiencyCorrection;
			histMan_->setCurrentBJetBin(weight_Index);
			fillHistograms("ElectronPlusJets/QCD non iso e+jets/ThreeJetChi2");
		}
	}

	if (qcdElectronAntiIDSelection_->passesSelectionUpToStep(event,
			TTbarEPlusJetsReferenceSelection::AtLeastThreeGoodJets)) { //passes all except b-tag!
		LeptonPointer selectedElectron = qcdElectronAntiIDSelection_->signalLepton(event);
		const ElectronPointer signalElectron(boost::static_pointer_cast<Electron>(selectedElectron));
		double efficiencyCorrection = event->isRealData() ? 1. : signalElectron->getEfficiencyCorrection(true, Globals::ElectronScaleFactorSystematic, event->runnumber());
		METPointer met = event->MET();
		JetCollection jets = qcdElectronAntiIDSelection_->cleanedJets(event);
		if (jets.size() != 3) //only consider 3 jet events
			return;
		JetCollection bJets = qcdElectronAntiIDSelection_->cleanedBJets(event);
		unsigned int numberOfBjets(bJets.size());
		vector<double> bjetWeights;
		if (event->isRealData()) {
			for (unsigned int index = 0; index <= numberOfBjets; ++index) {
				if (index == numberOfBjets)
					bjetWeights.push_back(1.);
				else
					bjetWeights.push_back(0);
			}
		} else
			bjetWeights = BjetWeights(jets, numberOfBjets);
		histMan_->setCurrentJetBin(jets.size());
		histMan_->setCurrentBJetBin(numberOfBjets);

		boost::scoped_ptr<ChiSquaredThreeJetsTopPairReconstruction> chi2Reco(
				new ChiSquaredThreeJetsTopPairReconstruction(selectedElectron, met, jets));
		if (!chi2Reco->meetsInitialCriteria()) { //reports details on failure and skips event
			cout << chi2Reco->getDetailsOnFailure();
			return;
		}
		allSolutions = chi2Reco->getAllSolutions();
		for (unsigned int weight_Index = 0; weight_Index < bjetWeights.size(); ++weight_Index) {
			double bjetWeight = bjetWeights.at(weight_Index);
			scale_ = bjetWeight * efficiencyCorrection;
			histMan_->setCurrentBJetBin(weight_Index);
			fillHistograms("ElectronPlusJets/QCDAntiID/ThreeJetChi2");
		}
	}

	if (qcdMuonNonIsoSelection_->passesSelectionUpToStep(event,
			TTbarMuPlusJetsReferenceSelection::AtLeastThreeGoodJets)) { //passes all except b-tag!
		LeptonPointer selectedMuon = qcdMuonNonIsoSelection_->signalLepton(event);
		const MuonPointer signalMuon(boost::static_pointer_cast<Muon>(selectedMuon));
				double efficiencyCorrection = event->isRealData() ? 1. : signalMuon->getEfficiencyCorrection(true, Globals::MuonScaleFactorSystematic, event->runnumber());
		METPointer met = event->MET();
		JetCollection jets = qcdMuonNonIsoSelection_->cleanedJets(event);
		if (jets.size() != 3) //only consider 3 jet events
			return;
		JetCollection bJets = qcdMuonNonIsoSelection_->cleanedBJets(event);
		unsigned int numberOfBjets(bJets.size());
		vector<double> bjetWeights;
		if (event->isRealData()) {
			for (unsigned int index = 0; index <= numberOfBjets; ++index) {
				if (index == numberOfBjets)
					bjetWeights.push_back(1.);
				else
					bjetWeights.push_back(0);
			}
		} else
			bjetWeights = BjetWeights(jets, numberOfBjets);
		histMan_->setCurrentJetBin(jets.size());
		histMan_->setCurrentBJetBin(numberOfBjets);

		boost::scoped_ptr<ChiSquaredThreeJetsTopPairReconstruction> chi2Reco(
				new ChiSquaredThreeJetsTopPairReconstruction(selectedMuon, met, jets));
		if (!chi2Reco->meetsInitialCriteria()) { //reports details on failure and skips event
			cout << chi2Reco->getDetailsOnFailure();
			return;
		}
		allSolutions = chi2Reco->getAllSolutions();
		for (unsigned int weight_Index = 0; weight_Index < bjetWeights.size(); ++weight_Index) {
			double bjetWeight = bjetWeights.at(weight_Index);
			scale_ = bjetWeight * efficiencyCorrection;
			histMan_->setCurrentBJetBin(weight_Index);
			fillHistograms("MuonPlusJets/QCD non iso mu+jets/ThreeJetChi2");
		}
	}
}

void MTtbarAnalyser::fillHistograms(std::string subcollection, std::string suffix) {
	histMan_->setCurrentHistogramFolder("MttbarAnalysis/" + subcollection);

	unsigned short numberOfSolutions = allSolutions.size();
	for (unsigned short index = 0; index < numberOfSolutions; ++index) {
		TtbarHypothesisPointer currentSolution = allSolutions.at(index);
		const ParticlePointer resonance = currentSolution->resonance;
		double mttbar = resonance->mass();
		double ttbarPt = resonance->pt();
		double m3 = currentSolution->M3();
		double hadronicTopMass = currentSolution->hadronicTop->mass();
		double leptonicTopMass = currentSolution->leptonicTop->mass();
		double hadronicWMass = currentSolution->hadronicW->mass();
		double leptonicWMass = currentSolution->leptonicW->mass();
		double disc = currentSolution->discriminator;

		if (index == 0) {
			histMan_->H1D_BJetBinned("mttbar" + suffix)->Fill(mttbar, weight_ * scale_);
			histMan_->H1D_BJetBinned("ttbar_pt" + suffix)->Fill(ttbarPt, weight_ * scale_);
			histMan_->H1D_BJetBinned("m3" + suffix)->Fill(m3, weight_ * scale_);
			histMan_->H1D_BJetBinned("leptonicTopMass" + suffix)->Fill(leptonicTopMass, weight_ * scale_);
			histMan_->H1D_BJetBinned("hadronicTopMass" + suffix)->Fill(hadronicTopMass, weight_ * scale_);
			histMan_->H1D_BJetBinned("allTopMasses" + suffix)->Fill(leptonicTopMass, weight_ * scale_);
			histMan_->H1D_BJetBinned("allTopMasses" + suffix)->Fill(hadronicTopMass, weight_ * scale_);

			histMan_->H1D_BJetBinned("leptonicWMass" + suffix)->Fill(leptonicWMass, weight_ * scale_);
			histMan_->H1D_BJetBinned("hadronicWMass" + suffix)->Fill(hadronicWMass, weight_ * scale_);

			histMan_->H1D_BJetBinned("discriminant" + suffix)->Fill(disc, weight_ * scale_);
		}

		double normalisedWeight = weight_ * scale_ / numberOfSolutions;
		histMan_->H1D_BJetBinned("mttbar_allSolutions" + suffix)->Fill(mttbar, normalisedWeight);
		histMan_->H1D_BJetBinned("ttbar_pt_allSolutions" + suffix)->Fill(ttbarPt, normalisedWeight);
		histMan_->H1D_BJetBinned("m3_allSolutions" + suffix)->Fill(m3, normalisedWeight);
		histMan_->H1D_BJetBinned("leptonicTopMass_allSolutions" + suffix)->Fill(leptonicTopMass, normalisedWeight);
		histMan_->H1D_BJetBinned("hadronicTopMass_allSolutions" + suffix)->Fill(hadronicTopMass, normalisedWeight);
		histMan_->H1D_BJetBinned("allTopMasses_allSolutions" + suffix)->Fill(leptonicTopMass, normalisedWeight);
		histMan_->H1D_BJetBinned("allTopMasses_allSolutions" + suffix)->Fill(hadronicTopMass, normalisedWeight);
		histMan_->H1D_BJetBinned("leptonicWMass_allSolutions" + suffix)->Fill(leptonicWMass, normalisedWeight);
		histMan_->H1D_BJetBinned("hadronicWMass_allSolutions" + suffix)->Fill(hadronicWMass, normalisedWeight);

		histMan_->H1D_BJetBinned("discriminant_allSolutions" + suffix)->Fill(disc, normalisedWeight);

	}
}

void MTtbarAnalyser::createHistograms() {
	histMan_->setCurrentHistogramFolder("MttbarAnalysis");

	createHistogramsFor("MuonPlusJets/Ref selection/FourJetChi2");
	createHistogramsFor("MuonPlusJets/Ref selection/ThreeJetChi2");
	createHistogramsFor("MuonPlusJets/Ref selection/FourJetTopMassDifference");
//	createHistogramsFor("MuonPlusJets/FourJetChi2MC"); //for input values!
	createHistogramsFor("MuonPlusJets/QCD non iso mu+jets/FourJetChi2");
	createHistogramsFor("MuonPlusJets/QCD non iso mu+jets/ThreeJetChi2");
	createHistogramsFor("MuonPlusJets/QCD non iso mu+jets/FourJetTopMassDifference");

	createHistogramsFor("ElectronPlusJets/Ref selection/FourJetChi2");
	createHistogramsFor("ElectronPlusJets/Ref selection/ThreeJetChi2");
	createHistogramsFor("ElectronPlusJets/Ref selection/FourJetTopMassDifference");
//		createHistogramsFor("ElectronPlusJets/FourJetChi2MC"); //for input values!
	createHistogramsFor("ElectronPlusJets/QCDConversions/FourJetChi2");
	createHistogramsFor("ElectronPlusJets/QCDConversions/ThreeJetChi2");
	createHistogramsFor("ElectronPlusJets/QCDConversions/FourJetTopMassDifference");
	createHistogramsFor("ElectronPlusJets/QCD non iso e+jets/FourJetChi2");
	createHistogramsFor("ElectronPlusJets/QCD non iso e+jets/ThreeJetChi2");
	createHistogramsFor("ElectronPlusJets/QCD non iso e+jets/FourJetTopMassDifference");
	createHistogramsFor("ElectronPlusJets/QCDAntiID/FourJetChi2");
	createHistogramsFor("ElectronPlusJets/QCDAntiID/ThreeJetChi2");
	createHistogramsFor("ElectronPlusJets/QCDAntiID/FourJetTopMassDifference");

}

void MTtbarAnalyser::createHistogramsFor(std::string collection) {
	boost::array<std::string, 4> histTypes = { { "", "_allSolutions", } };

	histMan_->setCurrentHistogramFolder("MttbarAnalysis/" + collection);

	for (unsigned short index = 0; index < histTypes.size(); ++index) {
		std::string suffix = histTypes.at(index);
		histMan_->addH1D_BJetBinned("mttbar" + suffix, "m(t#bar{t});m(t#bar{t}) [GeV]; events/1 GeV", 5000, 0, 5000);

		histMan_->addH1D_BJetBinned("leptonicTopMass" + suffix, "leptonic Top Mass; m(t_{lep}) [GeV]; events/1 GeV",
				1000, 0, 1000);
		histMan_->addH1D_BJetBinned("hadronicTopMass" + suffix, "hadronic Top Mass; m(t_{had}) [GeV]; events/1 GeV",
				1000, 0, 1000);
		histMan_->addH1D_BJetBinned("allTopMasses" + suffix, "all Top Masses;m(t) [GeV]; events/1 GeV", 1000, 0, 1000);
		histMan_->addH1D_BJetBinned("m3" + suffix, "m3;m(jjj) [GeV]; events/1 GeV", 5000, 0, 5000);
		histMan_->addH1D_BJetBinned("ttbar_pt" + suffix, "p_{T,t#bar{t}};p_{T,t#bar{t}} [GeV]; events/1 GeV", 1000, 0,
				1000);
		histMan_->addH1D_BJetBinned("leptonicWMass" + suffix, "leptonic W mass; m(W_{lep}) [GeV]; events/1 GeV", 500, 0,
				500);
		histMan_->addH1D_BJetBinned("hadronicWMass" + suffix, "hadronic W mass; m(W_{had}) [GeV]; events/1 GeV", 500, 0,
				500);
		histMan_->addH1D_BJetBinned("discriminant" + suffix, "discriminant;discriminant; events/0.1", 1000, 0, 100);

	}

}

MTtbarAnalyser::MTtbarAnalyser(boost::shared_ptr<HistogramManager> histMan, std::string histogramFolder) :
		BasicAnalyser(histMan, histogramFolder), //
		currentType("ElectronPlusJets"), //
		allSolutions(), //
		topElectronPlusJetsRefSelection_(new TopPairEPlusJetsReferenceSelection()), //
		topMuonPlusJetsRefSelection_(new TopPairMuPlusJetsReferenceSelection()), //
		qcdElectronAntiIDSelection_(new QCDAntiIDEPlusJetsSelection()), //
		qcdElectronConversionSelection_(new QCDConversionsSelection()), //
		qcdElectronNonIsoSelection_(new QCDNonIsolatedElectronSelection()), //
		qcdMuonNonIsoSelection_(new QCDNonIsolatedMuonSelection()) {

	qcdElectronAntiIDSelection_->useMVAID(true);
}

MTtbarAnalyser::~MTtbarAnalyser() {
}

} /* namespace BAT */