// SPDX-FileCopyrightText: 2015 - 2021 Marcin Łoś <marcin.los.91@gmail.com>
// SPDX-License-Identifier: MIT

#ifndef ADS_SIMULATION_BASIC_SIMULATION_2D_HPP
#define ADS_SIMULATION_BASIC_SIMULATION_2D_HPP

#include <array>
#include <cstddef>

#include <boost/range/counting_range.hpp>

#include "ads/lin/tensor.hpp"
#include "ads/simulation/boundary.hpp"
#include "ads/simulation/dimension.hpp"
#include "ads/util/function_value.hpp"
#include "ads/util/iter/product.hpp"

namespace ads {

class basic_simulation_2d {
public:
    virtual ~basic_simulation_2d() = default;

    basic_simulation_2d() = default;
    basic_simulation_2d(const basic_simulation_2d&) = delete;
    basic_simulation_2d& operator=(const basic_simulation_2d&) = delete;
    basic_simulation_2d(basic_simulation_2d&&) = delete;
    basic_simulation_2d& operator=(basic_simulation_2d&&) = delete;

protected:
    using vector_type = lin::tensor<double, 2>;
    using vector_view = lin::tensor_view<double, 2>;
    using value_type = function_value_2d;

    using index_type = std::array<int, 2>;
    using index_1d_iter_type = boost::counting_iterator<int>;
    using index_iter_type = util::iter_product2<index_1d_iter_type, index_type>;
    using index_range = boost::iterator_range<index_iter_type>;

    using point_type = std::array<double, 2>;

    struct L2 {
        double operator()(value_type a) const { return a.val * a.val; }
    };

    struct H10 {
        double operator()(value_type a) const { return a.dx * a.dx + a.dy * a.dy; }
    };

    struct H1 {
        double operator()(value_type a) const { return a.val * a.val + a.dx * a.dx + a.dy * a.dy; }
    };

    value_type eval_basis(index_type e, index_type q, index_type a, const dimension& x,
                          const dimension& y) const {
        auto loc = dof_global_to_local(e, a, x, y);

        const auto& bx = x.basis;
        const auto& by = y.basis;

        double B1 = bx.b[e[0]][q[0]][0][loc[0]];
        double B2 = by.b[e[1]][q[1]][0][loc[1]];
        double dB1 = bx.b[e[0]][q[0]][1][loc[0]];
        double dB2 = by.b[e[1]][q[1]][1][loc[1]];

        double v = B1 * B2;
        double dxv = dB1 * B2;
        double dyv = B1 * dB2;

        return {v, dxv, dyv};
    }

    double laplacian(index_type e, index_type q, index_type a, const dimension& x,
                     const dimension& y) const {
        auto loc = dof_global_to_local(e, a, x, y);

        const auto& bx = x.basis;
        const auto& by = y.basis;

        double B1 = bx.b[e[0]][q[0]][0][loc[0]];
        double B2 = by.b[e[1]][q[1]][0][loc[1]];
        double ddB1 = bx.b[e[0]][q[0]][2][loc[0]];
        double ddB2 = by.b[e[1]][q[1]][2][loc[1]];

        return B1 * ddB2 + ddB1 * B2;
    }

    template <typename Sol>
    value_type eval(const Sol& v, index_type e, index_type q, const dimension& x,
                    const dimension& y) const {
        value_type u{};
        for (auto b : dofs_on_element(e, x, y)) {
            double c = v(b[0], b[1]);
            value_type B = eval_basis(e, q, b, x, y);
            u += c * B;
        }
        return u;
    }

    index_range elements(const dimension& x, const dimension& y) const {
        return util::product_range<index_type>(x.element_indices(), y.element_indices());
    }

    index_range quad_points(const dimension& x, const dimension& y) const {
        auto rx = boost::counting_range(0, x.basis.quad_order);
        auto ry = boost::counting_range(0, y.basis.quad_order);
        return util::product_range<index_type>(rx, ry);
    }

    index_range dofs_on_element(index_type e, const dimension& x, const dimension& y) const {
        auto rx = x.basis.dof_range(e[0]);
        auto ry = y.basis.dof_range(e[1]);
        return util::product_range<index_type>(rx, ry);
    }

    index_range elements_supporting_dof(index_type dof, const dimension& x,
                                        const dimension& y) const {
        auto rx = x.basis.element_range(dof[0]);
        auto ry = y.basis.element_range(dof[1]);
        return util::product_range<index_type>(rx, ry);
    }

    bool supported_in(index_type dof, index_type e, const dimension& x, const dimension& y) const {
        auto xrange = x.basis.element_ranges[dof[0]];
        auto yrange = y.basis.element_ranges[dof[1]];
        return e[0] >= xrange.first && e[0] <= xrange.second && e[1] >= yrange.first
            && e[1] <= yrange.second;
    }

    index_type dof_global_to_local(index_type e, index_type a, const dimension& x,
                                   const dimension& y) const {
        const auto& bx = x.basis;
        const auto& by = y.basis;
        return {{a[0] - bx.first_dof(e[0]), a[1] - by.first_dof(e[1])}};
    }

    template <typename RHS>
    void update_global_rhs(RHS& global, const vector_type& local, index_type e, const dimension& x,
                           const dimension& y) const {
        for (auto a : dofs_on_element(e, x, y)) {
            auto loc = dof_global_to_local(e, a, x, y);
            global(a[0], a[1]) += local(loc[0], loc[1]);
        }
    }

    index_range dofs(const dimension& x, const dimension& y) const {
        auto rx = boost::counting_range(0, x.dofs());
        auto ry = boost::counting_range(0, y.dofs());
        return util::product_range<index_type>(rx, ry);
    }

    index_range internal_dofs(const dimension& x, const dimension& y) const {
        auto rx = boost::counting_range(1, x.dofs() - 1);
        auto ry = boost::counting_range(1, y.dofs() - 1);
        return util::product_range<index_type>(rx, ry);
    }

    double jacobian(index_type e, const dimension& x, const dimension& y) const {
        return x.basis.J[e[0]] * y.basis.J[e[1]];
    }

    double weight(index_type q, const dimension& x, const dimension& y) const {
        return x.basis.w[q[0]] * y.basis.w[q[1]];
    }

    point_type point(index_type e, index_type q, const dimension& x, const dimension& y) const {
        double px = x.basis.x[e[0]][q[0]];
        double py = y.basis.x[e[1]][q[1]];
        return {px, py};
    }

    auto overlapping_dofs(int dof, int begin, int end, const dimension& x) const {
        using std::max;
        using std::min;

        auto minx = max(begin, dof - x.B.degree);
        auto maxx = min(end, dof + x.B.degree + 1);

        return boost::counting_range(minx, maxx);
    }

    index_range overlapping_dofs(index_type dof, const dimension& x, const dimension& y) const {
        auto rx = overlapping_dofs(dof[0], 0, x.dofs(), x);
        auto ry = overlapping_dofs(dof[1], 0, y.dofs(), y);
        return util::product_range<index_type>(rx, ry);
    }

    index_range overlapping_dofs(index_type dof, const dimension& Ux, const dimension& Uy,
                                 const dimension& Vx, const dimension& Vy) const {
        auto xrange = Ux.basis.element_ranges[dof[0]];
        auto yrange = Uy.basis.element_ranges[dof[1]];

        auto x0 = Vx.basis.first_dof(xrange.first);
        auto x1 = Vx.basis.last_dof(xrange.second) + 1;

        auto y0 = Vy.basis.first_dof(yrange.first);
        auto y1 = Vy.basis.last_dof(yrange.second) + 1;

        auto rx = boost::counting_range(x0, x1);
        auto ry = boost::counting_range(y0, y1);

        return util::product_range<index_type>(rx, ry);
    }

    index_range overlapping_internal_dofs(index_type dof, const dimension& x,
                                          const dimension& y) const {
        auto rx = overlapping_dofs(dof[0], 1, x.dofs() - 1, x);
        auto ry = overlapping_dofs(dof[1], 1, y.dofs() - 1, y);
        return util::product_range<index_type>(rx, ry);
    }

    int linear_index(index_type dof, const dimension& x, const dimension& y) const {
        auto order = reverse_ordering<2>({x.dofs(), y.dofs()});
        return order.linear_index(dof[0], dof[1]);
    }

    template <typename Fun>
    void for_boundary_dofs(const dimension& x, const dimension& y, Fun&& fun) const {
        for (auto jx = 0; jx < x.dofs(); ++jx) {
            fun({jx, 0});
            fun({jx, y.dofs() - 1});
        }
        for (auto jy = 1; jy < y.dofs() - 1; ++jy) {
            fun({0, jy});
            fun({x.dofs() - 1, jy});
        }
    }

    bool is_boundary(int dof, const dimension& x) const { return dof == 0 || dof == x.dofs() - 1; }

    bool is_boundary(index_type dof, const dimension& x, const dimension& y) const {
        return is_boundary(dof[0], x) || is_boundary(dof[1], y);
    }

    template <typename MT1, typename MT2>
    double kron(const MT1& A, const MT2& B, index_type i, index_type j) const {
        return A(i[0], j[0]) * B(i[1], j[1]);
    }

    template <typename RHS, typename Fun,
              typename = std::enable_if<std::is_arithmetic<std::result_of_t<Fun(double)>>{}>>
    void dirichlet_bc(RHS& u, boundary side, dimension& x, dimension& y, Fun&& fun) const {
        bool horizontal = side == boundary::top || side == boundary::bottom;
        auto& basis = horizontal ? x : y;
        const auto& other = horizontal ? y : x;

        lin::vector buf{{basis.dofs()}};
        compute_projection(buf, basis.basis, std::forward<Fun>(fun));
        lin::solve_with_factorized(basis.M, buf, basis.ctx);

        int idx = side == boundary::left || side == boundary::bottom ? 0 : other.dofs() - 1;
        for (int i = 0; i < basis.dofs(); ++i) {
            if (horizontal) {
                u(i, idx) = buf(i);
            } else {
                u(idx, i) = buf(i);
            }
        }
    }

    template <typename RHS>
    void dirichlet_bc(RHS& u, boundary side, dimension& x, dimension& y, double value) const {
        dirichlet_bc(u, side, x, y, [value](double) { return value; });
    }

    template <typename Norm, typename Fun>
    double norm(const dimension& Ux, const dimension& Uy, Norm&& norm, Fun&& fun) const {
        double val = 0;

        for (auto e : elements(Ux, Uy)) {
            double J = jacobian(e, Ux, Uy);
            for (auto q : quad_points(Ux, Uy)) {
                double w = weight(q, Ux, Uy);
                auto x = point(e, q, Ux, Uy);
                auto d = fun(x);
                val += norm(d) * w * J;
            }
        }
        return std::sqrt(val);
    }

    template <typename Fun>
    double normL2(const dimension& Ux, const dimension& Uy, Fun&& fun) const {
        return norm(Ux, Uy, L2{}, fun);
    }

    template <typename Fun>
    double normH1(const dimension& Ux, const dimension& Uy, Fun&& fun) const {
        return norm(Ux, Uy, H1{}, fun);
    }

    template <typename Sol, typename Norm>
    double norm(const Sol& u, const dimension& Ux, const dimension& Uy, Norm&& norm) const {
        double val = 0;

        for (auto e : elements(Ux, Uy)) {
            double J = jacobian(e, Ux, Uy);
            for (auto q : quad_points(Ux, Uy)) {
                double w = weight(q, Ux, Uy);
                value_type uu = eval(u, e, q, Ux, Uy);
                val += norm(uu) * w * J;
            }
        }
        return std::sqrt(val);
    }

    template <typename Sol>
    double normL2(const Sol& u, const dimension& Ux, const dimension& Uy) const {
        return norm(u, Ux, Uy, L2{});
    }

    template <typename Sol>
    double normH1(const Sol& u, const dimension& Ux, const dimension& Uy) const {
        return norm(u, Ux, Uy, H1{});
    }

    template <typename Sol, typename Fun, typename Norm>
    double error(const Sol& u, const dimension& Ux, const dimension& Uy, Norm&& norm,
                 Fun&& fun) const {
        double error = 0;

        for (auto e : elements(Ux, Uy)) {
            double J = jacobian(e, Ux, Uy);
            for (auto q : quad_points(Ux, Uy)) {
                double w = weight(q, Ux, Uy);
                auto x = point(e, q, Ux, Uy);
                value_type uu = eval(u, e, q, Ux, Uy);

                auto d = uu - fun(x);
                error += norm(d) * w * J;
            }
        }
        return std::sqrt(error);
    }

    template <typename Sol, typename Fun>
    double errorL2(const Sol& u, const dimension& Ux, const dimension& Uy, Fun&& fun) const {
        return error(u, Ux, Uy, L2{}, fun);
    }

    template <typename Sol, typename Fun>
    double error_relative_L2(const Sol& u, const dimension& Ux, const dimension& Uy,
                             Fun&& fun) const {
        return error_relative(u, Ux, Uy, L2{}, fun);
    }

    template <typename Sol, typename Fun>
    double errorH1(const Sol& u, const dimension& Ux, const dimension& Uy, Fun&& fun) const {
        return error(u, Ux, Uy, H1{}, fun);
    }

