// Copyright (C) 2009  Davis E. King (davis@dlib.net)
// License: Boost Software License   See LICENSE.txt for the full license.

#include "../tester.h"
#include <dlib/matrix.h>

#ifndef DLIB_USE_BLAS
#error "BLAS bindings must be used for this test to make any sense"
#endif

namespace dlib
{
    namespace blas_bindings
    {
        // This is a little screwy.  This function is used inside the BLAS
        // bindings to count how many times each of the BLAS functions get called.
#ifdef DLIB_TEST_BLAS_BINDINGS
        int& counter_axpy() { static int counter = 0; return counter; }
        int& counter_scal() { static int counter = 0; return counter; }
#endif

    }
}

namespace
{
    using namespace test;
    using namespace std;
    // Declare the logger we will use in this test.  The name of the logger
    // should start with "test."
    dlib::logger dlog("test.scal_axpy");

    class blas_bindings_scal_axpy_tester : public tester
    {
    public:
        blas_bindings_scal_axpy_tester (
        ) :
            tester (
                "test_scal_axpy", // the command line argument name for this test
                "Run tests for DOT routines.", // the command line argument description
                0                     // the number of command line arguments for this test
            )
        {}

        template <typename matrix_type, typename cv_type, typename rv_type>
        void test_scal_axpy_stuff(
            matrix_type& m,
            rv_type& rv,
            cv_type& cv
        ) const
        {
            using namespace dlib;
            using namespace dlib::blas_bindings;

            rv_type rv2 = rv;
            cv_type cv2 = cv;
            matrix_type m2 = m;
            typedef typename matrix_type::type scalar_type;
            scalar_type val;

            counter_scal() = 0;
            m = 5*m;
            DLIB_TEST(counter_scal() == 1);

            counter_scal() = 0;
            rv = 5*rv;
            DLIB_TEST(counter_scal() == 1);

            counter_scal() = 0;
            rv = 5*rv;
            DLIB_TEST(counter_scal() == 1);

            counter_axpy() = 0;
            m2 += 5*m;
            DLIB_TEST(counter_axpy() == 1);

            counter_axpy() = 0;
            rv2 += 5*rv;
            DLIB_TEST(counter_axpy() == 1);

            counter_axpy() = 0;
            rv2 += 5*rv;
            DLIB_TEST(counter_axpy() == 1);

            counter_scal() = 0;
            m = m*5;
            DLIB_TEST(counter_scal() == 1);

            counter_scal() = 0;
            rv = rv*5;
            DLIB_TEST(counter_scal() == 1);

            counter_scal() = 0;
            cv = cv*5;
            DLIB_TEST(counter_scal() == 1);

            counter_axpy() = 0;
            m2 += m*5;
            DLIB_TEST(counter_axpy() == 1);

            counter_axpy() = 0;
            rv2 += rv*5;
            DLIB_TEST(counter_axpy() == 1);

            counter_axpy() = 0;
            cv2 += cv*5;
            DLIB_TEST(counter_axpy() == 1);

            counter_axpy() = 0;
            m2 = m2 + m*5;
            DLIB_TEST(counter_axpy() == 1);

            counter_axpy() = 0;
            rv2 = rv2 + rv*5;
            DLIB_TEST(counter_axpy() == 1);

            counter_axpy() = 0;
            cv2 = cv2 + cv*5;
            DLIB_TEST(counter_axpy() == 1);

            counter_axpy() = 0;
            cv2 = 1;
            cv = 1;
            cv2 = 2*cv2 + cv*5;
            DLIB_TEST(counter_axpy() == 1);
            DLIB_TEST(max(abs(cv2 - 7)) == 0);

            counter_axpy() = 0;
            rv2 = 1;
            rv = 1;
            rv2 = 2*rv2 + rv*5;
            DLIB_TEST(counter_axpy() == 1);
            DLIB_TEST(max(abs(rv2 - 7)) == 0);

            counter_axpy() = 0;
            m2 = 1;
            m = 1;
            m2 = 2*m2 + m*5;
            DLIB_TEST(counter_axpy() == 1);
            DLIB_TEST(max(abs(m2 - 7)) == 0);
        }

        void perform_test (
        )
        {
            using namespace dlib;
            typedef dlib::memory_manager<char>::kernel_1a mm;

            dlog << dlib::LINFO << "test double";
            {
                matrix<double> m = randm(4,4);
                matrix<double,1,0> rv = randm(1,4);
                matrix<double,0,1> cv = randm(4,1);
                test_scal_axpy_stuff(m,rv,cv);
            }

            dlog << dlib::LINFO << "test float";
            {
                matrix<float> m = matrix_cast<float>(randm(4,4));
                matrix<float,1,0> rv = matrix_cast<float>(randm(1,4));
                matrix<float,0,1> cv = matrix_cast<float>(randm(4,1));
                test_scal_axpy_stuff(m,rv,cv);
            }

            dlog << dlib::LINFO << "test complex<double>";
            {
                matrix<complex<double> > m = complex_matrix(randm(4,4), randm(4,4));
                matrix<complex<double>,1,0> rv = complex_matrix(randm(1,4), randm(1,4));
                matrix<complex<double>,0,1> cv = complex_matrix(randm(4,1), randm(4,1));
                test_scal_axpy_stuff(m,rv,cv);
            }

            dlog << dlib::LINFO << "test complex<float>";
            {
                matrix<complex<float> > m = matrix_cast<complex<float> >(complex_matrix(randm(4,4), randm(4,4)));
                matrix<complex<float>,1,0> rv = matrix_cast<complex<float> >(complex_matrix(randm(1,4), randm(1,4)));
                matrix<complex<float>,0,1> cv = matrix_cast<complex<float> >(complex_matrix(randm(4,1), randm(4,1)));
                test_scal_axpy_stuff(m,rv,cv);
            }

            dlog << dlib::LINFO << "test double, column major";
            {
                matrix<double,0,0,mm,column_major_layout> m = randm(4,4);
                matrix<double,1,0,mm,column_major_layout> rv = randm(1,4);
                matrix<double,0,1,mm,column_major_layout> cv = randm(4,1);
                test_scal_axpy_stuff(m,rv,cv);
            }

            dlog << dlib::LINFO << "test float, column major";
            {
                matrix<float,0,0,mm,column_major_layout> m = matrix_cast<float>(randm(4,4));
                matrix<float,1,0,mm,column_major_layout> rv = matrix_cast<float>(randm(1,4));
                matrix<float,0,1,mm,column_major_layout> cv = matrix_cast<float>(randm(4,1));
                test_scal_axpy_stuff(m,rv,cv);
            }

            dlog << dlib::LINFO << "test complex<double>, column major";
            {
                matrix<complex<double>,0,0,mm,column_major_layout > m = complex_matrix(randm(4,4), randm(4,4));
                matrix<complex<double>,1,0,mm,column_major_layout> rv = complex_matrix(randm(1,4), randm(1,4));
                matrix<complex<double>,0,1,mm,column_major_layout> cv = complex_matrix(randm(4,1), randm(4,1));
                test_scal_axpy_stuff(m,rv,cv);
            }

            dlog << dlib::LINFO << "test complex<float>, column major";
            {
                matrix<complex<float>,0,0,mm,column_major_layout > m = matrix_cast<complex<float> >(complex_matrix(randm(4,4), randm(4,4)));
                matrix<complex<float>,1,0,mm,column_major_layout> rv = matrix_cast<complex<float> >(complex_matrix(randm(1,4), randm(1,4)));
                matrix<complex<float>,0,1,mm,column_major_layout> cv = matrix_cast<complex<float> >(complex_matrix(randm(4,1), randm(4,1)));
                test_scal_axpy_stuff(m,rv,cv);
            }

            print_spinner();
        }
    };

    blas_bindings_scal_axpy_tester a;

}


/* Vocabulary:
 * RNG = Random Number Generator/Generation
 * "Die" is the singular, "dice" is the plural
*/
#include <iostream>
#include <sstream>
#include <chrono>
#include <thread>
#include <boost/random.hpp>
#include <boost/random/random_device.hpp>

#include "dice.hpp"

namespace dice_roller {

  int int_input(const std::string& prompt, const std::string& retry_prompt) {
    int num;
    // If provided, display initial prompt
    std::cout << (prompt.empty() ? retry_prompt : prompt);
    // Loop user prompts until they get it right
    // TODO: Consider refactoring to avoid while (true)
    while (true) {
      std::string input;
      getline(std::cin, input);
      std::stringstream check_stream(input);
      // Accept if it's a natural
      if (check_stream >> num && num > 0)
        break;
      std::cout << "Invalid input -- try again!" << std::endl;
      std::cout << retry_prompt;
    }
    std::cout << std::endl;
    return num;
  }

  void wait_time_adjustment(Options& options) {
    // Ensure wait_time works "as intended"
    if (options.wait_time < 0) {
      std::cout << "WARNING -- Given wait time was invalid.\n"
                << "Reverting to default (750 ms)." << std::endl;
      options.wait_time = 750;
    }
  }

  int roll_dice(Options& options, int dice, int sides) {
    wait_time_adjustment(options);
    // If not given in function call:
    // Determine how many n-sided dice to roll by input
    if (dice < 1)
      dice = int_input("Amount of dice to roll: ", "Dice count: ");
    if (sides < 1)
      sides = int_input("Sides on each die: ", "Die sides: ");

    // Create a generator based on OS-specific non-deterministic RNG
    boost::random::random_device engine;
    // Restrict outputs to a uniform int distribution based on the dice
    boost::random::uniform_int_distribution<int> dist(1, sides);

    int landing;       // Single die's value
    int roll_sum = 0;  // All dice landings
    // Iterate from 1 to allow natural counts
    for (int i = 1; i <= dice; ++i) {
      landing = dist(engine);
      if (options.verbose && dice > 1)
        verbosity(options, i, landing);
      roll_sum += landing;
    }
    // Return the total ndm roll value
    return roll_sum;
  }

  void verbosity(Options &options, int which_die, int landing) {
    std::cout << "Die " << which_die << ": " << landing << std::endl;
    if (options.slow)
      std::this_thread::sleep_for(std::chrono::milliseconds(options.wait_time));
  }
}


#include <fstream>
#include <functional>

#include <boost/test/unit_test.hpp>
#include <turtle/mock.hpp>

#include <common_defs.h>
#include <Describer.h>

BOOST_AUTO_TEST_SUITE(Describers)

BOOST_AUTO_TEST_SUITE(Dummy)
BOOST_AUTO_TEST_CASE(SimplyExtractsDoublesFromStream)
{
    std::stringstream s;
    s << "1;2.3;6.66;5.44;";
    DummyDescriber desc;
    desc.setInputStream(&s);

    auto obj = desc.generateObjectDescription();
    BOOST_REQUIRE_EQUAL(4, obj.size());

    BOOST_CHECK_CLOSE(1, obj[0], 0.1);
    BOOST_CHECK_CLOSE(2.3, obj[1], 0.1);
    BOOST_CHECK_CLOSE(6.66, obj[2], 0.1);
    BOOST_CHECK_CLOSE(5.44, obj[3], 0.1);
}
BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE(MPEG7)

