//============================================================================
//
// This file is part of the Thea toolkit.
//
// This software is distributed under the BSD license, as detailed in the
// accompanying LICENSE.txt file. Portions are derived from other works:
// their respective licenses and copyright information are reproduced in
// LICENSE.txt and/or in the relevant source files.
//
// Author: Siddhartha Chaudhuri
// First version: 2012
//
//============================================================================

#ifndef __Thea_Algorithms_JointBoost_hpp__
#define __Thea_Algorithms_JointBoost_hpp__

#include "../Common.hpp"
#include "../Array.hpp"
#include "../Iostream.hpp"
#include "../MatVec.hpp"
#include <boost/dynamic_bitset.hpp>
#include <iostream>

namespace Thea {
namespace Algorithms {

namespace JointBoostInternal {

typedef boost::dynamic_bitset<> SharingSet;

} // namespace JointBoostInternal

/**
 * A boosting classifier using shared features. Based on
 *
 * A. Torralba, K. Murphy and W. Freeman, "Sharing features: efficient boosting procedures for multiclass object detection",
 * Proc. CVPR, 2004.
 *
 * To use the class, implement an appropriate subclass of TrainingData, call train(), and then call predict().
 */
class THEA_API JointBoost
{
  public:
    THEA_DECL_SMART_POINTERS(JointBoost)

    /** Interface for accessing training data. */
    class THEA_API TrainingData
    {
      public:
        THEA_DECL_SMART_POINTERS(TrainingData)

        /** Destructor. */
        virtual ~TrainingData() {}

        /** Get the number of training examples. */
        virtual intx numExamples() const = 0;

        /** Get the number of features per example. */
        virtual intx numFeatures() const = 0;

        /**
         * Get the values of a particular feature for all training examples. \a feature_index must be in the range
         * 0... numFeatures() - 1.
         */
        virtual void getFeature(intx feature_index, Array<double> & values) const = 0;

        /** Get the class of each training example. */
        virtual void getClasses(Array<intx> & classes) const = 0;

        /** Get the weight of each training example. If an empty array is returned, all weights are set to 1. */
        virtual void getWeights(Array<double> & weights) const { weights.clear(); }

        /**
         * Get the name of each class. If it returns an empty vector, the numeric index of the class will be used as the label
         * name.
         */
        virtual void getClassNames(Array<std::string> & names) const { names.clear(); };

    }; // class TrainingData

    /**
     * %Options for the classifier. In most cases, passing a negative value for a normally non-negative parameter auto-selects a
     * suitable value for that parameter.
     */
    class THEA_API Options
    {
      public:
        /** Constructor. Sets default options. */
        Options();

        /**
         * Set the minimum number of boosting rounds that must be performed even if the error reduction between successive
         * rounds is below the threshold min_fractional_error_reduction (default -1).
         */
        Options & setMinBoostingRounds(intx rounds) { min_boosting_rounds = rounds; return *this; }

        /** Maximum number of boosting rounds (default -1). This also limits the maximum number of stumps added. */
        Options & setMaxBoostingRounds(intx rounds) { max_boosting_rounds = rounds; return *this; }

        /** Minimum error reduction required for boosting rounds to continue beyond min_boosting_rounds (default -1). */
        Options & setMinFractionalErrorReduction(double frac) { min_fractional_error_reduction = frac; return *this; }

        /** Set the fraction of features sampled in a round (default -1). */
        Options & setFeatureSamplingFraction(double frac) { feature_sampling_fraction = frac; return *this; }

        /** Set the maximum number of candidate thresholds generated, as a fraction of the number of features (default -1). */
        Options & setMaxThresholdsFraction(double frac) { max_thresholds_fraction = frac; return *this; }

        /**
         * Set if exhaustive O(2^C) optimization over all possible subsets of classes will be forced or not (default false).
         * Currently, this requires that there be no more classes than the number of bits in an uintx, minus 1. If you have a
         * large number of classes, use the greedy optimization instead. The latter is turned on by default when there are too
         * many classes, or if you call setForceGreedy(true).
         */
        Options & setForceExhaustive(bool value) { force_exhaustive = value; return *this; }

        /** Set if greedy O(C^2) optimization over subsets of classes will be forced or not (default false). */
        Options & setForceGreedy(bool value) { force_greedy = value; return *this; }

        /** Set whether progress information will be printed to the console or not (default true). */
        Options & setVerbose(bool value) { verbose = value; return *this; }

        /** Load classifier options from a disk file. */
        bool load(std::string const & path);

        /** Save classifier options to a disk file. */
        bool save(std::string const & path) const;

        /** Load classifier options from a standard input stream. */
        bool read(std::istream & in);

        /** Load classifier options from a text input stream. */
        bool read(TextInputStream & in);

        /** Save classifier options to a standard output stream. */
        bool write(std::ostream & out) const;

        /** Save classifier options to a text output stream. */
        bool write(TextOutputStream & out) const;

        /** Get the set of default options. */
        static Options const & defaults() { static Options const def; return def; }

      private:
        intx min_boosting_rounds;  /**< Minimum number of boosting rounds that must be performed even if the error reduction
                                        between successive rounds is below the threshold min_fractional_error_reduction. */
        intx max_boosting_rounds;  ///< Maximum number of boosting rounds. This also limits the maximum number of stumps added.
        double min_fractional_error_reduction;  /**< Minimum error reduction required for boosting rounds to continue beyond
                                                     min_boosting_rounds. */
        double feature_sampling_fraction;  ///< Fraction of features sampled in a round.
        double max_thresholds_fraction;  /**< Set the maximum number of candidate thresholds generated, as a fraction of the
                                              number of features. */
        bool force_exhaustive;  ///< Force exhaustive O(2^C) optimization over all possible subsets of classes.
        bool force_greedy;  ///< Force greedy O(C^2) optimization over subsets of classes.
        bool verbose;  ///< Print progress information to the console.

        friend class JointBoost;

    }; // class Options

    /**
     * Constructor.
     *
     * @param num_classes_ Number of classes for classification. The classes are numbered 0 to \a num_classes - 1.
     * @param num_features_ Number of features per object.
     * @param options_ Additional options controlling the behaviour of the classifier.
     */
    JointBoost(intx num_classes_, intx num_features_, Options const & options_ = Options::defaults());

    /** Construct a JointBoost classifier by loading it from a file. */
    JointBoost(std::string const & path);

    /** Destructor. */
    ~JointBoost();

    /** Reset the classifier to the initial state. */
    void clear();

    /** Get the number of classes into which objects may fall. The classes are numbered 0 to numClasses() - 1. */
    intx numClasses() const { return num_classes; }

    /** Get the number of features for an object. */
    intx numFeatures() const { return num_features; }

    /** Get the name of a class. */
    std::string getClassName(intx i) const;

    /** Get the current options for the classifier. */
    Options const & getOptions() const { return options; }

    /**
     * Train the strong classifier by several rounds of boosting.
     *
     * @param training_data_ Data used for training the classifier.
     * @param validation_data_ [Optional] Data used for cross-validation. Training is stopped if the error on the validation set
     *   starts increasing.
     *
     * @return The number of stumps added during the training process.
     */
    intx train(TrainingData const & training_data_, TrainingData const * validation_data_ = nullptr);

    /**
     * Predict the most likely class for an object with a given set of features, optionally also returning the probability of
     * belonging to each class.
     *
     * @param features The features of the object. Must contain numFeatures() values.
     * @param class_probabilities If non-null, used to return the probability of belonging to each class. Must be preallocated
     *   to numClasses() elements.
     *
     * @return The index of the most likely class of the object.
     */
    intx predict(double const * features, double * class_probabilities = nullptr) const;

    /** Load the classifier from a disk file. */
    bool load(std::string const & path);

    /** Save the trained classifier to disk. */
    bool save(std::string const & path) const;

    /** Print debugging information about this classifier to the console. */
    void dumpToConsole() const;

  private:
    /** A subset of classes encoded as a set of bits, one per class in global set. */
    typedef JointBoostInternal::SharingSet SharingSet;

    /** Decision stump (weak learner). */
    struct SharedStump
    {
      THEA_DECL_SMART_POINTERS(SharedStump)

      intx f;               ///< The index of the feature for this stump.
      SharingSet n;         ///< Indices of positive classes for this stump.
      double a;             ///< Regression weight a.
      double b;             ///< Regression weight b.
      double theta;         ///< Cut threshold.
      Array<double> k;      ///< Constants for classes not in the sharing set.

      /** Evaluate the stump for a given feature and class. */
      double operator()(double feature_value, intx class_index) const
      {
        if (n[(SharingSet::size_type)class_index])
          return feature_value > theta ? a : b;
        else
          return k[(size_t)class_index];
      }

      /** Get a printable description of the stump. */
      std::string toString() const;

      /** Load the stump from an input stream. */
      bool read(std::istream & in);

      /** Save the stump to an output stream. */
      bool write(std::ostream & out) const;

    }; // struct SharedStump

    /** Optimize a stump, for the current set of weights, by searching over features and subsets of classes. */
    double optimizeStump(SharedStump & stump, Array<intx> const & stump_classes);

    /** Optimize a stump via exhaustive O(2^C) search over all subsets of classes. */
    double optimizeStumpExhaustive(SharedStump & stump, Array<double> const & stump_features,
                                   Array<intx> const & stump_classes);

    /** Optimize a stump via greedy O(C^2) search over subsets of classes. */
    double optimizeStumpGreedy(SharedStump & stump, Array<double> const & stump_features, Array<intx> const & stump_classes);

    /** Fit stump parameters a, b and theta, for a particular subset of classes. */
    double fitStump(SharedStump & stump, Array<double> const & stump_features, Array<intx> const & stump_classes,
                    intx * num_generated_thresholds = nullptr);

    /**
     * Compute the prediction error (number of misclassified examples) on a validation set.
     *
     * @param validation_data_ Validation data set.
     * @param new_stump If non-null, added to the current set of stumps before measuring error (and removed afterwards).
     */
    double computeValidationError(TrainingData const * validation_data_, SharedStump::Ptr new_stump = SharedStump::Ptr());

    /** Load the classifier from an input stream. */
    bool read(std::istream & in);

    /** Save the trained classifier to an output stream. */
    bool write(std::ostream & out) const;

    intx num_classes;                ///< Number of object classes.
    intx num_features;               ///< Number of features per object.
    Array<std::string> class_names;  ///< Name of each class, if specified in training data.
    Options options;                 ///< Additional options.

    TrainingData const * training_data;  ///< Cached handle to training data, valid only during training.
    double feature_sampling_fraction;  ///< Cached fraction of features test per round, valid only during training.
    double max_thresholds_fraction;  /**< Cached number of candidate thresholds, expressed as a fraction of the number of
                                          features, valid only during training. */

    MatrixX<double> weights;  ///< Weights indexed by (class index, object_index).
    Array<SharedStump::Ptr> stumps;  ///< Current set of selected decision stumps.

}; // class JointBoost

} // namespace Algorithms
} // namespace Thea

#endif


// Author(s): Wieger Wesselink
// Copyright: see the accompanying file COPYING or copy at
// https://github.com/mCRL2org/mCRL2/blob/master/COPYING
//
// 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 enumerator_test.cpp
/// \brief Add your file description here.

#define BOOST_TEST_MODULE enumerator_test
#include <boost/test/included/unit_test_framework.hpp>
#include "mcrl2/core/detail/print_utility.h"
#include "mcrl2/data/substitutions/mutable_map_substitution.h"
#include "mcrl2/data/enumerator_with_iterator.h"
#include "mcrl2/pbes/enumerator.h"
#include "mcrl2/pbes/detail/parse.h"
#include "mcrl2/pbes/rewriters/simplify_rewriter.h"

using namespace mcrl2;
using namespace mcrl2::pbes_system;

  const std::string VARSPEC =
    "datavar         \n"
    "  m: Nat;       \n"
    "  n: Nat;       \n"
    "  b: Bool;      \n"
    "  c: Bool;      \n"
    "                \n"
    "predvar         \n"
    "  X: Bool, Pos; \n"
    "  Y: Nat;       \n"
    ;