    template <typename Sol, typename Fun, typename Norm>
    double error_relative(const Sol& u, const dimension& Ux, const dimension& Uy, Norm&& norm,
                          Fun&& fun) const {
        return error(u, Ux, Uy, norm, fun) / this->norm(Ux, Uy, norm, fun);
    }

    template <typename Sol, typename Fun>
    double error_relative_H1(const Sol& u, const dimension& Ux, const dimension& Uy,
                             Fun&& fun) const {
        return error_relative(u, Ux, Uy, H1{}, fun);
    }
};

}  // namespace ads

#endif  // ADS_SIMULATION_BASIC_SIMULATION_2D_HPP


/* boost test framework. */
#define BOOST_TEST_MAIN
#define BOOST_TEST_ALTERNATIVE_INIT_API
#define BOOST_TEST_MODULE closed interval fixed - point class test
#include <boost/test/unit_test.hpp>

/* user headers. */
#include "ml/all.h"

/*
 * Helpers.
 */

using namespace ml;
using namespace std;

BOOST_AUTO_TEST_SUITE(fp_closed_interval)

/*
 * number representations.
 */

const closed_unit_interval<uint32_t> zero{0}, half{0.5}, one{1};

BOOST_AUTO_TEST_CASE(representation)
{
    BOOST_CHECK(unwrap(zero) == 0);
    BOOST_CHECK(unwrap(half) == std::numeric_limits<uint32_t>::max() / 2);
    BOOST_CHECK(unwrap(one) == std::numeric_limits<uint32_t>::max());
}

/*
 * arithmetic.
 */

BOOST_AUTO_TEST_CASE(arithmetic)
{
    BOOST_CHECK(to_float(half + half) == to_float(one));
}

/*
 * comparisons.
 */

BOOST_AUTO_TEST_CASE(comparisons)
{
    BOOST_CHECK(zero < half);
    BOOST_CHECK(zero < one);
    BOOST_CHECK(half < one);

    BOOST_CHECK(!(zero > zero));
    BOOST_CHECK(zero >= zero);

    BOOST_CHECK(!(half > half));
    BOOST_CHECK(half >= half);
    BOOST_CHECK(one >= half);

    BOOST_CHECK(!(one > one));
    BOOST_CHECK(one >= one);

    BOOST_CHECK(half > zero);
    BOOST_CHECK(one > zero);
    BOOST_CHECK(one > half);

    BOOST_CHECK(!(zero > zero));
    BOOST_CHECK(zero >= zero);

    BOOST_CHECK(!(half > half));
    BOOST_CHECK(half >= half);
    BOOST_CHECK(one >= half);

    BOOST_CHECK(!(one > one));
    BOOST_CHECK(one >= one);
}

BOOST_AUTO_TEST_SUITE_END()


//          Copyright Carl Philipp Reh 2009 - 2018.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#include <fcppt/math/is_zero_boost_units.hpp>
#include <fcppt/math/vector/arithmetic.hpp>
#include <fcppt/math/vector/comparison.hpp>
#include <fcppt/math/vector/static.hpp>
#include <fcppt/optional/comparison.hpp>
#include <fcppt/optional/make.hpp>
#include <fcppt/config/external_begin.hpp>
#include <boost/units/quantity.hpp>
#include <boost/units/systems/si/length.hpp>
#include <boost/units/systems/si/time.hpp>
#include <boost/units/systems/si/velocity.hpp>
#include <catch2/catch.hpp>
#include <fcppt/config/external_end.hpp>

TEST_CASE(
	"math units",
	"[math]"
)
{
	typedef
	int
	unit_type;

	typedef
	boost::units::quantity<
		boost::units::si::length,
		unit_type
	>
	length;

	typedef
	boost::units::quantity<
		boost::units::si::time,
		unit_type
	>
	time;

	typedef
	boost::units::quantity<
		boost::units::si::velocity,
		unit_type
	>
	velocity;

	typedef
	fcppt::math::vector::static_<
		length,
		2
	>
	length2;

	typedef
	fcppt::math::vector::static_<
		time,
		2
	>
	time2;

	typedef
	fcppt::math::vector::static_<
		velocity,
		2
	>
	velocity2;

	length2 const l1(
		length(
			-100
			*
			boost::units::si::meter
		),
		length(
			200
			*
			boost::units::si::meter
		)
	);

	time2 const t1(
		time(
			4
			*
			boost::units::si::second
		),
		time(
			2
			*
			boost::units::si::second
		)
	);

	CHECK(
		l1
		/
		t1
		==
		fcppt::optional::make(
			velocity2{
				-25
				*
				boost::units::si::meter
				/
				boost::units::si::second
				,
				100
				*
				boost::units::si::meter
				/
				boost::units::si::second
			}
		)
	);
}


#include "layers/InputLayer.h"
#include "layers/OutputLayer.h"
#include "layers/FullyConnectedLayer.h"
#include "layers/MappingInputLayer.h"
#include "utils/MathUtils.h"
#include "train/SimpleTrainer.h"

#include <iostream>
#include <vector>
#include <cmath>
#include <stdexcept>
#include <random>
#include <unistd.h>
#include <functional>

using namespace std;
using namespace alex;

const double LearningRate = 0.03;
const int InputWidth = 2;
const int OutputWidth = 2;

double InputXWidth = 3.0;
double InputYWidth = 1.0;

static int setIndex(int index, double *output, int outputCount) {
  if (index < 0 || index >= outputCount)
    throw new std::invalid_argument("setIndex argument out of bound");
  for (int i = 0; i < outputCount; ++i) {
    output[i] = -1;
    if (i == index)
      output[i] = 1;
  }
  return index;
}
auto realFunction = [](const double *input, int iw, double *output, int ow) -> int {
  double x = input[0], y = input[1];
  if ((x+2)*(x+2) + y*y < 1) {
    return setIndex(0, output, ow);
  }
  else {
    return setIndex(1, output, ow);
  }
};

std::vector<Layer*> initializeNetwork() {

  MappingInputLayer *inputLayer = new MappingInputLayer(InputWidth, {
      [](const double *input, int w) -> double { return sin(input[0]); },
      [](const double *input, int w) -> double { return sin(input[1]); },
      [](const double *input, int w) -> double { return input[0] * input[0]; },
      [](const double *input, int w) -> double { return input[0] * input[1]; },
//      [](const double *input, int w) -> double { return input[1] * input[1]; }
  });
  FullyConnectedLayer *layer1 = new FullyConnectedLayer(6, inputLayer);
  FullyConnectedLayer *layer3 = new FullyConnectedLayer(6, layer1);
  OutputLayer *outputLayer = new OutputLayer(OutputWidth, layer3);

  vector<Layer*> layers;
  Layer *last = inputLayer;
  layers.push_back(last);
  while (true) {
    Layer *current = last->getNextLayer();
    if (!current)
      break;
    layers.push_back(current);
    last = current;
  }

  return layers;
}

void testLayers(std::vector<Layer *> &layers, unsigned int testTimes) {
  int successCount = 0;
  for (int i = 0; i < testTimes; ++i) {
    double expectedOutput[OutputWidth], *inputData = new double[InputWidth], *outputData = nullptr;
    inputData[0] = MathUtils::rand1() * InputXWidth;
    inputData[1] = MathUtils::rand1() * InputYWidth;
    int expectedIndex = realFunction(inputData, InputWidth, expectedOutput, OutputWidth);

    for (auto layer : layers) {
      outputData = new double[layer->OutputWidth];
      layer->forwardPropagation(inputData, outputData);

      delete [] inputData;
      inputData = outputData;
    }

    double max = -1; int maxIndex = 0;
    for (int j = 0; j < OutputWidth; ++j) {
      if (inputData[j] > max) {
        max = inputData[j];
        maxIndex = j;
      }
    }
    if (maxIndex == expectedIndex)
      successCount++;

    delete [] outputData;
  }

  // NOTE: layer 0 is not a FullyConnectedLayer
  for (int i = 1; i < layers.size(); ++i) {
    cout << "layer" << i << " weights: " << endl;
    static_cast<FullyConnectedLayer*>(layers[i])->printWeights();
  }

  cout << "success rate = " << 100.0 * successCount / testTimes << "%" << endl;
}

#include <boost/program_options.hpp>
int main(int argc, const char *argv[]) {
  srand(1);

  unsigned testTimes = 10000,
          trainTimes = 10000;

  try {
    boost::program_options::options_description desc("Options");
    desc.add_options()
            ("help", boost::program_options::value<string>(), "Print help messages")
            ("train-times", boost::program_options::value<unsigned int>(), "train times")
            ("test-times", boost::program_options::value<unsigned int>(), "test times");

    boost::program_options::variables_map vm;
    try {
      boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc),
                vm); // can throw

      /** --help option
       */
      if (vm.count("help")) {
        std::cout << "bp-ann" << std::endl
        << desc << std::endl;
        return 0;
      }

      boost::program_options::notify(vm); // throws on error, so do after help in case
      // there are any problems
    }
    catch(boost::program_options::error& e) {
      std::cerr << "ERROR: " << e.what() << std::endl << std::endl;
      std::cerr << desc << std::endl;
      return 1;
    }

    if (vm.count("train-times"))
      trainTimes = vm["train-times"].as<unsigned>();
    if (vm.count("test-times"))
      testTimes = vm["test-times"].as<unsigned>();

    auto layers = initializeNetwork();
    SimpleTrainer trainer(InputWidth, OutputWidth, LearningRate, trainTimes, layers,
                          [=](double *input, int w) -> void {
                            input[0] = MathUtils::rand1() * InputXWidth;
                            input[1] = MathUtils::rand1() * InputYWidth;
                          },
                          realFunction,
                          [](const double *output, const double *expected, double *error, int width) -> void {
                            for (int i = 0; i < width; ++i) {
                              error[i] = output[i] - expected[i];
                            }
                          },
                          [](const double *err, int width) -> double {
                            double sum = 0;
                            for (int i = 0; i < width; ++i) {
                              sum += err[i] * err[i];
                            }
                            return sum;
                          });
    trainer.startTraining();
    testLayers(layers, testTimes);
  }
  catch(std::exception& e) {
    std::cerr << "Unhandled Exception reached the top of main: "
      << e.what() << ", application will now exit" << std::endl;
    return 2;
  }
  return 0;
}


//==================================================================================================
/**
  EVE - Expressive Vector Engine
  Copyright : EVE Contributors & Maintainers
  SPDX-License-Identifier: MIT
**/
//==================================================================================================
#include <eve/function/zeta.hpp>
#include <boost/math/special_functions/zeta.hpp>
#include <eve/function/all.hpp>
#include <eve/constant/nan.hpp>
#include <eve/constant/inf.hpp>
#include <eve/constant/minf.hpp>
#include <eve/function/is_positive.hpp>
#include <eve/function/is_negative.hpp>
#include <eve/platform.hpp>

#include <cmath>

TTS_CASE_TPL("Check eve::zeta return type", EVE_TYPE)
{
  TTS_EXPR_IS(eve::zeta(T(0)), T);
}

TTS_CASE_TPL("Check eve::zeta behavior", EVE_TYPE)
{
  if constexpr( eve::platform::supports_invalids )
  {
    TTS_IEEE_EQUAL(eve::zeta(eve::nan(eve::as<T>())) , eve::nan(eve::as<T>()));
    TTS_IEEE_EQUAL(eve::zeta(eve::inf(eve::as<T>())) , eve::one(eve::as<T>()));
    TTS_IEEE_EQUAL(eve::zeta(eve::minf(eve::as<T>())), eve::nan(eve::as<T>()));
  }

  TTS_IEEE_EQUAL(eve::zeta(T(1))   , eve::nan(eve::as<T>()));
   using v_t = eve::element_type_t<T>;
   TTS_ULP_EQUAL(eve::zeta(T(0))    , T(boost::math::zeta(v_t(0))), 0.5);
   TTS_ULP_EQUAL(eve::zeta(T(-0.0))   ,T(boost::math::zeta(v_t(-0.0))), 0.5);
   TTS_ULP_EQUAL(eve::zeta(T(1.5))    , T(boost::math::zeta(v_t(1.5))), 0.5);
   TTS_ULP_EQUAL(eve::zeta(T(-1.5))   ,T(boost::math::zeta(v_t(-1.5))), 2.0);
   TTS_ULP_EQUAL(eve::zeta(T(14))    , T(boost::math::zeta(v_t(14))), 0.5);
   TTS_ULP_EQUAL(eve::zeta(T(-14))   ,T(boost::math::zeta(v_t(-14))), 0.5);
   TTS_ULP_EQUAL(eve::zeta(T(14.5))    , T(boost::math::zeta(v_t(14.5))), 0.5);
   TTS_ULP_EQUAL(eve::zeta(T(-14.5))   ,T(boost::math::zeta(v_t(-14.5))), 1.5);
}


// Boost.Geometry (aka GGL, Generic Geometry Library)
// QuickBook Example

// Copyright (c) 2011 Barend Gehrels, Amsterdam, the Netherlands.

// 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)

//[assign
//` Shows how to assign a geometry from another geometry

#include <iostream>

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/geometries/adapted/boost_tuple.hpp>