BOOST_AUTO_TEST_CASE(MPEG7DescriberExistsAndRequiresStream)
{
	const double MAX_PERCENTAGE_DIFFERENCE = 0.01;
	std::ifstream is("../../../InstrumentRecognizerTester/test_files/mp7/bassoon/1953_bassoon_034_1_6_1.mp7");
	BOOST_REQUIRE(is.is_open());

	MPEG7Describer describer(&is);
	ObjectDescription description = describer.generateObjectDescription();

	ObjectDescription expected(7);
	expected[0] = -1.2067; // LogAttackTime
	expected[1] = 256.85; // SpectralCentroid
	expected[2] = 0.074510; // TemporalCentroid
	expected[3] = 619.47; // HarmonicSpectralCentroid
	expected[4] = 0.18898; // HarmonicSpectralDeviation
	expected[5] = 0.99624; // HarmonicSpectralSpread
	expected[6] = 0.15982; //HarmonicSpectralVariation

	BOOST_REQUIRE_EQUAL(description.size(), expected.size());

	for (std::size_t i = 0; i < expected.size(); i++)
	{
//		BOOST_CHECK_CLOSE(description.at(i).real(), expected.at(i).real(), MAX_PERCENTAGE_DIFFERENCE);
//		BOOST_CHECK_CLOSE(description.at(i).imag(), expected.at(i).imag(), MAX_PERCENTAGE_DIFFERENCE);
		BOOST_CHECK_CLOSE(description.at(i), expected.at(i), MAX_PERCENTAGE_DIFFERENCE);
	}
}

BOOST_AUTO_TEST_CASE(MPEG7DetailedDescriberExistsAndRequiresStream)
{
	const double MAX_PERCENTAGE_DIFFERENCE = 0.1;
	std::ifstream is("../../../InstrumentRecognizerTester/test_files/mp7/bassoon/1953_bassoon_034_1_6_1.mp7");
	BOOST_REQUIRE(is.is_open());

	MPEG7DetailedDescriber describer(&is);
	ObjectDescription description = describer.generateObjectDescription();

	ObjectDescription expected(13);
	expected[0] = -1.2067; // LogAttackTime
	expected[1] = 256.85; // SpectralCentroid
	expected[2] = 0.074510; // TemporalCentroid
	expected[3] = 619.47; // HarmonicSpectralCentroid
	expected[4] = 0.18898; // HarmonicSpectralDeviation
	expected[5] = 0.99624; // HarmonicSpectralSpread
	expected[6] = 0.15982; //HarmonicSpectralVariation
	expected[7] = 86.6904; //average FundamentalFrequency
	expected[8] = 54.7215; //FundamentalFrequency variation
	expected[9] = 0.55203; //average AudioHarmonicity
	expected[10] = 0.19118; //AudioHarmonicity variation
	expected[11] = 5851.97; //average UpperLimitOfHarmonicity
	expected[12] = 6967.366; //UpperLimitOfHarmonicity variation

	BOOST_REQUIRE_EQUAL(description.size(), expected.size());
	for (std::size_t i = 0; i < expected.size(); i++)
	{
//		BOOST_CHECK_CLOSE(description.at(i).real(), expected.at(i).real(), MAX_PERCENTAGE_DIFFERENCE);
//		BOOST_CHECK_CLOSE(description.at(i).imag(), expected.at(i).imag(), MAX_PERCENTAGE_DIFFERENCE);
		BOOST_CHECK_CLOSE(description.at(i), expected.at(i), MAX_PERCENTAGE_DIFFERENCE);
	}
}

BOOST_AUTO_TEST_CASE(ViolinHandledCorrectly)
{
	const double MAX_PERCENTAGE_DIFFERENCE = 0.01;
	std::ifstream is("../../../InstrumentRecognizerTester/test_files/mp7/viola/1002_viola_055_3_10_107.mp7");
	BOOST_REQUIRE(is.is_open());

	MPEG7Describer describer(&is);
	ObjectDescription description = describer.generateObjectDescription();

	ObjectDescription expected(7);
	expected[0] = -1.198057;
	expected[1] = 198.7499;
	expected[2] = 0.2377094;
	expected[3] = 1045.8944;
	expected[4] = 0.19090895;
	expected[5] = 0.6714315;
	expected[6] = 0.19529179;

	for(std::size_t i = 0; i < expected.size(); i++)
	{
//		BOOST_CHECK_CLOSE(description.at(i).real(), expected.at(i).real(), MAX_PERCENTAGE_DIFFERENCE);
//		BOOST_CHECK_CLOSE(description.at(i).imag(), expected.at(i).imag(), MAX_PERCENTAGE_DIFFERENCE);
		BOOST_CHECK_CLOSE(description.at(i), expected.at(i), MAX_PERCENTAGE_DIFFERENCE);
	}
}

BOOST_AUTO_TEST_CASE(MPEG7DescriberThrowsOnTXTInput)
{
	std::ifstream is("../../../InstrumentRecognizerTester/test_files/single_file/one.txt");
	BOOST_REQUIRE(is.is_open());

	MPEG7Describer describer(&is);
	BOOST_CHECK_THROW(describer.generateObjectDescription(), MPEG7Describer::InputParsingError);
}

BOOST_AUTO_TEST_CASE(MPEG7DescriberThrowsOnInputWithoutLogAttackTimeType)
{
	std::ifstream is("../../../InstrumentRecognizerTester/test_files/mp7_modified/bassoon/1953_bassoon_034_1_6_1_modified.mp7");
	BOOST_REQUIRE(is.is_open());

	MPEG7Describer describer(&is);
	BOOST_CHECK_THROW(describer.generateObjectDescription(), MPEG7Describer::RequiredDescriptorNotFound);
}

BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()


//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2012   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#include <nt2/combinatorial/include/functions/factor.hpp>
#include <nt2/include/functions/cons.hpp>
#include <nt2/include/functions/two.hpp>
#include <nt2/include/constants/nbmantissabits.hpp>
#include <nt2/include/constants/maxflint.hpp>
#include <nt2/sdk/unit/tests/relation.hpp>
#include <nt2/sdk/unit/tests/exceptions.hpp>
#include <nt2/sdk/unit/tests/type_expr.hpp>
#include <nt2/sdk/unit/module.hpp>
#include <nt2/table.hpp>
#include <boost/simd/sdk/config.hpp>

NT2_TEST_CASE_TPL ( factor_integer__1_0, NT2_INTEGRAL_TYPES)
{

  using nt2::factor;
  using nt2::tag::factor_;

  T p[] = {3, 5};
  nt2::table<T> myp = factor(T(15));
  // specific values tests

  for(size_t i=1; i <= nt2::length(myp); ++i)
    {
      NT2_TEST_EQUAL(p[i-1], myp(i));
    }
  myp = factor(T(15*3*2));
  // specific values tests
  T p1[] = {2, 3, 3, 5};
  NT2_DISPLAY(myp);

  for(size_t i=1; i <= nt2::length(myp); ++i)
    {
      std::cout << i << std::endl;
      NT2_TEST_EQUAL(p1[i-1], myp(i));
    }

}

NT2_TEST_CASE_TPL ( factor_integer__2_0, (int8_t))
{
  using nt2::factor;
  using nt2::tag::factor_;

 NT2_TEST_ASSERT(factor(T(15*3*2*2)));

}

NT2_TEST_CASE_TPL ( factor_real, NT2_REAL_TYPES)
{
  using nt2::factor;
  using nt2::tag::factor_;
  nt2::table<T> myp = nt2::cons(T(2), T(2), T(3), T(3), T(5));
  NT2_TEST_EQUAL(factor(T(15*3*2*2)), myp);
  NT2_TEST_EQUAL(factor(nt2::Maxflint<T>()), nt2::two(1, nt2::Nbmantissabits<T>()+1, boost::dispatch::meta::as_<T>()));
}


#include <boost/python.hpp>
#include <boost/python/module.hpp>
#include <opengm/python/opengmpython.hxx>
#include <opengm/python/converter.hxx>
#include <opengm/python/numpyview.hxx>

#define DefaultErrorFn DefaultErrorFn_TrwsExternalB
#include "helper.hxx"

namespace bp = boost::python;
namespace op = opengm::python;
namespace ol = opengm::learning;

namespace opengm{

    template<class PARAM>
    PARAM * pyGridSearchParamConstructor(
        op::NumpyView<double> lowerBound,
        op::NumpyView<double> upperBound,
        op::NumpyView<size_t> nTestPoints
    ){
        PARAM * p  = new PARAM();
        p->parameterUpperbound_.assign(lowerBound.begin(), lowerBound.end());
        p->parameterLowerbound_.assign(upperBound.begin(), upperBound.end());
        p->testingPoints_.assign(nTestPoints.begin(), nTestPoints.end());
        return p;
    }

    template<class L >
    L * pyGridSearchConstructor(
        typename L::DatasetType & dataset,
        const typename L::Parameter & param
    ){
        L * l  = new L(dataset, param);
        return l;
    }

    template<class DATASET>
    void export_grid_search_learner(const std::string & clsName){
        typedef learning::GridSearchLearner<DATASET> PyLearner;
        typedef typename PyLearner::Parameter PyLearnerParam;

        const std::string paramClsName = clsName + std::string("Parameter");

        bp::class_<PyLearnerParam>(paramClsName.c_str(), bp::init<>())
            .def("__init__", make_constructor(&pyGridSearchParamConstructor<PyLearnerParam> ,boost::python::default_call_policies()))
        ;

        bp::class_<PyLearner>( clsName.c_str(), bp::no_init )
        .def("__init__", make_constructor(&pyGridSearchConstructor<PyLearner> ,boost::python::default_call_policies()))
        .def(LearnerInferenceSuite<PyLearner>())
        ;
    }

    template void
    export_grid_search_learner<op::GmAdderFlexibleLossDataset> (const std::string& className);

    //template void
    //export_grid_search_learner<op::GmAdderGeneralizedHammingLossDataset> (const std::string& className);
}


/**************************************************************************
 * Copyright (c) 2017-2019 by the mfmg authors                            *
 * All rights reserved.                                                   *
 *                                                                        *
 * This file is part of the mfmg library. mfmg is distributed under a BSD *
 * 3-clause license. For the licensing terms see the LICENSE file in the  *
 * top-level directory                                                    *
 *                                                                        *
 * SPDX-License-Identifier: BSD-3-Clause                                  *
 *************************************************************************/

#ifndef AMGE_HPP
#define AMGE_HPP

#include <deal.II/base/mpi.h>
#include <deal.II/dofs/dof_handler.h>
#include <deal.II/lac/la_parallel_vector.h>
#include <deal.II/lac/sparse_matrix.h>
#include <deal.II/lac/trilinos_sparse_matrix.h>
#include <deal.II/lac/trilinos_sparsity_pattern.h>

#include <boost/property_tree/ptree.hpp>

#include <array>
#include <map>
#include <string>

namespace mfmg
{
template <int dim, typename VectorType>
class AMGe
{
public:
  AMGe(MPI_Comm comm, dealii::DoFHandler<dim> const &dof_handler);

  /**
   * Flag cells to create agglomerates. This function returns the local number
   * of agglomerates that have been created.
   */
  unsigned int
  build_agglomerates(boost::property_tree::ptree const &ptree) const;

  /**
   * Return the interior agglomerates (agglomerates made of cells at the
   * boundary of the base agglomerates) and the halo agglomerates (agglomerates
   * made of halo cells of the base agglomerates). The unsigned int is
   * correspond to the active_cell_index.
   */
  std::pair<std::vector<std::vector<unsigned int>>,
            std::vector<std::vector<unsigned int>>>
  build_boundary_agglomerates() const;

  /**
   * Create a Triangulation \p agglomerate_triangulation associated with an
   * agglomerate of a given \p agglomerate_id and a map that matches cells in
   * the local triangulation with cells in the global triangulation.
   */
  void build_agglomerate_triangulation(
      unsigned int agglomerate_id,
      dealii::Triangulation<dim> &agglomerate_triangulation,
      std::map<typename dealii::Triangulation<dim>::active_cell_iterator,
               typename dealii::DoFHandler<dim>::active_cell_iterator>
          &agglomerate_to_global_tria_map) const;