BOOST_AUTO_TEST_CASE(test_enumerator)
{
  typedef pbes_system::simplify_data_rewriter<data::rewriter> pbes_rewriter;
  typedef data::enumerator_list_element<pbes_expression> enumerator_element;

  data::data_specification data_spec;
  data_spec.add_context_sort(data::sort_nat::nat());
  data::rewriter datar(data_spec);
  pbes_rewriter R(datar);

  data::variable_list v;
  v.push_front(data::variable("n", data::sort_nat::nat()));
  pbes_expression phi = parse_pbes_expression("val(n < 2)", VARSPEC);
  data::mutable_indexed_substitution<> sigma;
  data::enumerator_identifier_generator id_generator("x");
  bool accept_solutions_with_variables = true;
  data::enumerator_algorithm<pbes_rewriter> E(R, data_spec, datar, id_generator, accept_solutions_with_variables);
  std::set<pbes_system::pbes_expression> solutions;
  E.enumerate(enumerator_element(v, phi),
              sigma,
              [&](const enumerator_element& p)
              {
                solutions.insert(p.expression());
                return false; // do not interrupt
              },
              is_false
  );
  std::clog << "solutions = " << core::detail::print_list(solutions) << std::endl;
  BOOST_CHECK(solutions.size() == 1);
}

BOOST_AUTO_TEST_CASE(test_enumerator_with_iterator)
{
  typedef pbes_system::simplify_data_rewriter<data::rewriter> pbes_rewriter;
  typedef data::enumerator_list_element<pbes_expression> enumerator_element;

  data::data_specification data_spec;
  data_spec.add_context_sort(data::sort_nat::nat());
  data::rewriter datar(data_spec);
  pbes_rewriter R(datar);

  data::variable_list v;
  v.push_front(data::variable("n", data::sort_nat::nat()));
  pbes_expression phi = parse_pbes_expression("val(n < 2)", VARSPEC);
  data::mutable_indexed_substitution<> sigma;
  data::enumerator_identifier_generator id_generator;
  data::enumerator_algorithm_with_iterator<pbes_rewriter, enumerator_element, pbes_system::is_not_true> E(R, data_spec, datar, id_generator, 20);
  std::vector<pbes_system::pbes_expression> solutions;

  data::enumerator_queue<enumerator_element> P;
  P.push_back(enumerator_element(v, phi));
  for (auto i = E.begin(sigma, P); i != E.end(); ++i)
  {
    solutions.push_back(i->expression());
  }
  std::clog << "solutions = " << core::detail::print_list(solutions) << std::endl;
  BOOST_CHECK(solutions.size() >= 1);
}

BOOST_AUTO_TEST_CASE(test_enumerator_with_substitutions)
{
  typedef pbes_system::simplify_data_rewriter<data::rewriter> pbes_rewriter;
  typedef data::enumerator_list_element_with_substitution<pbes_expression> enumerator_element;

  data::data_specification data_spec;
  data_spec.add_context_sort(data::sort_nat::nat());
  data::rewriter datar(data_spec);
  pbes_rewriter R(datar);

  data::variable_list v;
  v.push_front(data::variable("n", data::sort_nat::nat()));
  pbes_expression phi = parse_pbes_expression("val(n < 2)", VARSPEC);
  data::mutable_indexed_substitution<> sigma;
  data::enumerator_identifier_generator id_generator;
  data::enumerator_algorithm_with_iterator<pbes_rewriter, enumerator_element, pbes_system::is_not_false> E(R, data_spec, datar, id_generator);
  std::vector<pbes_system::pbes_expression> solutions;

  data::enumerator_queue<enumerator_element> P;
  P.push_back(enumerator_element(v, phi));
  for (auto i = E.begin(sigma, P); i != E.end(); ++i)
  {
    solutions.push_back(i->expression());
    data::mutable_map_substitution<> sigma;
    i->add_assignments(v, sigma, datar);
    std::clog << "  solutions " << i->expression() << " substitution = " << sigma << std::endl;
    BOOST_CHECK(R(phi, sigma) == i->expression());
  }
  std::clog << "solutions = " << core::detail::print_list(solutions) << std::endl;
  BOOST_CHECK(solutions.size() >= 1);
}

BOOST_AUTO_TEST_CASE(enumerate_callback)
{
  typedef pbes_system::simplify_data_rewriter<data::rewriter> pbes_rewriter;
  typedef data::enumerator_list_element<pbes_expression> enumerator_element;
  data::enumerator_identifier_generator id_generator;
  data::data_specification dataspec;
  dataspec.add_context_sort(data::sort_int::int_());
  std::size_t max_count = 10;
  data::rewriter r(dataspec);
  pbes_rewriter R(r);
  data::enumerator_algorithm<pbes_rewriter> E(R, dataspec, r, id_generator, max_count);

  auto enumerate = [&](const pbes_expression& x)
  {
    data::rewriter::substitution_type sigma;
    pbes_expression result;
    id_generator.clear();
    if (is_forall(x))
    {
      const auto& x_ = atermpp::down_cast<pbes_system::forall>(x);
      result = pbes_system::true_();
      E.enumerate(enumerator_element(x_.variables(), R(x_.body())),
                  sigma,
                  [&](const enumerator_element& p)
                  {
                    result = data::optimized_and(result, p.expression());
                    return is_false(result);
                  },
                  pbes_system::is_true,
                  pbes_system::is_false
      );
    }
    else if (is_exists(x))
    {
      const auto& x_ = atermpp::down_cast<pbes_system::forall>(x);
      result = pbes_system::false_();
      E.enumerate(enumerator_element(x_.variables(), R(x_.body())),
                  sigma,
                  [&](const enumerator_element& p)
                  {
                    result = data::optimized_or(result, p.expression());
                    return is_true(result);
                  },
                  pbes_system::is_false,
                  pbes_system::is_true
      );
    }
    return result;
  };

  BOOST_CHECK_EQUAL(enumerate(parse_pbes_expression("forall n: Nat. val(n < 2)")), false_());
  BOOST_CHECK_EQUAL(enumerate(parse_pbes_expression("exists n: Nat. val(n < 2)")), true_());
}


//---------------------------------------------------------------------------//
//!
//! \file   DataGen_ElasticElectronMomentsEvaluator.cpp
//! \author Luke Kersting
//! \brief  Elastic electron cross section moments evaluator definition
//!
//---------------------------------------------------------------------------//

// Std Lib Includes
#include <limits>

// Boost Includes
#include <boost/function.hpp>
#include <boost/bind.hpp>

// FRENSIE Includes
#include "DataGen_ElasticElectronMomentsEvaluator.hpp"
#include "Utility_GaussKronrodIntegrator.hpp"
#include "Utility_LegendrePolynomial.hpp"
#include "Utility_PhysicalConstants.hpp"
#include "Utility_ContractException.hpp"
#include "MonteCarlo_TwoDDistributionHelpers.hpp"
#include "MonteCarlo_ElasticElectronScatteringDistributionNativeFactory.hpp"
#include "MonteCarlo_ElectroatomicReactionNativeFactory.hpp"
#include "Utility_StandardHashBasedGridSearcher.hpp"