BOOST_GEOMETRY_REGISTER_BOOST_TUPLE_CS(cs::cartesian)

int main()
{
    typedef boost::geometry::model::d2::point_xy<double> point;
    typedef boost::geometry::model::box<point> box;
    typedef boost::geometry::model::polygon<point> polygon;

    point p1;
    box b;
    boost::geometry::assign_values(p1, 1, 1);
    boost::geometry::assign_values(b, 1, 1, 2, 2);

    // Assign a box to a polygon (target = source)
    polygon p;
    boost::geometry::assign(p, b);

    // Assign a point to another point type (conversion of point-type)
    boost::tuple<double, double> p2;
    boost::geometry::assign(p2, p1);

    using boost::geometry::dsv;
    std::cout
        << "box: " << dsv(b) << std::endl
        << "polygon: " << dsv(p) << std::endl
        << "point: " << dsv(p1) << std::endl
        << "point tuples: " << dsv(p2) << std::endl
        ;

    return 0;
}

//]

//[assign_output
/*`
Output:
[pre
box: ((1, 1), (2, 2))
polygon: (((1, 1), (1, 2), (2, 2), (2, 1), (1, 1)))
point: (1, 1)
point tuples: (1, 1)
]
*/
//]


#pragma once

#include "common.hpp"
#include <boost/integer/static_log2.hpp>

namespace cindex
{
	template<typename BlockType>
	class block_info
	{
	public:
		typedef BlockType block_type;

		static const std::size_t bytes_per_block = sizeof(block_type);
		static const std::size_t bits_per_block = bytes_per_block * 8;
		static const std::size_t bit_mask = bits_per_block - 1;
		static const std::size_t log_bits_per_block = boost::static_log2<bits_per_block>::value;

		static std::size_t
		block_count(std::size_t n) CINDEX_WARN_UNUSED_RESULT
		{
			return (n + bits_per_block - 1) / bits_per_block;
		}

		static std::size_t
		size(std::size_t n) CINDEX_WARN_UNUSED_RESULT
		{
			return block_count(n) * bytes_per_block;
		}
	};
}


#define BOOST_TEST_MODULE blas

#include <boost/test/unit_test.hpp>
#include <boost/test/test_case_template.hpp>
#include <boost/mpl/list.hpp>

#include <mla/matrix/all.h++>
#include <mla/vector/all.h++>

#include <mla/operations/level2/syr.h++>

using Scalar = float;

typedef boost::mpl::list<
	mla::matrix::DenseRowMajor<Scalar>,
	mla::matrix::Diagonal<Scalar>,
	mla::matrix::SparseDOK<Scalar>,
	mla::matrix::SparseCRS<Scalar>
> matrix_type_list;

template<typename T> using MatrixType = mla::matrix::DenseRowMajor<T>;

typedef boost::mpl::list<
	mla::vector::Dense<Scalar>,
	mla::vector::SparseCS<Scalar>
> vector_type_list;

BOOST_AUTO_TEST_SUITE(test_boost_level2)

BOOST_AUTO_TEST_CASE_TEMPLATE( blas_level2_test_syr_DenseRowMajor_zero, VectorType, vector_type_list )
{
	using namespace mla;

	Scalar alpha = 1.0f;

	size_t length = 3;
	MatrixType<Scalar> A( length, length);

	VectorType x(length);
	x.setValue(0, 1.0f);
	x.setValue(1, 1.0f);
	x.setValue(2, 1.0f);

	syr(alpha, x, A);

	for(size_t i = 0; i < length; i++)
	{
		for(size_t j = 0; j < length; j++)
		{
			BOOST_CHECK_CLOSE( A.getValue(i,j), 1.0f, 1.0e-5 );
		}
	}
}

BOOST_AUTO_TEST_SUITE_END()


#include <stan/math/prim/scal.hpp>
#include <boost/math/special_functions/fpclassify.hpp>
#include <gtest/gtest.h>

TEST(MathFunctions, gamma_p) {
  using stan::math::gamma_p;

  EXPECT_FLOAT_EQ(0.63212055, gamma_p(1.0,1.0));
  EXPECT_FLOAT_EQ(0.82755178, gamma_p(0.1,0.1));
  EXPECT_FLOAT_EQ(0.76189667, gamma_p(3.0,4.0));
  EXPECT_FLOAT_EQ(0.35276812, gamma_p(4.0,3.0));
  EXPECT_THROW(gamma_p(-4.0,3.0), std::domain_error);
  EXPECT_THROW(gamma_p(4.0,-3.0), std::domain_error);
}

TEST(MathFunctions, gamma_p_nan) {
  double nan = std::numeric_limits<double>::quiet_NaN();

  EXPECT_PRED1(boost::math::isnan<double>,
               stan::math::gamma_p(1.0, nan));

  EXPECT_PRED1(boost::math::isnan<double>,
               stan::math::gamma_p(nan, 1.0));

  EXPECT_PRED1(boost::math::isnan<double>,
               stan::math::gamma_p(nan, nan));
}


/**
 * @file kernel_test.cpp
 * @author Ryan Curtin
 * @author Ajinkya Kale
 *
 * Tests for the various kernel classes.
 *
 * 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 <mlpack/core/kernels/cosine_distance.hpp>
#include <mlpack/core/kernels/epanechnikov_kernel.hpp>
#include <mlpack/core/kernels/gaussian_kernel.hpp>
#include <mlpack/core/kernels/hyperbolic_tangent_kernel.hpp>
#include <mlpack/core/kernels/laplacian_kernel.hpp>
#include <mlpack/core/kernels/linear_kernel.hpp>
#include <mlpack/core/kernels/polynomial_kernel.hpp>
#include <mlpack/core/kernels/spherical_kernel.hpp>
#include <mlpack/core/kernels/pspectrum_string_kernel.hpp>
#include <mlpack/core/kernels/cauchy_kernel.hpp>
#include <mlpack/core/metrics/lmetric.hpp>
#include <mlpack/core/metrics/mahalanobis_distance.hpp>

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

using namespace mlpack;
using namespace mlpack::kernel;
using namespace mlpack::metric;

BOOST_AUTO_TEST_SUITE(KernelTest);

/**
 * Basic test of the Manhattan distance.
 */
BOOST_AUTO_TEST_CASE(ManhattanDistanceTest)
{
  // A couple quick tests.
  arma::vec a = "1.0 3.0 4.0";
  arma::vec b = "3.0 3.0 5.0";

  BOOST_REQUIRE_CLOSE(ManhattanDistance::Evaluate(a, b), 3.0, 1e-5);
  BOOST_REQUIRE_CLOSE(ManhattanDistance::Evaluate(b, a), 3.0, 1e-5);

  // Check also for when the root is taken (should be the same).
  BOOST_REQUIRE_CLOSE((LMetric<1, true>::Evaluate(a, b)), 3.0, 1e-5);
  BOOST_REQUIRE_CLOSE((LMetric<1, true>::Evaluate(b, a)), 3.0, 1e-5);
}

/**
 * Basic test of squared Euclidean distance.
 */
BOOST_AUTO_TEST_CASE(SquaredEuclideanDistanceTest)
{
  // Sample 2-dimensional vectors.
  arma::vec a = "1.0  2.0";
  arma::vec b = "0.0 -2.0";

  BOOST_REQUIRE_CLOSE(SquaredEuclideanDistance::Evaluate(a, b), 17.0, 1e-5);
  BOOST_REQUIRE_CLOSE(SquaredEuclideanDistance::Evaluate(b, a), 17.0, 1e-5);
}

/**
 * Basic test of Euclidean distance.
 */
BOOST_AUTO_TEST_CASE(EuclideanDistanceTest)
{
  arma::vec a = "1.0 3.0 5.0 7.0";
  arma::vec b = "4.0 0.0 2.0 0.0";

  BOOST_REQUIRE_CLOSE(EuclideanDistance::Evaluate(a, b), sqrt(76.0), 1e-5);
  BOOST_REQUIRE_CLOSE(EuclideanDistance::Evaluate(b, a), sqrt(76.0), 1e-5);
}

/**
 * Arbitrary test case for coverage.
 */
BOOST_AUTO_TEST_CASE(ArbitraryCaseTest)
{
  arma::vec a = "3.0 5.0 6.0 7.0";
  arma::vec b = "1.0 2.0 1.0 0.0";

  BOOST_REQUIRE_CLOSE((LMetric<3, false>::Evaluate(a, b)), 503.0, 1e-5);
  BOOST_REQUIRE_CLOSE((LMetric<3, false>::Evaluate(b, a)), 503.0, 1e-5);

  BOOST_REQUIRE_CLOSE((LMetric<3, true>::Evaluate(a, b)), 7.95284762, 1e-5);
  BOOST_REQUIRE_CLOSE((LMetric<3, true>::Evaluate(b, a)), 7.95284762, 1e-5);
}

/**
 * Make sure two vectors of all zeros return zero distance, for a few different
 * powers.
 */
BOOST_AUTO_TEST_CASE(LMetricZerosTest)
{
  arma::vec a(250);
  a.fill(0.0);

  // We cannot use a loop because compilers seem to be unable to unroll the loop
  // and realize the variable actually is knowable at compile-time.
  BOOST_REQUIRE((LMetric<1, false>::Evaluate(a, a)) == 0);
  BOOST_REQUIRE((LMetric<1, true>::Evaluate(a, a)) == 0);
  BOOST_REQUIRE((LMetric<2, false>::Evaluate(a, a)) == 0);
  BOOST_REQUIRE((LMetric<2, true>::Evaluate(a, a)) == 0);
  BOOST_REQUIRE((LMetric<3, false>::Evaluate(a, a)) == 0);
  BOOST_REQUIRE((LMetric<3, true>::Evaluate(a, a)) == 0);
  BOOST_REQUIRE((LMetric<4, false>::Evaluate(a, a)) == 0);
  BOOST_REQUIRE((LMetric<4, true>::Evaluate(a, a)) == 0);
  BOOST_REQUIRE((LMetric<5, false>::Evaluate(a, a)) == 0);
  BOOST_REQUIRE((LMetric<5, true>::Evaluate(a, a)) == 0);
}

/**
 * Simple test of Mahalanobis distance with unset covariance matrix in
 * constructor.
 */
BOOST_AUTO_TEST_CASE(MDUnsetCovarianceTest)
{
  MahalanobisDistance<false> md;
  md.Covariance() = arma::eye<arma::mat>(4, 4);
  arma::vec a = "1.0 2.0 2.0 3.0";
  arma::vec b = "0.0 0.0 1.0 3.0";

  BOOST_REQUIRE_CLOSE(md.Evaluate(a, b), 6.0, 1e-5);
  BOOST_REQUIRE_CLOSE(md.Evaluate(b, a), 6.0, 1e-5);
}

/**
 * Simple test of Mahalanobis distance with unset covariance matrix in
 * constructor and t_take_root set to true.
 */
BOOST_AUTO_TEST_CASE(MDRootUnsetCovarianceTest)
{
  MahalanobisDistance<true> md;
  md.Covariance() = arma::eye<arma::mat>(4, 4);
  arma::vec a = "1.0 2.0 2.5 5.0";
  arma::vec b = "0.0 2.0 0.5 8.0";

  BOOST_REQUIRE_CLOSE(md.Evaluate(a, b), sqrt(14.0), 1e-5);
  BOOST_REQUIRE_CLOSE(md.Evaluate(b, a), sqrt(14.0), 1e-5);
}

/**
 * Simple test of Mahalanobis distance setting identity covariance in
 * constructor.
 */
BOOST_AUTO_TEST_CASE(MDEyeCovarianceTest)
{
  MahalanobisDistance<false> md(4);
  arma::vec a = "1.0 2.0 2.0 3.0";
  arma::vec b = "0.0 0.0 1.0 3.0";

  BOOST_REQUIRE_CLOSE(md.Evaluate(a, b), 6.0, 1e-5);
  BOOST_REQUIRE_CLOSE(md.Evaluate(b, a), 6.0, 1e-5);
}

/**
 * Simple test of Mahalanobis distance setting identity covariance in
 * constructor and t_take_root set to true.
 */
BOOST_AUTO_TEST_CASE(MDRootEyeCovarianceTest)
{
  MahalanobisDistance<true> md(4);
  arma::vec a = "1.0 2.0 2.5 5.0";
  arma::vec b = "0.0 2.0 0.5 8.0";

  BOOST_REQUIRE_CLOSE(md.Evaluate(a, b), sqrt(14.0), 1e-5);
  BOOST_REQUIRE_CLOSE(md.Evaluate(b, a), sqrt(14.0), 1e-5);
}

/**
 * Simple test with diagonal covariance matrix.
 */
BOOST_AUTO_TEST_CASE(MDDiagonalCovarianceTest)
{
  arma::mat cov = arma::eye<arma::mat>(5, 5);
  cov(0, 0) = 2.0;
  cov(1, 1) = 0.5;
  cov(2, 2) = 3.0;
  cov(3, 3) = 1.0;
  cov(4, 4) = 1.5;
  MahalanobisDistance<false> md(cov);

  arma::vec a = "1.0 2.0 2.0 4.0 5.0";
  arma::vec b = "2.0 3.0 1.0 1.0 0.0";

  BOOST_REQUIRE_CLOSE(md.Evaluate(a, b), 52.0, 1e-5);
  BOOST_REQUIRE_CLOSE(md.Evaluate(b, a), 52.0, 1e-5);
}