  /**
   * Create a Triangulation \p agglomerate_triangulation associated with an
   * agglomerate formed of a given vector of cell indices \p cell_index and a
   * map that matches cells in the local triangulation with cells in the global
   * triangulation.
   */
  void build_agglomerate_triangulation(
      std::vector<unsigned int> const &cell_index,
      dealii::Triangulation<dim> &agglomerate_triangulation,
      std::map<typename dealii::Triangulation<dim>::active_cell_iterator,
               typename dealii::DoFHandler<dim>::active_cell_iterator>
          &agglomerate_to_global_tria_map) const;

  /**
   * Compute the map between the dof indices of the local DoFHandler and the
   * dof indices of the global DoFHandler.
   */
  std::vector<dealii::types::global_dof_index> compute_dof_index_map(
      std::map<typename dealii::Triangulation<dim>::active_cell_iterator,
               typename dealii::DoFHandler<dim>::active_cell_iterator> const
          &patch_to_global_map,
      dealii::DoFHandler<dim> const &agglomerate_dof_handler) const;

  /**
   * Output the mesh and the agglomerate ids.
   */
  void output(std::string const &filename) const;

  void compute_restriction_sparse_matrix(
      std::vector<dealii::Vector<typename VectorType::value_type>> const
          &eigenvectors,
      std::vector<std::vector<typename VectorType::value_type>> const
          &diag_elements,
      std::vector<std::vector<dealii::types::global_dof_index>> const
          &dof_indices_maps,
      std::vector<unsigned int> const &n_local_eigenvectors,
      dealii::LinearAlgebra::distributed::Vector<
          typename VectorType::value_type> const
          &locally_relevant_global_diag_dev,
      dealii::TrilinosWrappers::SparseMatrix &restriction_sparse_matrix) const;

  void compute_restriction_sparse_matrix(
      std::vector<dealii::Vector<typename VectorType::value_type>> const
          &eigenvectors,
      std::vector<std::vector<typename VectorType::value_type>> const
          &diag_elements,
      std::vector<std::vector<dealii::types::global_dof_index>> const
          &dof_indices_maps,
      std::vector<unsigned int> const &n_local_eigenvectors,
      dealii::LinearAlgebra::distributed::Vector<
          typename VectorType::value_type> const
          &locally_relevant_global_diag_dev,
      std::shared_ptr<dealii::TrilinosWrappers::SparseMatrix>
          restriction_sparse_matrix,
      std::unique_ptr<dealii::TrilinosWrappers::SparseMatrix>
          &eigenvector_sparse_matrix,
      std::unique_ptr<dealii::TrilinosWrappers::SparseMatrix>
          &delta_eigenvector_matrix) const;

protected:
  MPI_Comm _comm;
  dealii::DoFHandler<dim> const &_dof_handler;

private:
  /**
   * Flag cells to create agglomerates. The desired size of the agglomerates is
   * given by \p agglomerate_dim. This function returns the local number of
   * agglomerates that have been created.
   */
  unsigned int build_agglomerates_block(
      std::array<unsigned int, dim> const &agglomerate_dim) const;

  /**
   * Flag cells to create agglomerates. \p partitioner_type is the partitioner
   * used (zoltan or metis) and \p n_agglomerates is the local number of
   * agglomerates that we would like to have. This function returns the local
   * number of agglomerates that have been created.
   */
  unsigned int
  build_agglomerates_partitioner(std::string const &partitioner_type,
                                 unsigned int n_agglomerates) const;

  dealii::TrilinosWrappers::SparsityPattern
  compute_restriction_sparsity_pattern(
      std::vector<dealii::Vector<double>> const &eigenvectors,
      std::vector<std::vector<dealii::types::global_dof_index>> const
          &dof_indices_maps,
      std::vector<unsigned int> const &n_local_eigenvectors) const;

  /**
   * This function contains the implementation that is common between the other
   * public functions with the same name. The input is a vector of active cell
   * iterator \p agglomerate.
   */
  void build_agglomerate_triangulation(
      std::vector<typename dealii::DoFHandler<dim>::active_cell_iterator> const
          &agglomerate,
      dealii::Triangulation<dim> &agglomerate_triangulation,
      std::map<typename dealii::Triangulation<dim>::active_cell_iterator,
               typename dealii::DoFHandler<dim>::active_cell_iterator>
          &agglomerate_to_global_tria_map) const;

  mutable unsigned int _n_agglomerates;
};
} // namespace mfmg

#endif


// -----------------------------------------------------------------------------
// Fern © Geoneric
//
// This file is part of Geoneric Fern which is available under the terms of
// the GNU General Public License (GPL), version 2. If you do not want to
// be bound by the terms of the GPL, you may purchase a proprietary license
// from Geoneric (http://www.geoneric.eu/contact).
// -----------------------------------------------------------------------------
#include "fern/language/ast/core/number_vertex.h"
#include <boost/format.hpp>

namespace fern {
namespace language {

template<typename T>
NumberVertex<T>::NumberVertex(
    T value)

    : ExpressionVertex((boost::format("%1%") % value).str()),
      _value(value)

{
}

template<typename T>
NumberVertex<T>::NumberVertex(
    int line_nr,
    int col_id,
    T value)

    : ExpressionVertex(line_nr, col_id, (boost::format("%1%") % value).str()),
      _value(value)

{
}

template<typename T>
T NumberVertex<T>::value() const
{
    return _value;
}

template class NumberVertex<int8_t>;
template class NumberVertex<int16_t>;
template class NumberVertex<int32_t>;
template class NumberVertex<int64_t>;
template class NumberVertex<uint8_t>;
template class NumberVertex<uint16_t>;
template class NumberVertex<uint32_t>;
template class NumberVertex<uint64_t>;
template class NumberVertex<float>;
template class NumberVertex<double>;

} // namespace language
} // namespace fern


/* Copyright (c) 2012-2019 Big Ladder Software LLC. All rights reserved.
 * See the LICENSE file for additional terms and conditions. */

#ifndef WEATHERDATA_H_
#define WEATHERDATA_H_

#include <cmath>
#include <fstream>
#include <iostream>
#include <numeric>

#include <boost/algorithm/string.hpp>
#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/tokenizer.hpp>

std::istream &safeGetline(std::istream &is, std::string &t);

class HourlyData : public std::vector<double> {
public:
  enum DataType { DT_SOLAR, DT_METEOROLOGICAL };

  DataType dataType;

  double getValue(boost::posix_time::ptime t);
  double getAverage();
  double getMin();
  double getMax();

  HourlyData();
  HourlyData(DataType dT);
};

class WeatherData {
public:
  std::string city;
  std::string state;
  std::string country;
  double timezone;
  double latitude;
  double longitude;
  double elevation;

  double hourOfMinimumTemperature;
  std::vector<double> dailyAverageTemperatures;
  std::vector<double> monthlyAverageTemperatures;
  double minimumAverageMontlyTemperature;
  double maximumAverageMontlyTemperature;

  HourlyData dryBulbTemp;
  // HourlyData wetBulbTemp;
  HourlyData dewPointTemp;
  HourlyData atmosphericPressure;
  // HourlyData density;
  HourlyData relativeHumidity;
  // HourlyData humidityRatio;
  // HourlyData enthalpy;

  // HourlyData solarAngle;
  // HourlyData globalHorizontalSolar;
  HourlyData directNormalSolar;
  HourlyData diffuseHorizontalSolar;

  HourlyData windDirection;
  HourlyData windSpeed;

  HourlyData totalSkyCover;
  HourlyData opaqueSkyCover;
  HourlyData skyEmissivity;
  HourlyData skyTemp;
  // HourlyData cloudType;
  // HourlyData snowFlag;
  // HourlyData rainFlag;
  HourlyData altitude;
  HourlyData azimuth;

public:
  WeatherData(std::string weatherFile);

private:
  void importEPW(std::string epwFile);
};

#endif /* WEATHERDATA_H_ */


/**
 * @file SurfaceSample.hpp
 * @author Adam Wolniakowski
 */

#include "SurfaceSample.hpp"

#include <iostream>
#include <rw/rw.hpp>
#include <rwsim/rwsim.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/optional.hpp>

#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include "XMLHelpers.hpp"

using namespace std;

USE_ROBWORK_NAMESPACE;
using namespace robwork;
using namespace rwsim;
using namespace rwsim::dynamics;

using namespace boost::property_tree;
using boost::algorithm::trim;

using namespace rwlibs::xml;

vector<SurfaceSample> SurfaceSample::loadFromXML(const std::string& filename)
{
	vector<SurfaceSample> samples;

    try {
        PTree tree;
        read_xml(filename, tree);

        PTree root = tree.get_child("SurfaceSamples");

        for (CI p = root.begin(); p != root.end(); ++p) {
			if (p->first == "Sample") {
				Q posq = XMLHelpers::readQ(p->second.get_child("Pos"));
				Q rpyq = XMLHelpers::readQ(p->second.get_child("RPY"));
				double graspW = XMLHelpers::readDouble(p->second.get_child("GraspW"));
				//cout << "pos=" << posq << " rot=" << rpyq << " graspW=" << graspW << endl;

				Vector3D<> pos(posq[0], posq[1], posq[2]);
				RPY<> rpy(rpyq[0], rpyq[1], rpyq[2]);
				samples.push_back(SurfaceSample(Transform3D<>(pos, rpy.toRotation3D()), graspW));
			}
		}
    } catch (const ptree_error& e) {
        RW_THROW(e.what());
    }

	return samples;
}

void SurfaceSample::saveToXML(const std::string& filename, const std::vector<SurfaceSample>& samples)
{
	PTree tree;

	// save all the samples!
	for(const SurfaceSample& sample : samples) {
		PTree node;

		node.put("Pos", XMLHelpers::QToString(Q(3, sample.transform.P()[0], sample.transform.P()[1],sample.transform.P()[2])));
		RPY<> rpy(sample.transform.R());
		node.put("RPY", XMLHelpers::QToString(Q(3, rpy[0], rpy[1], rpy[2])));
		node.put("GraspW", boost::lexical_cast<std::string>(sample.graspW));

		tree.add_child("SurfaceSamples.Sample", node);
	}

	// save to XML
	try {
		boost::property_tree::xml_writer_settings<char> settings('\t', 1);
        write_xml(filename, tree, std::locale(), settings);
    } catch (const ptree_error& e) {
        // Convert from parse errors to RobWork errors.
        RW_THROW(e.what());
    }
}


/* Copyright 2022 Zuru Tech HK Limited.
 *
 * 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 SOLVERS_SOLVER_HPP
#define SOLVERS_SOLVER_HPP

#include <Eigen/Dense>

#include <solvers/SparseSystem.hpp>

namespace solvers {

/**
 * \brief Solver Base class for linear system solvers.
 */
class Solver {
public:
    /**
     * \brief solve Solve a sparse linear system.
     *
     * \param[in] system Sparse linear system.
     * \param[out] duration Running time of the resolution in seconds.
     * \return Solution vector of the sparse linear system in Eigen format.
     */
    virtual Eigen::VectorXd solve(const SparseSystem& system,
                                  double& duration) const = 0;
};

}    // namespace solvers

#endif    // SOLVERS_SOLVER_HPP