namespace DataGen{

// Initialize static member data

// cutoff angle for analytical peak
double ElasticElectronMomentsEvaluator::s_rutherford_cutoff_angle = 1.0e-6;

// cutoff angle cosine for analytical peak
double ElasticElectronMomentsEvaluator::s_rutherford_cutoff_angle_cosine = 0.999999;

// Constructor
ElasticElectronMomentsEvaluator::ElasticElectronMomentsEvaluator(
    const Data::EvaluatedElectronDataContainer& native_eedl_data,
    const double& cutoff_angle )
  : d_native_eedl_data ( native_eedl_data ),
    d_cutoff_angle( cutoff_angle )
{
  // Make sure the data is valid
  testPrecondition( cutoff_angle >= s_rutherford_cutoff_angle );
  testPrecondition( cutoff_angle <= 2.0 );

  // Extract the common energy grid used for this atom
  Teuchos::ArrayRCP<double> energy_grid;
  energy_grid.assign( native_eedl_data.getElectronEnergyGrid().begin(),
                      native_eedl_data.getElectronEnergyGrid().end() );

  // Create the hard elastic distributions ( both Analog and Screened Rutherford )
  MonteCarlo::ElasticElectronScatteringDistributionNativeFactory::createHardElasticDistributions(
    d_analog_distribution,
    d_rutherford_distribution,
    native_eedl_data,
    cutoff_angle );

  // Construct the hash-based grid searcher for this atom
  Teuchos::RCP<Utility::HashBasedGridSearcher> grid_searcher(
     new Utility::StandardHashBasedGridSearcher<Teuchos::ArrayRCP<const double>, false>(
						     energy_grid,
						     100u ) );

  MonteCarlo::ElectroatomicReactionNativeFactory::createScreenedRutherfordElasticReaction(
					   native_eedl_data,
					   energy_grid,
                       grid_searcher,
					   d_rutherford_reaction );

  MonteCarlo::ElectroatomicReactionNativeFactory::createAnalogElasticReaction(
					   native_eedl_data,
					   energy_grid,
                       grid_searcher,
					   d_analog_reaction );
}

// Evaluate the Legnendre Polynomial expansion of the screened rutherford pdf
double ElasticElectronMomentsEvaluator::evaluateLegendreExpandedRutherford(
                                    const double scattering_angle,
                                    const double incoming_energy,
                                    const int polynomial_order ) const
{
  // Make sure the energy and angle are valid
  testPrecondition( incoming_energy > 0.0 );
  testPrecondition( scattering_angle >= 0.0 );
  testPrecondition( scattering_angle <= s_rutherford_cutoff_angle );

  // Evaluate the elastic pdf value at a given energy and scattering angle cosine
  double pdf_value =
            d_rutherford_distribution->evaluatePDF( incoming_energy,
                                                    scattering_angle );

  // Evaluate the Legendre Polynomial at the given angle and order
  double legendre_value =
    Utility::getLegendrePolynomial( 1.0-scattering_angle, polynomial_order );

  return pdf_value*legendre_value;
}

// Evaluate the Legnendre Polynomial expansion of the elastic scttering PDF
double ElasticElectronMomentsEvaluator::evaluateLegendreExpandedPDF(
                                    const double scattering_angle,
                                    const double incoming_energy,
                                    const int polynomial_order ) const
{
  // Make sure the energy and angle are valid
  testPrecondition( incoming_energy > 0.0 );
  testPrecondition( scattering_angle >= s_rutherford_cutoff_angle );
  testPrecondition( scattering_angle <= 2.0 );

  // Evaluate the elastic pdf value at a given energy and scattering angle cosine
  double pdf_value =
            d_analog_distribution->evaluatePDF( incoming_energy,
                                                scattering_angle );

  // Evaluate the Legendre Polynomial at the given angle and order
  double legendre_value =
    Utility::getLegendrePolynomial( 1.0 - scattering_angle, polynomial_order );

  return pdf_value*legendre_value;
}

// Evaluate the Legnendre Polynomial expansion of the elastic scttering PDF
double ElasticElectronMomentsEvaluator::evaluateLegendreExpandedPDFAtEnergyBin(
                                    const double scattering_angle,
                                    const unsigned incoming_energy_bin,
                                    const int polynomial_order ) const
{
  // Make sure the energy and angle are valid
  testPrecondition( incoming_energy_bin >= 0 );
  testPrecondition( scattering_angle >= s_rutherford_cutoff_angle );
  testPrecondition( scattering_angle <= 2.0 );

  // Evaluate the elastic pdf value at a given energy and scattering angle cosine
  double pdf_value =
            d_analog_distribution->evaluatePDF( incoming_energy_bin,
                                                scattering_angle );

  // Evaluate the Legendre Polynomial at the given angle and order
  double legendre_value =
    Utility::getLegendrePolynomial( 1.0 - scattering_angle, polynomial_order );

  return pdf_value*legendre_value;
}

// Evaluate the first n moments of the elastic scattering distribution at a given energy
void ElasticElectronMomentsEvaluator::evaluateElasticMoment(
            Teuchos::Array<Utility::long_float>& legendre_moments,
            const double energy,
            const int n,
            const double precision ) const
{
  // Make sure the energy and order is valid
  testPrecondition( energy > 0.0 );
  testPrecondition( n >= 0 );

  // Get common angular grid
  Teuchos::Array<double> angular_grid;

  angular_grid =
    MonteCarlo::ElasticElectronScatteringDistributionNativeFactory::getAngularGrid(
        d_native_eedl_data,
        energy,
        d_cutoff_angle );

  // resize array to the number of legendre moments wanted
  legendre_moments.resize(n+1);

  Utility::GaussKronrodIntegrator integrator( precision );

  // Calucuate the tabular and Rutherford component of the Legendre moment
  Utility::long_float tabular_moment,rutherford_moment;

  for ( int i = 0; i <= n; i++ )
  {
    double abs_error, moment_k;

    // Get the analog cross section
    double analog_cross_section =
        d_analog_reaction->getCrossSection( energy );

    // Calucuate the component of the moment from screened Rutherford peak
    evaluateScreenedRutherfordMoment( rutherford_moment, energy, i );

    // Create boost rapper function for the hard elastic differential cross section
    boost::function<double (double x)> distribution_wrapper =
      boost::bind<double>( &ElasticElectronMomentsEvaluator::evaluateLegendreExpandedPDF,
                         boost::cref( *this ),
                         _1,
                         energy,
                         i );

    Teuchos::Array<double>::iterator grid_point, grid_point_minus_one;
    grid_point_minus_one = angular_grid.begin();
    grid_point = ++angular_grid.begin();

    tabular_moment = Utility::long_float(0);
    for ( grid_point; grid_point != angular_grid.end(); grid_point++ )
    {
      moment_k = 0.0;
      abs_error = 0.0;
      integrator.integrateAdaptively<61>(
					distribution_wrapper,
					*grid_point_minus_one,
					*grid_point,
					moment_k,
					abs_error );

      grid_point_minus_one = grid_point;

      tabular_moment += moment_k;
    }
    legendre_moments[i] =
        rutherford_moment + tabular_moment*analog_cross_section;
  }
}

// Evaluate the first n moments of the elastic scattering distribution at a given energy
void ElasticElectronMomentsEvaluator::evaluateElasticMoment(
            Teuchos::Array<Utility::long_float>& legendre_moments,
            const unsigned energy_bin,
            const int n,
            const double precision ) const
{
  // Make sure the energy and order is valid
  testPrecondition( energy_bin >= 0 );
  testPrecondition( n >= 0 );

  // Get the energy at the given angular energy bin
  double energy = d_analog_distribution->getEnergy( energy_bin );

  // Get angular grid
  Teuchos::Array<double> angular_grid;

  angular_grid =
    MonteCarlo::ElasticElectronScatteringDistributionNativeFactory::getAngularGrid(
        d_native_eedl_data,
        energy,
        d_cutoff_angle );

  // resize array to the number of legendre moments wanted
  legendre_moments.resize(n+1);

  Utility::GaussKronrodIntegrator integrator( precision );

  // Calucuate the tabular and Rutherford component of the Legendre moment
  Utility::long_float tabular_moment,rutherford_moment;

  for ( int i = 0; i <= n; i++ )
  {
    double abs_error, moment_k;

    // Get the analog cross section
    double analog_cross_section =
        d_analog_reaction->getCrossSection( energy );

    // Calucuate the component of the moment from screened Rutherford peak
    evaluateScreenedRutherfordMoment( rutherford_moment, energy, i );

    // Create boost rapper function for the hard elastic differential cross section
    boost::function<double (double x)> distribution_wrapper =
      boost::bind<double>( &ElasticElectronMomentsEvaluator::evaluateLegendreExpandedPDFAtEnergyBin,
                         boost::cref( *this ),
                         _1,
                         energy_bin,
                         i );

    Teuchos::Array<double>::iterator grid_point, grid_point_minus_one;
    grid_point_minus_one = angular_grid.begin();
    grid_point = ++angular_grid.begin();

    tabular_moment = Utility::long_float(0);
    for ( grid_point; grid_point != angular_grid.end(); grid_point++ )
    {
      moment_k = 0.0;
      abs_error = 0.0;
      integrator.integrateAdaptively<61>(
					distribution_wrapper,
					*grid_point_minus_one,
					*grid_point,
					moment_k,
					abs_error );

      grid_point_minus_one = grid_point;

      tabular_moment += moment_k;
    }
    legendre_moments[i] =
        rutherford_moment + tabular_moment*analog_cross_section;
  }
}

// Evaluate the nth cross section moment of the screened Rutherford peak distribution
void ElasticElectronMomentsEvaluator::evaluateScreenedRutherfordMoment(
            Utility::long_float& rutherford_moment,
            const double energy,
            const int n ) const
{
  // Make sure the energy and angle are valid
  testPrecondition( energy > 0.0 );

  double angle;

  angle = s_rutherford_cutoff_angle;

  // Calcuate Moliere's modified screening constant (eta)
  double eta =
    d_rutherford_distribution->evaluateMoliereScreeningConstant( energy );

  /*!  \details If eta is small ( << 1 ) a recursion relationship can be used to
   *! calculate the moments of the screened Rutherford peak. For larger eta the
   *! moments will be calculated by numerical integration.
   */
  if ( eta <= 1.0e-2 )
  {
    rutherford_moment = d_rutherford_reaction->getCrossSection( energy );

    if ( n > 0 )
    {
      Teuchos::Array<Utility::long_float> coef_one( n+1 ), coef_two( n+1 );

      coef_one[0] = Utility::long_float(0);
      coef_one[1] = ( log( ( eta + angle )/( eta ) ) ) -
                     angle/( angle + eta );

      coef_two[0] = angle;
      coef_two[1] = ( Utility::long_float(2) - angle )*angle/
                      Utility::long_float(2);

      for ( int i = 1; i < n; i++ )
      {
        coef_one[i+1] =
          ( Utility::long_float(2) + Utility::long_float(1)/i )*
          ( Utility::long_float(1) + eta )*coef_one[i] -
          ( Utility::long_float(1) + Utility::long_float(1)/i )*coef_one[i-1] -
          ( ( Utility::long_float(2) + Utility::long_float(1)/i )/
          ( angle + eta ) )*( angle - coef_two[i] );

        coef_two[i+1] =
          ( Utility::long_float(2)*i + Utility::long_float(1) )/
          ( i + Utility::long_float(2) )*( Utility::long_float(1) - angle )*
          coef_two[i] -
          ( i - Utility::long_float(1) )/( i + Utility::long_float(2) )*
          coef_two[i-1];
      }
      Utility::long_float frac_disc =
          angle*( Utility::long_float(1) + eta/Utility::long_float(2) )/
        ( angle + eta );

      rutherford_moment *= (1.0 - eta*coef_one[n]/frac_disc );
    }
  }
  else // Numerically integrate the moment
  {
    Utility::GaussKronrodIntegrator integrator( 1e-13 );

    double abs_error, moment_n;

    double rutherford_cross_section =
        d_rutherford_reaction->getCrossSection( energy );

    double moment_zero =
        d_rutherford_distribution->evaluateIntegratedPDF( energy );

    if ( n == 0 )
    {
      moment_n = moment_zero;
    }
    else
    {
      // Create boost rapper function for the screened Rutherford peak
      boost::function<double (double x)> wrapper =
        boost::bind<double>( &ElasticElectronMomentsEvaluator::evaluateLegendreExpandedRutherford,
                         boost::cref( *this ),
                         _1,
                         energy,
                         n );

      integrator.integrateAdaptively<61>(
					wrapper,
                    0.0,
					s_rutherford_cutoff_angle,
					moment_n,
					abs_error );
    }
    rutherford_moment = moment_n/moment_zero*rutherford_cross_section;
  }
}

} // end DataGen namespace

//---------------------------------------------------------------------------//
// end DataGen_ElasticElectronMomentsEvaluator.cpp
//---------------------------------------------------------------------------//


/*
  This Source Code Form is subject to the terms of the Mozilla Public
  License, v. 2.0. If a copy of the MPL was not distributed with this
  file, You can obtain one at http://mozilla.org/MPL/2.0/.

  Copyright (c) 2019 Panda Team
*/
#include <chrono>
#include <functional>
#include <cstdlib>
#include <random>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/gregorian/greg_date.hpp>
#include "../crossfilter.hpp"

// #include "spdlog/spdlog.h"
// #include "spdlog/async.h"
// #include "spdlog/sinks/stdout_sinks.h"
// #include "spdlog/sinks/basic_file_sink.h"
// #include "spdlog/sinks/daily_file_sink.h"

struct Payment {
    Payment(const uint64_t& d, double a)
        : date(d)
        , amount(a)
    {
    }
    Payment() {}
    uint64_t date;
    double amount;
};
namespace std {
template <>
struct hash<Payment> {
    typedef Payment argument_type;
    typedef std::size_t result_type;
    result_type operator()(argument_type const& s) const noexcept
    {
        result_type const h1(std::hash<uint64_t> {}(s.date));
        result_type const h2(std::hash<double> {}(s.amount));
        return (h1 ^ (h2 << 1));  // or use boost::hash_combine (see Discussion)
    }
};
}

double drandom(int w)
{
    auto r = std::rand();
    auto r1 = r / (double)RAND_MAX;
    auto r2 = w * r1;
    auto r3 = std::floor(r2);

    return r3;
}

uint64_t mtime()
{
    return std::chrono::duration_cast<std::chrono::milliseconds>(
        std::chrono::high_resolution_clock::now().time_since_epoch())
        .count();
}
struct RandomIndex {
    std::vector<double> distr;
    std::random_device rd;
    std::mt19937 gen;
    std::discrete_distribution<> d;
    template <typename I>
    RandomIndex(I begin, I end)
        : distr(begin, end)
        , rd()
        , gen(rd())
        , d(distr.begin(), distr.end())
    {
    }
    int operator()() { return std::abs(d(gen)); }
};

uint64_t epoch(const boost::posix_time::ptime& t)
{
    boost::posix_time::ptime epoch = boost::posix_time::time_from_string("1970-01-01 00:00:00.000");
    boost::posix_time::time_duration const diff = t - epoch;
    return diff.total_seconds();
}
auto randomRecentDate(RandomIndex& randomDayOfWeek, RandomIndex& randomHourOfDay, int weeks)
{
    auto result = [&randomDayOfWeek, &randomHourOfDay, weeks]() {
        auto now = boost::posix_time::ptime(boost::posix_time::second_clock::local_time());
        int day_offset = int(drandom(weeks * 7));

        now = now - boost::gregorian::days(day_offset);
        now += boost::gregorian::days(randomDayOfWeek());

        return epoch(boost::posix_time::ptime(now.date(),
            boost::posix_time::hours(randomHourOfDay()) + boost::posix_time::minutes(std::rand() % 60)
                + boost::posix_time::seconds(std::rand() % 60)));
    };
    return result;
}

struct RandomLogNormal {
    std::random_device rd;
    std::mt19937 gen;
    std::normal_distribution<> d;
    RandomLogNormal(double mean, double stddev)
        : gen(rd())
        , d(mean, stddev)
    {
    }
    double operator()() { return std::abs(d(gen)); }
};

int makedate(uint64_t d)
{
    auto pt = boost::posix_time::from_time_t(d);
    auto ymd = pt.date().year_month_day();
    //  20181120
    return ymd.year * 10000 + ymd.month * 100 + ymd.day;
}
int makedayofweek(uint64_t d)
{
    auto pt = boost::posix_time::from_time_t(d);
    return pt.date().day_of_week().as_number();
}
int makehours(uint64_t d)
{
    auto pt = boost::posix_time::from_time_t(d);
    return pt.time_of_day().hours();
}