/**
 * More specific case with more difficult covariance matrix.
 */
BOOST_AUTO_TEST_CASE(MDFullCovarianceTest)
{
  arma::mat cov = "1.0 2.0 3.0 4.0;"
                  "0.5 0.6 0.7 0.1;"
                  "3.4 4.3 5.0 6.1;"
                  "1.0 2.0 4.0 1.0;";
  MahalanobisDistance<false> md(cov);

  arma::vec a = "1.0 2.0 2.0 4.0";
  arma::vec b = "2.0 3.0 1.0 1.0";

  BOOST_REQUIRE_CLOSE(md.Evaluate(a, b), 15.7, 1e-5);
  BOOST_REQUIRE_CLOSE(md.Evaluate(b, a), 15.7, 1e-5);
}

/**
 * Simple test case for the cosine distance.
 */
BOOST_AUTO_TEST_CASE(CosineDistanceSameAngleTest)
{
  arma::vec a = "1.0 2.0 3.0";
  arma::vec b = "2.0 4.0 6.0";

  BOOST_REQUIRE_CLOSE(CosineDistance::Evaluate(a, b), 1.0, 1e-5);
  BOOST_REQUIRE_CLOSE(CosineDistance::Evaluate(b, a), 1.0, 1e-5);
}

/**
 * Now let's have them be orthogonal.
 */
BOOST_AUTO_TEST_CASE(CosineDistanceOrthogonalTest)
{
  arma::vec a = "0.0 1.0";
  arma::vec b = "1.0 0.0";

  BOOST_REQUIRE_SMALL(CosineDistance::Evaluate(a, b), 1e-5);
  BOOST_REQUIRE_SMALL(CosineDistance::Evaluate(b, a), 1e-5);
}

/**
 * Some random angle test.
 */
BOOST_AUTO_TEST_CASE(CosineDistanceRandomTest)
{
  arma::vec a = "0.1 0.2 0.3 0.4 0.5";
  arma::vec b = "1.2 1.0 0.8 -0.3 -0.5";

  BOOST_REQUIRE_CLOSE(CosineDistance::Evaluate(a, b), 0.1385349024, 1e-5);
  BOOST_REQUIRE_CLOSE(CosineDistance::Evaluate(b, a), 0.1385349024, 1e-5);
}

/**
 * Linear Kernel test.
 */
BOOST_AUTO_TEST_CASE(LinearKernelTest)
{
  arma::vec a = ".2 .3 .4 .1";
  arma::vec b = ".56 .21 .623 .82";

  LinearKernel lk;
  BOOST_REQUIRE_CLOSE(lk.Evaluate(a, b), .5062, 1e-5);
  BOOST_REQUIRE_CLOSE(lk.Evaluate(b, a), .5062, 1e-5);
}

/**
 * Linear Kernel test, orthogonal vectors.
 */
BOOST_AUTO_TEST_CASE(LinearKernelOrthogonalTest)
{
  arma::vec a = "1 0 0";
  arma::vec b = "0 0 1";

  LinearKernel lk;
  BOOST_REQUIRE_SMALL(lk.Evaluate(a, b), 1e-5);
  BOOST_REQUIRE_SMALL(lk.Evaluate(b, a), 1e-5);
}

BOOST_AUTO_TEST_CASE(GaussianKernelTest)
{
  arma::vec a = "1 0 0";
  arma::vec b = "0 1 0";
  arma::vec c = "0 0 1";

  GaussianKernel gk(.5);
  BOOST_REQUIRE_CLOSE(gk.Evaluate(a, b), .018315638888734, 1e-5);
  BOOST_REQUIRE_CLOSE(gk.Evaluate(b, a), .018315638888734, 1e-5);
  BOOST_REQUIRE_CLOSE(gk.Evaluate(a, c), .018315638888734, 1e-5);
  BOOST_REQUIRE_CLOSE(gk.Evaluate(c, a), .018315638888734, 1e-5);
  BOOST_REQUIRE_CLOSE(gk.Evaluate(b, c), .018315638888734, 1e-5);
  BOOST_REQUIRE_CLOSE(gk.Evaluate(c, b), .018315638888734, 1e-5);
  /* check the single dimension evaluate function */
  BOOST_REQUIRE_CLOSE(gk.Evaluate(1.0), 0.1353352832366127, 1e-5);
  BOOST_REQUIRE_CLOSE(gk.Evaluate(2.0), 0.00033546262790251185, 1e-5);
  BOOST_REQUIRE_CLOSE(gk.Evaluate(3.0), 1.5229979744712629e-08, 1e-5);
  /* check the normalization constant */
  BOOST_REQUIRE_CLOSE(gk.Normalizer(1), 1.2533141373155001, 1e-5);
  BOOST_REQUIRE_CLOSE(gk.Normalizer(2), 1.5707963267948963, 1e-5);
  BOOST_REQUIRE_CLOSE(gk.Normalizer(3), 1.9687012432153019, 1e-5);
  BOOST_REQUIRE_CLOSE(gk.Normalizer(4), 2.4674011002723386, 1e-5);
  /* check the convolution integral */
  BOOST_REQUIRE_CLOSE(gk.ConvolutionIntegral(a, b), 0.024304474038457577, 1e-5);
  BOOST_REQUIRE_CLOSE(gk.ConvolutionIntegral(a, c), 0.024304474038457577, 1e-5);
  BOOST_REQUIRE_CLOSE(gk.ConvolutionIntegral(b, c), 0.024304474038457577, 1e-5);
}

BOOST_AUTO_TEST_CASE(GaussianKernelSerializationTest)
{
  GaussianKernel gk(0.5);
  GaussianKernel xmlGk(1.5), textGk, binaryGk(15.0);

  // Serialize the kernels.
  SerializeObjectAll(gk, xmlGk, textGk, binaryGk);

  BOOST_REQUIRE_CLOSE(gk.Bandwidth(), 0.5, 1e-5);
  BOOST_REQUIRE_CLOSE(xmlGk.Bandwidth(), 0.5, 1e-5);
  BOOST_REQUIRE_CLOSE(textGk.Bandwidth(), 0.5, 1e-5);
  BOOST_REQUIRE_CLOSE(binaryGk.Bandwidth(), 0.5, 1e-5);
}

BOOST_AUTO_TEST_CASE(SphericalKernelTest)
{
  arma::vec a = "1.0 0.0";
  arma::vec b = "0.0 1.0";
  arma::vec c = "0.2 0.9";

  SphericalKernel sk(.5);
  BOOST_REQUIRE_CLOSE(sk.Evaluate(a, b), 0.0, 1e-5);
  BOOST_REQUIRE_CLOSE(sk.Evaluate(a, c), 0.0, 1e-5);
  BOOST_REQUIRE_CLOSE(sk.Evaluate(b, c), 1.0, 1e-5);
  /* check the single dimension evaluate function */
  BOOST_REQUIRE_CLOSE(sk.Evaluate(0.10), 1.0, 1e-5);
  BOOST_REQUIRE_CLOSE(sk.Evaluate(0.25), 1.0, 1e-5);
  BOOST_REQUIRE_CLOSE(sk.Evaluate(0.50), 1.0, 1e-5);
  BOOST_REQUIRE_CLOSE(sk.Evaluate(1.00), 0.0, 1e-5);
  /* check the normalization constant */
  BOOST_REQUIRE_CLOSE(sk.Normalizer(1), 1.0, 1e-5);
  BOOST_REQUIRE_CLOSE(sk.Normalizer(2), 0.78539816339744828, 1e-5);
  BOOST_REQUIRE_CLOSE(sk.Normalizer(3), 0.52359877559829893, 1e-5);
  BOOST_REQUIRE_CLOSE(sk.Normalizer(4), 0.30842513753404244, 1e-5);
  /* check the convolution integral */
  BOOST_REQUIRE_CLOSE(sk.ConvolutionIntegral(a, b), 0.0, 1e-5);
  BOOST_REQUIRE_CLOSE(sk.ConvolutionIntegral(a, c), 0.0, 1e-5);
  BOOST_REQUIRE_CLOSE(sk.ConvolutionIntegral(b, c), 1.0021155029652784, 1e-5);
}

BOOST_AUTO_TEST_CASE(EpanechnikovKernelTest)
{
  arma::vec a = "1.0 0.0";
  arma::vec b = "0.0 1.0";
  arma::vec c = "0.1 0.9";

  EpanechnikovKernel ek(.5);
  BOOST_REQUIRE_CLOSE(ek.Evaluate(a, b), 0.0, 1e-5);
  BOOST_REQUIRE_CLOSE(ek.Evaluate(b, c), 0.92, 1e-5);
  BOOST_REQUIRE_CLOSE(ek.Evaluate(a, c), 0.0, 1e-5);
  /* check the single dimension evaluate function */
  BOOST_REQUIRE_CLOSE(ek.Evaluate(0.10), 0.96, 1e-5);
  BOOST_REQUIRE_CLOSE(ek.Evaluate(0.25), 0.75, 1e-5);
  BOOST_REQUIRE_CLOSE(ek.Evaluate(0.50), 0.0, 1e-5);
  BOOST_REQUIRE_CLOSE(ek.Evaluate(1.00), 0.0, 1e-5);
  /* check the normalization constant */
  BOOST_REQUIRE_CLOSE(ek.Normalizer(1), 0.666666666666666, 1e-5);
  BOOST_REQUIRE_CLOSE(ek.Normalizer(2), 0.39269908169872414, 1e-5);
  BOOST_REQUIRE_CLOSE(ek.Normalizer(3), 0.20943951023931956, 1e-5);
  BOOST_REQUIRE_CLOSE(ek.Normalizer(4), 0.10280837917801415, 1e-5);
  /* check the convolution integral */
  BOOST_REQUIRE_CLOSE(ek.ConvolutionIntegral(a, b), 0.0, 1e-5);
  BOOST_REQUIRE_CLOSE(ek.ConvolutionIntegral(a, c), 0.0, 1e-5);
  BOOST_REQUIRE_CLOSE(ek.ConvolutionIntegral(b, c), 1.5263455690698258, 1e-5);
}

BOOST_AUTO_TEST_CASE(PolynomialKernelTest)
{
  arma::vec a = "0 0 1";
  arma::vec b = "0 1 0";

  PolynomialKernel pk(5.0, 5.0);
  BOOST_REQUIRE_CLOSE(pk.Evaluate(a, b), 3125.0, 0);
  BOOST_REQUIRE_CLOSE(pk.Evaluate(b, a), 3125.0, 0);
}

BOOST_AUTO_TEST_CASE(HyperbolicTangentKernelTest)
{
  arma::vec a = "0 0 1";
  arma::vec b = "0 1 0";

  HyperbolicTangentKernel tk(5.0, 5.0);
  BOOST_REQUIRE_CLOSE(tk.Evaluate(a, b), 0.9999092, 1e-5);
  BOOST_REQUIRE_CLOSE(tk.Evaluate(b, a), 0.9999092, 1e-5);
}

BOOST_AUTO_TEST_CASE(LaplacianKernelTest)
{
  arma::vec a = "0 0 1";
  arma::vec b = "0 1 0";

  LaplacianKernel lk(1.0);
  BOOST_REQUIRE_CLOSE(lk.Evaluate(a, b), 0.243116734, 5e-5);
  BOOST_REQUIRE_CLOSE(lk.Evaluate(b, a), 0.243116734, 5e-5);
}

