/**
 * @file tests/main_tests/preprocess_scale_test.cpp
 * @author Jeffin Sam
 *
 * Test mlpackMain() of preprocess_scale_main.cpp.
 *
 * mlpack is free software; you may redistribute it and/or modify it under the
 * terms of the 3-clause BSD license.  You should have received a copy of the
 * 3-clause BSD license along with mlpack.  If not, see
 * http://www.opensource.org/licenses/BSD-3-Clause for more information.
 */
#define BINDING_TYPE BINDING_TYPE_TEST

#include <mlpack/core.hpp>
static const std::string testName = "PreprocessScale";

#include <mlpack/core/util/mlpack_main.hpp>
#include <mlpack/methods/preprocess/preprocess_scale_main.cpp>

#include "test_helper.hpp"
#include <boost/test/unit_test.hpp>
#include "../test_tools.hpp"

using namespace mlpack;

struct PreprocessScaleTestFixture
{
 public:
  PreprocessScaleTestFixture()
  {
    // Cache in the options for this program.
    IO::RestoreSettings(testName);
  }

  ~PreprocessScaleTestFixture()
  {
    // Clear the settings.
    bindings::tests::CleanMemory();
    IO::ClearSettings();
  }
};

BOOST_FIXTURE_TEST_SUITE(PreprocessScaleMainTest,
                         PreprocessScaleTestFixture);

arma::mat dataset = "-1 -0.5 0 1;"
                    "2 6 10 18;";

/**
 * Check that two different scalers give two different output.
 */
BOOST_AUTO_TEST_CASE(TwoScalerTest)
{
  // Input custom data points.
  std::string method = "max_abs_scaler";
  SetInputParam("input", dataset);
  SetInputParam("scaler_method", method);

  mlpackMain();
  arma::mat maxAbsScalerOutput = IO::GetParam<arma::mat>("output");

  bindings::tests::CleanMemory();

  method = "standard_scaler";
  SetInputParam("input", dataset);
  SetInputParam("scaler_method", std::move(method));

  mlpackMain();
  arma::mat standardScalerOutput = IO::GetParam<arma::mat>("output");

  CheckMatricesNotEqual(standardScalerOutput, maxAbsScalerOutput);
}

/**
 * Check that two different option for a particular scaler give two
 * different output.
 */
BOOST_AUTO_TEST_CASE(TwoOptionTest)
{
  std::string method = "min_max_scaler";
  // Input custom data points.
  SetInputParam("input", dataset);
  SetInputParam("scaler_method", method);

  mlpackMain();
  arma::mat output = IO::GetParam<arma::mat>("output");

  bindings::tests::CleanMemory();

  SetInputParam("input", dataset);
  SetInputParam("scaler_method", std::move(method));
  SetInputParam("min_value", 2);
  SetInputParam("max_value", 4);

  mlpackMain();
  arma::mat output_with_param = IO::GetParam<arma::mat>("output");

  CheckMatricesNotEqual(output, output_with_param);
}

/**
 * Check that passing unrelated option don't change anything.
 */
BOOST_AUTO_TEST_CASE(UnrelatedOptionTest)
{
  std::string method = "standard_scaler";
  // Input custom data points.
  SetInputParam("input", dataset);
  SetInputParam("scaler_method", method);

  mlpackMain();
  arma::mat scaled = IO::GetParam<arma::mat>("output");

  bindings::tests::CleanMemory();

  SetInputParam("input", dataset);
  SetInputParam("scaler_method", std::move(method));
  SetInputParam("min_value", 2);
  SetInputParam("max_value", 4);
  SetInputParam("epsilon", 0.005);

  mlpackMain();
  arma::mat output = IO::GetParam<arma::mat>("output");

  CheckMatrices(scaled, output);
}

/**
 * Check Inverse Scaling is working.
 */
BOOST_AUTO_TEST_CASE(InverseScalingTest)
{
  std::string method = "zca_whitening";
  // Input custom data points.
  SetInputParam("input", dataset);
  SetInputParam("scaler_method", std::move(method));

  mlpackMain();
  arma::mat scaled = IO::GetParam<arma::mat>("output");

  SetInputParam("input", scaled);
  SetInputParam("input_model",
                IO::GetParam<ScalingModel*>("output_model"));
  SetInputParam("inverse_scaling", true);

  mlpackMain();
  arma::mat output = IO::GetParam<arma::mat>("output");
  CheckMatrices(dataset, output);
}

/**
 * Check Saved model is working.
 */
BOOST_AUTO_TEST_CASE(SavedModelTest)
{
  std::string method = "pca_whitening";
  // Input custom data points.
  SetInputParam("input", dataset);
  SetInputParam("scaler_method", std::move(method));

  mlpackMain();
  arma::mat scaled = IO::GetParam<arma::mat>("output");

  SetInputParam("input", dataset);
  SetInputParam("input_model",
                IO::GetParam<ScalingModel*>("output_model"));

  mlpackMain();
  arma::mat output = IO::GetParam<arma::mat>("output");
  CheckMatrices(scaled, output);
}

/**
 * Check different epsilon for PCA give two different output.
 */
BOOST_AUTO_TEST_CASE(EpsilonTest)
{
  std::string method = "pca_whitening";
  // Input custom data points.
  SetInputParam("input", dataset);
  SetInputParam("scaler_method", method);

  mlpackMain();
  arma::mat scaled = IO::GetParam<arma::mat>("output");

  bindings::tests::CleanMemory();

  SetInputParam("scaler_method", std::move(method));
  SetInputParam("input", dataset);
  SetInputParam("epsilon", 1.0);

  mlpackMain();
  arma::mat output = IO::GetParam<arma::mat>("output");

  CheckMatricesNotEqual(scaled, output);
}

/**
 * Check for invalid epsilon.
 */
BOOST_AUTO_TEST_CASE(InvalidEpsilonTest)
{
  std::string method = "pca_whitening";
  // Input custom data points.
  SetInputParam("input", dataset);
  SetInputParam("scaler_method", std::move(method));
  SetInputParam("epsilon", -1.0);

  BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
}

/**
 * Check for invalid range in min_max_scaler.
 */
BOOST_AUTO_TEST_CASE(InvalidRangeTest)
{
  std::string method = "min_max_scaler";
  // Input custom data points.
  SetInputParam("input", dataset);
  SetInputParam("scaler_method", std::move(method));
  SetInputParam("min_value", 4);
  SetInputParam("max_value", 2);

  BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
}

/**
 * Check for invalid scaler type.
 */
BOOST_AUTO_TEST_CASE(InvalidScalerTest)
{
  std::string method = "invalid_scaler";
  // Input custom data points.
  SetInputParam("input", dataset);
  SetInputParam("scaler_method", std::move(method));
  SetInputParam("min_value", 4);
  SetInputParam("max_value", 2);

  Log::Fatal.ignoreInput = true;
  BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
  Log::Fatal.ignoreInput = false;
}

/**
 * Check for Standard scaler type.
 */
BOOST_AUTO_TEST_CASE(StandardScalerTest)
{
  std::string method = "standard_scaler";
  // Input custom data points.
  SetInputParam("input", dataset);
  SetInputParam("scaler_method", method);
  BOOST_REQUIRE_NO_THROW(mlpackMain());
  SetInputParam("scaler_method", std::move(method));
  SetInputParam("input", dataset);
  SetInputParam("input_model",
                IO::GetParam<ScalingModel*>("output_model"));
  SetInputParam("inverse_scaling", true);
  BOOST_REQUIRE_NO_THROW(mlpackMain());
}

/**
 * Check for MaxAbs scaler type.
 */
BOOST_AUTO_TEST_CASE(MaxAbsScalerTest)
{
  std::string method = "max_abs_scaler";
  // Input custom data points.
  SetInputParam("input", dataset);
  SetInputParam("scaler_method", method);
  BOOST_REQUIRE_NO_THROW(mlpackMain());
  SetInputParam("scaler_method", std::move(method));
  SetInputParam("input", dataset);
  SetInputParam("input_model",
                IO::GetParam<ScalingModel*>("output_model"));
  SetInputParam("inverse_scaling", true);
  BOOST_REQUIRE_NO_THROW(mlpackMain());
}

/**
 * Check for MinMax scaler type.
 */
BOOST_AUTO_TEST_CASE(MinMaxScalerTest)
{
  std::string method = "min_max_scaler";
  // Input custom data points.
  SetInputParam("input", dataset);
  SetInputParam("scaler_method", method);
  SetInputParam("min_value", 2);
  SetInputParam("max_value", 4);
  BOOST_REQUIRE_NO_THROW(mlpackMain());
  SetInputParam("scaler_method", std::move(method));
  SetInputParam("input", dataset);
  SetInputParam("input_model",
                IO::GetParam<ScalingModel*>("output_model"));
  SetInputParam("inverse_scaling", true);
  BOOST_REQUIRE_NO_THROW(mlpackMain());
}

/**
 * Check for PCA scaler type.
 */
BOOST_AUTO_TEST_CASE(PCAScalerTest)
{
  std::string method = "pca_whitening";
  // Input custom data points.
  SetInputParam("input", dataset);
  SetInputParam("scaler_method", method);
  SetInputParam("epsilon", 1.0);
  BOOST_REQUIRE_NO_THROW(mlpackMain());
  SetInputParam("scaler_method", std::move(method));
  SetInputParam("input", dataset);
  SetInputParam("input_model",
                IO::GetParam<ScalingModel*>("output_model"));
  SetInputParam("inverse_scaling", true);
  BOOST_REQUIRE_NO_THROW(mlpackMain());
}

/**
 * Check for ZCA scaler type.
 */
BOOST_AUTO_TEST_CASE(ZCAScalerTest)
{
  std::string method = "zca_whitening";
  // Input custom data points.
  SetInputParam("input", dataset);
  SetInputParam("scaler_method", method);
  SetInputParam("epsilon", 1.0);
  BOOST_REQUIRE_NO_THROW(mlpackMain());
  SetInputParam("scaler_method", std::move(method));
  SetInputParam("input", dataset);
  SetInputParam("input_model",
                IO::GetParam<ScalingModel*>("output_model"));
  SetInputParam("inverse_scaling", true);
  BOOST_REQUIRE_NO_THROW(mlpackMain());
}

/**
 * Check for Mean Normalization scaler type.
 */
BOOST_AUTO_TEST_CASE(MeanNormalizationTest)
{
  std::string method = "mean_normalization";
  // Input custom data points.
  SetInputParam("input", dataset);
  SetInputParam("scaler_method", method);
  BOOST_REQUIRE_NO_THROW(mlpackMain());
  SetInputParam("scaler_method", std::move(method));
  SetInputParam("input", dataset);
  SetInputParam("input_model",
                IO::GetParam<ScalingModel*>("output_model"));
  SetInputParam("inverse_scaling", true);
  BOOST_REQUIRE_NO_THROW(mlpackMain());
}

BOOST_AUTO_TEST_SUITE_END();


// Copyright 2013 Daniel Parker
// Distributed under Boost license

#include <boost/test/unit_test.hpp>
#include <boost/numeric/ublas/matrix.hpp>
#include "jsoncons/json.hpp"
#include "jsoncons/json_serializer.hpp"
#include "my_custom_data.hpp"
#include <sstream>
#include <vector>
#include <utility>
#include <ctime>

using jsoncons::json_serializer;
using jsoncons::output_format;
using jsoncons::json;
using jsoncons::wjson;
using jsoncons::basic_json_reader;
using std::string;
using boost::numeric::ublas::matrix;

BOOST_AUTO_TEST_CASE(test_direct_serialization)
{
    matrix<double> A(2, 2);
    A(0, 0) = 1;
    A(0, 1) = 2;
    A(1, 0) = 3;
    A(1, 1) = 4;

    output_format format;
    json_serializer os(std::cout, format, true); // pretty printing
    os.begin_array();
    for (size_t i = 0; i < A.size1(); ++i)
    {
        os.begin_array();
        for (size_t j = 0; j < A.size2(); ++j)
        {
            os.value(A(i, j));
        }
        os.end_array();
    }
    os.end_array();
}


#include "net.h"

#include <boost/functional/hash.hpp>
#include <fstream>
#include <unordered_set>