/*
	This program has been created by Stefan Zwaard, based on the work of Paul Baker. Current version: 0.5, 16-06-2020.
	The (Distributed) Model Training and Aggregation (DMTLA) program allows for training and aggregation of SVM (HOG based) and ERT models.
	The program is part of the EyeBlink Project, and works together with the Image processing and EAR calculation program to proces images using the newly created models.
	The program was also used as a proof of concept and for results gathering for the proposed Mean Weight Matrix Aggregation (MWMA) distributed training for L-SVM models, and Weighted Bin Aggregation (WBA) distributed training of ERT models,
	in the paper: "Privacy-Preserving Algorithms for Object Detection & Localization Using Distributed Machine Learning".
	Running the resulting program requires opencv_world400.dll to be present in the same folder. In order to compile this code, ensure both the OpenCV and DLib libraries are linked.
	Always use the AVX extended instructions on Release x64 for best preformance of program
*/

#include <dlib/svm_threaded.h>
#include <dlib/string.h>
#include <dlib/gui_widgets.h>
#include <dlib/image_processing.h>
#include <dlib/data_io.h>
#include <dlib/cmd_line_parser.h>
#include <dlib/image_io.h>

#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <stdio.h>
#include <cstdio>
#include <math.h>
#include <string>
#include <filesystem>

#include "Window.h"
#include "CERT.h"

#include <dlib/image_processing/frontal_face_detector.h>
#include <dlib/image_processing/render_face_detections.h>
#include <dlib/image_keypoint/draw_surf_points.h>
#include <opencv2/core/core.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/opencv.hpp>
#include <dlib/opencv.h>

using namespace std;
using namespace cv;
using namespace dlib;

// Define scnaner type for SVM training
typedef scan_fhog_pyramid<pyramid_down<6> > image_scanner_type;

// Here follows a few function definitions from DLib for SVM and ERT training
// ----------------------------------------------------------------------------------------

std::vector<std::vector<double> > get_interocular_distances(
	const std::vector<std::vector<full_object_detection> >& objects
);

void pick_best_window_size(
	const std::vector<std::vector<dlib::rectangle> >& boxes,
	unsigned long& width,
	unsigned long& height,
	const unsigned long target_size
)
/*!
	ensures
		- Finds the average aspect ratio of the elements of boxes and outputs a width
		  and height such that the aspect ratio is equal to the average and also the
		  area is equal to target_size.  That is, the following will be approximately true:
			- #width*#height == target_size
			- #width/#height == the average aspect ratio of the elements of boxes.
!*/
{
	// find the average width and height
	running_stats<double> avg_width, avg_height;
	for (unsigned long i = 0; i < boxes.size(); ++i)
	{
		for (unsigned long j = 0; j < boxes[i].size(); ++j)
		{
			avg_width.add(boxes[i][j].width());
			avg_height.add(boxes[i][j].height());
		}
	}

	// Adjusting the box size sp it is about target_pizels pixels in size
	double size = avg_width.mean()*avg_height.mean();
	double scale = std::sqrt(target_size / size);

	width = (unsigned long)(avg_width.mean()*scale + 0.5);
	height = (unsigned long)(avg_height.mean()*scale + 0.5);
	// make sure the width and height never round to zero.
	if (width == 0)
		width = 1;
	if (height == 0)
		height = 1;
}

bool contains_any_boxes(
	const std::vector<std::vector<dlib::rectangle> >& boxes
)
{
	for (unsigned long i = 0; i < boxes.size(); ++i)
	{
		if (boxes[i].size() != 0)
			return true;
	}
	return false;
}

void throw_invalid_box_error_message(
	const std::string& dataset_filename,
	const std::vector<std::vector<dlib::rectangle> >& removed,
	const unsigned long target_size
)
{
	image_dataset_metadata::dataset data;
	load_image_dataset_metadata(data, dataset_filename);

	std::ostringstream sout;
	sout << "Error!  An impossible set of object boxes was given for training. ";
	sout << "All the boxes need to have a similar aspect ratio and also not be ";
	sout << "smaller than about " << target_size << " pixels in area. ";
	sout << "The following images contain invalid boxes:\n";
	std::ostringstream sout2;
	for (unsigned long i = 0; i < removed.size(); ++i)
	{
		if (removed[i].size() != 0)
		{
			const std::string imgname = data.images[i].filename;
			sout2 << "  " << imgname << "\n";
		}
	}
	throw dlib::error("\n" + wrap_string(sout.str()) + "\n" + sout2.str());
}

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

// General system variables, mostly used for the main GUI window
bool Start = 0;
int TrainSVMConfig = 0;
int AggregateSVMConfig = 0;
std::string SVMModelsInPath = "E:\\EyeBlink\\Models\\ToAggregate.svm";
std::string SVMModelOutPath = "E:\\EyeBlink\\Models\\NewModel.svm";
int TrainERTConfig = 0;
int AggregateERTConfig = 0;
std::string ERTModelsInPath = "E:\\EyeBlink\\Models\\ToAggregate.dat";
std::string ERTModelOutPath = "E:\\EyeBlink\\Models\\NewModel.dat";
std::string SVMTrainingDatasetPath = "E:\\EyeBlink\\Data\\SVMTrainingdata.xml";
std::string SVMTestDatasetPath = "E:\\EyeBlink\\Data\\SVMTestingdata.xml";
std::string ERTTrainingDatasetPath = "E:\\EyeBlink\\Data\\ERTTrainingdata.xml";
std::string ERTTestDatasetPath = "E:\\EyeBlink\\Data\\ERTTestingdata.xml";

// Variable decleration for SVM training settings, changed and set with the SVM training GUI
bool SVMDone = 0;
int SVMThreads = 6;
double C = 100.0;
double eps = 0.01;
unsigned int num_folds = 3;
unsigned long target_size = 80 * 80;
unsigned long upsample_amount = 2;

// Variable decleration for ERT training settings, changed and set with the ERT training GUI
bool ERTDone = 0;
unsigned int ERTThreads = 6;
unsigned long oversampling_amount = 20; // Random deformations of input data to help overcome generalization
double nu = 0.1; // 0,1 for default, the lower the more generalized training, 1 for complete fitting
unsigned long tree_depth = 5; // Default 2 for normal, 8 for high accuracy, verry slow on higher values, also severly increases model size be extreemly carefull
unsigned int feature_pool_size = 400; // Number of pixels used for ERT feature processing in each cascade. 400 default
unsigned int num_test_splits = 20; // Default 50. Changes accuracy of training, but slows training speed
unsigned int cascade_depth = 10; // Number of iterations
unsigned long num_trees_per_cascade_level = 500; // Default is 500
double lambda = 0.1;