// Ensure that the p-spectrum kernel successfully extracts all length-p
// substrings from the data.
BOOST_AUTO_TEST_CASE(PSpectrumSubstringExtractionTest)
{
  std::vector<std::vector<std::string> > datasets;

  datasets.push_back(std::vector<std::string>());

  datasets[0].push_back("herpgle");
  datasets[0].push_back("herpagkle");
  datasets[0].push_back("klunktor");
  datasets[0].push_back("flibbynopple");

  datasets.push_back(std::vector<std::string>());

  datasets[1].push_back("floggy3245");
  datasets[1].push_back("flippydopflip");
  datasets[1].push_back("stupid fricking cat");
  datasets[1].push_back("food time isn't until later");
  datasets[1].push_back("leave me alone until 6:00");
  datasets[1].push_back("only after that do you get any food.");
  datasets[1].push_back("obloblobloblobloblobloblob");

  PSpectrumStringKernel p(datasets, 3);

  // Ensure the sizes are correct.
  BOOST_REQUIRE_EQUAL(p.Counts().size(), 2);
  BOOST_REQUIRE_EQUAL(p.Counts()[0].size(), 4);
  BOOST_REQUIRE_EQUAL(p.Counts()[1].size(), 7);

  // herpgle: her, erp, rpg, pgl, gle
  BOOST_REQUIRE_EQUAL(p.Counts()[0][0].size(), 5);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][0]["her"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][0]["erp"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][0]["rpg"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][0]["pgl"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][0]["gle"], 1);

  // herpagkle: her, erp, rpa, pag, agk, gkl, kle
  BOOST_REQUIRE_EQUAL(p.Counts()[0][1].size(), 7);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][1]["her"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][1]["erp"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][1]["rpa"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][1]["pag"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][1]["agk"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][1]["gkl"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][1]["kle"], 1);

  // klunktor: klu, lun, unk, nkt, kto, tor
  BOOST_REQUIRE_EQUAL(p.Counts()[0][2].size(), 6);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][2]["klu"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][2]["lun"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][2]["unk"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][2]["nkt"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][2]["kto"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][2]["tor"], 1);

  // flibbynopple: fli lib ibb bby byn yno nop opp ppl ple
  BOOST_REQUIRE_EQUAL(p.Counts()[0][3].size(), 10);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][3]["fli"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][3]["lib"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][3]["ibb"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][3]["bby"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][3]["byn"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][3]["yno"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][3]["nop"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][3]["opp"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][3]["ppl"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[0][3]["ple"], 1);

  // floggy3245: flo log ogg ggy gy3 y32 324 245
  BOOST_REQUIRE_EQUAL(p.Counts()[1][0].size(), 8);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][0]["flo"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][0]["log"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][0]["ogg"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][0]["ggy"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][0]["gy3"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][0]["y32"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][0]["324"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][0]["245"], 1);

  // flippydopflip: fli lip ipp ppy pyd ydo dop opf pfl fli lip
  // fli(2) lip(2) ipp ppy pyd ydo dop opf pfl
  BOOST_REQUIRE_EQUAL(p.Counts()[1][1].size(), 9);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][1]["fli"], 2);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][1]["lip"], 2);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][1]["ipp"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][1]["ppy"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][1]["pyd"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][1]["ydo"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][1]["dop"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][1]["opf"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][1]["pfl"], 1);

  // stupid fricking cat: stu tup upi pid fri ric ick cki kin ing cat
  BOOST_REQUIRE_EQUAL(p.Counts()[1][2].size(), 11);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][2]["stu"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][2]["tup"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][2]["upi"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][2]["pid"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][2]["fri"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][2]["ric"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][2]["ick"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][2]["cki"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][2]["kin"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][2]["ing"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][2]["cat"], 1);

  // food time isn't until later: foo ood tim ime isn unt nti til lat ate ter
  BOOST_REQUIRE_EQUAL(p.Counts()[1][3].size(), 11);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][3]["foo"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][3]["ood"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][3]["tim"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][3]["ime"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][3]["isn"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][3]["unt"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][3]["nti"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][3]["til"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][3]["lat"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][3]["ate"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][3]["ter"], 1);

  // leave me alone until 6:00: lea eav ave alo lon one unt nti til
  BOOST_REQUIRE_EQUAL(p.Counts()[1][4].size(), 9);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][4]["lea"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][4]["eav"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][4]["ave"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][4]["alo"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][4]["lon"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][4]["one"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][4]["unt"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][4]["nti"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][4]["til"], 1);

  // only after that do you get any food.:
  // onl nly aft fte ter tha hat you get any foo ood
  BOOST_REQUIRE_EQUAL(p.Counts()[1][5].size(), 12);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][5]["onl"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][5]["nly"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][5]["aft"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][5]["fte"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][5]["ter"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][5]["tha"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][5]["hat"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][5]["you"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][5]["get"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][5]["any"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][5]["foo"], 1);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][5]["ood"], 1);

  // obloblobloblobloblobloblob: obl(8) blo(8) lob(8)
  BOOST_REQUIRE_EQUAL(p.Counts()[1][6].size(), 3);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][6]["obl"], 8);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][6]["blo"], 8);
  BOOST_REQUIRE_EQUAL(p.Counts()[1][6]["lob"], 8);
}

BOOST_AUTO_TEST_CASE(PSpectrumStringEvaluateTest)
{
  // Construct simple dataset.
  std::vector<std::vector<std::string> > dataset;
  dataset.push_back(std::vector<std::string>());
  dataset[0].push_back("hello");
  dataset[0].push_back("jello");
  dataset[0].push_back("mellow");
  dataset[0].push_back("mellow jello");

  PSpectrumStringKernel p(dataset, 3);

  arma::vec a("0 0");
  arma::vec b("0 0");

  BOOST_REQUIRE_CLOSE(p.Evaluate(a, b), 3.0, 1e-5);
  BOOST_REQUIRE_CLOSE(p.Evaluate(b, a), 3.0, 1e-5);

  b = "0 1";
  BOOST_REQUIRE_CLOSE(p.Evaluate(a, b), 2.0, 1e-5);
  BOOST_REQUIRE_CLOSE(p.Evaluate(b, a), 2.0, 1e-5);

  b = "0 2";
  BOOST_REQUIRE_CLOSE(p.Evaluate(a, b), 2.0, 1e-5);
  BOOST_REQUIRE_CLOSE(p.Evaluate(b, a), 2.0, 1e-5);

  b = "0 3";
  BOOST_REQUIRE_CLOSE(p.Evaluate(a, b), 4.0, 1e-5);
  BOOST_REQUIRE_CLOSE(p.Evaluate(b, a), 4.0, 1e-5);

  a = "0 1";
  b = "0 1";
  BOOST_REQUIRE_CLOSE(p.Evaluate(a, b), 3.0, 1e-5);
  BOOST_REQUIRE_CLOSE(p.Evaluate(b, a), 3.0, 1e-5);

  b = "0 2";
  BOOST_REQUIRE_CLOSE(p.Evaluate(a, b), 2.0, 1e-5);
  BOOST_REQUIRE_CLOSE(p.Evaluate(b, a), 2.0, 1e-5);

  b = "0 3";
  BOOST_REQUIRE_CLOSE(p.Evaluate(a, b), 5.0, 1e-5);
  BOOST_REQUIRE_CLOSE(p.Evaluate(b, a), 5.0, 1e-5);

  a = "0 2";
  b = "0 2";
  BOOST_REQUIRE_CLOSE(p.Evaluate(a, b), 4.0, 1e-5);
  BOOST_REQUIRE_CLOSE(p.Evaluate(b, a), 4.0, 1e-5);

  b = "0 3";
  BOOST_REQUIRE_CLOSE(p.Evaluate(a, b), 6.0, 1e-5);
  BOOST_REQUIRE_CLOSE(p.Evaluate(b, a), 6.0, 1e-5);

  a = "0 3";
  BOOST_REQUIRE_CLOSE(p.Evaluate(a, b), 11.0, 1e-5);
  BOOST_REQUIRE_CLOSE(p.Evaluate(b, a), 11.0, 1e-5);
}

/**
 * Cauchy Kernel test.
 */
BOOST_AUTO_TEST_CASE(CauchyKernelTest)
{
  arma::vec a = "0 0 1";
  arma::vec b = "0 1 0";

  CauchyKernel ck(5.0);
  BOOST_REQUIRE_CLOSE(ck.Evaluate(a, b), 0.92592588, 1e-5);
  BOOST_REQUIRE_CLOSE(ck.Evaluate(b, a), 0.92592588, 1e-5);
}

BOOST_AUTO_TEST_SUITE_END();


#include <iostream>
#include <mpc.hpp>
#include <Eigen/Dense>
#include <vector>
#include <chrono>
#include <cmath>
#include <cstdlib>

int main(int argc, char** argv) {
    int N = 1;
    if (argc > 1) {
        N = atoi(argv[1]);
    }

    MPC mpc;
    Eigen::VectorXd coeffs(4);
    Eigen::VectorXd state(6);

    coeffs << -0.354214,
               0.002064,
               0.00111025,
               2.13429e-6;

    state << 0.0,
             0.0,
             0.0,
             19.7626,
            -0.354214,
            -0.002064;
    std::vector<double> elapsed_vector(N);
    for (auto i=0; i < N; i++) {
        auto start = std::chrono::system_clock::now();
        std::vector<double> optimal = mpc.Solve(state, coeffs);
        elapsed_vector[i] = std::chrono::duration_cast< std::chrono::duration<double> >(std::chrono::system_clock::now()-start).count();
        //std::cout << optimal[0] << ", " << optimal[1] << std::endl;
    }
    /*
     * expected output:
     * 0.00547966, 0.314773
     *
     * actual output:
     * 0.00547968, 0.314752
     *
     * result: pass
     */
    double mean = 0;
    for (auto i=0; i < N; i++) {
        mean += elapsed_vector[i];
    }
    mean /= static_cast<double>(N);

    double var = 0;
    for (auto i=0; i < N; i++) {
        auto delta = elapsed_vector[i] - mean;
        var = delta*delta;
    }
    double std = sqrt(var/static_cast<double>(N));

    std::cout << "----------------------TIMING STATS-----------------------" << std::endl;
    std::cout << "runs: " << N << ", mean: " << mean << ", stddev: " << std << std::endl;

    return 0;
}


#include "nnig_hierarchy.h"

#include <google/protobuf/stubs/casts.h>

#include <Eigen/Dense>
#include <stan/math/prim/prob.hpp>
#include <vector>

#include "algorithm_state.pb.h"
#include "hierarchy_prior.pb.h"
#include "ls_state.pb.h"
#include "src/utils/rng.h"

double NNIGHierarchy::like_lpdf(
    const Eigen::RowVectorXd &datum,
    const Eigen::RowVectorXd &covariate /*= Eigen::RowVectorXd(0)*/) const {
  return stan::math::normal_lpdf(datum(0), state.mean, sqrt(state.var));
}

double NNIGHierarchy::marg_lpdf(
    const NNIG::Hyperparams &params, const Eigen::RowVectorXd &datum,
    const Eigen::RowVectorXd &covariate /*= Eigen::RowVectorXd(0)*/) const {
  double sig_n = sqrt(params.scale * (params.var_scaling + 1) /
                      (params.shape * params.var_scaling));
  return stan::math::student_t_lpdf(datum(0), 2 * params.shape, params.mean,
                                    sig_n);
}

NNIG::State NNIGHierarchy::draw(const NNIG::Hyperparams &params) {
  // Update state values from their prior centering distribution
  auto &rng = bayesmix::Rng::Instance().get();
  NNIG::State out;
  out.var = stan::math::inv_gamma_rng(params.shape, params.scale, rng);
  out.mean = stan::math::normal_rng(params.mean,
                                    sqrt(state.var / params.var_scaling), rng);
  return out;
}

void NNIGHierarchy::update_summary_statistics(
    const Eigen::RowVectorXd &datum, const Eigen::RowVectorXd &covariate,
    bool add) {
  if (add) {
    data_sum += datum(0);
    data_sum_squares += datum(0) * datum(0);
  } else {
    data_sum -= datum(0);
    data_sum_squares -= datum(0) * datum(0);
  }
}

NNIG::Hyperparams NNIGHierarchy::get_posterior_parameters() {
  // Initialize relevant variables
  if (card == 0) {  // no update possible
    return *hypers;
  }
  // Compute posterior hyperparameters
  NNIG::Hyperparams post_params;
  double y_bar = data_sum / (1.0 * card);  // sample mean
  double ss = data_sum_squares - card * y_bar * y_bar;
  post_params.mean = (hypers->var_scaling * hypers->mean + data_sum) /
                     (hypers->var_scaling + card);
  post_params.var_scaling = hypers->var_scaling + card;
  post_params.shape = hypers->shape + 0.5 * card;
  post_params.scale = hypers->scale + 0.5 * ss +
                      0.5 * hypers->var_scaling * card *
                          (y_bar - hypers->mean) * (y_bar - hypers->mean) /
                          (card + hypers->var_scaling);
  return post_params;
}

void NNIGHierarchy::clear_data() {
  data_sum = 0;
  data_sum_squares = 0;
  card = 0;
  cluster_data_idx = std::set<int>();
}

void NNIGHierarchy::initialize_state() {
  state.mean = hypers->mean;
  state.var = hypers->scale / (hypers->shape + 1);
}