namespace salt {

void Net::RanInit(int i, int numPin, DTYPE width, DTYPE height) {
    pins.clear();
    id = i;
    name = "random_pin" + to_string(numPin) + "_id" + to_string(i);
    int k = 0;
    unordered_set<pair<DTYPE, DTYPE>, boost::hash<pair<DTYPE, DTYPE>>> exist;
    int trial = 0;
    while (exist.size() < numPin) {
        DTYPE x = rand() % (int)width, y = rand() % (int)height;  // TODO: better random generator
        if (exist.find({x, y}) == exist.end()) {
            pins.push_back(make_shared<Pin>(x, y, k++));
            exist.insert({x, y});
        }
        if (trial++ > numPin * 2) {
            break;
            log() << "randomly try 2 * #pins times. give up." << endl;
        }
    }
    // log() << "specify " << numPin << ", get " << k << endl;
}

// TODO: use catch to handle istream error
bool Net::Read(istream& is) {
    // header
    string buf, option;
    int numPin = 0;
    while (is >> buf && buf != "Net")
        ;
    if (buf != "Net") return false;
    getline(is, buf);
    istringstream iss(buf);
    iss >> id >> name >> numPin >> option;
    assert(numPin > 0);
    withCap = (option == "-cap");

    // pins
    int i;
    DTYPE x, y;
    double c = 0.0;
    pins.resize(numPin);
    for (auto& pin : pins) {
        is >> i >> x >> y;
        if (withCap) is >> c;
        pin = make_shared<Pin>(x, y, i, c);
    }

    return true;
}

void Net::Read(const string& fileName) {
    ifstream is(fileName);
    if (is.fail()) {
        cout << "ERROR: Cannot open file " << fileName << endl;
        exit(1);
    }
    Read(is);
}

string Net::GetHeader() const {
    string header = to_string(id) + " " + name + " " + to_string(pins.size());
    // string header = name + " " + to_string(pins.size());
    if (withCap) header += " -cap";
    return header;
}

void Net::Write(ostream& os) const {
    // header
    os << "Net " << GetHeader() << endl;

    // pins
    for (const auto& pin : pins) {
        os << pin->id << " " << pin->loc.x << " " << pin->loc.y;
        if (withCap) os << " " << pin->cap;
        os << endl;
    }
}

void Net::Write(const string& prefix, bool withNetInfo) const {
    ofstream os(prefix + (withNetInfo ? ("_" + name) : "") + ".net");
    Write(os);
}

}  // namespace salt


/*==============================================================================
    Copyright (c) 2002-2003 Joel de Guzman
    Copyright (c) 2006 Tobias Schwinger
    http://spirit.sourceforge.net/

    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)
==============================================================================*/
//------------------------------------------------------------------------------
//  This example shows a recursive grammar built using subrules and typeof.
//  See boost/spirit/include/rule_parser.hpp for details.
//  This example is based on subrule_calc.cpp.
//------------------------------------------------------------------------------
#include <string>
#include <iostream>

#include <boost/typeof/typeof.hpp>

#include <boost/spirit/include/classic_core.hpp>
#include <boost/spirit/include/classic_typeof.hpp>

#include <boost/spirit/include/classic_rule_parser.hpp>

// Don't forget to
#include BOOST_TYPEOF_INCREMENT_REGISTRATION_GROUP()

using namespace BOOST_SPIRIT_CLASSIC_NS;

// Semantic actions
namespace
{
  void do_int(int v)       { std::cout << "PUSH(" << v << ')' << std::endl; }
  void do_add(char const*, char const*)  { std::cout << "ADD" << std::endl; }
  void do_sub(char const*, char const*)  { std::cout << "SUB" << std::endl; }
  void do_mul(char const*, char const*)  { std::cout << "MUL" << std::endl; }
  void do_div(char const*, char const*)  { std::cout << "DIV" << std::endl; }
  void do_neg(char const*, char const*)  { std::cout << "NEG" << std::endl; }
}

// Operating at root namespace...
#define BOOST_SPIRIT__NAMESPACE -

// Our calculator grammar using subrules in a rule parser.
BOOST_SPIRIT_RULE_PARSER( calc,
  -,
  -,
  (3,( ((subrule<0>),expression,()),
       ((subrule<1>),term,()),
       ((subrule<2>),factor,() )) ),
  (
    expression =
        term
        >> *(   ('+' >> term)[&do_add]
            |   ('-' >> term)[&do_sub]
            )
    ,

    term =
        factor
        >> *(   ('*' >> factor)[&do_mul]
            |   ('/' >> factor)[&do_div]
            )
    ,

    factor =
        int_p[&do_int]
        |   ('(' >> expression >> ')')
        |   ('-' >> factor)[&do_neg]
        |   ('+' >> factor)
  )
)

#undef BOOST_SPIRIT__NAMESPACE

// Main program
int main()
{
  std::cout
  << "/////////////////////////////////////////////////////////\n"
  << "\t\tA ruleless calculator using subrules...\n"
  << "/////////////////////////////////////////////////////////\n"
  << "Type an expression...or an empty line to quit\n"
  << std::endl;

  std::string str;
  while (std::getline(std::cin, str))
  {
    if (str.empty()) break;

    parse_info<> info = parse(str.c_str(), calc, space_p);

    if (info.full)
      std::cout
      << "OK."
      << std::endl;
    else
      std::cout
      << "ERROR.\n"
      << "Stopped at: \": " << info.stop << "\".\n"
      << std::endl;
  }
  return 0;
}


/*
@copyright Louis Dionne 2015
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
 */

#include <boost/hana/map.hpp>

#include <boost/hana/assert.hpp>
#include <boost/hana/maybe.hpp>
#include <boost/hana/pair.hpp>
#include <boost/hana/tuple.hpp>

#include <laws/base.hpp>
#include <test/minimal_product.hpp>
#include <test/minimal_record.hpp>
#include <test/seq.hpp>
using namespace boost::hana;

template <int i>
auto key = test::ct_eq<i>{};

template <int i>
auto val = test::ct_eq<-i>{};

template <int i, int j>
auto p = test::minimal_product(key<i>, val<j>);

int main() {
    //////////////////////////////////////////////////////////////////////////
    // Map interface
    //////////////////////////////////////////////////////////////////////////
    {
        auto list = test::seq;
        auto foldable = test::seq;
        auto record = test::minimal_record;
        using L = test::Seq;

        // keys
        {
            BOOST_HANA_CONSTANT_CHECK(equal(
                keys(make<Map>()),
                list()
            ));
            BOOST_HANA_CONSTANT_CHECK(equal(
                keys(make<Map>(p<1, 1>)),
                list(key<1>)
            ));
            BOOST_HANA_CONSTANT_CHECK(equal(
                keys(make<Map>(p<1, 1>, p<2, 2>)),
                list(key<1>, key<2>)
            ));
            BOOST_HANA_CONSTANT_CHECK(equal(
                keys(make<Map>(p<1, 1>, p<2, 2>, p<3, 3>)),
                list(key<1>, key<2>, key<3>)
            ));
        }

        // values
        {
            BOOST_HANA_CONSTANT_CHECK(equal(
                values(make<Map>()),
                list()
            ));
            BOOST_HANA_CONSTANT_CHECK(equal(
                values(make<Map>(p<1, 1>)),
                list(val<1>)
            ));
            BOOST_HANA_CONSTANT_CHECK(equal(
                values(make<Map>(p<1, 1>, p<2, 2>)),
                list(val<1>, val<2>)
            ));
            BOOST_HANA_CONSTANT_CHECK(equal(
                values(make<Map>(p<1, 1>, p<2, 2>, p<3, 3>)),
                list(val<1>, val<2>, val<3>)
            ));
        }

        // Conversions
        {
            // Record -> Map
            {
                BOOST_HANA_CONSTANT_CHECK(equal(
                    to<Map>(record(test::ct_eq<1>{}, test::ct_eq<2>{})),
                    make<Map>(
                        make<Pair>(test::member1, test::ct_eq<1>{}),
                        make<Pair>(test::member2, test::ct_eq<2>{})
                    )
                ));
            }

            // Foldable -> Map
            {
                BOOST_HANA_CONSTANT_CHECK(equal(
                    to<Map>(foldable()),
                    make<Map>()
                ));
                BOOST_HANA_CONSTANT_CHECK(equal(
                    to<Map>(foldable(p<1, 1>)),
                    make<Map>(p<1, 1>)
                ));
                BOOST_HANA_CONSTANT_CHECK(equal(
                    to<Map>(foldable(p<1, 1>, p<2, 2>)),
                    make<Map>(p<1, 1>, p<2, 2>)
                ));
                BOOST_HANA_CONSTANT_CHECK(equal(
                    to<Map>(foldable(p<1, 1>, p<2, 2>, p<3, 3>)),
                    make<Map>(p<1, 1>, p<2, 2>, p<3, 3>)
                ));
            }

            // Map -> Sequence
            {
                BOOST_HANA_CONSTEXPR_LAMBDA auto check = [=](auto ...xs) {
                    BOOST_HANA_CONSTANT_CHECK(
                        elem(permutations(list(xs...)), to<L>(make<Map>(xs...)))
                    );
                };
                check();
                check(p<1, 1>);
                check(p<1, 1>, p<2, 2>);
                check(p<1, 1>, p<2, 2>, p<3, 3>);
                check(p<1, 1>, p<2, 2>, p<3, 3>, p<4, 4>);
            }
        }
    }
}


/**
 * @file tests/main_tests/bayesian_linear_regression_test.cpp
 * @author Clement Mercier
 *
 * Test mlpackMain() of bayesian_linear_regression_main.cpp.
 *
 * mlpack is free software; you may redistribute it and/or modify it under the
 * terms of the 3-clause BSD license.  You should have received a copy of the
 * 3-clause BSD license along with mlpack.  If not, see
 * http://www.opensource.org/licenses/BSD-3-Clause for more information.
 */
#include <string>

#define BINDING_TYPE BINDING_TYPE_TEST
static const std::string testName = "BayesianLinearRegression";

#include <mlpack/core.hpp>
#include <mlpack/core/util/mlpack_main.hpp>
#include "test_helper.hpp"
#include <mlpack/methods/bayesian_linear_regression/bayesian_linear_regression_main.cpp>

#include <boost/test/unit_test.hpp>
#include "../test_tools.hpp"

using namespace mlpack;

struct BRTestFixture
{
 public:
  BRTestFixture()
  {
    // Cache in the options for this program.
    IO::RestoreSettings(testName);
  }

  ~BRTestFixture()
  {
    // Clear the settings.
    bindings::tests::CleanMemory();
    IO::ClearSettings();
  }
};

BOOST_FIXTURE_TEST_SUITE(BayesianLinearRegressionMainTest, BRTestFixture);

/**
 * Check the center and scale options.
 */
BOOST_AUTO_TEST_CASE(BRCenter0Scale0)
{
  int n = 50, m = 4;
  arma::mat matX = arma::randu<arma::mat>(m, n);
  arma::rowvec omega = arma::randu<arma::rowvec>(m);
  arma::rowvec y =  omega * matX;

  SetInputParam("input", std::move(matX));
  SetInputParam("responses", std::move(y));
  SetInputParam("center", false);

  mlpackMain();

  BayesianLinearRegression* estimator =
      IO::GetParam<BayesianLinearRegression*>("output_model");

  BOOST_REQUIRE(estimator->DataOffset().n_elem == 0);
  BOOST_REQUIRE(estimator->DataScale().n_elem == 0);
}

/**
 * Check predictions of saved model and in code model are equal.
 */
BOOST_AUTO_TEST_CASE(BayesianLinearRegressionSavedEqualCode)
{
  int n = 10, m = 4;
  arma::mat matX = arma::randu<arma::mat>(m, n);
  arma::mat matXtest = arma::randu<arma::mat>(m, 2 * n);
  const arma::rowvec omega = arma::randu<arma::rowvec>(m);
  arma::rowvec y =  omega * matX;

  BayesianLinearRegression model;
  model.Train(matX, y);

  arma::rowvec responses;
  model.Predict(matXtest, responses);

  SetInputParam("input", std::move(matX));
  SetInputParam("responses", std::move(y));

  mlpackMain();

  IO::GetSingleton().Parameters()["input"].wasPassed = false;
  IO::GetSingleton().Parameters()["responses"].wasPassed = false;

  SetInputParam("input_model",
                IO::GetParam<BayesianLinearRegression*>("output_model"));
  SetInputParam("test", std::move(matXtest));

  mlpackMain();

  arma::mat ytest = std::move(responses);
  // Check that initial output and output using saved model are same.
  CheckMatrices(ytest, IO::GetParam<arma::mat>("predictions"));
}

/**
 * Check a crash happens if neither input or input_model are specified.
 * Check a crash happens if both input and input_model are specified.
 */
BOOST_AUTO_TEST_CASE(CheckParamsPassed)
{
  int n = 10, m = 4;
  arma::mat matX = arma::randu<arma::mat>(m, n);
  arma::mat matXtest = arma::randu<arma::mat>(m, 2 * n);
  const arma::rowvec omega = arma::randu<arma::rowvec>(m);
  arma::rowvec y =  omega * matX;

  BayesianLinearRegression model;
  model.Train(matX, y);

  arma::rowvec responses;
  model.Predict(matXtest, responses);

  // Check that std::runtime_error is thrown if neither input or input_model
  // is specified.
  SetInputParam("responses", std::move(y));

  BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);

  // Continue only with input passed.
  SetInputParam("input", std::move(matX));
  mlpackMain();

  // Now pass the previous trained model and one input matrix at the same time.
  // An error should occur.
  SetInputParam("input", std::move(matX));
  SetInputParam("input_model",
                IO::GetParam<BayesianLinearRegression*>("output_model"));
  SetInputParam("test", std::move(matXtest));

  BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
}

BOOST_AUTO_TEST_SUITE_END();


/* 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.
 */

#include <filesystem>
#include <memory>

#include <gtest/gtest.h>

#include <Eigen/Dense>

#include <solvers/Solver.hpp>
#include <solvers/SparseSystem.hpp>

namespace {

namespace fs = std::filesystem;

class SolverTest : public ::testing::Test {
protected:
    std::unique_ptr<solvers::SparseSystem> _system;