int main(int argc, char** argv)
{
	try
	{
		// Showing main GUI
		win my_window(&Start, &TrainSVMConfig, &AggregateSVMConfig, &SVMModelsInPath, &SVMModelOutPath, &TrainERTConfig, &AggregateERTConfig, &ERTModelsInPath, &ERTModelOutPath, &SVMTrainingDatasetPath, &SVMTestDatasetPath, &ERTTrainingDatasetPath, &ERTTestDatasetPath);
		while (true)
		{
			while (Start == 0)
			{
				sleep(100); // Sleep while system waits for user to start program, Idle running can be implemented here if neeeded
			}
			cout << "Program has started" << endl;

			// If *.svm or *.dat is used to select more then one model of each type, for either multi model aggregation or testing, each model is seperated into its own file.
			std::vector<String> VectorSVMInputModelPaths;
			std::vector<String> VectorERTInputModelPaths; // Keep in mind, this path can also be used to load CERT models for testing only (option 3 in main GUI)
			std::vector<String> VectorCERTInputModelPaths; // Not used at the moment, could be used later if adding or removing Subdivisions from CERT model using other ERT models is needed. For now the ERT input is used.
			std::vector < object_detector<image_scanner_type>> SVMModels; // Vector for all loaded SVM models
			std::vector < shape_predictor> ERTModels; // Vector for all loaded ERT models
			std::vector < CERT > CERTModels; // Vector for all loaded CERT models

			if (AggregateSVMConfig == 1 || AggregateSVMConfig == 2) // Load SVM models from input path if needed
			{
				glob(SVMModelsInPath, VectorSVMInputModelPaths); // If a incursive function is wanted, where all subfolders of the target are also searched, add '1' to the arguments
				cout << "Number of .svm models detected on SVM model(s) input path:" << VectorSVMInputModelPaths.size() << endl;
				for (int i = 0; i < VectorSVMInputModelPaths.size(); i++)
				{
					ifstream fin(VectorSVMInputModelPaths[i], ios::binary);
					if (fin)
					{
						object_detector<image_scanner_type> TempDetector;
						cout << "Loaded .svm file for either multi model aggregation or test only testing, file:" << VectorSVMInputModelPaths[i] << endl;
						deserialize(TempDetector, fin);
						SVMModels.push_back(TempDetector);
					}
					else
						cout << "Error reading .svm file on SVM model(s) input path, file was:" << VectorSVMInputModelPaths[i] << endl;
				}
			}

			if (AggregateERTConfig == 1 || AggregateERTConfig == 2) // Load ERT models from input path if needed
			{
				glob(ERTModelsInPath, VectorERTInputModelPaths); // If a incursive function is wanted, where all subfolders of the target are also searched, add '1' to the arguments
				cout << "Number of .dat models detected on ERT model(s) input path:" << VectorERTInputModelPaths.size() << endl;
				for (int i = 0; i < VectorERTInputModelPaths.size(); i++)
				{
					ifstream fin(VectorERTInputModelPaths[i], ios::binary);
					if (fin)
					{
						shape_predictor TempDetector;
						cout << "Loaded .dat file for either Weighted Bin Aggregation or test only testing, file:" << VectorERTInputModelPaths[i] << endl;
						deserialize(TempDetector, fin);
						ERTModels.push_back(TempDetector);
					}
					else
						cout << "Error reading .dat file on ERT model(s) input path, file was:" << VectorERTInputModelPaths[i] << endl;
				}
			}

			if (AggregateERTConfig == 3) // Load CERT models from input path if needed (Uses ERT input path at the moment)
			{
				glob(ERTModelsInPath, VectorERTInputModelPaths); // If a incursive function is wanted, where all subfolders of the target are also searched, add '1' to the arguments
				cout << "Number of .CERT models detected on ERT model(s) input path:" << VectorERTInputModelPaths.size() << endl;
				for (int i = 0; i < VectorERTInputModelPaths.size(); i++)
				{
					ifstream fin(VectorERTInputModelPaths[i], ios::binary);
					if (fin)
					{
						CERT TempDetector;
						cout << "Loaded .CERT file for either WBA CERT model only testing, file:" << VectorERTInputModelPaths[i] << endl;
						TempDetector.deserialize(fin);
						CERTModels.push_back(TempDetector);
					}
					else
						cout << "Error reading .CERT file on CERT model(s) input path, file was:" << VectorERTInputModelPaths[i] << endl;
				}
			}

			if (TrainSVMConfig == 1) // Getting SVM training settings from user if needed
			{
				cout << "Showing SVM model training window now" << endl;
				SVMsettingsWin SVMWindow(&SVMDone, &SVMThreads, &C, &eps, &num_folds, &target_size, &upsample_amount);
				while (SVMDone == 0)
				{
					sleep(100); // Wait while user finishes SVM training input
				}
				SVMDone = 0;
				// Callout settings back to user
				cout << "SVM model training configured, using the following settings:" << endl;
				cout << "threads:" << SVMThreads << endl;
				cout << "C:" << C << endl;
				cout << "eps:" << eps << endl;
				cout << "num_folds:" << num_folds << endl;
				cout << "target_size:" << target_size << endl;
				cout << "upsample_amount" << upsample_amount << endl;
			}

			if (TrainERTConfig == 1) // Getting ERT training settings from user if needed
			{
				cout << "Showing ERT model training window now" << endl;
				ERTsettingsWin ERTWindow(&ERTDone, &ERTThreads, &oversampling_amount, &nu, &tree_depth, &feature_pool_size, &num_test_splits, &cascade_depth, &num_trees_per_cascade_level, &lambda);
				while (ERTDone == 0)
				{
					sleep(100); // Wait while user finishes SVM training input
				}
				ERTDone = 0;
				// Callout settings back to user
				cout << "ERT model training configured, using the following settings:" << endl;
				cout << "threads:" << ERTThreads << endl;
				cout << "oversampling_amount:" << oversampling_amount << endl;
				cout << "nu:" << nu << endl;
				cout << "tree_depth:" << tree_depth << endl;
				cout << "feature_pool_size:" << feature_pool_size << endl;
				cout << "num_test_splits:" << num_test_splits << endl;
				cout << "cascade_depth:" << cascade_depth << endl;
				cout << "num_trees_per_cascade_level:" << num_trees_per_cascade_level << endl;
				cout << "lambda:" << lambda << endl;
			}

			// SVM Model training if needed (based on default DLib Training)
			object_detector<image_scanner_type> NewSVMDetector; // Placeholder for the new created SVM model
			if (TrainSVMConfig == 1) // Training new .svm model based on user settings
			{
				cout << "Starting preperations for SVM model training." << endl;
				dlib::array<array2d<unsigned char> > images;
				std::vector<std::vector<dlib::rectangle> > object_locations, ignore;

				cout << "Loading training image dataset from dataset .xml file " << SVMTrainingDatasetPath << endl;
				ignore = load_image_dataset(images, object_locations, SVMTrainingDatasetPath);
				cout << "Number of images loaded: " << images.size() << endl;

				// Check if there are more folds than there are images.
				if (num_folds > images.size())
					num_folds = images.size();

				// Upsampling if needed
				for (unsigned long i = 0; i < upsample_amount; ++i)
					upsample_image_dataset<pyramid_down<2> >(images, object_locations, ignore);

				image_scanner_type scanner;

				unsigned long width, height;
				pick_best_window_size(object_locations, width, height, target_size);
				scanner.set_detection_window_size(width, height);
				structural_object_detection_trainer<image_scanner_type> trainer(scanner);

				trainer.set_num_threads(SVMThreads);
				trainer.be_verbose(); // This is always set, this gives a progress overview in the CLI during training, remove this is unwanted, or make it depending on user settings.
				trainer.set_c(C);
				trainer.set_epsilon(eps);

				// Making sure input trainingsdata has boxes usable by detector
				std::vector<std::vector<dlib::rectangle> > removed;
				removed = remove_unobtainable_rectangles(trainer, images, object_locations);
				// Error handeling for if a box does not match (this happens a lot if there is no upsampling)
				if (contains_any_boxes(removed))
				{
					unsigned long scale = upsample_amount + 1;
					scale = scale * scale;
					throw_invalid_box_error_message(SVMTrainingDatasetPath, removed, target_size / scale);
				}
				cout << "Preperations completed. Starting training on new SVM model" << endl;
				NewSVMDetector = trainer.train(images, object_locations, ignore);
				cout << "Training of new SVM  model has completed." << endl;
			}

			// ERT model training if needed (based on default DLib Training)
			shape_predictor NewERTPredictor; // Placeholder for the new ERT model created
			if (TrainERTConfig == 1) // training new .dat model based on user settings
			{
				cout << "Starting preperations for ERT model training." << endl;
				cout << "Loading training image dataset from dataset .xml file " << ERTTrainingDatasetPath << endl;

				dlib::array<array2d<unsigned char> > images_train;
				std::vector<std::vector<full_object_detection> > faces_train;
				load_image_dataset(images_train, faces_train, ERTTrainingDatasetPath);
				cout << "Number of images loaded: " << images_train.size() << endl;

				shape_predictor_trainer trainer;

				// Applying ERT training settings
				trainer.set_nu(nu);
				trainer.set_tree_depth(tree_depth);
				trainer.set_num_threads(ERTThreads);
				if (oversampling_amount > 0)
					trainer.set_oversampling_amount(oversampling_amount);
				trainer.set_cascade_depth(cascade_depth);
				trainer.set_feature_pool_size(feature_pool_size);
				trainer.set_num_test_splits(num_test_splits);
				trainer.set_num_trees_per_cascade_level(num_trees_per_cascade_level);
				trainer.set_lambda(lambda);

				trainer.be_verbose(); // This is always set, this gives a progress overview in the CLI during training, remove this is unwanted, or make it depending on user settings.

				cout << "Preperations completed. Starting training on new ERT model" << endl;

				NewERTPredictor = trainer.train(images_train, faces_train);
				cout << "Training of new ERT  model has completed." << endl;
				cout << endl << "Number of parts of ERT Model: " << NewERTPredictor.num_parts();
				cout << endl << "Number of feutures of of ERT Model" << NewERTPredictor.num_features() << endl;
			}

			// SVM Model aggregation if needed, using the Mean Weight Matrix Aggregation (MWMA) algorithm, see paper for detailed explenation
			if (AggregateSVMConfig == 1) // Aggregating new .svm model based on user settings, aggregates any trained .svm model as well
			{
				cout << "Starting SVM multi model aggregation process" << endl;
				cout << "Loaded " << SVMModels.size() << " SVM models from input path for aggregation proces" << endl;
				if (TrainSVMConfig == 1)
					cout << "Trained SVM model shall be included in aggregation proces" << endl;

				cout << "Starting SVM multi aggregation" << endl;

				matrix<double, 0, 1> TempW; // MWMA step 1: New temp matrix for aggregation
				TempW.set_size((SVMModels[0].get_w()).size());
				TempW = SVMModels[0].get_w(); // MWMA step 1: init of w using first model
				for (int i = 1; i < SVMModels.size(); i++)
				{
					TempW += SVMModels[i].get_w(); // MWMA step 2a: Adding all other matrix values
				}
				if (TrainSVMConfig == 1)
				{
					TempW += NewSVMDetector.get_w(); // MWMA step 2a: Inclusion of trained SVM model in aggregation
					TempW /= (SVMModels.size() + 1); // MWMA step 2b: averaging all matrix values including trained model
				}
				else
					TempW /= SVMModels.size(); // MWMA step 2b: averageing all matrix values

				NewSVMDetector = object_detector<image_scanner_type>(SVMModels[0].get_scanner(), SVMModels[0].get_overlap_tester(), TempW); // MWMA step 3: Feuture extraction transfer and saving
				cout << "SVM Multi aggregation completed" << endl;
			}

			// ERT Model aggregation if needed, using tthe Weighted Bin Aggregation (MWMA) algorithm, this combines several ERT models into a new Comnbined-ERT model (CERT), see paper for detailed explenation
			CERT NewCERT;
			if (AggregateERTConfig == 1) // Aggregating new ERT model based on user settings, aggregates trained .dat model as well
			{
				cout << "Starting ERT Weighted Bin Aggregation process" << endl;
				cout << "Loaded " << ERTModels.size() << " ERT models from input path for aggregation proces" << endl;
				if (TrainERTConfig == 1)
				{
					cout << "Trained ERT model shall be included in aggregation proces" << endl;
					ERTModels.push_back(NewERTPredictor);
				}

				// WBA Step 1: Forrest combination
				cout << "ERT WBA: Forrest combination" << endl;
				std::vector<dlib::shape_predictor> NewSubDivisions;
				for (int i = 0; i < ERTModels.size(); i++)
				{
					NewSubDivisions.push_back(ERTModels[i]);
				}

				// WBA Step 2: Devider calculation
				cout << "ERT WBA: Devider calculation" << endl;
				std::vector<double> NewDevider; // The devider represents how much the subdevision partakes in the calculation of the end result and should total 1
				std::vector<double> DeviderOffSet; // This vector contains all offset values in order to bias end outcome towards a certain model. This could be filled using user config later, but for now its hardcoded for euqal participation (by filling it with zero's only.
				double TotalDeviationValue = 0; // This number is needed to determin how the end result index of 1 is reached. If all moddels have equal weight, this is the number of models (to ensure mean average). If a model has more or less weight, then this model should be changed.
				for (int i = 0; i < ERTModels.size(); i++) // Offset calculation
				{
					DeviderOffSet.push_back(1); // Adding ones first always, a 1 means 100% base of this model is used for final weight calculation, so place in vector is filled even if no offset is given, 1's only means all models partake equaly.
					DeviderOffSet[i] += 0; // Add specific model offset here (later trough GUI settings, but now hardcoded). For 20% more bias towards a model, add 0.2 (this ofcourse also means that other models lose participation value towards this model. Therefore If all models get the same increase in bias, it has no effect; 2's for all modes results in same for 1's in all.
					TotalDeviationValue += DeviderOffSet[i]; // If a model has increased or decreased weight, the toal deviation should always be changed, to ensure the later total result index always equals 1.
				}
				for (int i = 0; i < ERTModels.size(); i++) // Final devider calculation, by pushing back the final deviation values now instead of in the first loop, changes can be made depending on the TotalDeviationValue if needed.
				{
					double TempBinDeviation;
					TempBinDeviation = DeviderOffSet[i];
					NewDevider.push_back(TempBinDeviation);
				}

				// WBA Step 3: Weighted Bins Creation
				cout << "ERT WBA: Weighted bins creation" << endl;
				std::vector<std::pair<dlib::full_object_detection, double>> NewWeightedBins;
				for (int i = 0; i < ERTModels.size(); i++) // Creation of all bins, one bin for each subdevision, this is used to store sub results and used in combination with the corresponding devider for end result calculation
				{
					dlib::full_object_detection NewBin;
					std::pair<dlib::full_object_detection, double> NewWeightedBin(NewBin, NewDevider[i]);
					NewWeightedBins.push_back(NewWeightedBin);
				}

				// WBA step 4: Averaging of init shape model
				cout << "ERT WBA: Aggregating shapes"<< endl;
				matrix<float, 0, 1> NewShape; // New init Shape for averaging
				NewShape.set_size((ERTModels[0].get_shape()).size());
				NewShape = ERTModels[0].get_shape();
				for (int i = 1; i < ERTModels.size(); i++)
				{
					NewShape += ERTModels[i].get_shape();

				}
				NewShape /= ERTModels.size(); // Averageing all matrix values

				NewCERT = CERT(NewSubDivisions, TotalDeviationValue, NewWeightedBins, NewShape); // Creation of new combi ERT model

				cout << "ERT WBA completed" << endl;
			}

			// Model testing and saving
			if (AggregateSVMConfig == 1  || TrainSVMConfig == 1) // Saving and testing new .svm model if one has been created
			{
				cout << "Saving newly created SVM model to disk." << endl;
				serialize(SVMModelOutPath) << NewSVMDetector; // saving new SVM file to disk

				// Testing newly created SVM model
				dlib::array<array2d<unsigned char> > images;
				std::vector<std::vector<dlib::rectangle> > object_locations, ignore;
				cout << "Loading Test image dataset from dataset.xml file " << SVMTestDatasetPath << endl;
				ignore = load_image_dataset(images, object_locations, SVMTestDatasetPath);
				cout << "Number of images loaded: " << images.size() << endl;
				cout << "Testing new SVM model on specified test dataset." << endl;
				cout << "Accuracy of SVM model (precision,recall,AP): " << test_object_detection_function(NewSVMDetector, images, object_locations, ignore) << endl;
			}

			if (AggregateSVMConfig == 2) // Testing selected input .svm models if test only mode is used
			{
				dlib::array<array2d<unsigned char> > images;
				std::vector<std::vector<dlib::rectangle> > object_locations, ignore;
				cout << "Loading SVM Test image dataset from dataset.xml file " << SVMTestDatasetPath << endl;
				ignore = load_image_dataset(images, object_locations, SVMTestDatasetPath);
				cout << "Number of images loaded: " << images.size() << endl;

				cout << "Testing:" << SVMModels.size() << " SVM model(s) on specified test dataset." << endl;
				for (int i = 0; i < SVMModels.size(); i++)
				{
					cout << "Testing SVM model:" << VectorSVMInputModelPaths[i] << endl;
					cout << "Accuracy of SVM model  (precision,recall,AP): " << test_object_detection_function(SVMModels[i], images, object_locations, ignore) << endl;
				}
				cout << "Testing of SVM models completed." << endl;
			}

			if (TrainERTConfig == 1) // Saving and testing new .dat model if one has been trained
			{
				cout << "Saving newly trained ERT model to disk." << endl;
				serialize(ERTModelOutPath) << NewERTPredictor; // saving new .dat file to disk

				cout << "Loading ERT Test image dataset from dataset.xml file " << ERTTestDatasetPath << endl;
				dlib::array<array2d<unsigned char> > images_test;
				std::vector<std::vector<full_object_detection> > faces_test;
				load_image_dataset(images_test, faces_test, ERTTestDatasetPath);
				cout << "Number of images loaded: " << images_test.size() << endl;

				cout << "Mean testing error of new ERT model: " <<
					test_shape_predictor(NewERTPredictor, images_test, faces_test, get_interocular_distances(faces_test)) << endl;  // Test ERT model using internal test software
			}

			if (AggregateERTConfig == 1) // Saving and testing new .CERT model if one has been created
			{
				cout << "Saving newly aggregated CERT model to disk." << endl;
				ofstream fout(ERTModelOutPath, ios::binary);
				if (fout)
				{
					NewCERT.serialize(fout);
				}
				else
					cout << "Error saving CERT model. Model output path was:" << ERTModelOutPath << endl;

				cout << "Loading ERT Test image dataset from dataset.xml file " << ERTTestDatasetPath << endl;
				dlib::array<array2d<unsigned char> > images_test;
				std::vector<std::vector<full_object_detection> > faces_test;
				load_image_dataset(images_test, faces_test, ERTTestDatasetPath);
				cout << "Number of images loaded: " << images_test.size() << endl;

				const std::vector<std::vector<double> > scales = get_interocular_distances(faces_test);
				running_stats<double> rs;
				for (unsigned long i = 0; i < faces_test.size(); ++i)
				{
					for (unsigned long j = 0; j < faces_test[i].size(); ++j)
					{
						const double scale = scales.size() == 0 ? 1 : scales[i][j]; // Scale needed for testing
					    full_object_detection Shape = NewCERT.PredictFinalShape(images_test[i], faces_test[i][j].get_rect()); // This provides a full_object_detection from a CERT model, use this function if a CERT model is used where normaly a ERT model is used.
						for (unsigned long k = 0; k < Shape.num_parts(); k++) // The new CERT model has no internal test function like the default ERT code from DLib, therefore the Mean Error Rate is calculated here below.
						{
							double score = length(Shape.part(k) - faces_test[i][j].part(k)) / scale;
							rs.add(score);
						}
					}
				}
				cout << "Mean testing error of new ERT model: " << rs.mean() << endl;

			}

			if (AggregateERTConfig == 2) // Testing selected input .dat models if test only mode is used. This tests ERT models, not the Combined-ERT models, can be used to see most suitable models for aggregation
			{
				cout << "Loading ERT Test image dataset from dataset.xml file " << ERTTestDatasetPath << endl;
				dlib::array<array2d<unsigned char> > images_test;
				std::vector<std::vector<full_object_detection> > faces_test;
				load_image_dataset(images_test, faces_test, ERTTestDatasetPath);
				cout << "Number of images loaded: " << images_test.size() << endl;

				cout << "Testing:" << ERTModels.size() << " ERT model(s) on specified test dataset." << endl;

				for (int i = 0; i < ERTModels.size(); i++)
				{
					cout << "Testing ERT model:" << VectorERTInputModelPaths[i] << endl;
					cout << "Mean testing error of ERT model: " << test_shape_predictor(ERTModels[i], images_test, faces_test, get_interocular_distances(faces_test)) << endl; // Test ERT model using internal test software
				}

			}

			if (AggregateERTConfig == 3) // Testing selected input CERT .dat models if test only mode is used
			{
				cout << "Loading ERT Test image dataset from dataset.xml file " << ERTTestDatasetPath << endl;
				dlib::array<array2d<unsigned char> > images_test;
				std::vector<std::vector<full_object_detection> > faces_test;
				load_image_dataset(images_test, faces_test, ERTTestDatasetPath);
				cout << "Number of images loaded: " << images_test.size() << endl;

				cout << "Testing:" << CERTModels.size() << " CERT model(s) on specified test dataset." << endl;

				for (int m = 0; m < CERTModels.size(); m++)
				{
					cout << "Testing CERT model:" << VectorERTInputModelPaths[m] << endl;
					const std::vector<std::vector<double> > scales = get_interocular_distances(faces_test);
					running_stats<double> rs;
					for (unsigned long i = 0; i < faces_test.size(); ++i)
					{
						for (unsigned long j = 0; j < faces_test[i].size(); ++j)
						{
							const double scale = scales.size() == 0 ? 1 : scales[i][j]; // Scale needed for testing
							full_object_detection Shape = CERTModels[m].PredictFinalShape(images_test[i], faces_test[i][j].get_rect()); // This provides a full_object_detection from a CERT model, use this function if a CERT model is used where normaly a ERT model is used.
							for (unsigned long k = 0; k < Shape.num_parts(); k++) // The new CERT model has no internal test function like the default ERT code from DLib, therefore the Mean Error Rate is calculated here below.
							{
								double score = length(Shape.part(k) - faces_test[i][j].part(k)) / scale;
								rs.add(score);
							}
						}
					}
					cout << "Mean testing error of CERT model: " << rs.mean() << endl;
				}
			}

			Start = 0; // System is done and shall await next usage
			cout << "Program has completed task, a new task can now be started using the start button, please change settings where needed." << endl;
		}
	}
	catch (exception& e)
	{
		cout << "\nexception thrown!" << endl;
		cout << e.what() << endl;
		cout << "Restart of program needed, close window or terminate program from CLI." << endl;
	}
}