int main()
{
    uint64_t firstSize = 90000;
    uint64_t secondSize = 10000;
    auto totalSize = firstSize + secondSize;
    double v1[] = { 0, .6, .7, .75, .8, .76, 0 };
    double v2[] = { 0, 0, 0, 0, 0, 0, 0, .2, .5, .7, .85, .9, .8, .69, .72, .8, .78, .7, .3, 0, 0, 0, 0, 0 };
    std::random_device rd;
    std::mt19937 gen(rd());

    //  auto logger = spdlog::daily_logger_mt<spdlog::async_factory>("console", "thread_log.txt",10,0);
    //  logger->flush_on(spdlog::level::info);
    //  spdlog::set_pattern("[%H:%M:%S.%f] [%t]: %v");

    RandomIndex randomDayOfWeek(std::begin(v1), std::end(v1));
    RandomIndex randomHourOfDay(std::begin(v2), std::end(v2));
    auto randomDate = randomRecentDate(randomDayOfWeek, randomHourOfDay, 13);
    RandomLogNormal randomAmount(2.5, 5);
    auto then = mtime();

    std::vector<Payment> firstBatch;
    firstBatch.reserve(firstSize);

    std::vector<Payment> secondBatch;
    secondBatch.reserve(secondSize);

    for (std::size_t i = 0; i < firstSize; i++) {
        firstBatch.emplace_back(randomDate(), randomAmount());
    }
    for (std::size_t i = 0; i < secondSize; i++) {
        secondBatch.emplace_back(randomDate(), randomAmount());
    }

    std::cout << "Synthesizing " << totalSize << " records:" << (mtime() - then) << "ms." << std::endl;

    then = mtime();

    cross::filter<Payment> payments(firstBatch);
    auto all = payments.feature_count();
    auto amount = payments.dimension([](auto d) { return d.amount; });
    auto amounts = amount.feature_count([](auto d) { return std::floor(d); });
    auto date = payments.dimension([](auto d) { return d.date; });
    auto dates = date.feature_count([](auto d) { return makedate(d); });
    auto day = payments.dimension([](auto d) {
        return makedayofweek(d.date);  //d.date.date().day_of_week().as_number();
    });
    auto days = day.feature_count();
    auto hour = payments.dimension([](auto d) {
        return makehours(d.date);  // d.date.time_of_day().hours();
    });
    auto hours = hour.feature_count();

    std::cout << "Indexing " << firstSize << " records: " << (mtime() - then) << " ms." << std::endl;
    auto then3 = mtime();
    payments.add(secondBatch);

    std::cout << "Indexing " << secondSize << " records: " << (mtime() - then3) << " ms." << std::endl;
    std::cout << "Total indexing time: " << (mtime() - then) << " ms." << std::endl;

    std::cout << "dates.size()=" << dates.size() << std::endl;
    std::cout << "days.size()=" << days.size() << std::endl;
    std::cout << "hours.size()=" << hours.size() << std::endl;
    std::cout << "amounts.size()=" << amounts.size() << std::endl;
    then = mtime();
    auto today = boost::posix_time::second_clock::local_time();
    int k = 0;

    for (int i = 0; i < 90; ++i) {
        auto ti = epoch(today - boost::gregorian::days(i));
        for (int j = 0; j < i; j++) {
            auto tj = epoch(today - boost::gregorian::days(j));
            date.filter_range(ti, tj);
            k++;
            dates.all();
            days.all();
            hours.all();
            amounts.all();
            all.value();
            date.top(40).size();
        }
    }
    std::cout << "Filtering by date: " << ((mtime() - then) / double(k)) << " ms/op." << std::endl;
    date.filter_all();

    then = mtime();
    k = 0;

    //  for(int l = 0; l < 100; l++) {
    for (int i = 0; i < 7; i++) {
        for (int j = i; j < 7; j++) {
            day.filter_range(i, j);
            k++;
            dates.all();
            days.all();
            hours.all();
            amounts.all();
            all.value();
            date.top(40);
        }
    }
    //  }

    std::cout << "Filtering by day: " << ((mtime() - then) / double(k)) << " ms/op." << std::endl;
    day.filter_all();

    then = mtime();
    k = 0;

    for (int i = 0; i < 24; i++) {
        for (int j = i; j < 24; j++) {
            hour.filter_range(i, j);
            k++;
            dates.all();
            days.all();
            hours.all();
            amounts.all();
            all.value();
            date.top(40);
        }
    }
    std::cout << "Filtering by hour: " << ((mtime() - then) / double(k)) << " ms/op." << std::endl;
    hour.filter_all();

    then = mtime();
    k = 0;
    for (int i = 0; i < 35; i++) {
        for (int j = i; j < 35; j++) {
            amount.filter_range(i, j);
            k++;
            dates.all();
            days.all();
            hours.all();
            amounts.all();
            all.value();
            date.top(40);
        }
    }
    std::cout << "Filtering by amount: " << ((mtime() - then) / double(k)) << " ms/op." << std::endl;
    amount.filter_all();

    then = mtime();
    payments.remove([](auto, int i) { return i % 10 == 1; });

    std::cout << "Removing  " << totalSize / 10 << " records: " << (mtime() - then) << " ms." << std::endl;

    std::cout << std::endl;
    std::cout << "Day of Week:" << std::endl;
    for (auto& d : days.all()) {
        std::cout << "       " << d.first << ": " << d.second << std::endl;
    }
    std::cout << std::endl;
    std::cout << "Hour of Day:" << std::endl;
    for (auto& h : hours.all()) {
        std::cout << "       " << h.first << ": " << h.second << std::endl;
    }

    std::cout << std::endl;
    std::cout << "Date:" << std::endl;
    for (auto& h : dates.all()) {
        std::cout << "       " << h.first << ": " << h.second << std::endl;
    }
    std::cout << std::endl;
    std::cout << "Amount:" << std::endl;
    for (auto& h : amounts.all()) {
        std::cout << "       " << int(h.first) << ": " << h.second << std::endl;
    }

    return 0;
}


#include "pairnode.h"
#include <boost/foreach.hpp>

using namespace std;
int PairNode::m_numNodes;

PairNode::PairNode(const HyperParameters &hp, int depth) : m_hp( hp ), m_depth( depth ),
            m_bestFeature( 0 ), m_bestThreshold( 0.0 )
{
    m_isLeaf = false;
    m_nodeIndex = m_numNodes++;
}

PairNode::PairNode(const HyperParameters &hp, int depth, int reset) : m_hp(hp), m_depth( depth )
{
    m_isLeaf = false;
    m_numNodes = (reset >= 0) ? 0 : m_numNodes;
    m_nodeIndex = m_numNodes++;
}

PairNode::~PairNode()
{
}

void PairNode::train(const std::vector<Pair>& pairs)
{
    if ( shouldISplit(pairs) ) {
        int numFeatures = pairs[0].x1().size();
        std::vector<int> randFeatures = randPerm(numFeatures, m_hp.numRandomFeatures);
        findHypotheses(pairs,randFeatures);
        // split the data
        std::vector<Pair> leftPairs, rightPairs;
        evalNode(pairs,leftPairs, rightPairs);

        // pass them to the left and right child, respectively
        m_leftChildNode = Ptr(new PairNode(m_hp,m_depth + 1));
        m_rightChildNode = Ptr(new PairNode(m_hp,m_depth + 1));

        m_leftChildNode->train(leftPairs);
        m_rightChildNode->train(rightPairs);
    }
    else {
        m_isLeaf = true;
    }
}

void PairNode::evalNode(const std::vector<Pair>& pairs,std::vector<Pair>& leftPairs,std::vector<Pair>& rightPairs)
{
    // What shall we do with positives that are separated ?
    // -> for now, we simply discard them
    std::vector<Pair>::const_iterator it( pairs.begin() );
    std::vector<Pair>::const_iterator end( pairs.end() );
    while( it != end ) {
        float v1 = it->x1()[m_bestFeature];
        float v2 = it->x2()[m_bestFeature];

        if (v1 > m_bestThreshold && v2 > m_bestThreshold) {
            rightPairs.push_back( *it );
        }
        if (v1 < m_bestThreshold && v2 < m_bestThreshold) {
            leftPairs.push_back( *it );
        }
        ++it;
    }
}

void PairNode::findHypotheses(const std::vector<Pair>& pairs, const std::vector<int> randFeatures)
{
    std::vector<int>::const_iterator featureIt(randFeatures.begin());
    std::vector<int>::const_iterator featureEnd(randFeatures.end());

    double bestError = 1.0, bestPosError = 1.0, bestNegError = 1.0, bestThreshold = 0.0;
    int numPairs = pairs.size();
    int numPosPairs = 0, numNegPairs = 0;
    int bestFeature = *featureIt;
    float curError = 1.0, curPosError = 1.0, curNegError = 1.0;
    float curThreshold = 0.0;
    int NUM_THRESHOLDS = 10;
    std::vector<Pair>::const_iterator pairIt;
    std::vector<Pair>::const_iterator pairEnd( pairs.end() );

    while ( featureIt != featureEnd )
    {
        for(int t = 0; t < NUM_THRESHOLDS; t++) {
            curThreshold = _rand();
            int numWrongPos = 0, numWrongNeg = 0;
            pairIt = pairs.begin();
            while( pairIt != pairEnd ) {
                std::vector<float> x1 = pairIt->x1();
                std::vector<float> x2 = pairIt->x2();
                float v1 = x1.at( *featureIt );
                float v2 = x2.at ( *featureIt );

                if( pairIt->label() == 1 ) {
                    if ( (v1 < curThreshold && v2 >= curThreshold) || (v1 >= curThreshold && v2 < curThreshold) ) {
                        numWrongPos++;
                    }
                    numPosPairs++;
                }
                else {
                    if ( (v1 < curThreshold && v2 < curThreshold) || (v1 >= curThreshold && v2 >= curThreshold) ) {
                        numWrongNeg++;
                    }
                    numNegPairs++;
                }
                ++pairIt;
            }
            curError = (double)(numWrongPos+numWrongNeg)/(double)numPairs;
            curPosError = (double)numWrongPos/(double)numPosPairs;
            curNegError = (double)numWrongNeg/(double)numNegPairs;

            //if (curPosError < bestPosError && curNegError <= bestNegError)
            if (curPosError < bestPosError)
            {
                bestPosError = curPosError;
                bestNegError = curNegError;
                bestError = curError;
                bestFeature = *featureIt;
                bestThreshold = curThreshold;
            }
            else if (curPosError == bestPosError && curNegError < bestNegError)
            {
                bestPosError = curPosError;
                bestNegError = curNegError;
                bestError = curError;
                bestFeature = *featureIt;
                bestThreshold = curThreshold;
            }

        }
        ++featureIt;
    }

    //cout << "bestError: " << bestError << " bestThr: " << bestThreshold << endl; exit(0);

    m_bestFeature = bestFeature;
    m_bestThreshold = (float) bestThreshold;
}

void PairNode::getPath(const std::vector<float>& x, std::vector<int>& path)
{
    path.push_back(m_nodeIndex);

    if (!m_isLeaf)
    {
        float val = x[m_bestFeature];
        if (val >= m_bestThreshold) {
            m_rightChildNode->getPath(x,path);
        }
        else {
            m_leftChildNode->getPath(x,path);
        }
    }
}

bool PairNode::shouldISplit(const std::vector<Pair>& pairs)
{
    // Test depth and sample size
    if (m_depth >= m_hp.maxTreeDepth || pairs.size() < 3)// || inBagSamples[0] > m_hp.numLabeled)
    {
        return false;
    }

    // Test pureness of the node
    std::vector<Pair>::const_iterator pairIt( pairs.begin() );
    std::vector<Pair>::const_iterator pairEnd( pairs.end() );
    int startLabel = pairIt->label();
    while( pairIt != pairEnd)
    {
        if (pairIt->label() != startLabel)
        {
            return true;
        }
        pairIt++;
    }

    return false;
}

int PairNode::getDepth(const int oldDepth)
{
    if (m_isLeaf)
    {
        return (m_depth > oldDepth) ? m_depth : oldDepth;
    }
    else
    {
        int leftDepth = m_leftChildNode->getDepth(oldDepth);
        int rightDepth = m_rightChildNode->getDepth(oldDepth);

        if (leftDepth > oldDepth || rightDepth > oldDepth)
        {
            return (leftDepth > rightDepth) ? leftDepth : rightDepth;
        }
        else
        {
            return oldDepth;
        }
    }
}

std::vector<int> PairNode::getLeafNodeIds()
{
    std::vector<int> leafIds;
    if(m_isLeaf) {
        leafIds.push_back(m_nodeIndex);
        return leafIds;
    }
    else {
        std::vector<int> rightIds;
        std::vector<int> leftIds;
        rightIds = m_rightChildNode->getLeafNodeIds();
        leftIds = m_leftChildNode->getLeafNodeIds();
        leafIds.insert(leafIds.end(),rightIds.begin(),rightIds.end());
        leafIds.insert(leafIds.end(),leftIds.begin(),leftIds.end());
        return leafIds;
    }
}


//---------------------------------------------------------------------------//
// Copyright (c) 2018-2020 Mikhail Komarov <nemo@nil.foundation>
// Copyright (c) 2020 Nikita Kaskov <nbering@nil.foundation>
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt
//---------------------------------------------------------------------------//

#ifndef CRYPTO3_BLAKE2B_POLICY_HPP
#define CRYPTO3_BLAKE2B_POLICY_HPP

#include <boost/crypto3/detail/static_digest.hpp>
#include <boost/crypto3/detail/basic_functions.hpp>

namespace boost {
    namespace crypto3 {
        namespace hashes {
            namespace detail {
                template<std::size_t DigestBits>
                struct blake2b_policy : public ::boost::crypto3::detail::basic_functions<64> {

                    constexpr static const std::size_t state_bits = 512;
                    constexpr static const std::size_t state_words = state_bits / word_bits;
                    typedef std::array<word_type, state_words> state_type;