    SolverTest() : _system(nullptr)
    {
        const fs::path datasets_dir(DATASETS_DIR);
        const fs::path dataset_dir = datasets_dir / "stand_small_test";
        fs::path npz_path;
        for (const fs::path& entry : fs::directory_iterator(dataset_dir)) {
            if (entry.extension() == ".npz") {
                npz_path = entry;
                break;
            }
        }
        solvers::SparseSystem system = solvers::SparseSystem::loadNpz(npz_path);
        _system = std::make_unique<solvers::SparseSystem>(system);
    }

    bool testSolver(const solvers::Solver& solver,
                    const double tolerance = 1e-8)
    {
        double duration;
        Eigen::VectorXd result = solver.solve(*_system, duration);
        Eigen::VectorXd x = _system->getEigenSolution();
        return result.isApprox(x, tolerance);
    }
};

}    // namespace


#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include <cctbx/french_wilson.h>
#include <scitbx/array_family/boost_python/shared_wrapper.h>
#include <cctbx/import_scitbx_af.h>

namespace cctbx { namespace boost_python {

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

    def("expectEFW",
      (double(*)
        (double,
         double,
         bool)) expectEFW, (
      arg("eosq"),
      arg("sigesq"),
      arg("centric")));

    def("expectEsqFW",
      (double(*)
        (double,
         double,
         bool)) expectEsqFW, (
      arg("eosq"),
      arg("sigesq"),
      arg("centric")));

    def("is_FrenchWilson",
      (bool(*)
        (af::shared<double>,
         af::shared<double>,
         af::shared<bool>,
         double)) is_FrenchWilson, (
      arg("F"),
      arg("SIGF"),
      arg("is_centric"),
      arg("eps")));

  }

}}

BOOST_PYTHON_MODULE(cctbx_french_wilson_ext)
{
  cctbx::boost_python::init_module();
}


/*
 Copyright (C) 2006 Giorgio Facchinetti
 Copyright (C) 2006 Mario Pucci

 This file is part of QuantLib, a free-software/open-source library
 for financial quantitative analysts and developers - http://quantlib.org/

 QuantLib is free software: you can redistribute it and/or modify it
 under the terms of the QuantLib license.  You should have received a
 copy of the license along with this program; if not, please email
 <quantlib-dev@lists.sf.net>. The license is also available online at
 <http://quantlib.org/license.shtml>.

 This program is distributed in the hope that it will be useful, but
 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
 */

/*! \file conundrumpricer.hpp
    \brief
*/

#include <ql/cashflows/conundrumpricer.hpp>
#include <ql/math/integrals/kronrodintegral.hpp>
#include <ql/math/distributions/normaldistribution.hpp>
#include <ql/pricingengines/blackformula.hpp>
#include <ql/math/solvers1d/newton.hpp>
#include <ql/termstructures/volatility/smilesection.hpp>
#include <ql/cashflows/cmscoupon.hpp>
#include <ql/termstructures/yieldtermstructure.hpp>
#include <ql/quotes/simplequote.hpp>
#include <ql/indexes/swapindex.hpp>
#include <ql/indexes/interestrateindex.hpp>
#include <ql/time/schedule.hpp>
#include <ql/instruments/vanillaswap.hpp>

#if defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) || (__GNUC__ > 4))
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#endif

#include <boost/bind.hpp>

#if defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) || (__GNUC__ > 4))
#pragma GCC diagnostic pop
#endif

namespace QuantLib {

//===========================================================================//
//                          BlackVanillaOptionPricer                         //
//===========================================================================//

    BlackVanillaOptionPricer::BlackVanillaOptionPricer(
            Rate forwardValue,
            Date expiryDate,
            const Period& swapTenor,
            const boost::shared_ptr<SwaptionVolatilityStructure>& volatilityStructure) :
    forwardValue_(forwardValue), expiryDate_(expiryDate), swapTenor_(swapTenor),
        volatilityStructure_(volatilityStructure),
        smile_(volatilityStructure_->smileSection(expiryDate_, swapTenor_)) {
        }

    Real BlackVanillaOptionPricer::operator()(Real strike,
                                              Option::Type optionType,
                                              Real deflator) const {
        const Real variance = smile_->variance(strike);
        return deflator * blackFormula(optionType, strike,
            forwardValue_, std::sqrt(variance));
    }

//===========================================================================//
//                             HaganPricer                               //
//===========================================================================//
    HaganPricer::HaganPricer(
                const Handle<SwaptionVolatilityStructure>& swaptionVol,
                GFunctionFactory::YieldCurveModel modelOfYieldCurve,
                const Handle<Quote>& meanReversion)
    : CmsCouponPricer(swaptionVol),
      modelOfYieldCurve_(modelOfYieldCurve),
      cutoffForCaplet_(2), cutoffForFloorlet_(0),
      meanReversion_(meanReversion) {
          registerWith(meanReversion_);
    }

    void HaganPricer::initialize(const FloatingRateCoupon& coupon){
        coupon_ =  dynamic_cast<const CmsCoupon*>(&coupon);
        QL_REQUIRE(coupon_, "CMS coupon needed");
        gearing_ = coupon_->gearing();
        spread_ = coupon_->spread();
        Time accrualPeriod = coupon_->accrualPeriod();
        QL_REQUIRE(accrualPeriod != 0.0, "null accrual period");

        fixingDate_ = coupon_->fixingDate();
        paymentDate_ = coupon_->date();
        const boost::shared_ptr<SwapIndex>& swapIndex = coupon_->swapIndex();
        rateCurve_ = *(swapIndex->forwardingTermStructure());

        Date today = Settings::instance().evaluationDate();

        if(paymentDate_ > today)
            discount_ = rateCurve_->discount(paymentDate_);
        else discount_= 1.;

        spreadLegValue_ = spread_ * accrualPeriod * discount_;

        if (fixingDate_ > today){
            swapTenor_ = swapIndex->tenor();
            boost::shared_ptr<VanillaSwap> swap = swapIndex->underlyingSwap(fixingDate_);

            swapRateValue_ = swap->fairRate();

            static const Spread bp = 1.0e-4;
            annuity_ = std::fabs(swap->fixedLegBPS()/bp);

            Size q = swapIndex->fixedLegTenor().frequency();
            const Schedule& schedule = swap->fixedSchedule();
            const DayCounter& dc = swapIndex->dayCounter();
            //const DayCounter dc = coupon.dayCounter();
            Time startTime = dc.yearFraction(rateCurve_->referenceDate(),
                                             swap->startDate());
            Time swapFirstPaymentTime =
                dc.yearFraction(rateCurve_->referenceDate(), schedule.date(1));
            Time paymentTime = dc.yearFraction(rateCurve_->referenceDate(),
                                               paymentDate_);
            Real delta = (paymentTime-startTime) / (swapFirstPaymentTime-startTime);

            switch (modelOfYieldCurve_) {
                case GFunctionFactory::Standard:
                    gFunction_ = GFunctionFactory::newGFunctionStandard(q, delta, swapTenor_.length());
                    break;
                case GFunctionFactory::ExactYield:
                    gFunction_ = GFunctionFactory::newGFunctionExactYield(*coupon_);
                    break;
                case GFunctionFactory::ParallelShifts: {
                    Handle<Quote> nullMeanReversionQuote(boost::shared_ptr<Quote>(new SimpleQuote(0.0)));
                    gFunction_ = GFunctionFactory::newGFunctionWithShifts(*coupon_, nullMeanReversionQuote);
                    }
                    break;
                case GFunctionFactory::NonParallelShifts:
                    gFunction_ = GFunctionFactory::newGFunctionWithShifts(*coupon_, meanReversion_);
                    break;
                default:
                    QL_FAIL("unknown/illegal gFunction type");
            }
            vanillaOptionPricer_= boost::shared_ptr<VanillaOptionPricer>(new
                BlackVanillaOptionPricer(swapRateValue_, fixingDate_, swapTenor_,
                                        *swaptionVolatility()));
         }
    }

    Real HaganPricer::meanReversion() const { return meanReversion_->value();}

    Rate HaganPricer::swapletRate() const {
        return swapletPrice()/(coupon_->accrualPeriod()*discount_);
    }

    Real HaganPricer::capletPrice(Rate effectiveCap) const {
        // caplet is equivalent to call option on fixing
        Date today = Settings::instance().evaluationDate();
        if (fixingDate_ <= today) {
            // the fixing is determined
            const Rate Rs =
                std::max(coupon_->swapIndex()->fixing(fixingDate_)-effectiveCap, 0.);
            Rate price = (gearing_*Rs)*(coupon_->accrualPeriod()*discount_);
            return price;
        } else {
            Real cutoffNearZero = 1e-10;
            Real capletPrice = 0;
            if (effectiveCap < cutoffForCaplet_) {
                Rate effectiveStrikeForMax = std::max(effectiveCap,cutoffNearZero);
                capletPrice = optionletPrice(Option::Call, effectiveStrikeForMax);
            }
            return gearing_ * capletPrice;
        }
    }

    Rate HaganPricer::capletRate(Rate effectiveCap) const {
        return capletPrice(effectiveCap)/(coupon_->accrualPeriod()*discount_);
    }

    Real HaganPricer::floorletPrice(Rate effectiveFloor) const {
        // floorlet is equivalent to put option on fixing
        Date today = Settings::instance().evaluationDate();
        if (fixingDate_ <= today) {
            // the fixing is determined
            const Rate Rs =
                std::max(effectiveFloor-coupon_->swapIndex()->fixing(fixingDate_),0.);
            Rate price = (gearing_*Rs)*(coupon_->accrualPeriod()*discount_);
            return price;
        } else {
            Real cutoffNearZero = 1e-10;
            Real floorletPrice = 0;
            if (effectiveFloor > cutoffForFloorlet_){
                Rate effectiveStrikeForMin = std::max(effectiveFloor,cutoffNearZero);
                floorletPrice=optionletPrice(Option::Put, effectiveStrikeForMin);
            }
            return gearing_ * floorletPrice;
        }
    }

    Rate HaganPricer::floorletRate(Rate effectiveFloor) const {
        return floorletPrice(effectiveFloor)/(coupon_->accrualPeriod()*discount_);
    }

//===========================================================================//
//                  NumericHaganPricer                    //
//===========================================================================//

    namespace {

        class VariableChange {
          public:
            VariableChange(boost::function<Real (Real)>& f,
                           Real a, Real b, Size k)
            : a_(a), width_(b-a), f_(f), k_(k) {}
            Real value(Real x) const {
                Real newVar;
                Real temp = width_;
                for (Size i = 1; i < k_ ; ++i) {
                    temp *= x;
                }
                newVar = a_ + x* temp;
                return f_(newVar) * k_* temp;
            }
          private:
            Real a_, width_;
            boost::function<Real (Real)> f_;
            Size k_;
        };

        class Spy {
          public:
            Spy(boost::function<Real (Real)> f) : f_(f) {}
            Real value(Real x){
                abscissas.push_back(x);
                Real value = f_(x);
                functionValues.push_back(value);
                return value;
            }
          private:
            boost::function<Real (Real)> f_;
            std::vector<Real> abscissas;
            std::vector<Real> functionValues;
        };

    }

    NumericHaganPricer::NumericHaganPricer(
        const Handle<SwaptionVolatilityStructure>& swaptionVol,
        GFunctionFactory::YieldCurveModel modelOfYieldCurve,
        const Handle<Quote>& meanReversion,
        Real lowerLimit,
        Real upperLimit,
        Real precision,
        Real hardUpperLimit)
    : HaganPricer(swaptionVol, modelOfYieldCurve, meanReversion),
       upperLimit_(upperLimit),
       lowerLimit_(lowerLimit),
       requiredStdDeviations_(8),
       precision_(precision),
       refiningIntegrationTolerance_(.0001),
       hardUpperLimit_(hardUpperLimit) {

    }

    Real NumericHaganPricer::integrate(Real a,
        Real b, const ConundrumIntegrand& integrand) const {
            Real result =.0;
            //double abserr =.0;
            //double alpha = 1.0;

            //double epsabs = precision_;
            //double epsrel = 1.0; // we are interested only in absolute precision
            //size_t neval =0;

            // we use the non adaptive algorithm only for semi infinite interval
            if (a>0){

                // we estimate the actual boudary by testing integrand values
                Real upperBoundary = 2*a;
                while(integrand(upperBoundary)>precision_)
                    upperBoundary *=2.0;
                // sometimes b < a because of a wrong estimation of b based on stdev
                if (b > a)
                    upperBoundary = std::min(upperBoundary, b);

                boost::function<Real (Real)> f;
                GaussKronrodNonAdaptive
                    gaussKronrodNonAdaptive(precision_, 1000000, 1.0);
                // if the integration intervall is wide enough we use the
                // following change variable x -> a + (b-a)*(t/(a-b))^3
                upperBoundary = std::max(a,std::min(upperBoundary, hardUpperLimit_));
                if (upperBoundary > 2*a){
                    Size k = 3;
                    boost::function<Real (Real)> temp = boost::ref(integrand);
                    VariableChange variableChange(temp, a, upperBoundary, k);
                    f = boost::bind(&VariableChange::value, &variableChange, _1);
                    result = gaussKronrodNonAdaptive(f, .0, 1.0);
                } else {
                    f = boost::ref(integrand);
                    result = gaussKronrodNonAdaptive(f, a, upperBoundary);
                }

                // if the expected precision has not been reached we use the old algorithm
                if (!gaussKronrodNonAdaptive.integrationSuccess()){
                    const GaussKronrodAdaptive integrator(precision_, 100000);
                    b = std::max(a,std::min(b, hardUpperLimit_));
                    result = integrator(integrand,a , b);
                }

            } else {   // if a < b we use the old algorithm
                b = std::max(a,std::min(b,hardUpperLimit_));
                const GaussKronrodAdaptive integrator(precision_, 100000);
                result = integrator(integrand,a , b);
            }
            return result;
    }