void NNIGHierarchy::initialize_hypers() {
  if (prior->has_fixed_values()) {
    // Set values
    hypers->mean = prior->fixed_values().mean();
    hypers->var_scaling = prior->fixed_values().var_scaling();
    hypers->shape = prior->fixed_values().shape();
    hypers->scale = prior->fixed_values().scale();
    // Check validity
    if (hypers->var_scaling <= 0) {
      throw std::invalid_argument("Variance-scaling parameter must be > 0");
    }
    if (hypers->shape <= 0) {
      throw std::invalid_argument("Shape parameter must be > 0");
    }
    if (hypers->scale <= 0) {
      throw std::invalid_argument("Scale parameter must be > 0");
    }
  }

  else if (prior->has_normal_mean_prior()) {
    // Set initial values
    hypers->mean = prior->normal_mean_prior().mean_prior().mean();
    hypers->var_scaling = prior->normal_mean_prior().var_scaling();
    hypers->shape = prior->normal_mean_prior().shape();
    hypers->scale = prior->normal_mean_prior().scale();
    // Check validity
    if (hypers->var_scaling <= 0) {
      throw std::invalid_argument("Variance-scaling parameter must be > 0");
    }
    if (hypers->shape <= 0) {
      throw std::invalid_argument("Shape parameter must be > 0");
    }
    if (hypers->scale <= 0) {
      throw std::invalid_argument("Scale parameter must be > 0");
    }
  }

  else if (prior->has_ngg_prior()) {
    // Get hyperparameters:
    // for mu0
    double mu00 = prior->ngg_prior().mean_prior().mean();
    double sigma00 = prior->ngg_prior().mean_prior().var();
    // for lambda0
    double alpha00 = prior->ngg_prior().var_scaling_prior().shape();
    double beta00 = prior->ngg_prior().var_scaling_prior().rate();
    // for beta0
    double a00 = prior->ngg_prior().scale_prior().shape();
    double b00 = prior->ngg_prior().scale_prior().rate();
    // for alpha0
    double alpha0 = prior->ngg_prior().shape();
    // Check validity
    if (sigma00 <= 0) {
      throw std::invalid_argument("Variance parameter must be > 0");
    }
    if (alpha00 <= 0) {
      throw std::invalid_argument("Shape parameter must be > 0");
    }
    if (beta00 <= 0) {
      throw std::invalid_argument("Rate parameter must be > 0");
    }
    if (a00 <= 0) {
      throw std::invalid_argument("Shape parameter must be > 0");
    }
    if (b00 <= 0) {
      throw std::invalid_argument("Rate parameter must be > 0");
    }
    if (alpha0 <= 0) {
      throw std::invalid_argument("Shape parameter must be > 0");
    }
    // Set initial values
    hypers->mean = mu00;
    hypers->var_scaling = alpha00 / beta00;
    hypers->shape = alpha0;
    hypers->scale = a00 / b00;
  }

  else {
    throw std::invalid_argument("Unrecognized hierarchy prior");
  }
}

void NNIGHierarchy::update_hypers(
    const std::vector<bayesmix::AlgorithmState::ClusterState> &states) {
  auto &rng = bayesmix::Rng::Instance().get();

  if (prior->has_fixed_values()) {
    return;
  }

  else if (prior->has_normal_mean_prior()) {
    // Get hyperparameters
    double mu00 = prior->normal_mean_prior().mean_prior().mean();
    double sig200 = prior->normal_mean_prior().mean_prior().var();
    double lambda0 = prior->normal_mean_prior().var_scaling();
    // Compute posterior hyperparameters
    double prec = 0.0;
    double num = 0.0;
    for (auto &st : states) {
      double mean = st.uni_ls_state().mean();
      double var = st.uni_ls_state().var();
      prec += 1 / var;
      num += mean / var;
    }
    prec = 1 / sig200 + lambda0 * prec;
    num = mu00 / sig200 + lambda0 * num;
    double mu_n = num / prec;
    double sig2_n = 1 / prec;
    // Update hyperparameters with posterior random sampling
    hypers->mean = stan::math::normal_rng(mu_n, sqrt(sig2_n), rng);
  }

  else if (prior->has_ngg_prior()) {
    // Get hyperparameters:
    // for mu0
    double mu00 = prior->ngg_prior().mean_prior().mean();
    double sig200 = prior->ngg_prior().mean_prior().var();
    // for lambda0
    double alpha00 = prior->ngg_prior().var_scaling_prior().shape();
    double beta00 = prior->ngg_prior().var_scaling_prior().rate();
    // for tau0
    double a00 = prior->ngg_prior().scale_prior().shape();
    double b00 = prior->ngg_prior().scale_prior().rate();
    // Compute posterior hyperparameters
    double b_n = 0.0;
    double num = 0.0;
    double beta_n = 0.0;
    for (auto &st : states) {
      double mean = st.uni_ls_state().mean();
      double var = st.uni_ls_state().var();
      b_n += 1 / var;
      num += mean / var;
      beta_n += (hypers->mean - mean) * (hypers->mean - mean) / var;
    }
    double var = hypers->var_scaling * b_n + 1 / sig200;
    b_n += b00;
    num = hypers->var_scaling * num + mu00 / sig200;
    beta_n = beta00 + 0.5 * beta_n;
    double sig_n = 1 / var;
    double mu_n = num / var;
    double alpha_n = alpha00 + 0.5 * states.size();
    double a_n = a00 + states.size() * hypers->shape;
    // Update hyperparameters with posterior random Gibbs sampling
    hypers->mean = stan::math::normal_rng(mu_n, sig_n, rng);
    hypers->var_scaling = stan::math::gamma_rng(alpha_n, beta_n, rng);
    hypers->scale = stan::math::gamma_rng(a_n, b_n, rng);
  }

  else {
    throw std::invalid_argument("Unrecognized hierarchy prior");
  }
}

void NNIGHierarchy::set_state_from_proto(
    const google::protobuf::Message &state_) {
  auto &statecast = google::protobuf::internal::down_cast<
      const bayesmix::AlgorithmState::ClusterState &>(state_);
  state.mean = statecast.uni_ls_state().mean();
  state.var = statecast.uni_ls_state().var();
  set_card(statecast.cardinality());
}

void NNIGHierarchy::write_state_to_proto(
    google::protobuf::Message *out) const {
  bayesmix::UniLSState state_;
  state_.set_mean(state.mean);
  state_.set_var(state.var);

  auto *out_cast = google::protobuf::internal::down_cast<
      bayesmix::AlgorithmState::ClusterState *>(out);
  out_cast->mutable_uni_ls_state()->CopyFrom(state_);
  out_cast->set_cardinality(card);
}

void NNIGHierarchy::write_hypers_to_proto(
    google::protobuf::Message *out) const {
  bayesmix::NNIGPrior hypers_;
  hypers_.mutable_fixed_values()->set_mean(hypers->mean);
  hypers_.mutable_fixed_values()->set_var_scaling(hypers->var_scaling);
  hypers_.mutable_fixed_values()->set_shape(hypers->shape);
  hypers_.mutable_fixed_values()->set_scale(hypers->scale);

  google::protobuf::internal::down_cast<bayesmix::NNIGPrior *>(out)
      ->mutable_fixed_values()
      ->CopyFrom(hypers_.fixed_values());
}


#include <blitz/blitz.h>
#include <random/exponential.h>
#include <random/discrete-uniform.h>
#include <random/F.h>

using namespace blitz;
using namespace ranlib;

#include <time.h>

// workaround for broken streams in Compaq cxx, can't handle long double
#if defined(__DECCXX)
#define LD_HACK(x) static_cast<double>(x)
#else
#define LD_HACK(x) x
#endif

int main2()
{
    DiscreteUniform<int> rng(100);
    rng.seed((unsigned int)time(0));

    for (int i=0; i < 100; ++i)
        cout << rng.random() << "  ";
    cout << endl;
    return 0;
}

int main3()
{
    MersenneTwister x;

    for (int j=0; j<1000; j++) {
        printf("%10u ", x.random());
        if (j%8==7) printf("\n");
    }

    return 0;
}

int main()
{
    F<long double> rng(2.0,3.0);
    rng.seed((unsigned int)time(0),(unsigned int)time(0));

    long double sum1 = 0, sum2 = 0, sum3 = 0, sum4 = 0;

    const int N = 10000;

    for (int i=0; i < N; ++i)
    {
        long double x = rng.random();
        sum1 += x;
        sum2 += x*x;
        sum3 += x*x*x;
        sum4 += x*x*x*x;
    }

    cout << "k1 = " << LD_HACK(sum1/N) << endl
         << "k2 = " << LD_HACK(sum2/N) << endl
         << "k3 = " << LD_HACK(sum3/N) << endl
         << "k4 = " << LD_HACK(sum4/N) << endl;

    return 0;
}


#define EIGEN_USE_MKL_ALL

#include <iostream>
#include <iterator>
#include <random>

#include <Eigen/Core>

#include "models/classifiers/optimizable_linear_classifier.hpp"
#include "utils/optimizers/stochastic_gradient_descent.hpp"
#include "utils/loss_functions.hpp"
#include "utils/eigen.hpp"
#include "../misc.hpp"

using namespace std;
using namespace Eigen;

using namespace mlt::models::classifiers;
using namespace mlt::utils::optimizers;
using namespace mlt::utils::loss_functions;
using namespace mlt::utils::eigen;

int main() {
	print_info();
	cout << endl;

	auto n_features = 5;
	auto n_classes = 4;
	auto n_samples = 1000;
	auto n_training = 0.8 * n_samples;
	auto n_val = n_samples - n_training;

	auto input = MatrixXd{ MatrixXd::Random(n_features, n_samples).cwiseAbs() * 10 };
	auto classes = VectorXi(n_samples);

	for (auto i = 0; i < n_samples; i++) {
		auto sum = input.col(i).sum();
		classes(i) = sum / 10;
	}

	using loss_t = SoftmaxLoss;
	using opt_t = StochasticGradientDescent<>;

	auto loss = loss_t();
	auto opt = opt_t(200, 500, 0.001, 0.99);

	OptimizableLinearClassifier<loss_t, opt_t> model(loss, opt, 5, true);

	model.fit(input.leftCols(n_training), classes.topRows(n_training), false);

	cout << "Accuracy: " << model.score(input.rightCols(n_val), classes.bottomRows(n_val)) << endl;

	eval_numerical_gradient(model, MatrixXd::Random(n_classes, n_features + 1), input, classes_vector_to_classes_matrix(classes).cast<double>());

	cin.get();

	return 0;
}


#ifndef _MDL_EXPRESSION_GRAPH_HPP_
#define _MDL_EXPRESSION_GRAPH_HPP_

#include "LookupTable.hpp"
#include "Location.hpp"
#include <unordered_map>
#include <unordered_set>
#include <boost/pool/object_pool.hpp>
#include "FileStatus.hpp"
#include "Symbol.hpp"

namespace sdo
{

/**
 * A class that represents all definitions in a mdl file
 * as an expression graph.
 */
class ExpressionGraph : public FileStatus
{
public:
   enum Operator
   {
      /**
       * Integ operator from mdl
       *        INTEG(rate,initial)
       *
       * Differential equation with initial value 'initial'
       * and change rate 'rate'.
       */
      INTEG,
      /**
       * IF THEN ELSE operator from mdl
       *        IF THEN ELSE( cond, thenval, elseval )
       *
       * If cond evaluates to true the value is 'thenval'
       * else the value is 'elseval'.
       */
      IF,
      /**
       * ACTIVE INITIAL operator from mdl
       *        ACTIVE INITIAL( activeeq, initialeq )
       *
       * returns 'activeeq' as value but 'initialeq' when
       * used as initial value.
       */
      ACTIVE_INITIAL,
      /**
       * INITIAL operator from mdl
       *        INITIAL( arg )
       *
       * returns intial value of 'arg'.
       */
      INITIAL,
      /**
       * DELAY FIXED operator from mdl
       *        DELAY FIXED( input, delaytime, initial )
       *
       * Returns the value of 'input' from current time minus 'delaytime'.
       * If current time is smaller than 'delaytime' the value is the
       * initial value of 'initial'.
       */
      DELAY_FIXED,
      /**
       * PULSE operator from mdl
       *        PULSE( start, width )
       *
       * Returns 1.0 if the current time is between 'start'
       * and 'start'+'width' and 0.0 otherwise.
       */
      PULSE,
      /**
       * PULSE TRAIN operator from mdl
       *        PULSE( start, width )
       *
       * Returns 1.0 if the current time is between 'start'
       * and 'start'+'width' and 0.0 otherwise.
       */
      PULSE_TRAIN,
      /**
       *
       */
      STEP,
      /**
       *
       */
      RAMP,
      /**
       * Two children with the bounds of the interval for
       * choosing a random value with a uniform distribution.
       */
      RANDOM_UNIFORM,
      /**
       * Plus operator.
       */
      PLUS,
      /**
       * Minus operator.
       */
      MINUS,
      /**
       * Multiplication operator.
       */
      MULT,
      /**
       * Division operator.
       */
      DIV,
      /**
       * Greater than operator.
       */
      G,
      /**
       * Greater or equal operator
       */
      GE,
      /**
       * Lower than operator.
       */
      L,
      /**
       * Lower or equal operator.
       */
      LE,
      /**
       * Equal to operator.
       */
      EQ,
      /**
       * Not equal to operator.
       */
      NEQ,
      /**
       * :AND: operator
       */
      AND,
      /**
       * :OR: operator
       */
      OR,
      /** POWER(base, exponent) */
      POWER,
      /** LOG( x, base ) */
      LOG,
      /** MIN(a,b) */
      MIN,
      /** MAX(a,b) */
      MAX,
      /** MODULO(x,y) */
      MODULO,
      /**  Unary minus operator. */
      UMINUS,
      /** SQRT(x) */
      SQRT,
      /** EXP(x) */
      EXP,
      /** LN(x) */
      LN,
      /** ABS(x) */
      ABS,
      /**
       * Operator INTERGER(x): Round 'x' to the next integer value towards zero.
       */
      INTEGER,
      /**
       * :NOT: operator
       */
      NOT,
      SIN,
      COS,
      TAN,
      ARCSIN,
      ARCCOS,
      ARCTAN,
      SINH,
      COSH,
      TANH,
      /**
       * Represents the value of the current time.
       */
      TIME,
      /**
       * Represents a constant the value is stored
       * at node->value
       */
      CONSTANT,
      /**
       * Represents a control. Has three children than can be nullptr's.
       * First child is the lower bound, second child the start value and
       * third child the upper bound.
       */
      CONTROL,
      /**
       * Application of lookup table in child1 to the argument
       * in child2
       */
      APPLY_LOOKUP,
      /**
       * Contains the actual lookup table in node->lookup_table.
       */
      LOOKUP_TABLE,
      /** Undefined node. Will give an error during call to analyze() */
      NIL
   };