// Implementaiton of remaining DLib functions for testing and training of ERT models, needed to calculate the scale
// ----------------------------------------------------------------------------------------

double interocular_distance(
	const full_object_detection& det
)
{
	dlib::vector<double, 2> l, r;
	double cnt = 0;
	// Find the center of the left eye by averaging the points around
	// the eye.
	for (unsigned long i = 36; i <= 41; ++i)
	{
		l += det.part(i);
		++cnt;
	}
	l /= cnt;

	// Find the center of the right eye by averaging the points around
	// the eye.
	cnt = 0;
	for (unsigned long i = 42; i <= 47; ++i)
	{
		r += det.part(i);
		++cnt;
	}
	r /= cnt;

	// Now return the distance between the centers of the eyes
	return length(l - r);
}

std::vector<std::vector<double> > get_interocular_distances(
	const std::vector<std::vector<full_object_detection> >& objects
)
{
	std::vector<std::vector<double> > temp(objects.size());
	for (unsigned long i = 0; i < objects.size(); ++i)
	{
		for (unsigned long j = 0; j < objects[i].size(); ++j)
		{
			temp[i].push_back(interocular_distance(objects[i][j]));
		}
	}
	return temp;
}


//          Copyright Gavin Band 2008 - 2012.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#include <string>
#include <vector>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/math/distributions/chi_squared.hpp>
#include "genfile/Error.hpp"
#include "genfile/ToGP.hpp"
#include "genfile/VariantDataReader.hpp"
#include "metro/likelihood/Multinomial.hpp"
#include "components/SNPSummaryComponent/SNPHWE.hpp"
#include "components/SNPSummaryComponent/SNPSummaryComputation.hpp"
#include "components/SNPSummaryComponent/HWEComputation.hpp"
#include "components/SNPSummaryComponent/IntensitySummaryComputation.hpp"
#include "components/SNPSummaryComponent/ClusterFitComputation.hpp"

// #define DEBUG_SNP_SUMMARY_COMPUTATION 1

namespace snp_summary_component {

	namespace {
		struct AlleleCountClient: public genfile::VariantDataReader::PerSampleSetter {
			AlleleCountClient( std::vector< double >* counts ):
				m_counts( counts ),
				m_number_of_alleles( 0 )
			{
				assert( counts != 0 ) ;
			}

			~AlleleCountClient() throw() {}

			void set_counts( std::vector< double >* counts ) {
				assert( counts != 0 ) ;
				m_counts = counts ;
			}

			void initialise( std::size_t number_of_samples, std::size_t number_of_alleles ) {
				m_number_of_alleles = number_of_alleles ;
				assert( m_counts->size() == number_of_alleles ) ;
			}

			bool set_sample( std::size_t i ) {
				m_ploidy = 0 ;
				m_table = 0 ;
				return true ;
			}

			void set_number_of_entries( uint32_t ploidy, std::size_t n, OrderType const order_type, ValueType const value_type ) {
				assert( order_type == genfile::ePerUnorderedGenotype ) ;
				assert( value_type == genfile::eProbability ) ;

				std::map< uint32_t, genfile::impl::Enumeration >::iterator where = m_tables.find( ploidy ) ;
				if( where == m_tables.end() ) {
					std::pair< std::map< uint32_t, genfile::impl::Enumeration >::iterator, bool >
						result = m_tables.insert( std::make_pair( ploidy, genfile::impl::enumerate_unphased_genotypes( ploidy ) )) ;
					assert( result.second ) ;
					where = result.first ;
				}
				m_ploidy = ploidy ;
				m_table = &(where->second) ;
			}

			void set_value( std::size_t value_i, genfile::MissingValue const value ) {
				set_value( value_i, 0.0 ) ;
			}

			void set_value( std::size_t value_i, double const value ) {
				assert( value_i <= std::size_t( std::numeric_limits< uint16_t >::max() )) ;
				genfile::impl::Enumeration const& enumeration = *m_table ;
				std::size_t const& maxAlleles = enumeration.first.second ;
				assert( m_number_of_alleles <= maxAlleles ) ;
				uint16_t const encodedGenotype = enumeration.second.second[ uint16_t( value_i ) ] ;
				uint32_t const bitsPerAllele = enumeration.first.first ;
				uint16_t mask = uint16_t( 0xFFFF ) >> ( 16 - bitsPerAllele ) ;
				// Dosage of the 1st allele in the genotype is not encoded directly.
				// We compute it as the ploidy minus the dosage of other alleles.
				uint16_t dosage_of_nonref_alleles = 0 ;
				for( std::size_t allele = 1; allele < m_number_of_alleles; ++allele ) {
					uint16_t const dosage = ( encodedGenotype >> ( (allele-1) * bitsPerAllele )) & mask ;
					(*m_counts)[allele] += value * dosage ;
					dosage_of_nonref_alleles += dosage ;
				}
				(*m_counts)[0] += value * (m_ploidy - dosage_of_nonref_alleles) ;
			}

			void finalise() {}
		private:
			std::vector< double >* m_counts ;
			std::size_t m_number_of_alleles ;
			uint32_t m_ploidy ;
			std::map< uint32_t, genfile::impl::Enumeration > m_tables ;
			genfile::impl::Enumeration* m_table ;
		} ;
	}

	struct AlleleCountComputation: public SNPSummaryComputation {
	public:

		AlleleCountComputation():
			m_counter( &m_counts )
		{}