    Real NumericHaganPricer::optionletPrice(
                                Option::Type optionType, Real strike) const {

        boost::shared_ptr<ConundrumIntegrand> integrand(new
            ConundrumIntegrand(vanillaOptionPricer_, rateCurve_, gFunction_,
                               fixingDate_, paymentDate_, annuity_,
                               swapRateValue_, strike, optionType));
        stdDeviationsForUpperLimit_= requiredStdDeviations_;
        Real a, b, integralValue;
        if (optionType==Option::Call) {
            upperLimit_ = resetUpperLimit(stdDeviationsForUpperLimit_);
        //    while(upperLimit_ <= strike){
        //        stdDeviationsForUpperLimit_ += 1.;
        //        upperLimit_ = resetUpperLimit(stdDeviationsForUpperLimit_);
        //    }
            integralValue = integrate(strike, upperLimit_, *integrand);
            //refineIntegration(integralValue, *integrand);
        } else {
            a = std::min(strike, lowerLimit_);
            b = strike;
            integralValue = integrate(a, b, *integrand);
        }

        Real dFdK = integrand->firstDerivativeOfF(strike);
        Real swaptionPrice =
            (*vanillaOptionPricer_)(strike, optionType, annuity_);

        // v. HAGAN, Conundrums..., formule 2.17a, 2.18a
        return coupon_->accrualPeriod() * (discount_/annuity_) *
            ((1 + dFdK) * swaptionPrice + optionType*integralValue);
    }

    Real NumericHaganPricer::swapletPrice() const {

        Date today = Settings::instance().evaluationDate();
        if (fixingDate_ <= today) {
            // the fixing is determined
            const Rate Rs = coupon_->swapIndex()->fixing(fixingDate_);
            Rate price = (gearing_*Rs + spread_)*(coupon_->accrualPeriod()*discount_);
            return price;
        } else {
            Real atmCapletPrice = optionletPrice(Option::Call, swapRateValue_);
            Real atmFloorletPrice = optionletPrice(Option::Put, swapRateValue_);
            return gearing_ *(coupon_->accrualPeriod()* discount_ * swapRateValue_
                             + atmCapletPrice - atmFloorletPrice)
                   + spreadLegValue_;
        }
    }

    Real NumericHaganPricer::refineIntegration(Real integralValue,
                                                const ConundrumIntegrand& integrand) const {
        Real percDiff = 1000.;
        while(std::fabs(percDiff) < refiningIntegrationTolerance_){
            stdDeviationsForUpperLimit_ += 1.;
            Real lowerLimit = upperLimit_;
            upperLimit_ = resetUpperLimit(stdDeviationsForUpperLimit_);
            Real diff = integrate(lowerLimit, upperLimit_,integrand);
            percDiff = diff/integralValue;
            integralValue += diff;
        }
        return integralValue;
    }

    Real NumericHaganPricer::resetUpperLimit(
                        Real stdDeviationsForUpperLimit) const {
        //return 1.0;
        Real variance =
            swaptionVolatility()->blackVariance(fixingDate_,swapTenor_,swapRateValue_);
        return swapRateValue_ *
            std::exp(stdDeviationsForUpperLimit*std::sqrt(variance));
    }

//===========================================================================//
//                              ConundrumIntegrand                           //
//===========================================================================//

    NumericHaganPricer::ConundrumIntegrand::ConundrumIntegrand(
        const boost::shared_ptr<VanillaOptionPricer>& o,
        const boost::shared_ptr<YieldTermStructure>&,
        const boost::shared_ptr<GFunction>& gFunction,
        Date fixingDate,
        Date paymentDate,
        Real annuity,
        Real forwardValue,
        Real strike,
        Option::Type optionType)
    : vanillaOptionPricer_(o), forwardValue_(forwardValue), annuity_(annuity),
      fixingDate_(fixingDate), paymentDate_(paymentDate), strike_(strike),
      optionType_(optionType),
      gFunction_(gFunction) {}

    void NumericHaganPricer::ConundrumIntegrand::setStrike(Real strike) {
        strike_ = strike;
    }

    Real NumericHaganPricer::ConundrumIntegrand::strike() const {
        return strike_;
    }

    Real NumericHaganPricer::ConundrumIntegrand::annuity() const {
        return annuity_;
    }

    Date NumericHaganPricer::ConundrumIntegrand::fixingDate() const {
        return fixingDate_;
    }

    Real NumericHaganPricer::ConundrumIntegrand::functionF (const Real x) const {
        const Real Gx = gFunction_->operator()(x);
        const Real GR = gFunction_->operator()(forwardValue_);
        return (x - strike_) * (Gx/GR - 1.0);
    }

    Real NumericHaganPricer::ConundrumIntegrand::firstDerivativeOfF (const Real x) const {
        const Real Gx = gFunction_->operator()(x);
        const Real GR = gFunction_->operator()(forwardValue_) ;
        const Real G1 = gFunction_->firstDerivative(x);
        return (Gx/GR - 1.0) + G1/GR * (x - strike_);
    }

    Real NumericHaganPricer::ConundrumIntegrand::secondDerivativeOfF (const Real x) const {
        const Real GR = gFunction_->operator()(forwardValue_) ;
        const Real G1 = gFunction_->firstDerivative(x);
        const Real G2 = gFunction_->secondDerivative(x);
        return 2.0 * G1/GR + (x - strike_) * G2/GR;
    }

    Real NumericHaganPricer::ConundrumIntegrand::operator()(Real x) const {
        const Real option = (*vanillaOptionPricer_)(x, optionType_, annuity_);
        return option * secondDerivativeOfF(x);
    }

//===========================================================================//
//                          AnalyticHaganPricer                           //
//===========================================================================//

    AnalyticHaganPricer::AnalyticHaganPricer(
        const Handle<SwaptionVolatilityStructure>& swaptionVol,
        GFunctionFactory::YieldCurveModel modelOfYieldCurve,
        const Handle<Quote>& meanReversion)
    : HaganPricer(swaptionVol, modelOfYieldCurve, meanReversion)
      { }

    //Hagan, 3.5b, 3.5c
    Real AnalyticHaganPricer::optionletPrice(Option::Type optionType,
                                                  Real strike) const {
        Real variance = swaptionVolatility()->blackVariance(fixingDate_,
                                                           swapTenor_,
                                                           swapRateValue_);
        Real firstDerivativeOfGAtForwardValue = gFunction_->firstDerivative(
                                                        swapRateValue_);
        Real price = 0;

        Real CK = (*vanillaOptionPricer_)(strike, optionType, annuity_);
        price += (discount_/annuity_)*CK;
        const Real sqrtSigma2T = std::sqrt(variance);
        const Real lnRoverK =  std::log(swapRateValue_/strike);
        const Real d32 = (lnRoverK+1.5*variance)/sqrtSigma2T;
        const Real d12 =  (lnRoverK+.5*variance)/sqrtSigma2T;
        const Real dminus12 =  (lnRoverK-.5*variance)/sqrtSigma2T;

        CumulativeNormalDistribution cumulativeOfNormal;
        const Real N32 = cumulativeOfNormal(optionType*d32);
        const Real N12 = cumulativeOfNormal(optionType*d12);
        const Real Nminus12 = cumulativeOfNormal(optionType*dminus12);

        price += optionType * firstDerivativeOfGAtForwardValue * annuity_ *
            swapRateValue_ * (swapRateValue_ * std::exp(variance) * N32-
            (swapRateValue_+strike) * N12 + strike * Nminus12);
        price *= coupon_->accrualPeriod();
        return price;
    }

    //Hagan 3.4c
    Real AnalyticHaganPricer::swapletPrice() const {

        Date today = Settings::instance().evaluationDate();
        if (fixingDate_ <= today) {
            // the fixing is determined
            const Rate Rs = coupon_->swapIndex()->fixing(fixingDate_);
            Rate price = (gearing_*Rs + spread_)*(coupon_->accrualPeriod()*discount_);
            return price;
        } else {
            Real variance(swaptionVolatility()->blackVariance(fixingDate_,
                                                               swapTenor_,
                                                               swapRateValue_));
            Real firstDerivativeOfGAtForwardValue(gFunction_->firstDerivative(
                                                            swapRateValue_));
            Real price = 0;
            price += discount_*swapRateValue_;
            price += firstDerivativeOfGAtForwardValue*annuity_*swapRateValue_*
                     swapRateValue_*(std::exp(variance)-1.);
            return gearing_ * price * coupon_->accrualPeriod() + spreadLegValue_;
        }
    }

//===========================================================================//
//                              GFunctionStandard                            //
//===========================================================================//

    Real GFunctionFactory::GFunctionStandard::operator()(Real x) {
        Real n = static_cast<Real>(swapLength_) * q_;
        return x / std::pow((1.0 + x/q_), delta_) * 1.0 /
            (1.0 - 1.0 / std::pow((1.0 + x/q_), n));
    }

    Real GFunctionFactory::GFunctionStandard::firstDerivative(Real x) {
        Real n = static_cast<Real>(swapLength_) * q_;
        Real a = 1.0 + x / q_;
        Real AA = a - delta_/q_ * x;
        Real B = std::pow(a,(n - delta_ - 1.0))/(std::pow(a,n) - 1.0);

        Real secNum = n * x * std::pow(a,(n-1.0));
        Real secDen = q_ * std::pow(a, delta_) * (std::pow(a, n) - 1.0) *
            (std::pow(a, n) - 1.0);
        Real sec = secNum / secDen;

        return AA * B - sec;
    }

    Real GFunctionFactory::GFunctionStandard::secondDerivative(Real x) {
        Real n = static_cast<Real>(swapLength_) * q_;
        Real a = 1.0 + x/q_;
        Real AA = a - delta_/q_ * x;
        Real A1 = (1.0 - delta_)/q_;
        Real B = std::pow(a,(n - delta_ - 1.0))/(std::pow(a,n) - 1.0);
        Real Num = (1.0 + delta_ - n) * std::pow(a, (n-delta_-2.0)) -
            (1.0 + delta_) * std::pow(a, (2.0*n-delta_-2.0));
        Real Den = (std::pow(a, n) - 1.0) * (std::pow(a, n) - 1.0);
        Real B1 = 1.0 / q_ * Num / Den;

        Real C =  x / std::pow(a, delta_);
        Real C1 = (std::pow(a, delta_)
            - delta_ /q_ * x * std::pow(a, (delta_ - 1.0))) / std::pow(a, 2 * delta_);

        Real D =  std::pow(a, (n-1.0))/ ((std::pow(a, n) - 1.0) * (std::pow(a, n) - 1.0));
        Real D1 = ((n - 1.0) * std::pow(a, (n-2.0)) * (std::pow(a, n) - 1.0)
            - 2 * n * std::pow(a, (2 * (n-1.0))))
            / (q_ * (std::pow(a, n) - 1.0)*(std::pow(a, n) - 1.0)*(std::pow(a, n) - 1.0));

        return A1 * B + AA * B1 - n/q_ * (C1 * D + C * D1);
    }

    boost::shared_ptr<GFunction> GFunctionFactory::newGFunctionStandard(Size q,
                                                            Real delta, Size swapLength) {
        return boost::shared_ptr<GFunction>(new GFunctionStandard(q, delta, swapLength));
    }

//===========================================================================//
//                              GFunctionExactYield                          //
//===========================================================================//

    GFunctionFactory::GFunctionExactYield::GFunctionExactYield(const CmsCoupon& coupon){

        const boost::shared_ptr<SwapIndex>& swapIndex = coupon.swapIndex();
        const boost::shared_ptr<VanillaSwap>& swap =
            swapIndex->underlyingSwap(coupon.fixingDate());

        const Schedule& schedule = swap->fixedSchedule();
        Handle<YieldTermStructure> rateCurve =
            swapIndex->forwardingTermStructure();

        const DayCounter& dc = swapIndex->dayCounter();

        Real swapStartTime = dc.yearFraction(rateCurve->referenceDate(),
                                             schedule.startDate());
        Real swapFirstPaymentTime = dc.yearFraction(rateCurve->referenceDate(),
                                                    schedule.date(1));

        Real paymentTime = dc.yearFraction(rateCurve->referenceDate(),
                                                 coupon.date());

        delta_ = (paymentTime-swapStartTime) / (swapFirstPaymentTime-swapStartTime);

        const Leg& fixedLeg(swap->fixedLeg());
        Size n = fixedLeg.size();
        accruals_.reserve(n);
        for (Size i=0; i<n; ++i) {
            boost::shared_ptr<Coupon> coupon =
                boost::dynamic_pointer_cast<Coupon>(fixedLeg[i]);
            accruals_.push_back(coupon->accrualPeriod());
        }
    }