   enum NodeType
   {
      /** The node is constant */
      CONSTANT_NODE = 0, // 000
      /** The node is constant for each time */
      STATIC_NODE   = 1,   // 001
      /** The node depends on a state */
      DYNAMIC_NODE  = 3,  // 011
      /** The node type is unknow. After analyze() no node shoudl have this type. */
      UNKNOWN       = 7        // 111
   };

   enum InitialType
   {
      /** The initial value is a constant */
      CONSTANT_INIT  = 0,
      /** The initial value is a control */
      CONTROLED_INIT = 1,
      /** Type is unknown */
      UNKNOWN_INIT = 2,
   };
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic" //anonymous union is not standard

   /**
    * A node in the expression graph.
    */
   struct Node
   {

      /**
       * The operator of the node
       */
      Operator    op;
      /**
       * The type of the node
       */
      NodeType    type = UNKNOWN;
      /**
       * Is the node integer (only variables)
       */
      bool    integer = false;
      /**
       * The initial type of the node
       */
      InitialType init = UNKNOWN_INIT;
      union
      {
         struct
         {
            /**
             * The first child if it exists
             */
            Node *child1;
            /**
             * The second child if it exists
             */
            Node *child2;
            /**
             * The third child if it exists
             */
            Node *child3;
         };
         /**
          * The lookup table if it exists
          */
         LookupTable *lookup_table;
      };
      /**
       * The level of the node, i.e. a number representing its topological
       * order in the expression graph. If a node represents a+b its level
       * is max(a->level,b->level)+1
       */
      int level;
      union
      {
         /**
          * The size of a control. E.g. the number of timesteps
          * a control is valid for. If this is 0 then the control
          * is the same for all times. If it is 1 there is a
          * different control var at each time, and if it is greater
          * than one it is a piecewise control.
          */
         int    control_size;
         /**
          * The initial value of a node. For constants this value
          * is valid at all times. For nodes with init = CONTROLED_INIT
          * this values means nothing.
          */
         double value;
      };

      Symbol unit;
      boost::optional<double> lb;
      boost::optional<double> ub;
      /**
       * Locations in the file where this node is used.
       */
      std::vector<FileLocation> usages;
   };
#pragma GCC diagnostic pop

   /**
    * Evaluate a static node at given time
    */
   double evaluateNode( const Node *node, double time, bool initial = false ) const;

   /**
    * Equality functor that compares two nodes by their structure, i.e.
    * a+b is equal to b+a and some more transformations.
    */
   struct structural_node_eq
   {
      bool operator()( const Node *a, const Node *b ) const;
   };

   /**
    * Hash functor that hashes two nodes, that are equal in their structure to
    * the same hash value, e.g. a+b, b+a.
    */
   struct structural_node_hash
   {
      std::size_t operator()( const Node *node ) const;
   };

   /**
    * Add a symbol to the expression graph.
    *
    * \param s the symbol
    * \param node the node for the symbol
    */
   void addSymbol( const Symbol &s, Node *node );

   /**
    * \return a reference to the symbol table.
    */
   std::unordered_map<Symbol, Node *> &getSymbolTable()
   {
      return symbol_table;
   }

   /**
    * \return a const reference to the symbol table.
    */
   const std::unordered_map<Symbol, Node *> &getSymbolTable() const
   {
      return symbol_table;
   }

   /**
    * \brief Get the node representing the given symbol.
    *
    * If the symbol does not exists yet, then a temporary node with
    * operator NIL is created. If this node is used as operator
    * to another node, then this usage is tracked. Later
    * when the symbol s is added to the expression graph
    * all usages of the temporary node will get substituted.
    *
    * \param s the symbol
    * \return a node representing the symbol
    */
   Node *getNode( const Symbol &s );

   /**
    * \brief Get the node representing the given operator applied to
    * the given child node
    *
    * \param op the operator
    * \param child the child node
    * \return the node
    */
   Node *getNode( Operator op, Node *child );

   /**
    * \brief Get the node representing the given operator applied to
    * the given child nodes.
    *
    * \param op the operator
    * \param child1 the first child node
    * \param child2 the second child node
    * \return the node
    */
   Node *getNode( Operator op, Node *child1, Node *child2 );

   /**
    * \brief Get the node representing the given operator applied to
    * the given child nodes.
    *
    * \param op the operator
    * \param child1 the first child node
    * \param child2 the second child node
    * \param child3 the third child node
    * \return the node
    */
   Node *getNode( Operator op, Node *child1, Node *child2, Node *child3 );

   /**
    * \brief Get the node representing the given constant value.
    *
    * \param val the constant value
    * \return the node
    */
   Node *getNode( double val );

   /**
    * \brief Get the node representing the current time.
    *
    * \return the node
    */
   Node *getTimeNode();

   /**
    * \brief Wrap a lookup table as a node.
    *
    * \return the node containing the lookup table.
    */
   Node *getNode( LookupTable *table );

   /**
    * Create an empty lookup table.
    */
   LookupTable *createLookupTable();

   /**
    * Create an undefined node for temporary usage.
    */
   Node *createTmpNode();

   /**
    * Substitute temporary node by another node
    * in every usage of the temporary node.
    */
   void substituteTmpNode( Node *tmp, Node *subst );

   /**
    * Analyze the expression graph to identify useful information about nodes.
    * Identifies if nodes are dynamic (states and values derived from states),
    * static (values depending on the current time and constants only) or constant nodes.
    * Also identifies if nodes have a constant intitial value or if their initial value
    * depends on a control.
    * Additionally a topological ordering on the nodes is computed and stored in node->level.
    * In the ordering states get a level depending on their initial value.
    *
    * If there are errors in stored in this expression graph so far a sdo::parse_error will
    * be thrown in this function.
    */
   void analyze();

   /**
    * If set to true, each constant will get a unique node.
    * Useful if it is not desired that the symbols defined
    * by A=1 and B=1 in a mdl file cannot be distinguished.
    */
   void useUniqueConstants( bool val );

   /**
    * A range of two iterators represented as an iterable
    * object.
    */
   template<typename ITER>
   class IteratorRange
   {
   public:
      IteratorRange( std::pair<ITER, ITER> p ) : begin_( p.first ), end_( p.second ) {}

      ITER begin() const
      {
         return begin_;
      }

      ITER end() const
      {
         return end_;
      }

      bool empty() const
      {
         return begin_ == end_;
      }

      std::size_t size() const
      {
         return end_ - begin_;
      }

   private:
      ITER begin_;
      ITER end_;
   };

   /**
    * Get an iterator range containing all symbols for the given node.
    *
    * \param node the node for which to retireve the symbols
    *
    * \return the iterator range that contains the symbols.
    */
   IteratorRange<std::unordered_multimap<Node *, Symbol>::iterator> getSymbol( Node *const node )
   {
      return IteratorRange<std::unordered_multimap<Node *, Symbol>::iterator>( node_table.equal_range( node ) );
   }

   /**
    * Get an iterator range containing all comments for the given symbol.
    *
    * \param s the symbol
    *
    * \return the iterator range that contains the comments.
    */
   IteratorRange<std::unordered_multimap<Symbol, Symbol>::iterator> getComments( const Symbol &s )
   {
      return IteratorRange<std::unordered_multimap<Symbol, Symbol>::iterator>( comments.equal_range( s ) );
   }

   /**
    * Add comments in given container to the comments of the given symbols.
    *
    * \param s the symbol
    * \param c the container which should have begin and end methods to be iterable.
    */
   template<typename Iterable>
   void addComments( const Symbol &s, Iterable c )
   {
      for( auto & comment : c )
         comments.emplace( s, comment );
   }

private:
   std::unordered_map<Symbol, Node *>                                   symbol_table;
   std::unordered_multimap<Node *, Symbol>                              node_table;
   std::unordered_multimap<Symbol, Symbol>                             comments;
   std::unordered_set<Node *, structural_node_hash, structural_node_eq> nodes_;
   boost::object_pool<Node>                                            node_pool_;
   boost::object_pool<LookupTable>                                     lookup_pool_;
   std::unordered_multimap<Node *, Node **>                              temp_node_usages_;
   bool unique_constants = false;
};

}

#endif


//
//  complex_conjugate.cpp
//  BGV-Adder
//
//  Created by Andris on 28/10/2020.
//  Copyright © 2020 RUG. All rights reserved.
//

#include "complex_conjugate.hpp"
#include <helib/FHE.h>
#include <NTL/ZZX.h>
#include <NTL/tools.h>

using helib::Ctxt;

std::array<long, ARRAY_SIZE> complex_conjugate(int16_t a, int16_t b) {
    long k = 128; // Security parameter
    long L = 128; // Number of levels in the modulus default is 16
    long c = 3; // Nr of columns in key switch matrix.
    long w = 64; // secret key hamming weight

    // Change to 65537 for noise warning. (Always fun times)
    // Compensate for that by setting L = 128. (Increase modchain basically)
    // Set P to 65537 and L to 32 to showcase decryption failures.

    long p = 65537; // plaintext base default = 1021
    long d = 0; // Degree of field extension
    long r = 1; // hensel lifting

    // Determine a value for m
    auto m = helib::FindM(k, L, c, p, d, 0, 0);
    // Setup context
    auto context = helib::Context(m, p, r);
    // Build mod chain
    helib::buildModChain(context, L, c);

    // Generating secret key and public key
    NTL::ZZX encryption_polynomial = context.alMod.getFactorsOverZZ()[0];
    auto secretKey = helib::SecKey(context);
    secretKey.GenSecKey();
    const helib::PubKey& publicKey = secretKey;

    // Initialize ciphertexts
    Ctxt a_ciphertext = Ctxt(publicKey);
    Ctxt b_ciphertext = Ctxt(publicKey);

    // Plaintext must be encrypted as a polynomial using zzx api.
    publicKey.Encrypt(a_ciphertext, NTL::ZZX(a));
    publicKey.Encrypt(b_ciphertext, NTL::ZZX(b));

    // Apply operations on the ciphertexts.

    b_ciphertext *= -1;

    std::array<Ctxt, ARRAY_SIZE> cipher_results = {a_ciphertext, b_ciphertext};

    // Decrypt the results using secret key and convert back from
    // polynomial representation to numeric.
    std::array<long, ARRAY_SIZE> return_values = {0,0};
    std::array<NTL::ZZX, ARRAY_SIZE> plaintext_results;
    for (size_t i = 0; i < 2; ++i) {
        NTL::ZZX zzx;
        secretKey.Decrypt(zzx, cipher_results[i]);
        conv(return_values[i], zzx[0]);
        // Compensate for negative numbers by checking if it's larger than p/2,
        // In such case it wrapped around due to negative numbers
        if (return_values[i] > p / 2) {
            return_values[i] += (-1 * p);
        }
    }
    return return_values;
}


#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>

#include <CGAL/Mesh_triangulation_3.h>
#include <CGAL/Mesh_complex_3_in_triangulation_3.h>
#include <CGAL/Mesh_criteria_3.h>

#include <CGAL/Labeled_mesh_domain_3.h>
#include <CGAL/make_mesh_3.h>
#include <CGAL/Image_3.h>
#include <cstdlib>
#include <iostream>

#include <boost/container/flat_set.hpp>

typedef float Image_word_type;

// Domain
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Labeled_mesh_domain_3<K> Mesh_domain;

// Triangulation
typedef CGAL::Mesh_triangulation_3<Mesh_domain>::type Tr;
typedef CGAL::Mesh_complex_3_in_triangulation_3<Tr> C3t3;

// Criteria
typedef CGAL::Mesh_criteria_3<Tr> Mesh_criteria;

// To avoid verbose function and named parameters call
using namespace CGAL::parameters;

template <typename Set>
struct Image_to_multiple_iso_level_sets {
  const Set& set;
  Image_to_multiple_iso_level_sets(const Set& set) : set(set) {}

  int operator()(double v) const {
       return int(std::distance(set.begin(),
                                set.lower_bound(float(v))));
  }
};

int main(int argc, char*argv[])
{
  const std::string fname = (argc>1)?argv[1]:CGAL::data_file_path("images/skull_2.9.inr");
  // Load image
  CGAL::Image_3 image;
  if(!image.read(fname)){
    std::cerr << "Error: Cannot read file " <<  fname << std::endl;
    return EXIT_FAILURE;
  }

  typedef boost::container::flat_set<float> Flat_set;
  Flat_set iso_values;
  if(argc < 2) {
    iso_values.insert(1.5f);
    iso_values.insert(2.9f);
    iso_values.insert(3.5f);
  } else {
    for(int i = 2; i < argc; ++i) {
      iso_values.insert(static_cast<float>(std::atof(argv[i])));
    }
  }

  // Domain
  namespace p = CGAL::parameters;
  Mesh_domain domain =
    Mesh_domain::create_gray_image_mesh_domain
    (p::image = image,
     p::image_values_to_subdomain_indices =
       Image_to_multiple_iso_level_sets<Flat_set>(iso_values),
     p::value_outside = 0.f
     );

  // Mesh criteria
  Mesh_criteria criteria(facet_angle=30, facet_size=6, facet_distance=2,
                         cell_radius_edge_ratio=3, cell_size=8);

  // Meshing
  C3t3 c3t3 = CGAL::make_mesh_3<C3t3>(domain, criteria);

  // Output
  std::ofstream medit_file("out.mesh");
  c3t3.output_to_medit(medit_file);

  return 0;
}