                    constexpr static const std::size_t block_bits = 1024;
                    constexpr static const std::size_t block_words = block_bits / word_bits;
                    typedef std::array<word_type, block_words> block_type;

                    constexpr static const std::size_t length_bits = word_bits;

                    typedef typename stream_endian::little_octet_big_bit digest_endian;

                    constexpr static const std::size_t digest_bits = DigestBits;
                    typedef static_digest<digest_bits> digest_type;

                    constexpr static const std::size_t rounds = 12;

                    struct iv_generator {
                        state_type const &operator()() const {
                            constexpr static const state_type H0 = {
                                {0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
                                 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179}};
                            return H0;
                        }
                    };

                    constexpr static const std::size_t salt_bits = 64;
                    typedef typename boost::uint_t<salt_bits>::exact salt_type;
                    constexpr static const salt_type salt_value = 0xFFFFFFFFFFFFFFFF;
                };
            }    // namespace detail
        }        // namespace hashes
    }            // namespace crypto3
}    // namespace boost

#endif    // CRYPTO3_BLAKE2B_POLICY_HPP


/*
 * Copyright 2019 Forschungszentrum Juelich GmbH
 * Copyright 2009-2019 The VOTCA Development Team (http://www.votca.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 *     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.
 *
 */
#define BOOST_TEST_MAIN

#define BOOST_TEST_MODULE linalg_test

// Third party includes
#include <boost/test/unit_test.hpp>

// Local private VOTCA includes
#include "QDMEwald.hpp"

BOOST_AUTO_TEST_SUITE(NaCl)

BOOST_AUTO_TEST_CASE(NaCl) {
  Kokkos::initialize();
  {

    // testing system size
    const int cry_l = 16;
    const int N = cry_l * cry_l * cry_l;
    const double l = (double)cry_l / 2.0;

    // testing particle positions
    std::vector<double> xyz(3 * N);

    // testing charges
    std::vector<double> q(N);

    // testing dipole momenta
    std::vector<double> d(3 * N);
    std::vector<double> Q(9 * N);

    for (int i = 0; i < N; ++i) {
      int ix = i % cry_l;
      int iy = (i % (cry_l * cry_l)) / cry_l;
      int iz = i / (cry_l * cry_l);

      xyz.at(3 * i + 0) = (double)(ix)*0.5;
      xyz.at(3 * i + 1) = (double)(iy)*0.5;
      xyz.at(3 * i + 2) = (double)(iz)*0.5;

      q.at(i) = (((ix + iy + iz) % 2) ? 1.0 : -1.0);
    }

    for (double& i : d) i = 0.0;

    for (double& i : Q) i = 0.0;

    double alpha = 1.02113246946;
    double r_max = 3.64;
    double k_max = std::pow(7.59093986701, 2.0);

    // testing the initialization with different data types
    QDMEwald<double> qdme_d(alpha, k_max, r_max, l);
    QDMEwald<float> qdme_f((float)alpha, (float)k_max, (float)r_max, (float)l);
    QDMEwald<long double> qdme_ld((long double)alpha, (long double)k_max,
                                  (long double)r_max, (long double)l);

    qdme_d.compute(xyz, q, d, Q);

    auto total_energy = qdme_d.get_energy();
    auto total_force = qdme_d.get_total_force();

    BOOST_CHECK_CLOSE(total_energy, N * -1.747564594633, 1e-9);
    BOOST_REQUIRE(std::abs(total_force.at(0)) < 1e-9);
    BOOST_REQUIRE(std::abs(total_force.at(1)) < 1e-9);
    BOOST_REQUIRE(std::abs(total_force.at(2)) < 1e-9);
  }
  Kokkos::finalize();
}

BOOST_AUTO_TEST_SUITE_END()


/**
 * @file
 * @copyright This code is licensed under the 3-clause BSD license.\n
 *            Copyright ETH Zurich, Laboratory for Physical Chemistry, Reiher Group.\n
 *            See LICENSE.txt for details.
 */

#include <Utils/IO/Logger.h>
#include <Utils/Math/IterativeDiagonalizer/DavidsonDiagonalizer.h>
#include <gmock/gmock.h>
#include <Eigen/Eigenvalues>
#include <Eigen/Sparse>
#include <chrono>
#include <random>

namespace Scine {
namespace Utils {

using namespace testing;

/**
 * @class ADavidsonDiagonalizerTest @file DavidsonDiagonalizerTest.cpp
 * @test
 */
class ADavidsonDiagonalizerTest : public Test {
 public:
  int arbitraryNumberEigenvalue = 4;
  int arbitraryTrialSubspace = 8;
  int arbitraryDimension = 100;
  Eigen::MatrixXd arbitraryDenseMatrix;
  Eigen::SparseMatrix<double> arbitrarySparseMatrix;