    Real GFunctionFactory::GFunctionExactYield::operator()(Real x) {
        Real product = 1.;
        for(Size i=0; i<accruals_.size(); i++) {
            product *= 1./(1.+ accruals_[i]*x);
        }
        return x*std::pow(1.+ accruals_[0]*x,-delta_)*(1./(1.-product));
    }

    Real GFunctionFactory::GFunctionExactYield::firstDerivative(Real x) {
        Real c = -1.;
        Real derC = 0.;
        std::vector<Real> b;
        b.reserve(accruals_.size());
        for (Size i=0; i<accruals_.size(); i++) {
            Real temp = 1.0/(1.0+ accruals_[i]*x);
            b.push_back(temp);
            c *= temp;
            derC += accruals_[i]*temp;
        }
        c += 1.;
        c = 1./c;
        derC *= (c-c*c);

        return -delta_*accruals_[0]*std::pow(b[0],delta_+1.)*x*c+
                std::pow(b[0],delta_)*c+ std::pow(b[0],delta_)*x*derC;
        //Real dx = 1.0e-8;
        //return (operator()(x+dx)-operator()(x-dx))/(2.0*dx);
    }

    Real GFunctionFactory::GFunctionExactYield::secondDerivative(Real x) {
        Real c = -1.;
        Real sum = 0.;
        Real sumOfSquare = 0.;
        std::vector<Real> b;
        b.reserve(accruals_.size());
        for(Size i=0; i<accruals_.size(); i++) {
            Real temp = 1.0/(1.0+ accruals_[i]*x);
            b.push_back(temp);
            c *= temp;
            sum += accruals_[i]*temp;
            sumOfSquare += std::pow(accruals_[i]*temp, 2.0);
        }
        c += 1.;
        c = 1./c;
        Real derC =sum*(c-c*c);

        return (-delta_*accruals_[0]*std::pow(b[0],delta_+1.)*c+ std::pow(b[0],delta_)*derC)*
               (-delta_*accruals_[0]*b[0]*x + 1. + x*(1.-c)*sum)+
                std::pow(b[0],delta_)*c*(delta_*std::pow(accruals_[0]*b[0],2.)*x - delta_* accruals_[0]*b[0] -
                x*derC*sum + (1.-c)*sum - x*(1.-c)*sumOfSquare);
        //Real dx = 1.0e-8;
        //return (firstDerivative(x+dx)-firstDerivative(x-dx))/(2.0*dx);
    }

    boost::shared_ptr<GFunction> GFunctionFactory::newGFunctionExactYield(const CmsCoupon& coupon) {
        return boost::shared_ptr<GFunction>(new GFunctionExactYield(coupon));
    }

//===========================================================================//
//                            GFunctionWithShifts                            //
//===========================================================================//

    GFunctionFactory::GFunctionWithShifts::GFunctionWithShifts(
                    const CmsCoupon& coupon,
                    const Handle<Quote>& meanReversion)
    : meanReversion_(meanReversion), calibratedShift_(0.03),
      tmpRs_(10000000.0), accuracy_( 1.0e-14) {

        const boost::shared_ptr<SwapIndex>& swapIndex = coupon.swapIndex();
        const boost::shared_ptr<VanillaSwap>& swap = swapIndex->underlyingSwap(coupon.fixingDate());

        swapRateValue_ = swap->fairRate();

        objectiveFunction_ = boost::shared_ptr<ObjectiveFunction>(new ObjectiveFunction(*this, swapRateValue_));

        const Schedule& schedule = swap->fixedSchedule();
        Handle<YieldTermStructure> rateCurve =
            swapIndex->forwardingTermStructure();
        const DayCounter& dc = swapIndex->dayCounter();

        swapStartTime_ = dc.yearFraction(rateCurve->referenceDate(),
                                         schedule.startDate());
        discountAtStart_ = rateCurve->discount(schedule.startDate());

        Real paymentTime = dc.yearFraction(rateCurve->referenceDate(),
                                                 coupon.date());

        shapedPaymentTime_ = shapeOfShift(paymentTime);

        const Leg& fixedLeg(swap->fixedLeg());
        Size n = fixedLeg.size();
        accruals_.reserve(n);
        shapedSwapPaymentTimes_.reserve(n);
        swapPaymentDiscounts_.reserve(n);
        for(Size i=0; i<n; ++i) {
            boost::shared_ptr<Coupon> coupon =
                boost::dynamic_pointer_cast<Coupon>(fixedLeg[i]);
            accruals_.push_back(coupon->accrualPeriod());
            const Date paymentDate(coupon->date());
            const double swapPaymentTime(dc.yearFraction(rateCurve->referenceDate(), paymentDate));
            shapedSwapPaymentTimes_.push_back(shapeOfShift(swapPaymentTime));
            swapPaymentDiscounts_.push_back(rateCurve->discount(paymentDate));
        }
        discountRatio_ = swapPaymentDiscounts_.back()/discountAtStart_;
    }

    Real GFunctionFactory::GFunctionWithShifts::operator()(Real Rs) {
        const Real calibratedShift = calibrationOfShift(Rs);
        return Rs* functionZ(calibratedShift);
    }

    Real GFunctionFactory::GFunctionWithShifts::functionZ(Real x) {
        return std::exp(-shapedPaymentTime_*x)
            / (1.-discountRatio_*std::exp(-shapedSwapPaymentTimes_.back()*x));
    }

    Real GFunctionFactory::GFunctionWithShifts::derRs_derX(Real x) {
        Real sqrtDenominator = 0;
        Real derSqrtDenominator = 0;
        for(Size i=0; i<accruals_.size(); i++) {
            sqrtDenominator += accruals_[i]*swapPaymentDiscounts_[i]
                *std::exp(-shapedSwapPaymentTimes_[i]*x);
            derSqrtDenominator -= shapedSwapPaymentTimes_[i]* accruals_[i]*swapPaymentDiscounts_[i]
                *std::exp(-shapedSwapPaymentTimes_[i]*x);
        }
        const Real denominator = sqrtDenominator* sqrtDenominator;

        Real numerator = 0;
        numerator += shapedSwapPaymentTimes_.back()* swapPaymentDiscounts_.back()*
                     std::exp(-shapedSwapPaymentTimes_.back()*x)*sqrtDenominator;
        numerator -= (discountAtStart_ - swapPaymentDiscounts_.back()* std::exp(-shapedSwapPaymentTimes_.back()*x))*
                     derSqrtDenominator;
        QL_REQUIRE(denominator!=0, "GFunctionWithShifts::derRs_derX: denominator == 0");
        return numerator/denominator;
    }

    Real GFunctionFactory::GFunctionWithShifts::der2Rs_derX2(Real x) {
        Real denOfRfunztion = 0.;
        Real derDenOfRfunztion = 0.;
        Real der2DenOfRfunztion = 0.;
        for(Size i=0; i<accruals_.size(); i++) {
            denOfRfunztion += accruals_[i]*swapPaymentDiscounts_[i]
                *std::exp(-shapedSwapPaymentTimes_[i]*x);
            derDenOfRfunztion -= shapedSwapPaymentTimes_[i]* accruals_[i]*swapPaymentDiscounts_[i]
                *std::exp(-shapedSwapPaymentTimes_[i]*x);
            der2DenOfRfunztion+= shapedSwapPaymentTimes_[i]*shapedSwapPaymentTimes_[i]* accruals_[i]*
                swapPaymentDiscounts_[i]*std::exp(-shapedSwapPaymentTimes_[i]*x);
        }

        const Real denominator = std::pow(denOfRfunztion, 4);

        Real numOfDerR = 0;
        numOfDerR += shapedSwapPaymentTimes_.back()* swapPaymentDiscounts_.back()*
                     std::exp(-shapedSwapPaymentTimes_.back()*x)*denOfRfunztion;
        numOfDerR -= (discountAtStart_ - swapPaymentDiscounts_.back()* std::exp(-shapedSwapPaymentTimes_.back()*x))*
                     derDenOfRfunztion;

        const Real denOfDerR = std::pow(denOfRfunztion,2);

        Real derNumOfDerR = 0.;
        derNumOfDerR -= shapedSwapPaymentTimes_.back()*shapedSwapPaymentTimes_.back()* swapPaymentDiscounts_.back()*
                     std::exp(-shapedSwapPaymentTimes_.back()*x)*denOfRfunztion;
        derNumOfDerR += shapedSwapPaymentTimes_.back()* swapPaymentDiscounts_.back()*
                     std::exp(-shapedSwapPaymentTimes_.back()*x)*derDenOfRfunztion;

        derNumOfDerR -= (shapedSwapPaymentTimes_.back()*swapPaymentDiscounts_.back()*
                        std::exp(-shapedSwapPaymentTimes_.back()*x))* derDenOfRfunztion;
        derNumOfDerR -= (discountAtStart_ - swapPaymentDiscounts_.back()* std::exp(-shapedSwapPaymentTimes_.back()*x))*
                     der2DenOfRfunztion;

        const Real derDenOfDerR = 2*denOfRfunztion*derDenOfRfunztion;

        const Real numerator = derNumOfDerR*denOfDerR -numOfDerR*derDenOfDerR;
        QL_REQUIRE(denominator!=0, "GFunctionWithShifts::der2Rs_derX2: denominator == 0");
        return numerator/denominator;
    }

    Real GFunctionFactory::GFunctionWithShifts::derZ_derX(Real x) {
        const Real sqrtDenominator = (1.-discountRatio_*std::exp(-shapedSwapPaymentTimes_.back()*x));
        const Real denominator = sqrtDenominator* sqrtDenominator;
        QL_REQUIRE(denominator!=0, "GFunctionWithShifts::derZ_derX: denominator == 0");

        Real numerator = 0;
        numerator -= shapedPaymentTime_* std::exp(-shapedPaymentTime_*x)* sqrtDenominator;
        numerator -= shapedSwapPaymentTimes_.back()* std::exp(-shapedPaymentTime_*x)* (1.-sqrtDenominator);

        return numerator/denominator;
    }

    Real GFunctionFactory::GFunctionWithShifts::der2Z_derX2(Real x) {
        const Real denOfZfunction = (1.-discountRatio_*std::exp(-shapedSwapPaymentTimes_.back()*x));
        const Real derDenOfZfunction = shapedSwapPaymentTimes_.back()*discountRatio_*std::exp(-shapedSwapPaymentTimes_.back()*x);
        const Real denominator = std::pow(denOfZfunction, 4);
        QL_REQUIRE(denominator!=0, "GFunctionWithShifts::der2Z_derX2: denominator == 0");

        Real numOfDerZ = 0;
        numOfDerZ -= shapedPaymentTime_* std::exp(-shapedPaymentTime_*x)* denOfZfunction;
        numOfDerZ -= shapedSwapPaymentTimes_.back()* std::exp(-shapedPaymentTime_*x)* (1.-denOfZfunction);

        const Real denOfDerZ = std::pow(denOfZfunction,2);
        const Real derNumOfDerZ = (-shapedPaymentTime_* std::exp(-shapedPaymentTime_*x)*
                             (-shapedPaymentTime_+(shapedPaymentTime_*discountRatio_-
                               shapedSwapPaymentTimes_.back()*discountRatio_)* std::exp(-shapedSwapPaymentTimes_.back()*x))
                              -shapedSwapPaymentTimes_.back()*std::exp(-shapedPaymentTime_*x)*
                              (shapedPaymentTime_*discountRatio_- shapedSwapPaymentTimes_.back()*discountRatio_)*
                              std::exp(-shapedSwapPaymentTimes_.back()*x));

        const Real derDenOfDerZ = 2*denOfZfunction*derDenOfZfunction;
        const Real numerator = derNumOfDerZ*denOfDerZ -numOfDerZ*derDenOfDerZ;

        return numerator/denominator;
    }

    Real GFunctionFactory::GFunctionWithShifts::firstDerivative(Real Rs) {
        //Real dRs = 1.0e-8;
        //return (operator()(Rs+dRs)-operator()(Rs-dRs))/(2.0*dRs);
        const Real calibratedShift = calibrationOfShift(Rs);
        return functionZ(calibratedShift) + Rs * derZ_derX(calibratedShift)/derRs_derX(calibratedShift);
    }

    Real GFunctionFactory::GFunctionWithShifts::secondDerivative(Real Rs) {
        //Real dRs = 1.0e-8;
        //return (firstDerivative(Rs+dRs)-firstDerivative(Rs-dRs))/(2.0*dRs);
        const Real calibratedShift = calibrationOfShift(Rs);
        return 2.*derZ_derX(calibratedShift)/derRs_derX(calibratedShift) +
            Rs * der2Z_derX2(calibratedShift)/std::pow(derRs_derX(calibratedShift),2.)-
            Rs * derZ_derX(calibratedShift)*der2Rs_derX2(calibratedShift)/
            std::pow(derRs_derX(calibratedShift),3.);
    }