		void list_variables( NameCallback callback ) const {
			using genfile::string_utils::to_string ;
			// By default we list 10 alleles
			callback( "number_of_alleles" ) ;
			for( std::size_t i = 0; i < 10; ++i ) {
				callback( "allele" + to_string(i+1) + "_count" ) ;
			}
		}

		void operator()(
			VariantIdentifyingData const& snp,
			Genotypes const& genotypes,
			Ploidy const& ploidy,
			genfile::VariantDataReader& data_reader,
			ResultCallback callback
		) {
			using genfile::string_utils::to_string ;
			m_counts = std::vector< double >( snp.number_of_alleles(), 0.0 ) ;
			m_counter.set_counts( &m_counts ) ;
			callback( "number_of_alleles", int64_t( snp.number_of_alleles() )) ;
			data_reader.get( ":genotypes:", genfile::to_GP_unphased( m_counter ) ) ;
			std::size_t i = 0 ;
			for( ; i < snp.number_of_alleles(); ++i ) {
				callback( "allele" + to_string(i+1) + "_count", m_counts[i] ) ;
			}
			for( ; i < 10; ++i ) {
				callback( "allele" + to_string(i+1) + "_count", genfile::MissingValue() ) ;
			}
		}

		std::string get_summary( std::string const& prefix, std::size_t column_width ) const {
			return prefix + "AlleleCountComputation" ;
		}
	private:
		std::vector< double > m_counts ;
		AlleleCountClient m_counter ;
	} ;

	struct AlleleFrequencyComputation: public SNPSummaryComputation
	{
		AlleleFrequencyComputation( std::string const& what ):
			m_compute_counts( what == "everything" || what == "counts" ),
			m_compute_frequencies( what == "everything" )
		{
			assert( what == "counts" || what == "everything" ) ;
		}
		void operator()(
			VariantIdentifyingData const& snp,
			Genotypes const& genotypes,
			Ploidy const& ploidy,
			genfile::VariantDataReader&,
			ResultCallback callback
		) {
			genfile::Chromosome const& chromosome = snp.get_position().chromosome() ;
			if( snp.number_of_alleles() == 2 ) {
				bool const allDiploid = ( ploidy.array() == 2 ).cast< int >().sum() == ploidy.size() ;
				if( allDiploid ) {
					compute_autosomal_frequency( snp, genotypes, callback ) ;
				} else {
					compute_sex_chromosome_frequency( snp, genotypes, ploidy, callback ) ;
				}
			}
		}

		void compute_sex_chromosome_frequency(
			VariantIdentifyingData const& snp,
			Genotypes const& genotypes,
			Ploidy const& ploidy,
			ResultCallback callback
		) {
			Genotypes haploid_genotypes = genotypes ;
			Genotypes diploid_genotypes = genotypes ;
			assert( std::size_t( genotypes.rows() ) == ploidy.size() ) ;

			for( int i = 0; i < genotypes.rows(); ++i ) {
				if( ploidy(i) != 1 ) {
					haploid_genotypes.row(i).setZero() ;
				}
				if( ploidy(i) != 2 ) {
					diploid_genotypes.row(i).setZero() ;
				}
			}

			double const a_allele_count = haploid_genotypes.col(0).sum()
				+ ( ( 2.0 * diploid_genotypes.col(0).sum() ) + diploid_genotypes.col(1).sum() ) ;
			double const b_allele_count = haploid_genotypes.col(1).sum()
				+ ( ( 2.0 * diploid_genotypes.col(2).sum() ) + diploid_genotypes.col(1).sum() ) ;

			if( m_compute_counts ) {
				callback( "alleleA_count", a_allele_count ) ;
				callback( "alleleB_count", b_allele_count ) ;
			}

			if( m_compute_frequencies ) {
				double const total_allele_count = ( haploid_genotypes.sum() + 2.0 * diploid_genotypes.sum() ) ;
				double const a_allele_freq = a_allele_count / total_allele_count ;
				double const b_allele_freq = b_allele_count / total_allele_count ;

				callback( "alleleA_frequency", a_allele_freq ) ;
				callback( "alleleB_frequency", b_allele_freq ) ;

				if( a_allele_freq < b_allele_freq ) {
					callback( "minor_allele_frequency", a_allele_freq ) ;
					callback( "minor_allele", snp.get_allele(0) ) ;
					callback( "major_allele", snp.get_allele(1) ) ;
				}
				else if( a_allele_freq > b_allele_freq ) {
					callback( "minor_allele_frequency", b_allele_freq ) ;
					callback( "minor_allele", snp.get_allele(1) ) ;
					callback( "major_allele", snp.get_allele(0) ) ;
				} else {
					callback( "minor_allele_frequency", a_allele_freq ) ;
				}
			}
		}

		void compute_autosomal_frequency( VariantIdentifyingData const& snp, Genotypes const& genotypes, ResultCallback callback ) {
			double const a_allele_count = ( 2.0 * genotypes.col(0).sum() ) + genotypes.col(1).sum() ;
			double const b_allele_count = ( 2.0 * genotypes.col(2).sum() ) + genotypes.col(1).sum() ;

			if( m_compute_counts ) {
				callback( "alleleA_count", a_allele_count ) ;
				callback( "alleleB_count", b_allele_count ) ;
			}

			if( m_compute_frequencies ) {
				double const total_allele_count = ( 2.0 * genotypes.sum() ) ;
				double const a_allele_freq = a_allele_count / total_allele_count ;
				double const b_allele_freq = b_allele_count / total_allele_count ;

				callback( "alleleA_frequency", a_allele_freq ) ;
				callback( "alleleB_frequency", b_allele_freq ) ;

				if( a_allele_freq < b_allele_freq ) {
					callback( "minor_allele_frequency", a_allele_freq ) ;
					callback( "minor_allele", snp.get_allele(0) ) ;
					callback( "major_allele", snp.get_allele(1) ) ;
				}
				else if( a_allele_freq > b_allele_freq ) {
					callback( "minor_allele_frequency", b_allele_freq ) ;
					callback( "minor_allele", snp.get_allele(1) ) ;
					callback( "major_allele", snp.get_allele(0) ) ;
				} else {
					callback( "minor_allele_frequency", a_allele_freq ) ;
				}
			}
		}

		std::string get_summary( std::string const& prefix, std::size_t column_width ) const {
			return prefix + "AlleleFrequencyComputation" ;
		}
	private:
		bool const m_compute_counts ;
		bool const m_compute_frequencies ;
	} ;

	// What proportion of the mass on a genotype is due to high-confidence calls?
	struct CallMassComputation: public SNPSummaryComputation
	{

		CallMassComputation( double const threshhold = 0.9 ):
			m_threshhold(threshhold)
		{}

		void operator()( VariantIdentifyingData const& snp, Genotypes const& genotypes, Ploidy const& ploidy, genfile::VariantDataReader&, ResultCallback callback ) {
			genfile::Chromosome const& chromosome = snp.get_position().chromosome() ;
			bool const allDiploid = ( ploidy.array() == 2 ).cast< int >().sum() == ploidy.size() ;
			if( allDiploid ) {
				compute_autosomal_call_mass( snp, genotypes, callback ) ;
			} else {
				return ;
			}
		}

		void compute_autosomal_call_mass( VariantIdentifyingData const& snp, Genotypes const& genotypes, ResultCallback callback ) {
			Eigen::VectorXd const masses = genotypes.colwise().sum() ;
			m_hcGenotypes = ( genotypes.array() > m_threshhold ).cast< double >()  * genotypes.array() ;
			Eigen::VectorXd const hcMasses = m_hcGenotypes.colwise().sum() ;

			callback( "AA_mass_propn", hcMasses(0)/masses(0) ) ;
			callback( "AB_mass_propn", hcMasses(1)/masses(1) ) ;
			callback( "BB_mass_propn", hcMasses(2)/masses(2) ) ;

			callback( "non-AA-mass_propn", (hcMasses(1)+hcMasses(2))/(masses(1)+masses(2))) ;
			callback( "non-BB-mass_propn", (hcMasses(1)+hcMasses(0))/(masses(1)+masses(0))) ;
		}

		std::string get_summary( std::string const& prefix, std::size_t column_width ) const {
			return prefix + "CallMassComputation" ;
		}
	private:
		double const m_threshhold ;
		Genotypes m_hcGenotypes ;
	} ;

	struct MissingnessComputation: public SNPSummaryComputation {
		MissingnessComputation( double call_threshhold = 0.9 ): m_call_threshhold( call_threshhold ) {}
		void operator()(
			VariantIdentifyingData const& snp,
			Genotypes const& genotypes,
			Ploidy const& ploidy,
			genfile::VariantDataReader&,
			ResultCallback callback
		) {
			assert( std::size_t( genotypes.rows() ) == ploidy.size() ) ;

			double missingness = double( genotypes.rows() ) - genotypes.array().sum() ;
			callback( "missing_proportion", missingness / double( genotypes.rows() ) ) ;

			bool const allDiploid = ( ploidy.array() == 2 ).cast< int >().sum() == ploidy.size() ;
			if( allDiploid ) {
				callback( "A", 0 ) ;
				callback( "B", 0 ) ;
				if( snp.number_of_alleles() == 2 ) {
					callback( "AA", genotypes.col(0).sum() ) ;
					callback( "AB", genotypes.col(1).sum() ) ;
					callback( "BB", genotypes.col(2).sum() ) ;
				}
				callback( "NULL", genotypes.rows() - genotypes.sum() ) ;
			} else {
				compute_haploid_diploid_counts( snp, genotypes, ploidy, callback ) ;
			}
			callback( "total", genfile::VariantEntry::Integer( genotypes.rows() )) ;
		}

		void compute_haploid_diploid_counts(
			VariantIdentifyingData const& snp,
			Genotypes const& genotypes,
			Ploidy const& ploidy,
			ResultCallback callback
		) {
			std::map< int, Eigen::VectorXd > counts ;
			std::map< int, double > null_counts ;
			counts[ -1 ] = Eigen::VectorXd::Zero( 3 ) ;
			counts[ 0 ] = Eigen::VectorXd::Zero( 3 ) ;
			counts[ 1 ] = Eigen::VectorXd::Zero( 3 ) ;
			counts[ 2 ] = Eigen::VectorXd::Zero( 3 ) ;
			char countKey[5] = { '.', '.', 'm', 'f', '.' } ;
			std::map< int, std::size_t > sample_counts ;

			for( std::size_t i = 0; i < ploidy.size(); ++i ) {
				counts[ ploidy(i) ] += genotypes.row( i ) ;
				null_counts[ ploidy(i) ] += ( 1 - genotypes.row(i).sum() ) ;
				++sample_counts[ ploidy(i) ] ;
#if DEBUG_SNP_SUMMARY_COMPUTATION
				if( ploidy(i) == 1 && genotypes(i,2) != 0 ) {
					std::cerr << "! ( MissingnessComputation::compute_sex_chromosome_counts() ): individual " << (i+1) << "is male but has genotype " << genotypes.row(i) << "!!\n" ;
				}
#endif
			}

			callback( "A", counts[ 1 ]( 0 ) ) ;
			callback( "B", counts[ 1 ]( 1 ) ) ;
			callback( "AA", counts[ 2 ]( 0 ) ) ;
			callback( "AB", counts[ 2 ]( 1 ) ) ;
			callback( "BB", counts[ 2 ]( 2 ) ) ;
			callback( "NULL", null_counts[ 'm' ] + null_counts[ 'f' ] ) ;
			callback( "unknown_ploidy", counts[ -1 ].sum() + null_counts[ -1 ] ) ;
			assert( counts[ 1 ]( 2 ) == 0 ) ;
		}