 private:
  void SetUp() final {
    Eigen::VectorXd diagonal = Eigen::VectorXd::Ones(arbitraryDimension);
    for (int i = 0; i < arbitraryDimension; ++i) {
      diagonal(i) *= i;
    }
    Eigen::MatrixXd arbitraryMatrix = 0.01 * Eigen::MatrixXd::Random(arbitraryDimension, arbitraryDimension);
    arbitraryDenseMatrix = arbitraryMatrix + arbitraryMatrix.transpose();
    arbitraryDenseMatrix.diagonal() += diagonal;

    arbitrarySparseMatrix.resize(arbitraryDimension, arbitraryDimension);

    Eigen::MatrixXd randomMatrix = Eigen::MatrixXd::Random(arbitraryDimension, arbitraryDimension);
    for (Eigen::Index i = 0; i < arbitraryDimension; ++i) {
      for (Eigen::Index j = i; j < arbitraryDimension; ++j) {
        double randomValue = arbitraryDenseMatrix(i, j);
        if (i == j)
          arbitrarySparseMatrix.coeffRef(i, j) = arbitraryDenseMatrix(i, j);
        else if (randomMatrix(i, j) > 0.8) // Random c [-1,1]
          arbitrarySparseMatrix.coeffRef(i, j) = arbitraryDenseMatrix(i, j);
      }
    }
  }
};

TEST_F(ADavidsonDiagonalizerTest, DavidsonDiagonalizerCanBeConstructed) {
  DavidsonDiagonalizer<Eigen::MatrixXd> davidson{arbitraryNumberEigenvalue, arbitraryTrialSubspace, arbitraryDimension};
  DavidsonDiagonalizer<Eigen::SparseMatrix<double>> davidsonSparse{arbitraryNumberEigenvalue, arbitraryTrialSubspace,
                                                                   arbitraryDimension};
  DavidsonDiagonalizer<Eigen::MatrixXd, DavidsonBalancedType::balanced> davidsonBalanced{
      arbitraryNumberEigenvalue, arbitraryTrialSubspace, arbitraryDimension};
  DavidsonDiagonalizer<Eigen::SparseMatrix<double>, DavidsonBalancedType::balanced> davidsonSparseBalanced{
      arbitraryNumberEigenvalue, arbitraryTrialSubspace, arbitraryDimension};
}

TEST_F(ADavidsonDiagonalizerTest, CanDiagonalizeWithStandardMethod) {
  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> ref(arbitraryDenseMatrix);
  auto actualEigenvalues = ref.eigenvalues().head(1);
  auto actualEigenvectors = ref.eigenvectors().leftCols(1);

  DavidsonDiagonalizer<Eigen::MatrixXd> davidson{1, 1, arbitraryDimension};
  davidson.setMatrixToDiagonalize(arbitraryDenseMatrix);
  auto result = davidson.solve();

  ASSERT_THAT(result.first(0), DoubleNear(actualEigenvalues(0), 1e-5));
  for (int j = 0; j < arbitraryDimension; ++j) {
    ASSERT_THAT(abs(result.second(j, 0)), DoubleNear(abs(actualEigenvectors(j, 0)), 1e-5));
  }
}

TEST_F(ADavidsonDiagonalizerTest, CanDiagonalizeWithStandardMethodAndManyEigenvaluesWithLargeGuessSpace) {
  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> ref(arbitraryDenseMatrix);
  auto actualEigenvalues = ref.eigenvalues().head(arbitraryDimension - 3);
  auto actualEigenvectors = ref.eigenvectors().leftCols(arbitraryDimension - 3);

  DavidsonDiagonalizer<Eigen::MatrixXd> davidson{arbitraryDimension - 3, arbitraryDimension - 3, arbitraryDimension};
  davidson.setMatrixToDiagonalize(arbitraryDenseMatrix);
  auto result = davidson.solve();

  for (int i = 0; i < arbitraryDimension - 3; ++i) {
    ASSERT_THAT(result.first(i), DoubleNear(actualEigenvalues(i), 1e-5));
    for (int j = 0; j < arbitraryDimension; ++j) {
      ASSERT_THAT(abs(result.second(j, i)), DoubleNear(abs(actualEigenvectors(j, i)), 1e-5));
    }
  }
}

TEST_F(ADavidsonDiagonalizerTest, CanDiagonalizeWithStandardMethodAndManyEigenvalues) {
  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> ref(arbitraryDenseMatrix);
  auto actualEigenvalues = ref.eigenvalues().head(arbitraryNumberEigenvalue);
  auto actualEigenvectors = ref.eigenvectors().leftCols(arbitraryNumberEigenvalue);

  DavidsonDiagonalizer<Eigen::MatrixXd> davidson{arbitraryNumberEigenvalue, arbitraryTrialSubspace, arbitraryDimension};
  davidson.setMatrixToDiagonalize(arbitraryDenseMatrix);
  auto result = davidson.solve();

  for (int i = 0; i < arbitraryNumberEigenvalue; ++i) {
    ASSERT_THAT(result.first(i), DoubleNear(actualEigenvalues(i), 1e-5));
    for (int j = 0; j < arbitraryDimension; ++j) {
      ASSERT_THAT(abs(result.second(j, i)), DoubleNear(abs(actualEigenvectors(j, i)), 1e-5));
    }
  }
}

TEST_F(ADavidsonDiagonalizerTest, CanDiagonalizeWithSparseMatrix) {
  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> ref(arbitrarySparseMatrix);
  auto actualEigenvalues = ref.eigenvalues().head(1);
  auto actualEigenvectors = ref.eigenvectors().leftCols(1);

  DavidsonDiagonalizer<Eigen::SparseMatrix<double>> davidson{1, 1, arbitraryDimension};
  davidson.setMatrixToDiagonalize(arbitrarySparseMatrix);
  auto result = davidson.solve();

  ASSERT_THAT(result.first(0), DoubleNear(actualEigenvalues(0), 1e-5));
  for (int j = 0; j < 1; ++j) {
    ASSERT_THAT(abs(result.second(j, 0)), DoubleNear(abs(actualEigenvectors(j, 0)), 1e-5));
  }
}

TEST_F(ADavidsonDiagonalizerTest, CanDiagonalizeWithSparseMatrixAndManyEigenvaluesAndLargeStartingSpace) {
  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> ref(arbitrarySparseMatrix);
  auto actualEigenvalues = ref.eigenvalues().head(arbitraryDimension - 3);
  auto actualEigenvectors = ref.eigenvectors().leftCols(arbitraryDimension - 3);

  DavidsonDiagonalizer<Eigen::SparseMatrix<double>> davidson{arbitraryDimension - 3, arbitraryDimension - 3, arbitraryDimension};
  davidson.setMatrixToDiagonalize(arbitrarySparseMatrix);
  auto result = davidson.solve();

  for (int i = 0; i < arbitraryDimension - 3; ++i) {
    ASSERT_THAT(result.first(i), DoubleNear(actualEigenvalues(i), 1e-5));
    for (int j = 0; j < arbitraryDimension; ++j) {
      ASSERT_THAT(abs(result.second(j, i)), DoubleNear(abs(actualEigenvectors(j, i)), 1e-5));
    }
  }
}

TEST_F(ADavidsonDiagonalizerTest, CanDiagonalizeWithSparseMatrixAndManyEigenvalues) {
  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> ref(arbitrarySparseMatrix);
  auto actualEigenvalues = ref.eigenvalues().head(arbitraryNumberEigenvalue);
  auto actualEigenvectors = ref.eigenvectors().leftCols(arbitraryNumberEigenvalue);

  DavidsonDiagonalizer<Eigen::SparseMatrix<double>> davidson{arbitraryNumberEigenvalue, arbitraryTrialSubspace,
                                                             arbitraryDimension};
  davidson.setMatrixToDiagonalize(arbitrarySparseMatrix);
  auto result = davidson.solve();

  for (int i = 0; i < arbitraryNumberEigenvalue; ++i) {
    ASSERT_THAT(result.first(i), DoubleNear(actualEigenvalues(i), 1e-5));
    for (int j = 0; j < arbitraryNumberEigenvalue; ++j) {
      ASSERT_THAT(abs(result.second(j, i)), DoubleNear(abs(actualEigenvectors(j, i)), 1e-5));
    }
  }
}

TEST_F(ADavidsonDiagonalizerTest, CanDiagonalizeMatrixOfSizeOne) {
  Eigen::MatrixXd matrix(1, 1);
  matrix << 13.0;

  DavidsonDiagonalizer<Eigen::MatrixXd> davidson{1, 1, 1};
  davidson.setMatrixToDiagonalize(matrix);
  auto result = davidson.solve();

  ASSERT_THAT(result.first(0), Eq(13.0));
}

TEST_F(ADavidsonDiagonalizerTest, CanDiagonalizeWithStandardBalancedMethod) {
  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> ref(arbitraryDenseMatrix);
  auto actualEigenvalues = ref.eigenvalues().head(1);
  auto actualEigenvectors = ref.eigenvectors().leftCols(1);

  DavidsonDiagonalizer<Eigen::MatrixXd, DavidsonBalancedType::balanced> davidson{1, 1, arbitraryDimension};
  davidson.setMatrixToDiagonalize(arbitraryDenseMatrix);
  auto result = davidson.solve();

  EXPECT_THAT(result.first(0), DoubleNear(actualEigenvalues(0), 1e-5));
  for (int j = 0; j < arbitraryDimension; ++j) {
    EXPECT_THAT(abs(result.second(j, 0)), DoubleNear(abs(actualEigenvectors(j, 0)), 1e-5));
  }
}

TEST_F(ADavidsonDiagonalizerTest, CanDiagonalizeWithBalancedStandardMethodAndManyEigenvaluesWithLargeStartingSpace) {
  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> ref(arbitraryDenseMatrix);
  auto actualEigenvalues = ref.eigenvalues().head(arbitraryDimension - 3);
  auto actualEigenvectors = ref.eigenvectors().leftCols(arbitraryDimension - 3);

  DavidsonDiagonalizer<Eigen::MatrixXd, DavidsonBalancedType::balanced> davidson{
      arbitraryDimension - 3, arbitraryDimension - 3, arbitraryDimension};
  davidson.setMatrixToDiagonalize(arbitraryDenseMatrix);
  auto result = davidson.solve();

  for (int i = 0; i < arbitraryDimension - 3; ++i) {
    ASSERT_THAT(result.first(i), DoubleNear(actualEigenvalues(i), 1e-5));
    for (int j = 0; j < arbitraryDimension; ++j) {
      ASSERT_THAT(abs(result.second(j, i)), DoubleNear(abs(actualEigenvectors(j, i)), 1e-5));
    }
  }
}

TEST_F(ADavidsonDiagonalizerTest, CanDiagonalizeWithBalancedStandardMethodAndManyEigenvalues) {
  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> ref(arbitraryDenseMatrix);
  auto actualEigenvalues = ref.eigenvalues().head(arbitraryNumberEigenvalue);
  auto actualEigenvectors = ref.eigenvectors().leftCols(arbitraryNumberEigenvalue);

  DavidsonDiagonalizer<Eigen::MatrixXd, DavidsonBalancedType::balanced> davidson{
      arbitraryNumberEigenvalue, arbitraryTrialSubspace, arbitraryDimension};
  davidson.setMatrixToDiagonalize(arbitraryDenseMatrix);
  auto result = davidson.solve();

  for (int i = 0; i < arbitraryNumberEigenvalue; ++i) {
    ASSERT_THAT(result.first(i), DoubleNear(actualEigenvalues(i), 1e-5));
    for (int j = 0; j < arbitraryDimension; ++j) {
      ASSERT_THAT(abs(result.second(j, i)), DoubleNear(abs(actualEigenvectors(j, i)), 1e-5));
    }
  }
}

TEST_F(ADavidsonDiagonalizerTest, CanDiagonalizeWithBalancedMethodSparseMatrix) {
  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> ref(arbitrarySparseMatrix);
  auto actualEigenvalues = ref.eigenvalues().head(1);
  auto actualEigenvectors = ref.eigenvectors().leftCols(1);

  DavidsonDiagonalizer<Eigen::SparseMatrix<double>, DavidsonBalancedType::balanced> davidson{1, 1, arbitraryDimension};
  davidson.setMatrixToDiagonalize(arbitrarySparseMatrix);
  auto result = davidson.solve();

  ASSERT_THAT(result.first(0), DoubleNear(actualEigenvalues(0), 1e-5));
  for (int j = 0; j < 1; ++j) {
    ASSERT_THAT(abs(result.second(j, 0)), DoubleNear(abs(actualEigenvectors(j, 0)), 1e-5));
  }
}

TEST_F(ADavidsonDiagonalizerTest, CanDiagonalizeWithBalancedMethodSparseMatrixAndManyEigenvaluesAndLargeStartingSpace) {
  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> ref(arbitrarySparseMatrix);
  auto actualEigenvalues = ref.eigenvalues().head(arbitraryDimension - 3);
  auto actualEigenvectors = ref.eigenvectors().leftCols(arbitraryDimension - 3);

  DavidsonDiagonalizer<Eigen::SparseMatrix<double>, DavidsonBalancedType::balanced> davidson{
      arbitraryDimension - 3, arbitraryDimension - 3, arbitraryDimension};
  davidson.setMatrixToDiagonalize(arbitrarySparseMatrix);
  auto result = davidson.solve();

  for (int i = 0; i < arbitraryDimension - 3; ++i) {
    ASSERT_THAT(result.first(i), DoubleNear(actualEigenvalues(i), 1e-5));
    for (int j = 0; j < arbitraryDimension; ++j) {
      ASSERT_THAT(abs(result.second(j, i)), DoubleNear(abs(actualEigenvectors(j, i)), 1e-5));
    }
  }
}

TEST_F(ADavidsonDiagonalizerTest, CanDiagonalizeWithBalancedMethodSparseMatrixAndManyEigenvalues) {
  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> ref(arbitrarySparseMatrix);
  auto actualEigenvalues = ref.eigenvalues().head(arbitraryNumberEigenvalue);
  auto actualEigenvectors = ref.eigenvectors().leftCols(arbitraryNumberEigenvalue);

  DavidsonDiagonalizer<Eigen::SparseMatrix<double>, DavidsonBalancedType::balanced> davidson{
      arbitraryNumberEigenvalue, arbitraryNumberEigenvalue, arbitraryDimension};
  davidson.setMatrixToDiagonalize(arbitrarySparseMatrix);
  auto result = davidson.solve();

  for (int i = 0; i < arbitraryNumberEigenvalue; ++i) {
    ASSERT_THAT(result.first(i), DoubleNear(actualEigenvalues(i), 1e-5));
    for (int j = 0; j < arbitraryDimension; ++j) {
      ASSERT_THAT(abs(result.second(j, i)), DoubleNear(abs(actualEigenvectors(j, i)), 1e-5));
    }
  }
}

TEST_F(ADavidsonDiagonalizerTest, BalancedMethodCanDiagonalizeMatrixOfSizeOne) {
  Eigen::MatrixXd matrix(1, 1);
  matrix << 13.0;

  DavidsonDiagonalizer<Eigen::MatrixXd> davidson{1, 1, 1};
  davidson.setMatrixToDiagonalize(matrix);
  auto result = davidson.solve();

  ASSERT_THAT(result.first(0), Eq(13.0));
}
} // namespace Utils
} // namespace Scine


// Boost.Geometry Index
//
// n-dimensional Indexable validity check
//
// Copyright (c) 2011-2013 Adam Wulkiewicz, Lodz, Poland.
//
// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_GEOMETRY_EXTENSIONS_INDEX_DETAIL_ALGORITHMS_IS_VALID_HPP
#define BOOST_GEOMETRY_EXTENSIONS_INDEX_DETAIL_ALGORITHMS_IS_VALID_HPP

#include <boost/geometry/index/detail/algorithms/is_valid.hpp>

namespace boost { namespace geometry { namespace index { namespace detail {

namespace dispatch {

template <typename Indexable>
struct is_valid<Indexable, nsphere_tag>
{
    static inline bool apply(Indexable const& i)
    {
        return 0 <= geometry::get_radius<0>(i);
    }
};

} // namespace dispatch

}}}} // namespace boost::geometry::index::detail

#endif // BOOST_GEOMETRY_EXTENSIONS_INDEX_DETAIL_ALGORITHMS_IS_VALID_HPP


/**
 * SAMPLE APPLICATION FOR DEMONSTRATING gdb-plot functionality
 *
 * BUILD: g++ -g boost_numerics_example.cpp -o boost_example
 * RUN: gdb boost_example
 *      (gdb) b 91
 *      (gdb) plot vx vc
 */

#include <iostream>
#include <complex>
#include <list>
#include <set>
#include <map>
#include <vector>

// Boost types
#include <boost/numeric/ublas/vector.hpp>
#include <boost/numeric/ublas/vector_proxy.hpp>
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/io.hpp>
#include <boost/numeric/ublas/storage.hpp>

int main(void)
{

    boost::numeric::ublas::vector< double > vx(10,0);
    boost::numeric::ublas::vector< std::complex < double > > vc( 10, 0 );

    // Push some data around
    for ( std::size_t ii=0; ii < 10; ii++ )
    {

        vx[ii] = ii;
        std::complex<double> q;
        q.real(M_PI *ii);
        q.imag(M_PI *ii);
        vc(ii) = q;
    }

    std::cout << "\n\n----------------------------------------------\n\n"
              <<  "Break on line " <<  __LINE__ << " to plot: \n"
              << " plot vx: Boost Numerics double vector\n"
              << " plot vc: Boost Numerics complex double vector\n"
              << std::endl;

    std::cout << "Now try combined plots: plot vx vc" << std::endl;

    return 0;
}


#include <algorithm>
#include <functional>
#include <numeric>
#include <limits>
#include <iostream>
#include <cmath>
#include <cassert>

#include <boost/timer.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/math/special_functions/fpclassify.hpp>

#include "Likelihood.h"
#include "StructuredSVM.h"
#include "FunctionMinimization.h"
#include "StochasticFunctionMinimization.h"
#include "StructuredHammingLoss.h"

using namespace boost::lambda;