    Real GFunctionFactory::GFunctionWithShifts::ObjectiveFunction::operator ()(const Real& x) const {
        Real result = 0;
        derivative_ = 0;
        for(Size i=0; i<o_.accruals_.size(); i++) {
            Real temp = o_.accruals_[i]*o_.swapPaymentDiscounts_[i]
                *std::exp(-o_.shapedSwapPaymentTimes_[i]*x);
            result += temp;
            derivative_ -= o_.shapedSwapPaymentTimes_[i] * temp;
        }
        result *= Rs_;
        derivative_ *= Rs_;
        Real temp = o_.swapPaymentDiscounts_.back()
            * std::exp(-o_.shapedSwapPaymentTimes_.back()*x);

        result += temp-o_.discountAtStart_;
        derivative_ -= o_.shapedSwapPaymentTimes_.back()*temp;
        return result;
    }

    Real GFunctionFactory::GFunctionWithShifts::ObjectiveFunction::derivative(const Real&) const {
        return derivative_;
    }

    void GFunctionFactory::GFunctionWithShifts::ObjectiveFunction::setSwapRateValue(Real x) {
        Rs_ = x;
    }

    Real GFunctionFactory::GFunctionWithShifts::shapeOfShift(Real s) const {
        const Real x(s-swapStartTime_);
        Real meanReversion = meanReversion_->value();
        if(meanReversion>0) {
            return (1.-std::exp(-meanReversion*x))/meanReversion;
        }
        else {
            return x;
        }
    }

    Real GFunctionFactory::GFunctionWithShifts::calibrationOfShift(Real Rs){

        if(Rs!=tmpRs_){
            Real initialGuess, N=0, D=0;
            for(Size i=0; i<accruals_.size(); i++) {
                N+=accruals_[i]*swapPaymentDiscounts_[i];
                D+=accruals_[i]*swapPaymentDiscounts_[i]*shapedSwapPaymentTimes_[i];
            }
            N *= Rs;
            D *= Rs;
            N += accruals_.back() * swapPaymentDiscounts_.back()
                - objectiveFunction_->gFunctionWithShifts().discountAtStart_;
            D += accruals_.back() * swapPaymentDiscounts_.back()*
                            shapedSwapPaymentTimes_.back();
            initialGuess = N/D;

            objectiveFunction_->setSwapRateValue(Rs);
            Newton solver;
            solver.setMaxEvaluations(1000);

            // these boundaries migth not be big enough if the volatility
            // of big swap rate values is too high . In this case the G function
            // is not even integrable, so better to fix the vol than increasing
            // these values
            const Real lower = -20, upper = 20.;

            try {
                calibratedShift_ = solver.solve(*objectiveFunction_, accuracy_,
                    std::max( std::min(initialGuess, upper*.99), lower*.99),
                    lower, upper);
            } catch (std::exception& e) {
                QL_FAIL("meanReversion: " << meanReversion_->value() <<
                        ", swapRateValue: " << swapRateValue_ <<
                        ", swapStartTime: " << swapStartTime_ <<
                        ", shapedPaymentTime: " << shapedPaymentTime_ <<
                        "\n error message: " << e.what());
            }
            tmpRs_=Rs;
        }
        return calibratedShift_;
    }

    boost::shared_ptr<GFunction> GFunctionFactory::newGFunctionWithShifts(const CmsCoupon& coupon,
                                                                          const Handle<Quote>& meanReversion) {
        return boost::shared_ptr<GFunction>(new GFunctionWithShifts(coupon, meanReversion));
    }

}


//
// Copyright © 2017 Arm Ltd. All rights reserved.
// SPDX-License-Identifier: MIT
//

#include "Gather.hpp"

#include "RefWorkloadUtils.hpp"

#include <backendsCommon/WorkloadData.hpp>

#include <boost/numeric/conversion/cast.hpp>

namespace armnn
{

void Gather(const TensorInfo& paramsInfo,
            const TensorInfo& indicesInfo,
            const TensorInfo& outputInfo,
            Decoder<float>& params,
            const int32_t* indices,
            Encoder<float>& output)
{
    const TensorShape& paramsShape = paramsInfo.GetShape();

    unsigned int paramsProduct = 1;
    for (unsigned int i = 1; i < paramsInfo.GetNumDimensions(); ++i)
    {
        paramsProduct = paramsProduct * paramsShape[i];
    }

    unsigned int outIndex = 0;
    for (unsigned int i = 0; i < indicesInfo.GetNumElements(); ++i)
    {
        unsigned int indx = boost::numeric_cast<unsigned int>(indices[i]);

        BOOST_ASSERT(indices[i] >= 0 && indx < paramsShape[0]);

        unsigned int startOffset = indx * paramsProduct;
        unsigned int endOffset = startOffset + paramsProduct;

        for (unsigned int j = startOffset; j < endOffset; ++j)
        {
            params[j];
            float outputValue = params.Get();
            output[outIndex];
            output.Set(outputValue);
            ++outIndex;
        }
    }

    BOOST_ASSERT(outIndex == outputInfo.GetNumElements());
}

} //namespace armnn


/**
 * @brief Provide simple macros for checking the size of a matrix and giving informative errors
 * @note Currently uses common_assert_msg(). Could refactor to use a normal assert
 * @note Should be included after <runtime_config.hpp> !!!
 * @author Eric Cousineau <eacousineau@gmail.com>, member of Dr. Aaron
 * Ames's AMBER Lab
 */

#ifndef EIGEN_UTILITIES_ASSERT_SIZE_H_
    #define EIGEN_UTILITIES_ASSERT_SIZE_H_

#include <Eigen/Dense>
#include <eigen_utilities/assert_error.hpp>

namespace eigen_utilities
{

#define assert_size_matrix(X, rows_expected, cols_expected) \
    { \
        common_assert_msg_ex( \
            X.rows() == rows_expected && X.cols() == cols_expected, \
            "matrix [row, col] mismatch" << std::endl << \
                "actual: [" << X.rows() << ", " << X.cols() << "]" << std::endl << \
                "expected: [" << rows_expected << ", " << cols_expected << "]", \
            eigen_utilities::assert_error); \
    }

#define assert_size_vector(X, size_expected) \
    { \
        common_assert_msg_ex( \
            X.size() == size_expected, \
            "matrix size mismatch" << std::endl << \
                "actual: " << X.size() << std::endl << \
                "expected: " << size_expected, \
            eigen_utilities::assert_error); \
    }

    template<typename Derived>
void assert_size(const Eigen::MatrixBase<Derived> &X, int rows_expected, int cols_expected)
{
    common_assert_msg_ex(
            X.rows() == rows_expected && X.cols() == cols_expected,
            "matrix [row, col] mismatch" << std::endl <<
                "actual: [" << X.rows() << ", " << X.cols() << "]" << std::endl <<
                "expected: [" << rows_expected << ", " << cols_expected << "]",
            eigen_utilities::assert_error);
}

    template<typename Derived>
void assert_size(const Eigen::MatrixBase<Derived> &X, int size_expected)
{
    common_assert_msg_ex(
        X.size() == size_expected,
        "matrix size mismatch" << std::endl <<
            "actual: " << X.size() << std::endl <<
            "expected: " << size_expected,
        eigen_utilities::assert_error);
}

} // namespace eigen_utilities

#endif // EIGEN_UTILITIES_ASSERT_SIZE_H_


#define BOOST_TEST_MODULE coordinate
#include <boost/test/unit_test.hpp>

#include <yatc/coordinate.h>
using namespace yatc;

BOOST_AUTO_TEST_CASE(default_constructor)
{
	coordinate zero;
	BOOST_TEST(zero.x() == 0);
	BOOST_TEST(zero.y() == 0);
}

BOOST_AUTO_TEST_CASE(default_constructor_const)
{
	const coordinate zero;
	BOOST_TEST(zero.x() == 0);
	BOOST_TEST(zero.y() == 0);
}

BOOST_AUTO_TEST_CASE(constructor)
{
	coordinate pt{ 1, 3 };
	BOOST_TEST(pt.x() == 1);
	BOOST_TEST(pt.y() == 3);
}

BOOST_AUTO_TEST_CASE(const_accessors)
{
	const coordinate pt{ 4, 2 };
	BOOST_TEST(pt.x() == 4);
	BOOST_TEST(pt.y() == 2);
}

BOOST_AUTO_TEST_CASE(x)
{
	coordinate pt{ 1, 3 };
	BOOST_TEST(pt.x() == 1);
	pt.x() = -10;
	BOOST_TEST(pt.x() == -10);
}

BOOST_AUTO_TEST_CASE(y)
{
	coordinate pt{ 1, 3 };
	BOOST_TEST(pt.y() == 3);
	pt.y() = -13;
	BOOST_TEST(pt.y() == -13);
}

BOOST_AUTO_TEST_CASE(plus_equal)
{
	coordinate pt{ -3, 2 };
	pt += coordinate(1, 3);
	BOOST_TEST(pt == coordinate(-2, 5));
}

BOOST_AUTO_TEST_CASE(minus_equal)
{
	coordinate pt{ -3, 2 };
	pt -= coordinate(3, 1);
	BOOST_TEST(pt == coordinate(-6, 1));
}

BOOST_AUTO_TEST_CASE(rotate_zero)
{
	coordinate pt{ 0, 0 };
	pt.rotate(rotation::ccw);
	BOOST_TEST(pt == coordinate(0, 0));
}

BOOST_AUTO_TEST_CASE(ccw_rotate)
{
	coordinate pt{ 5, 0 };
	pt.rotate(rotation::ccw);
	BOOST_TEST(pt == coordinate(0, 5));
}

BOOST_AUTO_TEST_CASE(cw_rotate)
{
	coordinate pt{ 0, -5 };
	pt.rotate(rotation::cw);
	BOOST_TEST(pt == coordinate(-5, 0));
}

BOOST_AUTO_TEST_CASE(unary_minus)
{
	coordinate pt{ -3, 2 };
	BOOST_TEST(-pt == coordinate(3, -2));
}

BOOST_AUTO_TEST_CASE(plus)
{
	coordinate pt{ -3, 2 };
	coordinate v(1, 3);
	BOOST_TEST((pt + v) == coordinate(-2, 5));
}

BOOST_AUTO_TEST_CASE(minus)
{
	coordinate pt{ -3, 2 };
	coordinate v(3, 1);
	BOOST_TEST((pt - v) == coordinate(-6, 1));
}

BOOST_AUTO_TEST_CASE(equal)
{
	coordinate pt1{ 1, 3 };
	coordinate pt2{ 1, 3 };
	BOOST_TEST(pt1 == pt2);
}

BOOST_AUTO_TEST_CASE(not_equal)
{
	coordinate pt1{ 1, 3 };
	coordinate pt2{ 3, 1 };
	BOOST_TEST(pt1 != pt2);
}

BOOST_AUTO_TEST_CASE(less)
{
	BOOST_TEST(coordinate(2, 2) < coordinate(3, 2));
	BOOST_TEST(coordinate(1, 2) < coordinate(1, 4));
}

BOOST_AUTO_TEST_CASE(less_equal)
{
	BOOST_TEST(coordinate(2, 2) <= coordinate(2, 2));
	BOOST_TEST(coordinate(2, 2) <= coordinate(3, 2));
	BOOST_TEST(coordinate(1, 2) <= coordinate(1, 4));
}

BOOST_AUTO_TEST_CASE(greater)
{
	BOOST_TEST(coordinate(3, 2) > coordinate(2, 2));
	BOOST_TEST(coordinate(1, 4) > coordinate(1, 2));
}

BOOST_AUTO_TEST_CASE(greater_equal)
{
	BOOST_TEST(coordinate(2, 2) >= coordinate(2, 2));
	BOOST_TEST(coordinate(3, 2) >= coordinate(2, 2));
	BOOST_TEST(coordinate(1, 4) >= coordinate(1, 2));
}

BOOST_AUTO_TEST_CASE(ccw_rotate_copy)
{
	coordinate pt{ 3, 1 };
	BOOST_TEST(rotate(pt, rotation::ccw) == coordinate(-1, 3));
}

BOOST_AUTO_TEST_CASE(cw_rotate_copy)
{
	coordinate pt{ 3, -4 };
	BOOST_TEST(rotate(pt, rotation::cw) == coordinate(-4, -3));
}


/*! file qubo_test.cpp
 *
 * @copyright Licensed under MIT license by hyperQ – Ewa Hendzel
 *
 */

#define BOOST_TEST_MODULE qubo_test_module

#include "model/qubo.hpp"
#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_SUITE(qubo_test)

BOOST_AUTO_TEST_CASE(qubo_linear_variable) {

  qubo::LinearCoef<int, double> linear_c{
      {1, -0.4}, {2, 1.1}, {3, -1.0}, {4, -1.2}};

  qubo::QuadraticCoef<int, double> quadratic_c{
      {std::make_pair(1, 2), 1.0}, {std::make_pair(1, 3), 7.2},
      {std::make_pair(1, 4), 1.0}, {std::make_pair(2, 3), 2.0},
      {std::make_pair(2, 4), 1.9}, {std::make_pair(3, 4), 3.0}};

  qubo::QUBOModel<int, double> qubos(linear_c, quadratic_c);
  qubos.add_variable(1, 20);

  BOOST_TEST_REQUIRE(qubos.get_variable(1) == 20);
}

BOOST_AUTO_TEST_CASE(qubo_quadratic_variable) {

  qubo::LinearCoef<int, double> linear_c{};

  qubo::QuadraticCoef<int, double> quadratic_c{};

  qubo::QUBOModel<int, double> qubos(linear_c, quadratic_c);
  qubos.add_connection(std::make_pair(1, 2), 20);

  BOOST_TEST_REQUIRE(qubos.get_connection(std::make_pair(1, 2)) == 20);
}