		std::string get_summary( std::string const& prefix = "", std::size_t column_width = 20 ) const { return prefix + "MissingnessComputation" ; }
	private:
		double const m_call_threshhold ;
	} ;

	struct InfoComputation: public SNPSummaryComputation {
		void operator()(
			VariantIdentifyingData const& snp,
			Genotypes const& genotypes,
			Ploidy const& ploidy,
			genfile::VariantDataReader&,
			ResultCallback callback
		) {
			if( snp.number_of_alleles() == 2 ) {
				bool const allDiploid = ( ploidy.array() == 2 ).cast< int >().sum() == ploidy.size() ;
				if( allDiploid ) {
					compute_autosomal_info( snp, genotypes, callback ) ;
				} else {
					compute_sex_chromosome_info( snp, genotypes, ploidy, callback ) ;
				}
			} else {
				// we don't compute for multiallelics currently
			}
		}

		void compute_autosomal_info( VariantIdentifyingData const& snp, Genotypes const& genotypes, ResultCallback callback ) {
			double const theta_mle = ( genotypes.col( 1 ).sum() + 2.0 * genotypes.col( 2 ).sum() ) / ( 2.0 * genotypes.sum() ) ;
			double const theta_est = theta_mle ;

			Eigen::VectorXd const impute_fallback_distribution = Eigen::VectorXd::Zero( 3 ) ;
			Eigen::VectorXd fallback_distribution = Eigen::VectorXd::Zero( 3 ) ;
			fallback_distribution( 0 ) = ( 1 - theta_est ) * ( 1 - theta_est ) ;
			fallback_distribution( 1 ) = 2.0 * theta_est * ( 1 - theta_est ) ;
			fallback_distribution( 2 ) = theta_est * theta_est ;

			//std::cerr << "theta = " << theta_est << ", fallback_distribution = " << fallback_distribution.transpose() << ".\n" ;

			Eigen::VectorXd const levels = Eigen::VectorXd::LinSpaced( 3, 0, 2 ) ;

			double const info = 1.0 - (
				compute_sum_of_variances( levels, genotypes, fallback_distribution )
				/ ( genotypes.rows() * 2.0 * theta_est * ( 1 - theta_est ) )
			) ;

			double const impute_info = 1.0 - (
				compute_sum_of_variances( levels, genotypes, impute_fallback_distribution )
				/ ( genotypes.sum() * 2.0 * theta_mle * ( 1 - theta_mle ) )
			) ;

			callback( "info", info ) ;
			callback( "impute_info", impute_info ) ;
		}

		void compute_sex_chromosome_info(
			VariantIdentifyingData const& snp,
			Genotypes const& genotypes,
			Ploidy const& ploidy,
			ResultCallback callback
		) {
			Eigen::MatrixXd hap_or_diploid( genotypes.rows(), 2 ) ;
			for( std::size_t i = 0; i < ploidy.size(); ++i ) {
				if( ploidy(i) == 1 ) {
					hap_or_diploid(i,0) = 1 ;
					hap_or_diploid(i,1) = 0 ;
				}
				else if( ploidy[ i ] == 2 ) {
					hap_or_diploid(i,0) = 0 ;
					hap_or_diploid(i,1) = 1 ;
				}
				else {
					// Don't understand, so treat ploidy as missing.
					// individuals with missing sex do not contribute to the computation.
					// I think this is the least surprising thing to do.
					hap_or_diploid(i,0) = 0 ;
					hap_or_diploid(i,1) = 0 ;
				}
			}

			double const a_allele_count_diploid = (
					( genotypes.col( 1 ) + 2.0 * genotypes.col( 0 ) ).array() * ( hap_or_diploid.col(1).array() )
				).sum() ;
			double const b_allele_count_diploid = (
					( genotypes.col( 1 ) + 2.0 * genotypes.col( 2 ) ).array() * ( hap_or_diploid.col(1).array() )
				).sum() ;
			double const a_allele_count_haploid = 	(
					genotypes.col( 0 ).array() * hap_or_diploid.col(0).array()
				).sum() ;
			double const b_allele_count_haploid = 	(
					genotypes.col( 1 ).array() * hap_or_diploid.col(0).array()
				).sum() ;

			// MLE estimate of allele frequency
			double const theta_mle = ( b_allele_count_diploid + b_allele_count_haploid )
				/ ( a_allele_count_diploid + b_allele_count_diploid + a_allele_count_haploid + b_allele_count_haploid ) ;

			// For the new info measure, could regularise by data augmentation, adding one allele of each type.
			// This makes info better behaved at low frequencies.
			// Note adding one of each allele constitutes minimal prior information that the variant is polymorphic.
			// double const theta_est = ( b_allele_count_diploid + b_allele_count_haploid + 1 )
			//	/ ( a_allele_count_diploid + b_allele_count_diploid + a_allele_count_haploid + b_allele_count_haploid + 2 ) ;

			// ...but don't actually do that; it is less conservative at rare SNPs.
			double const theta_est = theta_mle ;

			Eigen::VectorXd diploid_fallback_distribution = Eigen::VectorXd::Zero( 3 ) ;
			diploid_fallback_distribution( 0 ) = ( 1 - theta_est ) * ( 1 - theta_est ) ;
			diploid_fallback_distribution( 1 ) = 2.0 * theta_est * ( 1 - theta_est ) ;
			diploid_fallback_distribution( 2 ) = theta_est * theta_est ;

			Eigen::VectorXd haploid_fallback_distribution = Eigen::VectorXd::Zero( 3 ) ;
			haploid_fallback_distribution( 0 ) = 1 - theta_est ;
			haploid_fallback_distribution( 1 ) = theta_est ;

			//std::cerr << "theta = " << theta_mle << ", fallback_distribution = " << fallback_distribution.transpose() << ".\n" ;

			Eigen::VectorXd const levels = Eigen::VectorXd::LinSpaced( 3, 0, 2 ) ;

			double info = 0.0 ;
			Eigen::VectorXd const haploids = ( hap_or_diploid.col( 0 ).array() == 1 ).cast< double >() ;
			Eigen::VectorXd const diploids = ( hap_or_diploid.col( 1 ).array() == 1 ).cast< double >() ;
			{
				if( haploids.sum() > 0 ) {
					info -= compute_sum_of_variances( levels, genotypes, haploid_fallback_distribution, haploids )
						/ ( theta_est * ( 1 - theta_est ) ) ;
				}

				if( diploids.sum() > 0 ) {
					info -= compute_sum_of_variances( levels, genotypes, diploid_fallback_distribution, diploids )
						/ ( 2.0 * theta_est * ( 1 - theta_est ) ) ;
				}

				double const totalProb = (
					( genotypes.block( 0, 0, genotypes.rows(), 2 ).rowwise().sum().array() * haploids.array() ).sum()
					+ ( genotypes.rowwise().sum().array() * diploids.array() ).sum()
				) ;

				info = 1.0 + (info / (haploids.sum() + diploids.sum() )) ;
			}

			{
				// IMPUTE-style MLE, treat all samples as diploid ignoring gender.
				// Haploids are encoded in the 0/1 columns here so we have to reverse-engineer the
				// original info computation to allow for this.
				double const autosomal_theta_mle = (
					( 2.0 * genotypes.col( 1 ).array() * haploids.array() )
					+ (( genotypes.col( 1 ) + 2.0 * genotypes.col( 2 ) ).array() * ( 1.0 - haploids.array() ))
				).sum() / ( 2.0 * genotypes.sum() ) ;

#if DEBUG
				std::cerr << "autosomal_theta_mle  = " << std::setprecision(10) << autosomal_theta_mle << ".\n" ;
				std::cerr << genotypes << ".\n" ;
#endif
				Eigen::VectorXd haploidLevels = Eigen::VectorXd::Zero(3) ;
				haploidLevels(1) = 2 ;
				double const impute_info = 1.0 - (
					(
						compute_sum_of_variances( haploidLevels, genotypes, Eigen::VectorXd::Zero( 3 ), haploids )
						+ compute_sum_of_variances( levels, genotypes, Eigen::VectorXd::Zero( 3 ), 1.0 - haploids.array() )
					) / ( genotypes.sum() * 2.0 * autosomal_theta_mle * ( 1 - autosomal_theta_mle ) )
				) ;

				callback( "info", info ) ;
				callback( "impute_info", impute_info ) ;
			}
		}

		std::string get_summary( std::string const& prefix = "", std::size_t column_width = 20 ) const { return prefix + "InfoComputation" ; }

	private:
		double compute_sum_of_variances( Eigen::VectorXd const& levels, Eigen::MatrixXd const& probabilities, Eigen::VectorXd const& fallback ) const {
			return compute_sum_of_variances( levels, probabilities, fallback, Eigen::VectorXd::Constant( probabilities.rows(), 1 ) ) ;
		}

		// treat the rows of the probabilities matrix as probabilities.
		// distribution for individual i is taken as a mixture of the distribution given by row i of probabilities,
		// and the fallback distribution if the sum of row i < 1, appropriately weighted.
		// Only individuals with inclusion = 1 are used.
		double compute_sum_of_variances( Eigen::VectorXd const& levels, Eigen::MatrixXd const& probabilities, Eigen::VectorXd const& fallback, Eigen::VectorXd const& inclusion ) const {
			assert( levels.size() == fallback.size() ) ;
			assert( levels.size() == probabilities.cols() ) ;
			assert( inclusion.size() == probabilities.rows() ) ;
			Eigen::VectorXd levels_squared = ( levels.array() * levels.array() ) ;

			double result = 0.0 ;
			for( int i = 0; i < probabilities.rows(); ++i ) {
				if( inclusion( i ) == 1 ) {
					double const c = probabilities.row( i ).sum() ;
					result += compute_variance( levels, levels_squared, probabilities.row( i ).transpose() + ( 1 - c ) * fallback ) ;
				}
			}
			return result ;
		}

		double compute_variance( Eigen::VectorXd const& levels, Eigen::VectorXd const& levels_squared, Eigen::VectorXd const& probs ) const {
			double const mean = ( probs.transpose() * levels )(0) ;
			double const variance = ( probs.transpose() * levels_squared )(0) - ( mean * mean ) ;
			// std::cerr << "compute_variance: levels = " << levels.transpose() << ", levels_squared = " << levels_squared.transpose() << ", probs = " << probs.transpose() << ", variance = " << variance << ".\n" ;
			return variance ;
		}
	} ;
}

SNPSummaryComputation::UniquePtr SNPSummaryComputation::create(
	std::string const& name
) {
	UniquePtr result ;
	if( name == "allele-frequencies" ) { result.reset( new snp_summary_component::AlleleFrequencyComputation( "everything" )) ; }
	else if( name == "allele-counts" ) { result.reset( new snp_summary_component::AlleleFrequencyComputation( "counts" )) ; }
	else if( name == "HWE" ) { result.reset( new snp_summary_component::HWEComputation()) ; }
	else if( name == "missingness" ) { result.reset( new snp_summary_component::MissingnessComputation()) ; }
	else if( name == "info" ) { result.reset( new snp_summary_component::InfoComputation()) ; }
	else if( name == "call-mass-proportion" ) { result.reset( new snp_summary_component::CallMassComputation()) ; }
	else if( name == "intensity-stats" ) { result.reset( new snp_summary_component::IntensitySummaryComputation() ) ; }
	else if( name == "multi-allele-counts" ) { result.reset( new snp_summary_component::AlleleCountComputation() ) ; }
	else {
		throw genfile::BadArgumentError( "SNPSummaryComputation::create()", "name=\"" + name + "\"" ) ;
	}
	return result ;
}