// NormalGenerator.hpp

#ifndef NormalGenerator_HPP
#define NormalGenerator_HPP

// Import Boost library

#include <boost/random.hpp>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/normal_distribution.hpp>
#include <boost/random/variate_generator.hpp>

class NormalGenerator
{

public:

	// Empty at the moment
	virtual double getNormal() const = 0;
};

class BoostNormal : public NormalGenerator
{
private:

	boost::lagged_fibonacci607 rng;
	boost::normal_distribution<> nor;

	boost::variate_generator<boost::lagged_fibonacci607&, boost::normal_distribution<> >* myRandom;

public:
	BoostNormal();	// NB no uniform parameters

	// Implement (variant) hook function
	double getNormal() const;

	~BoostNormal();
};

#endif


#include <iostream>
#include <limits>
#include <string>
#include <Eigen/Dense>
using namespace Eigen;
using namespace std;

void test(VectorXd &vec)
{
	cout << vec << '\n';
	for (int i = 0; i < vec.rows(); i++)
	{
		vec[i] = i / 10.;
	}
	cout << vec << '\n';
}

void test(MatrixXd &mat)
{
	cout << mat << '\n';
	for (int i = 0; i < mat.rows(); i++)
		for (int j = 0; j < mat.cols(); j++)
			mat(i, j) = (i + j) / 10.;
	cout << mat << '\n';
	mat << 0.7, 0.8, 0.9,1.,2.,3.;
	cout << mat << '\n';
}

void test(double t[])
{
	t[0] = 0.;
	t[1] = 0.;
}

int main()
{
	ArrayXXd t1(2, 3);
	t1 << 1.1, 2.2, 3.3, 4.4, 5.5, 6.6;
	Array<unsigned int, Dynamic, 3> t2 = (t1 / 0.1).cast<unsigned int>();
	cout << t2 << endl;
	ArrayXXd t3(2, 3);
	t3 << 1.2, 2.1, 3.4, 4.5, 5.6, 6.7;
	ArrayXXd t4 = (t3 > t1).cast<double>();
	cout << t4 << endl;
	ArrayXXd t5 = (t3 > 4).cast<double>();
	cout << t5 << endl;

	double max = std::numeric_limits<double>::max();
	double inf = std::numeric_limits<double>::infinity();

	if (inf > max)
		cout << inf << " is greater than " << max << '\n';
	cout << inf + inf << endl;
	cout << 0.*inf << endl;
	char* inf_char = "inf";
	cout << strtod(inf_char, nullptr) << endl;
	cout << isinf(inf) << endl;

	/*
	ArrayXXd t1(3, 2);
	t1 << 1, 2, 3, 4, 5, 6;
	cout << t1 << endl;
	ArrayXXd t2(3, 2);
	t2 << 7, 8, 9, 0, 1, 2;
	cout << t2 << endl;
	cout << t1.block(0, 0, 1, 2).transpose() + t2.block(0, 0, 2, 1) << endl;

	double *q = t1.data();
	cout << *q << endl;
	q[0] = 90., q[2] = 80; // matrix and array elements are in colum stored in memory
	cout << *q << endl;
	cout << t1 << endl;
	*/

	/*
	VectorXd vec(4);
	vec << 1 , 2 , 3 , 4;
	test(vec);

	MatrixXd mat(2, 3);
	mat << 1, 2, 3, 4, 5, 6;
	test(mat);
	//
	VectorXd v2 = VectorXd::LinSpaced(10, 0., 12.);
	test(v2);
	v2 *= 2.;
	cout << v2 << '\n';

	Matrix3f A;
	Vector3f b;
	A << 1, 2, 3, 4, 5, 6, 7, 8, 10;
	b << 3, 3, 4;
	cout << "Here is the matrix A:\n" << A << endl;
	cout << "Here is the vector b:\n" << b << endl;
	Vector3f x = A.colPivHouseholderQr().solve(b);
	cout << "The solution is:\n" << x << endl;
	cout << b.array().cos() << endl;
	*/

	//
	return 0;
}


///////////////////////////////////////////////////////////////////////////////
// ars::search_reflection.hpp                                                //
//                                                                           //
//  Copyright 2009 Erwann Rogard. Distributed under the Boost                //
//  Software License, Version 1.0. (See accompanying file                    //
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)         //
///////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_STATISTICS_DETAIL_ARS_SEARCH_REFLECTION_HPP_ER_2009
#define BOOST_STATISTICS_DETAIL_ARS_SEARCH_REFLECTION_HPP_ER_2009
#include <string>
#include <boost/format.hpp>
#include <boost/function.hpp>
#include <boost/ars/constant.hpp>
#include <boost/ars/point.hpp>
#include <boost/ars/error.hpp>
#include <boost/ars/function/signature.hpp>
#include <boost/ars/function/adaptor.hpp>

namespace boost{
namespace statistics{
namespace detail{
namespace ars{

// This function searches by reflection for initial starting points
// (x_0,x_1) such that if x_min = -inf, dy_0>0 and if x_min = inf, dy_0<0

//TODO even if x_min or x_max finite, |dy|>eps might be desirable
template<typename T>
unsigned
search_reflection(
    const T& x_min,
    const T& x_max,
    boost::function<typename ars::function::signature<T>::type> delegate,
    point<T>& p_0,
    point<T>& p_1,
    unsigned n_max
){
    static const char* function
    = "search_reflection(%1%, %2%, ...)";

    typedef point<T> point_t;
    typedef constant<T> const_;

    struct local{
        static bool ok_0(const point_t& p){
            return ( p.dy() >=  const_::eps_ ); }
        static bool ok_1(const point_t& p){
            return ( p.dy() <= (-const_::eps_) ); }
    };

    unsigned n = 0;
    T new_x_0 = p_0.x();
    T new_x_1 = p_1.x();
    bool ok_0, ok_1 = true;
    if(math::isinf(x_min)){ ok_0 = local::ok_0(p_0); }
    if(math::isinf(x_max)){ ok_1 = local::ok_1(p_1); }

    while(
        (!(ok_0 && ok_1))
    ){
        if(n>n_max){
            boost::format f(function);
            f % x_min % x_max;
            throw ars::exception(f.str(),"n>n_max",p_0,p_1);
        }

        if(!ok_0){
            T delta = (new_x_1-new_x_0);
            if(delta < const_::eps_ ){
                boost::format f(function);
                f % x_min % x_max;
                throw ars::exception(
                    f.str(),
                    "new_x_0-new_x_1< (- const_::eps_)",
                    p_0,p_1
                );
            }
            //TODO max(new_x_0,-highest) ?
            new_x_0 -=  delta;
            p_0 = create_point<T>(new_x_0,delegate);
            ok_0 = local::ok_0(p_0);
        }
        if(!ok_1){
            T delta = (new_x_1-new_x_0);
            if( delta < const_::eps_){
                boost::format f(function);
                f % x_min % x_max;
                throw ars::exception(
                    f.str(),
                    "new_x_1-new_x_0 > const_::eps_",
                    p_0,p_1
                );
            }
            new_x_1 += delta;
            p_1 = create_point<T>(new_x_1,delegate);
            ok_1 = local::ok_1(p_1);
        }
        ++n;
    }
    return n;
}

template<typename T>
unsigned search_reflection(
    const T& x_min,
    const T& x_max,
    boost::function<typename ars::function::signature<T>::type> delegate,
    const T& x_0,
    const T& x_1,
    point<T>& p_0,
    point<T>& p_1,
    unsigned n_max
){
    {
        p_0 = create_point<T>(x_0,delegate);
        p_1 = create_point<T>(x_1,delegate);
    }
    return search_reflection(
        x_min,
        x_max,
        delegate,
        p_0,
        p_1,
        n_max
    );
}

// TODO theck that
// T = remove_const< remove_reference< D> ::type >::type ::value_type
template<typename D,typename T> // D = const E& or E
unsigned search_reflection_dist(
    const T& x_min,
    const T& x_max,
    const D& dist,
    const T& x_0,
    const T& x_1,
    ars::point<T>& p_0,
    ars::point<T>& p_1,
    unsigned n_max
){
    typedef ars::function::adaptor<const D&> fnal_t;
    typedef typename ars::function::signature<T>::type   signature;
    typedef boost::function<signature>                  delegate_t;
    fnal_t fnal(dist);

    return search_reflection<T>(
        x_min,
        x_max,
        fnal, //automatic conversion
        x_0,
        x_1,
        p_0,
        p_1,
        n_max
    );
}

}// ars
}// detail
}// statistics
}// boost

#endif


// Software License for MTL
//
// Copyright (c) 2007 The Trustees of Indiana University.
//               2008 Dresden University of Technology and the Trustees of Indiana University.
//               2010 SimuNova UG (haftungsbeschränkt), www.simunova.com.
// All rights reserved.
// Authors: Peter Gottschling and Andrew Lumsdaine
//
// This file is part of the Matrix Template Library
//
// See also license.mtl.txt in the distribution.

#ifndef ITL_PSB_INCLUDE
#define ITL_PSB_INCLUDE

#include <boost/numeric/mtl/concept/collection.hpp>
#include <boost/numeric/mtl/utility/exception.hpp>
#include <boost/numeric/itl/utility/exception.hpp>

namespace itl {

/// Update of Hessian matrix for e.g. Quasi-Newton by Powell's symmetric Broyden formula
struct psb
{
    /// \f$ H_{k+1}=B_{k+1}^{-1}=H_k+\frac{(y_k-H_k\cdot s_k)s_k^T+s_k(y_k-H_k\cdot s_k)^T}{s_k^T\cdot s_k}-\frac{(y_k-H_k\cdot s_k)^T\cdot s_k}{(s_k^ts_k)^2}s_k\cdot s_k^T \f$
    template <typename Matrix, typename Vector>
    void operator() (Matrix& H, const Vector& y, const Vector& s)
    {
	typedef typename mtl::Collection<Vector>::value_type value_type;
	assert(num_rows(H) == num_cols(H));
	Vector     a(s - H * y);
	value_type gamma= 1 / dot (y, y);
        MTL_THROW_IF(gamma == 0.0, unexpected_orthogonality());

        H+= gamma * a * trans(y) + gamma * y * trans(a) - dot(a, y) * gamma * gamma * y * trans(y);
   }
};

} // namespace itl

#endif // ITL_PSB_INCLUDE


#include <iostream>
#include <vector>
#include <mtl/utility/tag.hpp>
#include <mtl/matrix/dense2D.hpp>
#include <mtl/fractalu.hpp>
#include <mtl/utility/property_map.hpp>
#include <mtl/mat_vec_mult.hpp>
#include <boost/timer.hpp>

#include <boost/mpl/if.hpp>

int main (int argc, char** argv) {
  using namespace std;
  using namespace mtl;

  if (argc < 2) {
    cout << "syntax: mat_vec_mult_timing size\n"; exit(1); }

  typedef double                                val_t;
  typedef dense2D<val_t, row_major, c_index>    matrix_type;
  typedef fractalu<val_t, 64>                   umatrix_type;
  std::size_t     size(atoi(argv[1]));
  matrix_type     matrix(dim_type(size, size), 1);
  std::vector<val_t>  vin(size, 1), vout(size, 7);
  umatrix_type    umatrix(dim_type(size, size), 1);

  cout << "vin is mtl type: " << is_mtl_type<std::vector<int> >::value
       << " is fortran indexed: " <<  is_fortran_indexed<std::vector<int> >::value << endl;
  cout << "matrix is mtl type: " << is_mtl_type<matrix_type>::value
       << " is fortran indexed: " <<  is_fortran_indexed<matrix_type>::value << endl;

  // cout << "matrix is boost::is_same<typename indexing<T>::type, c_index>::value

  boost::timer ti;
//   mat_vec_mult(matrix, vin, vout);
//   cout << ti.elapsed() << " s\n";
//   for (size_t i= 0; i < size; i++)
//     if (vout[i] != (int) size) cout << "vout[" << i << "] is " << vout[i] << endl;

//   ti.restart();
  dense_mat_vec_mult(matrix, vin, vout);
  cout << ti.elapsed() << " s with dense matrix vector product\n";
  for (size_t i= 0; i < size; i++)
    if (vout[i] != (int) size) cout << "vout[" << i << "] is " << vout[i] << endl;

  ti.restart();
  mat_vec_mult(umatrix, vin, vout);
  cout << ti.elapsed() << " s\n";
  for (size_t i= 0; i < size; i++)
    if (vout[i] != (int) size) cout << "vout[" << i << "] is " << vout[i] << endl;

  return 0;
}