BOOST_AUTO_TEST_CASE(qubo_print) {

  qubo::LinearCoef<int, int> linear_c{};

  qubo::QuadraticCoef<int, int> quadratic_c{};

  qubo::QUBOModel<int, int> qubos(linear_c, quadratic_c);
  qubos.add_variable(1, 10);
  qubos.add_connection(std::make_pair(1, 2), 20);

  auto s = qubos.str();

  BOOST_TEST_REQUIRE(s.compare("QUBO model 1--1:10 1--2:20") == 0);
}

BOOST_AUTO_TEST_SUITE_END()


#pragma once

#include "../Math/math.hh"

#include <vector>
#include <string>
#include <Eigen/Core>
#include <opencv2/core/core.hpp>

namespace bold
{
  namespace Colour
  {
    struct bgr
    {
      bgr() = default;

      bgr(uint8_t b, uint8_t g, uint8_t r)
        : b(b), g(g), r(r)
      {}

      bgr invert() const
      {
        return bgr(255-b, 255-g, 255-r);
      }

      cv::Scalar toScalar() const
      {
        return cv::Scalar(b, g, r);
      }

      Eigen::Vector3d toRgbUnitVector() const
      {
        return Eigen::Vector3d(r/255.0, g/255.0, b/255.0);
      }

      bool operator==(bgr const& other) const
      {
        return
          b == other.b &&
          g == other.g &&
          r == other.r;
      }

      uint8_t b;
      uint8_t g;
      uint8_t r;

      static const bgr black;
      static const bgr grey;
      static const bgr white;
      static const bgr red;
      static const bgr lightRed;
      static const bgr darkRed;
      static const bgr green;
      static const bgr lightGreen;
      static const bgr darkGreen;
      static const bgr blue;
      static const bgr lightBlue;
      static const bgr darkBlue;
      static const bgr orange;
      static const bgr purple;
      static const bgr yellow;
      static const bgr cyan;
      static const bgr magenta;
    };

    struct YCbCr
    {
      // http://www.equasys.de/colorconversion.html

      YCbCr() = default;
      YCbCr(uint8_t y, uint8_t cb, uint8_t cr);

      bool isValid() const;

      Colour::bgr toBgrInt() const;
      Colour::bgr toBgrFloat() const;

      bool operator==(YCbCr const& other) const
      {
        return
          y  == other.y  &&
          cb == other.cb &&
          cr == other.cr;
      }

      uint8_t y;
      uint8_t cb;
      uint8_t cr;
    };

    struct hsv;

    hsv bgr2hsv(bgr const& in);
    bgr hsv2bgr(hsv const& in);

    struct hsv
    {
      hsv()
      {}

      hsv(uint8_t h, uint8_t s, uint8_t v)
        : h(h), s(s), v(v)
      {}

      bool operator==(hsv const& other) const
      {
        return
          h == other.h &&
          s == other.s &&
          v == other.v;
      }

      bgr toBgr() const
      {
        return hsv2bgr(*this);
      }

      uint8_t operator[](unsigned idx)
      {
        switch (idx)
        {
        case 0:
          return h;
          break;
        case 1:
          return s;
          break;
        case 2:
          return v;
          break;
        default:
          return 0;
        }
      }

      uint8_t h;
      uint8_t s;
      uint8_t v;
    };

    struct hsvRange
    {
      uint8_t hMin;
      uint8_t hMax;
      uint8_t sMin;
      uint8_t sMax;
      uint8_t vMin;
      uint8_t vMax;

      hsvRange();

      hsvRange(
        uint8_t hMin, uint8_t hMax,
        uint8_t sMin, uint8_t sMax,
        uint8_t vMin, uint8_t vMax);

      uint8_t getHMid() const;
      uint8_t getSMid() const;
      uint8_t getVMid() const;

      Colour::hsv toHsv() const;

      /** Obtain the center colour for the range, in BGR format. */
      Colour::bgr toBgr() const;

      bool contains(hsv const& hsv) const;

      bool isValid() const;

      bool operator==(hsvRange const& other) const
      {
        return
          hMin == other.hMin &&
          hMax == other.hMax &&
          sMin == other.sMin &&
          sMax == other.sMax &&
          vMin == other.vMin &&
          vMax == other.vMax;
      }

      static hsvRange fromBytes(uint8_t hMin, uint8_t hMax, uint8_t sMin, uint8_t sMax, uint8_t vMin, uint8_t vMax);
      static hsvRange fromDoubles(double hMin, double hMax, double sMin, double sMax, double vMin, double vMax);

      hsvRange withHMin(uint8_t value) const { return hsvRange(value, hMax, sMin, sMax, vMin, vMax); }
      hsvRange withHMax(uint8_t value) const { return hsvRange(hMin, value, sMin, sMax, vMin, vMax); }
      hsvRange withSMin(uint8_t value) const { return hsvRange(hMin, hMax, value, sMax, vMin, vMax); }
      hsvRange withSMax(uint8_t value) const { return hsvRange(hMin, hMax, sMin, value, vMin, vMax); }
      hsvRange withVMin(uint8_t value) const { return hsvRange(hMin, hMax, sMin, sMax, value, vMax); }
      hsvRange withVMax(uint8_t value) const { return hsvRange(hMin, hMax, sMin, sMax, vMin, value); }

      hsvRange containing(hsv const& hsv);

    };

    inline void yCbCrToBgrInPlace(uint8_t* pxl)
    {
      Colour::YCbCr* ycbcr = reinterpret_cast<Colour::YCbCr*>(pxl);
      Colour::bgr* bgr = reinterpret_cast<Colour::bgr*>(pxl);
      *bgr = (*ycbcr).toBgrInt();
    }
  }

  std::ostream& operator<<(std::ostream &stream, Colour::bgr const& bgr);
  std::ostream& operator<<(std::ostream &stream, Colour::hsv const& hsv);
  std::ostream& operator<<(std::ostream &stream, Colour::hsvRange const& hsv);
}


/*
@copyright Louis Dionne 2014
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
 */

#include <boost/hana/ext/boost/fusion.hpp>

#include <boost/hana/detail/constexpr.hpp>

#include "../helper.hpp"
#include <cassert>
using namespace boost::hana;

BOOST_HANA_CONSTEXPR_LAMBDA auto is_even = [](auto x) {
    return x % 2 == 0;
};

int main() {
    with_nonassociative_forward_sequences([=](auto container) {
        assert(count(container(), is_even) == 0);
        assert(count(container(1), is_even) == 0);
        assert(count(container(2), is_even) == 1);
        assert(count(container(1, 2), is_even) == 1);
        assert(count(container(1, 2, 3), is_even) == 1);
        assert(count(container(1, 2, 3, 4), is_even) == 2);
    });
}


// © 2016 PORT INC.

#include <sstream>
#include <boost/numeric/ublas/io.hpp>
#include <boost/format.hpp>
#include "Optimizer.hpp"
#include "Error.hpp"
#include "Logger.hpp"
#include "Signal.hpp"

namespace Optimizer {

	using namespace boost::numeric::ublas;

	////////

	UnconstrainedNLP_::UnconstrainedNLP_(int d, ObjectiveFunction f)
		: flg(0x0)
		, dim(d)
		, itr(0)
		, maxIteration(256)
		, ofunc(f)
		, e0(1.0e-2)
		, alpha(1.0)
		, beta(1.0)
		, minBeta(1.0e-16)
		, xi(0.5)
		, tau(0.125)
		, r0(0.0)
		, re(1.0e-6)
		, ae(1.0e-6)
		, x(dim)
		, dx(dim)
		, g0(dim)
		, g1(dim)
		, d(dim)
	{
	}

	void UnconstrainedNLP_::iteration_limit_error(std::string msg, double beta)
	{
		std::stringstream ss;
		if( !msg.empty() ) ss << msg << ": ";
		ss << "avoidDivergence: iteration limit " << maxIteration << " exceeded: alpha=" << beta;
		throw Error(ss.str());
	}

	void UnconstrainedNLP_::minimum_limit_error(std::string msg, double beta)
	{
		std::stringstream ss;
		if( !msg.empty() ) ss << msg << ": ";
		ss << "avoidDivergence: minimum value limet " << minBeta << " excceded: alpha=" << beta;
		throw Error(ss.str());
	}

	double UnconstrainedNLP_::avoidDivergence(vector& d, double& f1)
	{
		int counter = 0;
		double beta1 = beta;
		vector x1 = x + beta1*d;
		std::string msg;

		// 目的関数で数値的な問題が発生しないかチェックする
		while(1) {
			try {
				f1 = ofunc->value(x1);
				break;
			} catch(Error& e) {
				// 問題があれば beta1 を小さくして再試行
				beta1 *= tau;
				x1 = x + beta1*d;
				msg = e.what();
			}
			if( counter++ == maxIteration ) {
				iteration_limit_error(msg, beta1);
			} else if( beta1 < minBeta ) {
				minimum_limit_error(msg, beta1);
			}
		}

		// 目的関数の勾配で数値的な問題が発生しないかチェックする
		while(1) {
			try {
				ofunc->grad(x1);
				break;
			} catch(Error& e) {
				// 問題があれば beta1 を小さくして再試行
				beta1 *= tau;
				x1 = x + beta1*d;
				msg = e.what();
			}
			if( counter++ == maxIteration ) {
				iteration_limit_error(msg, beta1);
			} else if( beta1 < minBeta ) {
				minimum_limit_error(msg, beta1);
			}
		}

		return beta1;
	}

	double UnconstrainedNLP_::linearSearch(vector& d)
	{
		int counter = 0;
		vector x1;
		double gd = inner_prod(g0, d);
		double f0 = ofunc->value(x);
		double f1 = 0.0;
		Logger::debug("avoidDivergence");
		double beta1 = avoidDivergence(d, f1);

		while( xi*beta1*gd < f1 - f0 ) { // Armijo's rule
			beta1 *= tau;
			x1 = x + beta1*d;
			f1 = ofunc->value(x1);
			if( counter++ == maxIteration ) {
				iteration_limit_error("", beta1);
			} else if( beta1 < minBeta ) {
				minimum_limit_error("", beta1);
			}
			if( Signal::getFlg() ) {
				std::string msg("liner search interrrputed");
				Logger::info(msg.c_str());
				break;
			}
		}

		beta = std::min(beta1/tau, 1.0);
		return beta1;
	}

	bool UnconstrainedNLP_::isConv()
	{
		bool flg = false;
		double r = sqrt(inner_prod(g0, g0));

		if( itr == 0 ) {

			r0 = r;

		} else {

			double err = r/(r0*re + ae);
			flg = ( err < 1.0 );
			double f = ofunc->value(x);
			Logger::info() << boost::format("f= %10.6e |∇f|= %10.6e alp=%10.6e") % f % err % alpha;
		}

		if( !flg && itr == maxIteration ) {
			std::string msg("iteration limit");
			Logger::info(msg.c_str());
			throw Error(msg);
		}

		if( Signal::getFlg() ) {
			Logger::info() << "interrupt signal received";
			flg = true;
		}

		return flg;
	}

	////////

	SteepestDescent::SteepestDescent(int d, ObjectiveFunction f)
		: UnconstrainedNLP_(d, f)
		, adagrad(dim)
	{
	}

	UnconstrainedNLP createSteepestDescent(int dim, ObjectiveFunction ofunc)
	{
		return std::shared_ptr<SteepestDescent>( new SteepestDescent(dim, ofunc) );
	}

	void SteepestDescent::optimize()
	{
		Logger::debug("preProcess");
		ofunc->preProcess(x);

		Logger::debug("grad");
		g0 = ofunc->grad(x);                 Logger::trace() << "g0=" << g0;
		alpha = 1.0;

		while(1) {

			Logger::debug("beginLoopProcess");
			ofunc->beginLoopProcess(x);

			d = - g0;                        Logger::trace() << "d=" << d;

			if( flg & ENABLE_ADAGRAD ) {
				auto iad = adagrad.begin();
				auto id = d.begin();
				for( auto& idx : dx ) {
					idx = e0/(1.0+sqrt(*iad++))*(*id++);
				}
				double f1 = 0.0;
				Logger::debug("avoidDivergence");
				alpha = avoidDivergence(d, f1);
			} else {
				Logger::debug("linearSearch");
				alpha = linearSearch(d);
			}

			dx = alpha * d;                  Logger::trace() << "dx=" << dx;
			Logger::debug("update x");
			x = x + dx;                      Logger::trace() << "x=" << x;
			Logger::debug("afterUpdateXProcess");
			ofunc->afterUpdateXProcess(x);
			if( isConv() ) break;
			Logger::debug("grad");
			g0 = ofunc->grad(x);             Logger::trace() << "g0=" << g0;

			Logger::debug("endLoopProcess");
			ofunc->endLoopProcess(x);

			++itr;
		}

		Logger::debug("postProcess");
		ofunc->postProcess(x);
	}