namespace Grante {

StructuredSVM::StructuredSVM(FactorGraphModel* fg_model, double ssvm_C,
	const std::string& opt_method)
	: ParameterEstimationMethod(fg_model), ssvm_C(ssvm_C),
		opt_method(opt_method) {
	assert(opt_method == "stochastic" || opt_method == "bmrm");
}

StructuredSVM::~StructuredSVM() {
	for (unsigned int tn = 0; tn < t_loss.size(); ++tn)
		delete (t_loss[tn]);
}

void StructuredSVM::SetupTrainingData(
	const std::vector<labeled_instance_type>& training_data,
	const std::vector<InferenceMethod*> inference_methods) {
	t_instances.clear();
	t_loss.clear();
	t_instances.reserve(training_data.size());
	t_loss.reserve(training_data.size());
	for (unsigned int n = 0; n < training_data.size(); ++n) {
		t_instances.push_back(training_data[n].first);
		t_loss.push_back(new StructuredHammingLoss(training_data[n].second));
	}
	SetupTrainingData(t_instances, t_loss, inference_methods);
	t_instances.clear();	// no longer needed
}

void StructuredSVM::SetupTrainingData(
	const std::vector<FactorGraph*>& instances,
	const std::vector<StructuredLossFunction*>& loss,
	const std::vector<InferenceMethod*>& inference_methods) {
	training_instances = instances;
	loss_functions = loss;
	this->inference_methods = inference_methods;
}

double StructuredSVM::Train(double conv_tol, unsigned int max_iter) {
	StructuredSVMProblem ssvm_prob(this);
	std::vector<double> x_opt;

	// Stochastic or BMRM training
	double obj = std::numeric_limits<double>::signaling_NaN();
	if (opt_method == "stochastic") {
		StochasticStructuredSVMProblem ssvm_sprob(&ssvm_prob);
		obj = StochasticFunctionMinimization::StochasticSubgradientMethodMinimize(
			ssvm_sprob, x_opt, conv_tol, max_iter, true);
	} else if (opt_method == "bmrm") {
		obj = TrainBMRM(&ssvm_prob, conv_tol, max_iter);
		x_opt.resize(ssvm_prob.Dimensions());
		std::fill(x_opt.begin(), x_opt.end(), 0.0);
		ssvm_prob.FactorWeightsToLinear(x_opt);
	} else {
		assert(0);
	}

	// Save optimal weights into model
	ssvm_prob.LinearToFactorWeights(x_opt);

	return (obj);
}

double StructuredSVM::TrainBMRM(StructuredSVMProblem* ssvm_prob,
	double conv_tol, unsigned int max_iter) {
	BMRM2StructuredSVMProblem bmrm(ssvm_prob);

	// Best (lowest) true feasible objective
	double J_best = std::numeric_limits<double>::infinity();
	double gap = std::numeric_limits<double>::infinity();
 std::vector<double> w_opt;
	for (unsigned int iter = 1; max_iter == 0 || iter <= max_iter; ++iter) {
		std::cout << "BMRM iter " << iter << ", J_best " << J_best
			<< ", gap " << gap << std::endl;

		// Compute: R_emp, subgradient of R_emp
		ssvm_prob->ClearParameterGradient();
		std::cout << "   Doing loss-augmented MAP inference." << std::endl;
		boost::timer inf_timer;
		double R_emp = ssvm_prob->EvaluateLossGradient();
		std::cout << "   " << inf_timer.elapsed()
			<< "s for loss-augmented MAP inference" << std::endl;

		// Enlarge cutting plane model
		bmrm.AddCurrentSubgradient(R_emp);

		// Compute exact objective and keep track of best feasible solution
		double J_t_exact = R_emp + ssvm_prob->AddRegularizer();
		if (J_t_exact < J_best)
			J_best = J_t_exact;

		// Minimize cutting plane model
		// std::vector<double> w_opt;
		double pd_gap;
		double eg_conv_tol = 0.1*gap;
		std::vector<double> alpha_opt;
		boost::timer bmrm_sub_timer;
		double J_t_lowerbound = bmrm.OptimizeDual(w_opt, alpha_opt,
			eg_conv_tol, 20000, pd_gap, false);
		if (pd_gap > eg_conv_tol) {
			std::cout << "### WARNING: required BMRM subproblem accuracy "
				<< "not reached." << std::endl;
		}
		std::cout << "   " << bmrm_sub_timer.elapsed()
			<< "s for solving the BMRM sub problem" << std::endl;

		// Compute model gap
		// TODO: relax this condition using pd_gap
		if ((J_best + conv_tol) < J_t_lowerbound) {
			std::cout << "### WARNING: J_best " << J_best << " < "
				<< J_t_lowerbound << " J_t_lowerbound, when using exact "
				<< "inference this should not happen."
				<< std::endl;
		}
		std::cout << "J_best " << J_best << ", "
				<< "J_t_lowerbound " << J_t_lowerbound
				<< ", subproblem pd_gap " << pd_gap << std::endl;
		assert((J_best + conv_tol) >= J_t_lowerbound);
		gap = J_best - J_t_lowerbound;
		if (gap < conv_tol) {
			std::cout << "   * Converged with tolerance " << gap << " < "
				<< conv_tol << std::endl;
			break;
		}
	}
	return (J_best);
}

StructuredSVM::StructuredSVMProblem::StructuredSVMProblem(
	StructuredSVM* ssvm_base)
	: ssvm_base(ssvm_base) {
	// Compute dimension once and setup factor type orders
	dim = 0;
	const std::vector<FactorType*>& factor_types =
		ssvm_base->fg_model->FactorTypes();
	for (std::vector<FactorType*>::const_iterator fti = factor_types.begin();
		fti != factor_types.end(); ++fti) {
		dim += (*fti)->WeightDimension();
		parameter_order.push_back((*fti)->Name());
	}
}

StructuredSVM::StructuredSVMProblem::~StructuredSVMProblem() {
}

double StructuredSVM::StructuredSVMProblem::EvaluateLossGradient() {
	Likelihood lh(ssvm_base->fg_model);
	double obj = 0.0;
	int sample_count = static_cast<int>(ssvm_base->training_instances.size());
	#pragma omp parallel for schedule(dynamic)
	for (int n = 0; n < sample_count; ++n) {
		double obj_n = EvaluateLossGradient(lh, static_cast<unsigned int>(n));
		#pragma omp critical
		{
			obj += obj_n;
		}
	}

	return (obj);
}

double StructuredSVM::StructuredSVMProblem::EvaluateLossGradient(
	Likelihood& lh, unsigned int n) {
	// For each sample: run forward map, loss-augmentation, run MAP inference,
	// compute gradient
	double obj = 0.0;
	double reg_scale = ssvm_base->ssvm_C /
		static_cast<double>(ssvm_base->training_instances.size());

	// Get sample
	FactorGraph* ts_fg = ssvm_base->training_instances[n];
	const StructuredLossFunction* h_loss =
		ssvm_base->loss_functions[n];
	InferenceMethod* ts_inf = ssvm_base->inference_methods[n];

	// Compute forward map: parameters (changed) to energies
	ts_fg->ForwardMap();

	// Compute: -E(y_n;x_n,w)
	#pragma omp critical
	{
		double obj_e = lh.ComputeObservationEnergy(ts_fg,
			h_loss->Truth(), parameter_gradient, reg_scale);
		obj += obj_e;
	}

	// Compute: min_y [E(y;x_n,w)-Delta(y,y_n)]
	//   i) Find loss-augmented MAP state (y_star)
	std::vector<unsigned int> y_star;
	h_loss->PerformLossAugmentation(ts_fg, -1.0);	// +Delta(.,y_n)
	double obj_m_1 = -reg_scale * ts_inf->MinimizeEnergy(y_star);

	//  ii) Compute gradient (already of negative sign)
	#pragma omp critical
	{
		double obj_m = lh.ComputeObservationEnergy(ts_fg, y_star,
			parameter_gradient, -reg_scale);
		assert(std::fabs(obj_m_1 - obj_m) < 1e-8);
		obj += obj_m;
	}

	ts_inf->ClearInferenceResult();

	return (obj);
}

double StructuredSVM::StructuredSVMProblem::AddRegularizer(double scale) {
	double obj = 0.0;
	for (std::multimap<std::string, Prior*>::const_iterator
		prior = ssvm_base->priors.begin();
		prior != ssvm_base->priors.end(); ++prior)
	{
		FactorType* ft = ssvm_base->fg_model->FindFactorType(prior->first);
		obj += prior->second->EvaluateNegLogP(ft->Weights(),
			parameter_gradient[prior->first], scale);
	}
	return (obj);
}

double StructuredSVM::StructuredSVMProblem::EvaluateFenchelDual(void) {
	double obj = 0.0;
	for (std::multimap<std::string, Prior*>::const_iterator
		prior = ssvm_base->priors.begin();
		prior != ssvm_base->priors.end(); ++prior)
	{
		FactorType* ft = ssvm_base->fg_model->FindFactorType(prior->first);
		obj += prior->second->EvaluateFenchelDual(ft->Weights(),
			parameter_gradient[prior->first]);
	}
	return (obj);
}

void StructuredSVM::StructuredSVMProblem::ClearParameterGradient() {
	for (std::vector<std::string>::const_iterator
		ft_name = parameter_order.begin();
		ft_name != parameter_order.end(); ++ft_name) {
		FactorType* ft = ssvm_base->fg_model->FindFactorType(*ft_name);
		parameter_gradient[*ft_name] =
			std::vector<double>(ft->WeightDimension(), 0.0);
	}
}

void StructuredSVM::StructuredSVMProblem::LinearToFactorWeights(
	const std::vector<double>& x) {
	unsigned int base_idx = 0;
	for (std::vector<std::string>::const_iterator
		ft_name = parameter_order.begin();
		ft_name != parameter_order.end(); ++ft_name) {
		// Get factor type
		FactorType* ft = ssvm_base->fg_model->FindFactorType(*ft_name);
		unsigned int ft_w_len = ft->WeightDimension();
		std::copy(x.begin() + base_idx, x.begin() + base_idx + ft_w_len,
			ft->Weights().begin());
		base_idx += ft_w_len;
	}
	assert(base_idx == x.size());
}

void StructuredSVM::StructuredSVMProblem::FactorWeightsToLinear(
	std::vector<double>& x) {
	unsigned int base_idx = 0;
	for (std::vector<std::string>::const_iterator
		ft_name = parameter_order.begin();
		ft_name != parameter_order.end(); ++ft_name) {
		// Get factor type
		FactorType* ft = ssvm_base->fg_model->FindFactorType(*ft_name);
		unsigned int ft_w_len = ft->WeightDimension();
		std::copy(ft->Weights().begin(), ft->Weights().end(),
			x.begin() + base_idx);
		base_idx += ft_w_len;
	}
	assert(base_idx == x.size());
}

void StructuredSVM::StructuredSVMProblem::ParameterGradientToLinear(
	std::vector<double>& grad) {
	size_t base_idx = 0;
	for (std::vector<std::string>::const_iterator
		ft_name = parameter_order.begin();
		ft_name != parameter_order.end(); ++ft_name) {
		std::copy(parameter_gradient[*ft_name].begin(),
			parameter_gradient[*ft_name].end(), grad.begin() + base_idx);
		base_idx += parameter_gradient[*ft_name].size();
	}
}

unsigned int StructuredSVM::StructuredSVMProblem::Dimensions() const {
	return (dim);
}

StructuredSVM* StructuredSVM::StructuredSVMProblem::Base() {
	return (ssvm_base);
}

///
/// STOCHASTIC structured SVM problem
///
StructuredSVM::StochasticStructuredSVMProblem::StochasticStructuredSVMProblem(
	StructuredSVM::StructuredSVMProblem* ssvm_prob)
	: ssvm_prob(ssvm_prob),
		elements_count(ssvm_prob->Base()->loss_functions.size()) {
}

StructuredSVM::StochasticStructuredSVMProblem::~StochasticStructuredSVMProblem() {
}

// Evaluate objective at x and subgradient at x.
double StructuredSVM::StochasticStructuredSVMProblem::Eval(
	unsigned int sample_id, const std::vector<double>& x,
	std::vector<double>& grad) {
	assert(x.size() == ssvm_prob->Dimensions());
	assert(grad.size() == ssvm_prob->Dimensions());
	assert(sample_id < elements_count);

	// 1. Convert x into factor parameters
	ssvm_prob->LinearToFactorWeights(x);
	std::fill(grad.begin(), grad.end(), 0.0);

	// 2. Setup parameter gradient
	ssvm_prob->ClearParameterGradient();

	// 3. Compute loss-related parameter gradient
	Likelihood lh(ssvm_prob->Base()->fg_model);
	double obj = ssvm_prob->EvaluateLossGradient(lh, sample_id);

	// 4. Add (1/N)'th of the regularizer
	obj += ssvm_prob->AddRegularizer(1.0/static_cast<double>(elements_count));

	// 5. Convert gradient into linear form
	ssvm_prob->ParameterGradientToLinear(grad);

	return (obj);
}

unsigned int StructuredSVM::StochasticStructuredSVMProblem::Dimensions() const {
	return (ssvm_prob->Dimensions());
}

size_t
StructuredSVM::StochasticStructuredSVMProblem::NumberOfElements() const {
	return (elements_count);
}

void StructuredSVM::StochasticStructuredSVMProblem::ProvideStartingPoint(
	std::vector<double>& x0) const {
	assert(x0.size() == ssvm_prob->Dimensions());
	std::fill(x0.begin(), x0.end(), 0.0);
}

///
/// BMRM2
///

StructuredSVM::BMRM2StructuredSVMProblem::BMRM2StructuredSVMProblem(
	StructuredSVMProblem* ssvm_prob)
	: ssvm_prob(ssvm_prob) {
	std::vector<double> zero_vec(ssvm_prob->Dimensions(), 0.0);
	At.push_back(zero_vec);
	bt.push_back(0.0);
}

StructuredSVM::BMRM2StructuredSVMProblem::~BMRM2StructuredSVMProblem() {
}

void StructuredSVM::BMRM2StructuredSVMProblem::AddCurrentSubgradient(
	double R_emp) {
	// Obtain gradient
	std::vector<double> grad(ssvm_prob->Dimensions());
	ssvm_prob->ParameterGradientToLinear(grad);

	// Obtain current weights
	std::vector<double> w(ssvm_prob->Dimensions());
	ssvm_prob->FactorWeightsToLinear(w);
	double b_t = R_emp - std::inner_product(w.begin(), w.end(),
		grad.begin(), 0.0);

	// Add aggregated cutting plane: <a,x> + b_t <= xi
	At.push_back(grad);
	bt.push_back(b_t);
}

double StructuredSVM::BMRM2StructuredSVMProblem::OptimizeDual(
	std::vector<double>& w_opt, std::vector<double>& alpha_opt,
	double conv_tol, unsigned int max_iter, double& pd_gap, bool verbose) {
	// Basic problem properties
	size_t t = At.size();
	assert(t >= 1);
	size_t n = At[0].size();
	std::vector<double> alpha_grad(t, 0.0);

	// Repeatedly used quantities
	std::vector<double> neg_A_alpha;
	std::vector<double> A_w(t);

	// Initialize alpha, w
	if (alpha_opt.empty()) {
		alpha_opt.resize(t);
		std::fill(alpha_opt.begin(), alpha_opt.end(),
			1.0 / static_cast<double>(t));
	}
	w_opt.resize(n);
	pd_gap = std::numeric_limits<double>::infinity();

	// SPGA parameters
	double nu = 1.0e-4;
	double ss_min = 1.0e-10;
	double ss_max = 1.0e10;
	size_t spga_h = 10;
	size_t spga_hi = 0;
	std::vector<double> spga_hval(spga_h,
		-std::numeric_limits<double>::infinity());
	double ss_bb = -1.0;
	double interp_cubic_allowed = 1.0e-6;

	// Problem objective to be returned later
	double obj_dual = std::numeric_limits<double>::signaling_NaN();
	double d_fval = std::numeric_limits<double>::signaling_NaN();
	bool is_uptodate = false;

	// Spectral Projected Gradient Algorithm (SPG2 in Birgin et al., 1999)
	// (see prototype/eg/spga.m)
	double conv = std::numeric_limits<double>::infinity();
	for (unsigned int iter = 1; conv >= conv_tol
		&& (max_iter == 0 || iter <= max_iter); ++iter) {
		// Evaluate dual objective and gradient in alpha
		if (is_uptodate == false) {
			d_fval = Eval(alpha_opt, alpha_grad, w_opt);
		}
		obj_dual = -d_fval;

		// Initial step size heuristic: ss_bb = 1/max(abs(grad))
		if (ss_bb < 0.0) {
			double grad_max = -std::numeric_limits<double>::infinity();
			for (size_t ti = 0; ti < t; ++ti)
				grad_max = std::max(std::fabs(alpha_grad[ti]), grad_max);
			if (grad_max <= 1.0e-10) {
				if (verbose) {
					std::cout << "   [spga] initial gradient zero, "
						<< "assuming convergence." << std::endl;
				}
				break;	// converged
			}
			ss_bb = 1.0 / grad_max;
		}
		ss_bb = std::min(ss_max, std::max(ss_min, ss_bb));

		// Evaluate primal objective and compute primal-dual gap
		double obj_primal = EvalPrimal(w_opt);
		pd_gap = obj_primal - obj_dual;
		conv = pd_gap;
		if (verbose) {
			std::cout << "   [spga] iter " << iter << ", pobj " << obj_primal
				<< ", dobj " << obj_dual << ", gap " << pd_gap
				<< ", ss_bb " << ss_bb << std::endl;
		}
		if (pd_gap < -1.0e-5) {
			std::cout << "### WARNING: pd_gap " << pd_gap << " < 0" << std::endl;
		}
		if (conv <= conv_tol) {
			if (verbose) {
				std::cout << "   [spga] converged with tolerance "
					<< conv << std::endl;
			}
			break;
		}

		// Manage monotonicity array
		spga_hval[spga_hi] = d_fval;
		spga_hi = (spga_hi + 1) % spga_h;

		// Project the gradient onto the simplex: d_k = P(x - ss_bb*grad) - x
		std::vector<double> d_k(alpha_opt);
		std::transform(d_k.begin(), d_k.end(), alpha_grad.begin(), d_k.begin(),
			[ss_bb](double alpha_i, double grad_i) -> double {
				return (alpha_i - ss_bb*grad_i); });
		ProjectOntoSimplex(d_k);
		std::transform(d_k.begin(), d_k.end(), alpha_opt.begin(), d_k.begin(),
			[](double di, double ai) -> double { return (di - ai); });

		// Monotonicity guard
		double f_b = *std::max_element(spga_hval.begin(), spga_hval.end());

		// Line search
		double gxk_dk = std::inner_product(alpha_grad.begin(),
			alpha_grad.end(), d_k.begin(), 0.0);
		std::vector<double> alpha_opt_ss(alpha_opt.size(), 0.0);
		std::vector<double> alpha_grad_ss(alpha_opt_ss.size(), 0.0);
		std::vector<double> w_opt_ss(w_opt.size(), 0.0);
		double d_fval_xss = 0.0;
		double ss = 1.0;
		unsigned int tries = 0;
		do {
			// f(x + ss*d_k)
			std::transform(alpha_opt.begin(), alpha_opt.end(), d_k.begin(),
				alpha_opt_ss.begin(), [ss](double ai, double dki) -> double {
					return (ai + ss*dki); });
			d_fval_xss = Eval(alpha_opt_ss, alpha_grad_ss, w_opt_ss);
			tries += 1;
			if (d_fval_xss <= f_b + nu*ss*gxk_dk) {
				// Accept
				if (verbose) {
					std::cout << "      accepted step size " << ss
						<< " in " << tries << " steps" << std::endl;
				}
				break;
			}

			// Safe-guarded cubic interpolation
			double g_a = d_fval;
			double gt_a = gxk_dk;
			double g_b = d_fval_xss;
			double gt_b = std::inner_product(alpha_grad_ss.begin(),
				alpha_grad_ss.end(), d_k.begin(), 0.0);
			double z = (3.0*(g_a - g_b) / (ss - 0.0)) + gt_a + gt_b;
			double w = std::sqrt(z*z - gt_a*gt_b);
			double ss_cubic = ss - (ss-0.0)*((gt_b+w-z)/(gt_b-gt_a+2.0*w));
			if (ss_cubic >= interp_cubic_allowed*ss &&
				ss_cubic <= (1.0 - interp_cubic_allowed)*ss) {
				ss = ss_cubic;
			} else {
				ss *= 0.5;
			}
		} while (true);

		// Compute Barzilai-Borwein step size ss_bb
		std::vector<double> s_k(alpha_opt.size(), 0.0);
		std::transform(alpha_opt_ss.begin(), alpha_opt_ss.end(),
			alpha_opt.begin(), s_k.begin(),
			[](double ani, double ai) -> double { return (ani - ai); });
		std::vector<double> y_k(alpha_grad.size(), 0.0);
		std::transform(alpha_grad_ss.begin(), alpha_grad_ss.end(),
			alpha_grad.begin(), y_k.begin(),
			[](double agni, double agi) -> double { return (agni - agi); });
		double b_k = std::inner_product(s_k.begin(), s_k.end(),
			y_k.begin(), 0.0);
		if (b_k <= 0.0) {
			ss_bb = ss_max;
		} else {
			ss_bb = std::inner_product(y_k.begin(), y_k.end(),
				y_k.begin(), 0.0) / b_k;
		}

		// Update
		alpha_opt = alpha_opt_ss;
		alpha_grad = alpha_grad_ss;
		d_fval = d_fval_xss;
		w_opt = w_opt_ss;
		is_uptodate = true;
	}
	ssvm_prob->LinearToFactorWeights(w_opt);
	return (obj_dual);
}

// Return minimization objective (maximization obj_dual is negative of it)
double StructuredSVM::BMRM2StructuredSVMProblem::Eval(
	const std::vector<double>& alpha, std::vector<double>& alpha_grad,
	std::vector<double>& w_opt) {
	// Obtain dual problem gradient
	std::vector<double> neg_A_alpha;
	Eval_neg_A_alpha(alpha, neg_A_alpha);	// u = -A alpha
	ssvm_prob->LinearToFactorWeights(neg_A_alpha);

	// [d_fval,Og]=Omega_fd(-A*alpha)
	ssvm_prob->ClearParameterGradient();
	double d_fval = ssvm_prob->EvaluateFenchelDual();	// Omega_fd(u)
	d_fval -= std::inner_product(bt.begin(), bt.end(), alpha.begin(), 0.0);

	// w_opt is Og
	size_t n = At[0].size();
	w_opt.resize(n);
	std::fill(w_opt.begin(), w_opt.end(), 0.0);
	ssvm_prob->ParameterGradientToLinear(w_opt);

	// alpha_grad = -A'*Og - b
	Eval_neg_AT_u(w_opt, alpha_grad);
	std::transform(bt.begin(), bt.end(), alpha_grad.begin(),
		alpha_grad.begin(), [](double bt_i, double ag_i) -> double {
			return (ag_i - bt_i);   // - b
		});

	return (d_fval);
}

double StructuredSVM::BMRM2StructuredSVMProblem::EvalPrimal(
	const std::vector<double>& w_opt) {
	// Compute primal objective: Omega(w_opt) + max(A'*w_opt + b)
	ssvm_prob->LinearToFactorWeights(w_opt);
	double obj_primal = ssvm_prob->AddRegularizer();

	// alpha_grad = -A'*Og - b
	size_t t = At.size();
	assert(t >= 1);
	std::vector<double> nATw(t, 0.0);
	Eval_neg_AT_u(w_opt, nATw);	// -A'w_opt
	std::transform(bt.begin(), bt.end(), nATw.begin(),
		nATw.begin(), [](double bt_i, double ag_i) -> double {
			return (ag_i - bt_i);	// - b
		});

	// + max(A'*w_opt + b)
	double max_resp = -std::numeric_limits<double>::infinity();
	for (size_t ti = 0; ti < t; ++ti)
		max_resp = std::max(max_resp, -nATw[ti]);
	obj_primal += max_resp;

	return (obj_primal);
}

// neg_A_alpha = -A alpha, A is (n,t), alpha is (t,1)
void StructuredSVM::BMRM2StructuredSVMProblem::Eval_neg_A_alpha(
	const std::vector<double>& alpha,
	std::vector<double>& neg_A_alpha) const {
	assert(At.size() >= 1);
	assert(At.size() == alpha.size());

	neg_A_alpha.resize(At[0].size());	// (n,1)
	std::fill(neg_A_alpha.begin(), neg_A_alpha.end(), 0.0);
	for (size_t c = 0; c < At.size(); ++c) {
		std::transform(At[c].begin(), At[c].end(), neg_A_alpha.begin(),
			neg_A_alpha.begin(),
			[&alpha,c](double At_i, double nAa_i) -> double {
				return (nAa_i - alpha[c]*At_i);
			});
	}
}

// neg_AT_u = -A'u, A' is (t,n), u is (n,1)
void StructuredSVM::BMRM2StructuredSVMProblem::Eval_neg_AT_u(
	const std::vector<double>& u, std::vector<double>& neg_AT_u) const {
	assert(At.size() >= 1);
	assert(At[0].size() == u.size());

	neg_AT_u.resize(At.size());	// (t,1)
	for (size_t c = 0; c < At.size(); ++c) {
		neg_AT_u[c] = -std::inner_product(At[c].begin(), At[c].end(),
			u.begin(), 0.0);
	}
}

// Project alpha \in R^d onto the standard simplex
void StructuredSVM::BMRM2StructuredSVMProblem::ProjectOntoSimplex(
	std::vector<double>& alpha) const {
	if (alpha.empty())
		return;
	if (alpha.size() == 1) {
		alpha[0] = 1.0;
		return;
	}

	// d >= 2 case
	std::vector<double> ta(alpha);
	std::sort(ta.begin(), ta.end(), std::greater<double>());
	double ts = 0.0;
	double tx = 0.0;
	bool set_tx = false;
	for (size_t i = 0; i < alpha.size()-1; ++i) {
		ts += ta[i];
		tx = (ts - 1.0) / (static_cast<double>(i + 1));
		if (tx < ta[i+1])
			continue;

		set_tx = true;
		break;
	}
	if (set_tx == false) {
		tx = (ts + ta[ta.size()-1] - 1.0) / static_cast<double>(ta.size());
	}
	std::transform(alpha.begin(), alpha.end(), alpha.begin(),
		[tx](double a) -> double { return (std::max(0.0, a - tx)); });
}

}