	bool SteepestDescent::isConv()
	{
		bool flg = false;
		vector g02 = element_prod(g0, g0);
		double r = sqrt( sum( g02 ) );
		adagrad += g02;

		if( itr == 0 ) {

			r0 = r;

		} else {

			double err = r/(r0*re + ae);
			flg = ( err < 1.0 );
			double f = ofunc->savedValue();
			Logger::info() << boost::format("f= %10.6e |∇f|= %10.6e alp=%10.6e") % f % err % alpha;
		}

		if( !flg && itr == maxIteration ) {
			std::string msg("iteration limit");
			Logger::info(msg.c_str());
			throw Error(msg);
		}

		if( Signal::getFlg() ) {
			Logger::info() << "interrupt signal received";
			flg = true;
		}

		return flg;
	}

	////////

	QuasiNewton_::QuasiNewton_(int d, ObjectiveFunction f)
		: UnconstrainedNLP_(d, f)
		, y(dim)
		, I(dim)
		, H0(dim, dim)
		, H1(dim, dim)
		, A(dim, dim)
		, B(dim, dim)
	{
	}

	void QuasiNewton_::optimize()
	{
		Logger::debug("preProcess");
		ofunc->preProcess(x);

		H0 = I;
		Logger::debug("grad");
		g0 = ofunc->grad(x);                 Logger::trace() << "g0=" << g0;
		alpha = 1.0;

		while(1) {

			Logger::debug("beginLoopProcess");
			ofunc->beginLoopProcess(x);

			Logger::debug("prod");
			d = - prod(H0, g0);              Logger::trace() << "d=" << d;
			Logger::debug("linearSearch");
			alpha = linearSearch(d);
			dx = alpha * d;                  Logger::trace() << "dx=" << dx;
			Logger::debug("update x");
			x = x + dx;                      Logger::trace() << "x=" << x;
			Logger::debug("afterUpdateXProcess");
			ofunc->afterUpdateXProcess(x);
			if( isConv() ) break;
			Logger::debug("grad");
			g1 = ofunc->grad(x);             Logger::trace() << "g1=" << g1;
			y = g1 - g0;                     Logger::trace() << "y=" << y;
			g0 = g1;
			Logger::debug("update matrix");
			updateMatrix();

			Logger::debug("endLoopProcess");
			ofunc->endLoopProcess(x);

			++itr;
		}

		Logger::debug("postProcess");
		ofunc->postProcess(x);
	}

	bool QuasiNewton_::isConv()
	{
		bool flg = false;
		double r = sqrt(inner_prod(g0, g0));

		if( itr == 0 ) {

			r0 = r;
			f0 = ofunc->savedValue();

		} else {

			double err0 = r/(r0*re + ae);
			double f = ofunc->savedValue();
			double err1 = fabs( (f - f0)/(re*f0+ae) );
			flg = ( err1 < 1.0 );
			Logger::info() << boost::format("f= %10.6e |∇f|= %10.6e |Δf/f|= %10.6e alp= %10.6e") % f % err0 % err1 % alpha;
			f0 = f;
		}

		if( !flg && itr == maxIteration ) {
			std::string msg("iteration limit");
			Logger::info(msg.c_str());
			throw Error(msg);
		}

		if( Signal::getFlg() ) {
			Logger::info() << "interrupt signal received";
			flg = true;
		}

		return flg;
	}

	////////

	UnconstrainedNLP createBfgs(int dim, ObjectiveFunction ofunc)
	{
		return std::shared_ptr<UnconstrainedNLP_>( new Bfgs(dim, ofunc) );
	}

	Bfgs::Bfgs(int dim, ObjectiveFunction ofunc)
		: QuasiNewton_(dim, ofunc)
	{
	}

	void Bfgs::updateMatrix()
	{
		double p = inner_prod(y, dx);                     Logger::trace() << "p=" << p;
		A = outer_prod(dx, dx)/p; 	                      Logger::trace() << "A=" << A;
		B = I - outer_prod(y, dx)/p;                      Logger::trace() << "B=" << B;
		H1 = prod(trans(B), matrix(prod(H0, B))) + A;     Logger::trace() << "H=" << H1;
		H0 = H1;
	}

	////////

	class test1 : public ObjectiveFunction_ {
	public:
		test1(){}
		virtual ~test1(){}
		virtual double savedValue(){
			return f;
		}
		virtual double value(vector& x){
			double a = (x[0]-x[1]*x[1]);
			double b = (x[1]-2);
			f = ( 0.5*a*a + 0.25*b*b*b*b );
			return f;
		}
		virtual vector grad(vector& x){
			vector g(2);
			g(0) = x[0]-x[1]*x[1];
			double a = (x[1]-2);
			g(1) = -2.0*x[1]*(x[0]-x[1]*x[1])+a*a*a;
			return std::move(g);
		}
		virtual void preProcess(vector& x){
			x[0] = 0;
			x[1] = 0;
			Logger::info() << " " << x[0] << " " << x[1];
		}
		virtual void beginLoopProcess(vector& x){}
		virtual void afterUpdateXProcess(vector& x){}
		virtual void endLoopProcess(vector& x){}
		virtual void postProcess(vector& x){}
	private:
		double x0;
		double x1;
		double f;
	};

	class test2 : public ObjectiveFunction_ {
	public:
		test2(){}
		virtual ~test2(){}
		virtual double savedValue(){
			return f;
		}
		virtual double value(vector& x){
			double a = (x[1]-x[0]*x[0]);
			double b = (1-x[0]);
			f = ( 100*a*a + b*b );
			return f;
		}
		virtual vector grad(vector& x){
			vector g(2);
			g(0) = -400*(x[1]-x[0]*x[0])*x[0] - 2*(1-x[0]);
			g(1) = 200*(x[1]-x[0]*x[0]);
			return std::move(g);
		}
		virtual void preProcess(vector& x){
			x[0] = -1.9;
			x[1] = 2;
			Logger::info() << " " << x[0] << " " << x[1];
		}
		virtual void beginLoopProcess(vector& x){}
		virtual void afterUpdateXProcess(vector& x){
			Logger::info() << " " << x[0] << " " << x[1];
		}
		virtual void endLoopProcess(vector& x){}
		virtual void postProcess(vector& x){
			Logger::info() << " " << x[0] << " " << x[1];
		}
	private:
		double x0;
		double x1;
		double f;
	};

	void test() {
		UnconstrainedNLP qn = createBfgs(2, std::shared_ptr<ObjectiveFunction_>(new test2()) );
		qn->setRe(1.0e-4);
		qn->setAe(1.0e-4);
		qn->setMaxIteration(10000);
		qn->optimize();
	}
}


// MIT License
//
// Copyright (c) 2021 Oliver Schick
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#ifndef MPO19_POINT_12062020
#define MPO19_POINT_12062020

#include <cassert>
#include <vector>
#include <type_traits>
#include <iostream>
#include <memory>
#include <scoped_allocator>

#include <boost/range/algorithm.hpp>

template<typename, typename> struct point;

template<bool Cond, typename T, std::enable_if_t<Cond, int> = 0>
T&& move_if(T& t)
{
    return static_cast<T&&>(t);
}

template<bool Cond, typename T, std::enable_if_t<!Cond, int> = 0>
T& move_if(T& t)
{
    return t;
}

constexpr struct apply_on_elements_t {
    template<typename R, typename RAlloc
             , typename NAryOp
             , typename T, typename Alloc
             ,  typename... Ts, typename... Allocs>
    void operator()(point<R, RAlloc>& res
                    , NAryOp const& op
                    , point<T, Alloc> const& p
                    , point<Ts, Allocs> const&... ps) const
    {
        size_t d = p.dimension();
        assert(res.dimension() == d);
        for(size_t i = 0; i != d; ++i) {
            res[i] = op(p[i], ps[i]...);
        }
    }

    template<typename NAryOp, typename T, typename Alloc, typename... Ts, typename... Allocs>
    auto operator()(NAryOp const& op
                    , point<T, Alloc> const& p
                    , point<Ts, Allocs> const&... ps) const
    {
        using result_element_t = decltype(op(p[0], ps[0]...));
        using result_allocator_t = typename std::allocator_traits<Alloc>::template rebind_alloc<result_element_t>;
        using result_t = point<result_element_t, result_allocator_t>;
        size_t d = p.dimension();
        result_t res;
        res.reserve(d);
        for(size_t i = 0; i != d; ++i) {
            res.push_back(op(p[i], ps[i]...));
        }
        assert(res.dimension() == d);
        return res;
    }

} apply_on_elements;

template<typename T, typename Alloc = std::allocator<T>>
struct point : private std::vector<T, Alloc> {
private:
    using Base_ = std::vector<T, Alloc>;
public:
    using Base_::Base_;
    using Base_::data;
    using element_type = typename Base_::value_type;

    using Base_::operator[];

    point(point const&) = default;
    point(point&&) = default;
    ~point() = default;
    point& operator=(point const&) = default;
    point& operator=(point&&) = default;

    point& operator+=(point const& rhs)
    {
        apply_on_elements(*this, std::plus<> {}, *this, rhs);
        return *this;
    }

    point& operator-=(point const& rhs)
    {
        apply_on_elements(*this, std::minus<> {}, *this, rhs);
        return *this;
    }

    point& operator*=(element_type rhs)
    {
        auto scalar_multiply = [&rhs](auto&& a) {
            return std::forward<decltype(a)>(a) * rhs;
        };
        apply_on_elements(*this, scalar_multiply, *this);
        return *this;
    }

    size_t dimension() const
    {
        return Base_::size();
    }

    Base_ const& elements() const
    {
        return static_cast<Base_ const&>(*this);
    }

    template<typename BinaryOp = std::plus<>>
    auto fold(BinaryOp&& op = std::plus<> {}) const&
    {
        return fold_impl(*this, std::forward<BinaryOp>(op));
    }

    template<typename BinaryOp = std::plus<>>
    auto fold(BinaryOp&& op = std::plus<> {}) && {
        return fold_impl(std::move(*this), std::forward<BinaryOp>(op));
    }

private:

    template<typename Point, typename BinaryOp>
    static auto fold_impl(Point&& p, BinaryOp&& op)
    {
        using res_t = decltype(op(std::declval<T>(), std::declval<T>()));
        constexpr bool p_is_rvalue = std::is_rvalue_reference<Point&&>::value;
        size_t d = p.dimension();
        assert(1 <= d);
        res_t res{move_if<p_is_rvalue>(p[0])};
        for(size_t i = 1; i != d; ++i) {
            res = op(std::move(res), move_if<p_is_rvalue>(p[i]));
        }
        return res;
    }

    friend void swap(point& lhs, point& rhs)
    {
        using std::swap;
        using base_t = point::Base_;
        swap(static_cast<base_t&>(lhs), static_cast<base_t&>(rhs));
    }

    friend std::istream& operator>>(std::istream& is, point& p)
    {
        is >> std::ws;
        char c;
        is >> c;
        if('/' == c) return is;
        assert('(' == c);
        size_t i = 0;
        size_t dim = p.dimension();
        do {
            //point does have a value at that position, so insert
            if(i < dim) {
                is >> p[i];
                //point does not have a value at that position, so push_back
            } else {
                T t;
                is >> t;
                p.push_back(std::move(t));
            }
            ++i;
            is >> c;
        } while(c != ')');

        return is;
    }

    friend struct apply_on_elements_t;
};

template<typename T>
std::ostream& operator<<(std::ostream& os, point<T> const& p)
{
    size_t dim = p.dimension();
    if(0 == dim) return os << '/';
    os << '(';
    for(size_t i = 0; i != dim; ++i) {
        os << p[i];
        if(i != dim-1) os << ',';
    }
    os << ')';
    return os;
}

template<typename A, typename AllocA, typename B, typename AllocB>
inline auto operator+(point<A, AllocA> const& lhs, point<B, AllocB> const& rhs)
{
    return apply_on_elements(std::plus<> {}, lhs, rhs);
}

template<typename A, typename AllocA, typename B, typename AllocB>
inline auto operator-(point<A, AllocA> const& lhs, point<B, AllocB> const& rhs)
{
    return apply_on_elements(std::minus<> {}, lhs, rhs);
}

template<typename A, typename AllocA>
inline auto operator-(point<A, AllocA> const& p)
{
    return apply_on_elements(std::negate<> {}, p);
}

template<typename A, typename AllocA, typename ArithT
         , std::enable_if_t<std::is_arithmetic<ArithT>::value, int> = 0>
inline auto operator*(point<A, AllocA> const& lhs, ArithT const& rhs)
{
    auto scalar_multiply = [&rhs](auto&& a) {
        return std::forward<decltype(a)>(a) * rhs;
    };
    return apply_on_elements(scalar_multiply, lhs);
}

template<typename A, typename AllocA, typename ArithT
         , std::enable_if_t<std::is_arithmetic<ArithT>::value, int> = 0>
inline auto operator*(ArithT const& lhs, point<A, AllocA> const& rhs)
{
    return rhs * lhs;
}

template<typename A, typename AllocA, typename B, typename AllocB>
inline auto operator==(point<A, AllocA> const& lhs, point<B, AllocB> const& rhs)
{
    using boost::range::equal;
    return equal(lhs.elements(), rhs.elements());
}

template<typename A, typename AllocA, typename B, typename AllocB>
inline auto operator!=(point<A, AllocA> const& lhs, point<B, AllocB> const& rhs)
{
    return !(lhs == rhs);
}

#endif //MPO19_POINT_12062020