#include "catch.hpp"

#include <boost/graph/graph_concepts.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/properties.hpp>

#include <arlib/details/onepass_plus_impl.hpp>
#include <arlib/graph_types.hpp>
#include <arlib/graph_utils.hpp>
#include <arlib/onepass_plus.hpp>
#include <arlib/terminators.hpp>

#include "cittastudi_graph.hpp"
#include "test_types.hpp"
#include "utils.hpp"

#include <kspwlo_ref/algorithms/kspwlo.hpp>
#include <kspwlo_ref/exploration/graph_utils.hpp>

#include <algorithm>
#include <chrono>
#include <experimental/filesystem>
#include <fstream>
#include <memory>
#include <string_view>

using namespace arlib::test;

//===----------------------------------------------------------------------===//
//                                Test cases
//===----------------------------------------------------------------------===//

TEST_CASE("OnePassLabel builds a right path back to source", "[onepassplus]") {
  using arlib::VPair;
  using namespace boost;
  auto G = arlib::read_graph_from_string<Graph>(std::string(graph_gr));
  using Label = arlib::details::OnePassLabel<Graph, Length>;

  auto s = std::make_unique<Label>(0, 0, 0, 0, 0);
  auto n1 = std::make_unique<Label>(3, 1, 1, s.get(), 1, 1);
  auto n2 = std::make_unique<Label>(5, 2, 2, n1.get(), 2, 1);
  auto n3 = std::make_unique<Label>(6, 3, 2, n2.get(), 3, 1);

  auto path = n3->get_path(G);

  REQUIRE(std::find(std::begin(path), std::end(path), edge(0, 3, G).first) !=
          std::end(path));
  REQUIRE(std::find(std::begin(path), std::end(path), edge(3, 5, G).first) !=
          std::end(path));
  REQUIRE(std::find(std::begin(path), std::end(path), edge(5, 6, G).first) !=
          std::end(path));
}

TEST_CASE("Computing distance from target", "[onepassplus]") {
  using namespace boost;
  auto G = arlib::read_graph_from_string<Graph>(std::string(graph_gr));

  Vertex target = 6;
  auto distance = arlib::details::distance_from_target<Length>(G, target);

  auto index = get(vertex_index, G);
  REQUIRE(distance[index[1]] == 6);
  REQUIRE(distance[index[2]] == 8);
  REQUIRE(distance[index[3]] == 5);
  REQUIRE(distance[index[4]] == 3);
  REQUIRE(distance[index[5]] == 2);
  REQUIRE(distance[index[6]] == 0);
}

TEST_CASE("Computing path from dijkstra_shortest_paths", "[onepassplus]") {
  using namespace boost;

  auto G = arlib::read_graph_from_string<Graph>(std::string(graph_gr));
  auto predecessor = std::vector<Vertex>(num_vertices(G), 0);
  auto vertex_id = get(vertex_index, G);
  dijkstra_shortest_paths(
      G, vertex_id[0],
      predecessor_map(make_iterator_property_map(std::begin(predecessor),
                                                 vertex_id, vertex_id[0])));
  auto weight_G = get(edge_weight, G);
  auto path = arlib::details::build_path_from_dijkstra(G, weight_G, predecessor,
                                                       0lu, 6lu);

  auto weight = get(edge_weight, path);
  REQUIRE(edge(0, 3, path).second);
  REQUIRE(weight[edge(0, 3, path).first] == 3);
  REQUIRE(edge(3, 5, path).second);
  REQUIRE(weight[edge(3, 5, path).first] == 3);
  REQUIRE(edge(5, 6, path).second);
  REQUIRE(weight[edge(5, 6, path).first] == 2);
}

TEST_CASE("onepass_plus kspwlo algorithm runs on Boost::Graph",
          "[onepassplus]") {
  auto G = arlib::read_graph_from_string<Graph>(std::string{graph_gr});
  Vertex s = 0, t = 6;
  auto predecessors = arlib::multi_predecessor_map<Vertex>{};
  arlib::onepass_plus(G, predecessors, s, t, 3, 0.5);
  auto res = arlib::to_paths(G, predecessors, s, t);

  // Create a new tmp file out of graph_gr
  namespace fs = std::experimental::filesystem;
  auto path = fs::temp_directory_path() / std::string("graph_gr_file.gr");
  auto of = std::ofstream(path.string());
  of << graph_gr;
  of.close();

  auto G_regr = std::make_unique<RoadNetwork>(path.c_str());
  auto res_regression = onepass_plus(G_regr.get(), 0, 6, 3, 0.5);

  using boost::edges;
  using boost::source;
  using boost::target;
  std::cout << "boost::graph result:\n";
  for (auto &p : res) {
    for (auto it = edges(p).first; it != edges(p).second; ++it) {
      std::cout << "(" << source(*it, p) << ", " << target(*it, p) << ") ";
    }
    std::cout << "\n";
  }

  std::cout << "regression result:\n";
  for (auto &regPath : res_regression) {
    auto es = regPath.getEdges();
    // Cleaning loops coming from dijkstra algorithm (for no reason)
    remove_self_loops(es.begin(), es.end());

    for (auto edge : es) {
      std::cout << "(" << edge.first << ", " << edge.second << ") ";
    }
    std::cout << "\n";
  }

  // Same number of paths are computed
  REQUIRE(res.size() == res_regression.size());

  // For each k-spwlo check if its edges are in a solution of the regression
  // test
  for (auto &p : res) {
    REQUIRE(one_regression_path_have_edges(res_regression, p));
  }

  using boost::edge_weight;
  using boost::get;
  REQUIRE(alternative_paths_are_dissimilar(res, get(edge_weight, G), 0.5));
}

TEST_CASE("OnePass+ times-out on large graph", "[onepassplus]") {
  using namespace boost;
  using namespace std::chrono_literals;

  auto G = arlib::read_graph_from_string<Graph>(std::string(cittastudi_gr));

  Vertex s = 0, t = 20;
  auto k = 3;
  auto theta = 0.5;
  auto predecessors = arlib::multi_predecessor_map<Vertex>{};

  REQUIRE_THROWS_AS(
      arlib::onepass_plus(G, predecessors, s, t, k, theta, arlib::timer{1us}),
      arlib::terminator_stop_error);
}


#include "sigen/extractor/extractor.h"
#include <boost/foreach.hpp>
#include <gtest/gtest.h>
#include <iostream>
using namespace std;
using namespace sigen;

static BinaryCube vectorStringToBinaryCube(const std::vector<std::string> &vs) {
  const int zdepth = 3;
  const int size_x = vs[0].size();
  const int size_y = vs.size();
  for (int i = 0; i < (int)size_y; ++i) {
    assert(size_x == (int)vs[i].size());
  }
  BinaryCube cube(size_x + 2, size_y + 2, zdepth);
  for (int y = 0; y < size_y; ++y) {
    for (int x = 0; x < size_x; ++x) {
      cube[x + 1][y + 1][1] = (vs[y][x] == '#');
    }
  }
  return cube;
}

TEST(Extractor, labeling) {
  std::vector<std::string> vs;
  vs.push_back("##.");
  vs.push_back("...");
  vs.push_back("###");
  BinaryCube cube = vectorStringToBinaryCube(vs);

  Extractor ext(cube);
  std::vector<ClusterPtr> ret = ext.Extract();
  EXPECT_EQ(2, (int)ext.components_.size());
  EXPECT_EQ(3, (int)ext.components_[0].size());
  EXPECT_EQ(2, (int)ext.components_[1].size());
  EXPECT_EQ(5, (int)ret.size());
  BOOST_FOREACH (ClusterPtr c, ret) {
    EXPECT_EQ(1, (int)c->points_.size());
  }
}
TEST(Extractor, labeling2) {
  std::vector<std::string> vs;
  vs.push_back("##.");
  vs.push_back("..#");
  vs.push_back("##.");
  BinaryCube cube = vectorStringToBinaryCube(vs);
  Extractor ext(cube);
  std::vector<ClusterPtr> ret = ext.Extract();
  EXPECT_EQ(1, (int)ext.components_.size());
  EXPECT_EQ(5, (int)ext.components_[0].size());
  EXPECT_EQ(5, (int)ret.size());
}
TEST(Extractor, labeling_with_loops) {
  std::vector<std::string> vs;
  vs.push_back(".#.");
  vs.push_back("#.#");
  vs.push_back(".#.");
  BinaryCube cube = vectorStringToBinaryCube(vs);
  Extractor ext(cube);
  std::vector<ClusterPtr> ret = ext.Extract();
  EXPECT_EQ(1, (int)ext.components_.size());
  EXPECT_EQ(4, (int)ext.components_[0].size());
}
TEST(Extractor, same_distance) {
  std::vector<std::string> vs;
  vs.push_back("##");
  vs.push_back("##");
  BinaryCube cube = vectorStringToBinaryCube(vs);
  Extractor ext(cube);
  std::vector<ClusterPtr> ret = ext.Extract();
  EXPECT_EQ(1, (int)ext.components_.size());
  EXPECT_EQ(4, (int)ext.components_[0].size());
  EXPECT_EQ(2, (int)ret.size());
  EXPECT_EQ(3, (int)ret[0]->points_.size());
  EXPECT_EQ(1, (int)ret[1]->points_.size());
}


/* ----------------------------------------------------------------------------

 * GTSAM Copyright 2010, Georgia Tech Research Corporation,
 * Atlanta, Georgia 30332-0415
 * All Rights Reserved
 * Authors: Frank Dellaert, et al. (see THANKS for the full author list)

 * See LICENSE for the license information

 * -------------------------------------------------------------------------- */

/*
 * @file    testDiscreteConditional.cpp
 * @brief   unit tests for DiscreteConditional
 * @author  Duy-Nguyen Ta
 * @author  Frank dellaert
 * @date    Feb 14, 2011
 */

#include <boost/assign/std/map.hpp>
#include <boost/assign/std/vector.hpp>
#include <boost/make_shared.hpp>
using namespace boost::assign;

#include <CppUnitLite/TestHarness.h>
#include <gtsam/discrete/DecisionTreeFactor.h>
#include <gtsam/discrete/DiscreteConditional.h>
#include <gtsam/inference/Symbol.h>

using namespace std;
using namespace gtsam;

/* ************************************************************************* */
TEST(DiscreteConditional, constructors) {
  DiscreteKey X(0, 2), Y(2, 3), Z(1, 2);  // watch ordering !

  DiscreteConditional expected(X | Y = "1/1 2/3 1/4");
  EXPECT_LONGS_EQUAL(0, *(expected.beginFrontals()));
  EXPECT_LONGS_EQUAL(2, *(expected.beginParents()));
  EXPECT(expected.endParents() == expected.end());
  EXPECT(expected.endFrontals() == expected.beginParents());

  DecisionTreeFactor f1(X & Y, "0.5 0.4 0.2 0.5 0.6 0.8");
  DiscreteConditional actual1(1, f1);
  EXPECT(assert_equal(expected, actual1, 1e-9));

  DecisionTreeFactor f2(
      X & Y & Z, "0.2 0.5 0.3 0.6 0.4 0.7 0.25 0.55 0.35 0.65 0.45 0.75");
  DiscreteConditional actual2(1, f2);
  EXPECT(assert_equal(f2 / *f2.sum(1), *actual2.toFactor(), 1e-9));
}

/* ************************************************************************* */
TEST(DiscreteConditional, constructors_alt_interface) {
  DiscreteKey X(0, 2), Y(2, 3), Z(1, 2);  // watch ordering !

  Signature::Table table;
  Signature::Row r1, r2, r3;
  r1 += 1.0, 1.0;
  r2 += 2.0, 3.0;
  r3 += 1.0, 4.0;
  table += r1, r2, r3;
  DiscreteConditional actual1(X, {Y}, table);
  DecisionTreeFactor f1(X & Y, "0.5 0.4 0.2 0.5 0.6 0.8");
  DiscreteConditional expected1(1, f1);
  EXPECT(assert_equal(expected1, actual1, 1e-9));

  DecisionTreeFactor f2(
      X & Y & Z, "0.2 0.5 0.3 0.6 0.4 0.7 0.25 0.55 0.35 0.65 0.45 0.75");
  DiscreteConditional actual2(1, f2);
  EXPECT(assert_equal(f2 / *f2.sum(1), *actual2.toFactor(), 1e-9));
}

/* ************************************************************************* */
TEST(DiscreteConditional, constructors2) {
  // Declare keys and ordering
  DiscreteKey C(0, 2), B(1, 2);
  DecisionTreeFactor actual(C & B, "0.8 0.75 0.2 0.25");
  Signature signature((C | B) = "4/1 3/1");
  DiscreteConditional expected(signature);
  DecisionTreeFactor::shared_ptr expectedFactor = expected.toFactor();
  EXPECT(assert_equal(*expectedFactor, actual));
}

/* ************************************************************************* */
TEST(DiscreteConditional, constructors3) {
  // Declare keys and ordering
  DiscreteKey C(0, 2), B(1, 2), A(2, 2);
  DecisionTreeFactor actual(C & B & A, "0.8 0.5 0.5 0.2 0.2 0.5 0.5 0.8");
  Signature signature((C | B, A) = "4/1 1/1 1/1 1/4");
  DiscreteConditional expected(signature);
  DecisionTreeFactor::shared_ptr expectedFactor = expected.toFactor();
  EXPECT(assert_equal(*expectedFactor, actual));
}

/* ************************************************************************* */
TEST(DiscreteConditional, Combine) {
  DiscreteKey A(0, 2), B(1, 2);
  vector<DiscreteConditional::shared_ptr> c;
  c.push_back(boost::make_shared<DiscreteConditional>(A | B = "1/2 2/1"));
  c.push_back(boost::make_shared<DiscreteConditional>(B % "1/2"));
  DecisionTreeFactor factor(A & B, "0.111111 0.444444 0.222222 0.222222");
  DiscreteConditional expected(2, factor);
  auto actual = DiscreteConditional::Combine(c.begin(), c.end());
  EXPECT(assert_equal(expected, *actual, 1e-5));
}

/* ************************************************************************* */
TEST(DiscreteConditional, likelihood) {
  DiscreteKey X(0, 2), Y(1, 3);
  DiscreteConditional conditional(X | Y = "2/8 4/6 5/5");

  auto actual0 = conditional.likelihood(0);
  DecisionTreeFactor expected0(Y, "0.2 0.4 0.5");
  EXPECT(assert_equal(expected0, *actual0, 1e-9));

  auto actual1 = conditional.likelihood(1);
  DecisionTreeFactor expected1(Y, "0.8 0.6 0.5");
  EXPECT(assert_equal(expected1, *actual1, 1e-9));
}

/* ************************************************************************* */
// Check markdown representation looks as expected, no parents.
TEST(DiscreteConditional, markdown_prior) {
  DiscreteKey A(Symbol('x', 1), 3);
  DiscreteConditional conditional(A % "1/2/2");
  string expected =
      " *P(x1)*:\n\n"
      "|x1|value|\n"
      "|:-:|:-:|\n"
      "|0|0.2|\n"
      "|1|0.4|\n"
      "|2|0.4|\n";
  string actual = conditional.markdown();
  EXPECT(actual == expected);
}

/* ************************************************************************* */
// Check markdown representation looks as expected, multivalued.
TEST(DiscreteConditional, markdown_multivalued) {
  DiscreteKey A(Symbol('a', 1), 3), B(Symbol('b', 1), 5);
  DiscreteConditional conditional(
      A | B = "2/88/10 2/20/78 33/33/34 33/33/34 95/2/3");
  string expected =
      " *P(a1|b1)*:\n\n"
      "|b1|0|1|2|\n"
      "|:-:|:-:|:-:|:-:|\n"
      "|0|0.02|0.88|0.1|\n"
      "|1|0.02|0.2|0.78|\n"
      "|2|0.33|0.33|0.34|\n"
      "|3|0.33|0.33|0.34|\n"
      "|4|0.95|0.02|0.03|\n";
  string actual = conditional.markdown();
  EXPECT(actual == expected);
}

/* ************************************************************************* */
// Check markdown representation looks as expected, two parents.
TEST(DiscreteConditional, markdown) {
  DiscreteKey A(2, 2), B(1, 2), C(0, 3);
  DiscreteConditional conditional(A, {B, C}, "0/1 1/3  1/1 3/1  0/1 1/0");
  string expected =
      " *P(A|B,C)*:\n\n"
      "|B|C|0|1|\n"
      "|:-:|:-:|:-:|:-:|\n"
      "|0|0|0|1|\n"
      "|0|1|0.25|0.75|\n"
      "|0|2|0.5|0.5|\n"
      "|1|0|0.75|0.25|\n"
      "|1|1|0|1|\n"
      "|1|2|1|0|\n";
  vector<string> names{"C", "B", "A"};
  auto formatter = [names](Key key) { return names[key]; };
  string actual = conditional.markdown(formatter);
  EXPECT(actual == expected);
}

/* ************************************************************************* */
int main() {
  TestResult tr;
  return TestRegistry::runAllTests(tr);
}
/* ************************************************************************* */


/*
 * test_cluterize.cpp
 *
 *  Created on: 28 mai 2016
 *      Author: boubad
 */
#include <boost/test/unit_test.hpp>
/////////////////////////
#include <clusterize.h>
/////////////////////////////
#include <mytestfixture.h>
////////////////////////////////////
#include <global_defs.h>
////////////////////
using namespace info;
using namespace std;
///////////////////////////////////
using MyFixture = MyTestFixture<IDTYPE, INTTYPE, STRINGTYPE, WEIGHTYPE>;
/////////////////////////////////////
using IndivType = typename MyFixture::IndivType;
using DataMap = typename MyFixture::DataMap;
using IndivTypePtr = typename MyFixture::IndivTypePtr;
using SourceType = typename MyFixture::SourceType;
////////////////////////////////
using ClusterizeKMeansType = ClusterizeKMeans<IDTYPE, STRINGTYPE, DISTANCETYPE>;
/////////////////////////////////////
BOOST_FIXTURE_TEST_SUITE(ClusterizeTestSuite,MyFixture)
BOOST_AUTO_TEST_CASE(testMortalClusterize) {
	SourceType *pProvider = this->mortal_source();
	BOOST_CHECK(pProvider != nullptr);
	//
	ClusterizeKMeansType oMan;
	size_t nbClusters = 5;
	size_t nbIters = 20;
	size_t nIters = oMan.process(pProvider, nbClusters, nbIters);
	BOOST_CHECK(nIters > 0);
	/*
	STRINGTYPE ss;
	oMan.to_string(ss);
	BOOST_TEST_MESSAGE("");
	BOOST_TEST_MESSAGE("CLUSTERIZE KMEANS MORTAL");
	BOOST_TEST_MESSAGE("Nb iterations: " << nIters);
	BOOST_TEST_MESSAGE("Nb clusters: " << oMan.clusters_count() << "/" << nbClusters);
	BOOST_TEST_MESSAGE(ss);
	*/
} //testMortalClusterize
BOOST_AUTO_TEST_CASE(testConsoClusterize) {
	SourceType *pProvider = this->conso_source();
	BOOST_CHECK(pProvider != nullptr);
	//
	ClusterizeKMeansType oMan;
	size_t nbClusters = 3;
	size_t nbIters = 20;
	size_t nIters = oMan.process(pProvider, nbClusters, nbIters);
	BOOST_CHECK(nIters > 0);
	/*
	STRINGTYPE ss;
	oMan.to_string(ss);
	BOOST_TEST_MESSAGE("");
	BOOST_TEST_MESSAGE("CLUSTERIZE KMEANS CONSO");
	BOOST_TEST_MESSAGE("Nb iterations: " << nIters);
	BOOST_TEST_MESSAGE("Nb clusters: " << oMan.clusters_count() << "/" << nbClusters);
	BOOST_TEST_MESSAGE(ss);
	*/
} //testConsoClusterize
/*
BOOST_AUTO_TEST_CASE(testTestClusterize) {
	SourceType *pProvider = this->test_source();
	BOOST_CHECK(pProvider != nullptr);
	//
	ClusterizeKMeansType oMan;
	size_t nbClusters = 5;
	size_t nbIters = 20;
	size_t nIters = oMan.process(pProvider, nbClusters, nbIters);
	BOOST_CHECK(nIters > 0);
	STRINGTYPE ss;
	oMan.to_string(ss);
	BOOST_TEST_MESSAGE("");
	BOOST_TEST_MESSAGE("CLUSTERIZE KMEANS TEST");
	BOOST_TEST_MESSAGE("Nb iterations: " << nIters);
	BOOST_TEST_MESSAGE("Nb clusters: " << oMan.clusters_count() << "/" << nbClusters);
	BOOST_TEST_MESSAGE(ss);
} //testTestClusterize
*/
BOOST_AUTO_TEST_SUITE_END();


#include "derivative.hpp"

#include <geometrix/utility/scope_timer.ipp>

#include <boost/units/systems/si.hpp>

#include <gtest/gtest.h>

#include "google_test_fixture.hpp"

//////////////////////////////////////////////////////////////////////////
//!
//! Tests
//!

TEST(derivative_grammarTestSuite, testOptimizeAdd)
{
	derivative_expr<boost::proto::terminal<x_var>::type> x;
	derivative_expr<derivative_constant_terminal<0>> zero;
	derivative_expr<derivative_constant_terminal<1>> one;

	auto result0 = derivative_detail::optimize()(one * x + zero);
	static_assert(std::is_same<decltype(x), decltype(result0)>::value, "optimize not working");
}

TEST(derivative_grammarTestSuite, testOptimizeSubtract)
{
	derivative_expr<boost::proto::terminal<x_var>::type> x;
	derivative_expr<derivative_constant_terminal<0>> zero;

	auto result0 = derivative_detail::optimize()(x - zero);
	static_assert(std::is_same<decltype(x), decltype(result0)>::value, "optimize not working");
}

TEST(derivative_grammarTestSuite, testOptimizeDivide)
{
	derivative_expr<boost::proto::terminal<x_var>::type> x;
	derivative_expr<derivative_constant_terminal<1>> one;
	derivative_detail::optimize opt;

	auto result0 = opt(x / one);
	static_assert(std::is_same<decltype(x), decltype(result0)>::value, "optimize not working");

	derivative_expr<derivative_constant_terminal<0>> zero;
	auto result1 = opt(zero / x);
	static_assert(std::is_same<decltype(zero), decltype(result1)>::value, "optimize not working");

	auto result2 = opt((x-x) / (x*x));
}

TEST(derivative_grammarTestSuite, testOptimizeProductDerivative)
{
	derivative_expr<boost::proto::terminal<x_var>::type> x;
	derivative_expr<derivative_constant_terminal<1>> one;
	derivative_detail::optimize opt;

	const derivative_expr<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<x_var>, 0> > & a1 = x;
	const derivative_expr<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<x_var>, 0> > & a2 = x;
	const derivative_expr<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<derivative_constant<1> >, 0> > & da1 = one;
	const derivative_expr<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<derivative_constant<1> >, 0> > & da2 = one;

	auto result0 = opt(da1 * a2 + a1 * da2);
	auto result1 = opt(opt(one * x) + opt(x * one));
	static_assert(std::is_same<decltype(result1), decltype(result0)>::value, "optimize not working");
}

TEST(derivative_grammarTestSuite, testOptimizeQuotient)
{
	derivative_expr<boost::proto::terminal<x_var>::type> x;
	derivative_expr<derivative_constant_terminal<1>> one;
	derivative_detail::optimize opt;

	const derivative_expr<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<x_var>, 0> > & a1 = x;
	auto a2 = x * x;
	const derivative_expr<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<derivative_constant<1> >, 0> > & da1 = one;
	auto da2 = x + x;
	auto result0 = opt((da1 * a2 - da2 * a1) / (a2 * a2));
	auto dresult = result0(2.0);
}

TEST(derivative_grammarTestSuite, testDerivativeGrammar)
{
	derivative_expr<boost::proto::terminal<x_var>::type> x;
	derivative_grammar derivative;

	auto d0 = derivative(x);
	double result = d0(0);
	EXPECT_EQ(1.0, result);

	result = derivative(boost::proto::as_expr(3))(0);
	EXPECT_EQ(0.0, result);

	auto d = derivative(x + 3);
	result = d(5.0);
	EXPECT_EQ(1.0, result);

	auto d2 = derivative(x+x);
	result = d2(6.0);
	EXPECT_EQ(2.0, result);

 	auto d3 = derivative(x*x);
 	result = d3(6);
	EXPECT_EQ(12.0, result);

	auto d4 = derivative(x / (x*x));
	result = d4(2.0);
	EXPECT_EQ(-0.25, result);
}

TEST(derivative_grammarTestSuite, testConstantTimesDegree1Poly_ReturnsConstant)
{
	derivative_expr<boost::proto::terminal<x_var>::type> x;
	derivative_grammar derivative;

	double constant = 4.0;
	auto d0 = derivative(constant * x + 1.0);
	double result = d0(0);

	EXPECT_EQ(constant, result);
}

TEST(derivative_grammarTestSuite, testConstantTimesDegree2Poly_ReturnsConstantTimes2TimesVariableEval)
{
	derivative_expr<boost::proto::terminal<x_var>::type> x;
	derivative_grammar derivative;

	double constant = 4.0;
	auto d0 = derivative(constant * x * x + 1.0);
	double result = d0(2.0);

	EXPECT_EQ(2.0 * 2.0 * constant, result);
}

TEST(derivative_grammarTestSuite, testConstantTimesDegree3Poly_Returns68)
{
	derivative_expr<boost::proto::terminal<x_var>::type> x;
	derivative_grammar derivative;

	double constant = 4.0;
	auto d0 = derivative(constant * x * x * x + constant * x * x + constant * x + 1.0);

	//! Derivative should be c*3x^2 + c*2x + c = 12.0 * x^2 + 8.0 * x + 4.0 = 12.0 * 4.0 + 8.0 * 2.0 + 4.0 = 48.0 + 16.0 + 4.0 = 68.0
	double result = d0(2.0);

	EXPECT_EQ(68.0, result);
}

TEST(derivative_grammarTestSuite, testPowDerivative)
{
	derivative_expr<boost::proto::terminal<x_var>::type> x;
	derivative_grammar derivative;
	pow_fun<2> fn;
	const derivative_expr<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<x_var>, 0>> arg;
	derivative_expr<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<derivative_constant<1>>, 0>> darg;

	auto d = chain_rule()(fn, arg, darg);
	auto d0 = derivative(pow<2>(x));

	double result = d0(2.0);

	EXPECT_EQ(4.0, result);
}

TEST(derivative_grammarTestSuite, testPowDerivative4)
{
	derivative_expr<boost::proto::terminal<x_var>::type> x;
	derivative_grammar derivative;

	auto d0 = derivative(pow<4>(x));

	double result = d0(2.0);

	EXPECT_EQ(32.0, result);
}

TEST(derivative_grammarTestSuite, testPowDerivativePolynomialDegree2)
{
	derivative_expr<boost::proto::terminal<x_var>::type> x;
	derivative_grammar derivative;

	//! = 2 * pow<1>(x*x + 2 * x) * 2x + 2
	//! = 2 * (x*x + 2*x) * 2x + 2 | x = 2
	//! = 2 * (4 + 4) * (4 + 2)
	//! = 2 * 8 * 6
	//! = 2 * 48
	//! = 96
	auto d0 = derivative(pow<2>(x*x + 2.0*x));

	double result = d0(2.0);

	EXPECT_EQ(96.0, result);
}

TEST(derivative_grammarTestSuite, testSinDerivative)
{
	derivative_expr<boost::proto::terminal<x_var>::type> x;
	derivative_grammar derivative;

	auto d0 = derivative(sin(x));

	double result = d0(2.0);

	EXPECT_EQ(std::cos(2.), result);
}

TEST(derivative_grammarTestSuite, testCosDerivative)
{
	derivative_expr<boost::proto::terminal<x_var>::type> x;
	derivative_grammar derivative;

	auto d0 = derivative(cos(x));

	double result = d0(2.0);

	EXPECT_EQ(-1.0 * std::sin(2.), result);
}

TEST(derivative_grammarTestSuite, testExpDerivative)
{
	derivative_expr<boost::proto::terminal<x_var>::type> x;
	derivative_grammar derivative;

	auto d0 = derivative(exp(x));

	double result = d0(2.0);

	EXPECT_EQ(std::exp(2.), result);
}

TEST(derivative_grammarTestSuite, testLogDerivative)
{
	derivative_expr<boost::proto::terminal<x_var>::type> x;
	derivative_grammar derivative;

	auto d0 = derivative(log(pow<3>(x)));

	double result = d0(4.0);

	EXPECT_EQ(0.75, result);
}

TEST(derivative_grammarTestSuite, testSqrtDerivative)
{
	derivative_expr<boost::proto::terminal<x_var>::type> x;
	derivative_grammar derivative;

	auto d0 = derivative(sqrt(pow<3>(x)));

	double result = d0(4.0);

	EXPECT_EQ(3.0, result);
}

TEST(derivative_grammarTestSuite, testSqrtDerivativeWithOptimization)
{
	derivative_expr<boost::proto::terminal<x_var>::type> x;
	derivative_expr<boost::proto::terminal<derivative_constant<0>>::type> zero;
	derivative_grammar derivative;

	auto d0 = derivative(sqrt(pow<3>(x - zero)));
	double result = d0(4.0);

	EXPECT_EQ(3.0, result);
}

TEST(derivative_grammarTestSuite, testMulipleDerivative)
{
	derivative_expr<boost::proto::terminal<x_var>::type> x;
	derivative_grammar derivative;

	//! 24 * x
	//! = 24 * 4
	//! = 96
	auto d0 = derivative(derivative(derivative(x*x*x*x)));

	auto result = d0(4.0);

	EXPECT_EQ(96.0, result);
}

TEST(derivative_grammarTestSuite, testUnitsSingleDerivative)
{
	derivative_expr<boost::proto::terminal<x_var>::type> x;
	derivative_grammar derivative;

	auto d0 = derivative(x);

	auto result = d0(4.0 * boost::units::si::meters);

	EXPECT_TRUE(1.0 == result);
}

TEST(derivative_grammarTestSuite, testUnitsPolynomialDegree2Derivative)
{
	derivative_expr<boost::proto::terminal<x_var>::type> x;
	derivative_grammar derivative;

	auto d0 = derivative(x*x);

	auto result = d0(4.0 * boost::units::si::meters);

	EXPECT_TRUE(8.0 * boost::units::si::meters == result);
}

TEST(derivative_grammarTestSuite, testUnitsPolynomialDegree3Derivative)
{
	derivative_expr<boost::proto::terminal<x_var>::type> x;
	derivative_grammar derivative;

	auto d0 = derivative(x*x*x);

	auto result = d0(4.0 * boost::units::si::meters);

	EXPECT_TRUE(48.0 * boost::units::si::square_meters == result);
}

TEST(derivative_grammarTestSuite, testUnitsPolynomialDegree3Derivative2)
{
	derivative_expr<boost::proto::terminal<x_var>::type> x;
	derivative_grammar derivative;

	auto d0 = derivative(derivative(x*x*x));

	auto result = d0(4.0 * boost::units::si::meters);

	EXPECT_TRUE(24.0 * boost::units::si::meters == result);
}

TEST(derivative_grammarTestSuite, testComplexDerivative)
{
	derivative_expr<boost::proto::terminal<x_var>::type> x;
	derivative_grammar derivative;

	auto d0 = derivative( sqrt( pow<2>(x) * sin(x) ) );

	//! d0 == (x*x * cos(x) + 2 * x * sin(x)) / (2 * sqrt(x*x*sin(x)))
	auto result = d0(4.0);

	double v = 4.0;
	double expected = (v*v * std::cos(v) + 2.0 * v * std::sin(v)) / (2.0 * std::sqrt(std::abs(v*v*std::sin(v))));

	EXPECT_EQ(expected, result);
}

TEST(derivative_grammarTestSuite, testUnitsPolynomialDegree3Derivative2WithUnitConstant)
{
	derivative_expr<boost::proto::terminal<x_var>::type> x;
	derivative_grammar derivative;

	auto d0 = derivative(derivative(x*x*x + 2.0 * boost::units::pow<3>(boost::units::si::meters)));

	auto result = d0(4.0 * boost::units::si::meters);

	EXPECT_TRUE(24.0 * boost::units::si::meters == result);
}

//! Timings...

//! Test the above mock registrations.
TEST(derivative_grammarTestSuite, time_grammar_evaluation)
{
	using namespace ::testing;

	derivative_expr<boost::proto::terminal<x_var>::type> x;
	derivative_grammar derivative;
#ifdef NDEBUG
	std::size_t nRuns = 100000000;
#else
	std::size_t nRuns = 100;
#endif
	std::vector<double> results(nRuns,0), results1(nRuns,0);

	{
		//auto d0(x + pow<2>(x) + pow<3>(x) + pow<4>(x) + pow<5>(x) + pow<6>(x) + pow<7>(x) + pow<8>(x) + pow<9>(x));
		//auto d1(x + pow<2>(x) * (1.0 + x + pow<2>(x) + pow<3>(x) + pow<4>(x) + pow<5>(x) + pow<6>(x) + pow<7>(x)));
		//auto d2(x + pow<2>(x) * (1.0 + x + pow<2>(x) * (1.0 + x + pow<2>(x) + pow<3>(x) + pow<4>(x) + pow<5>(x))));
		//auto d3(x + pow<2>(x) * (1.0 + x + pow<2>(x) * (1.0 + x + pow<2>(x) * (1.0 + x + pow<2>(x) + pow<3>(x)))));
		auto d4 = derivative(x + pow<2>(x) * (1.0 + x + pow<2>(x) * (1.0 + x + pow<2>(x) * (1.0 + x + pow<2>(x) * (1.0 + x)))));

		auto d = derivative(x + pow<2>(x) + pow<3>(x) + pow<4>(x) + pow<5>(x) + pow<6>(x) + pow<7>(x) + pow<8>(x) + pow<9>(x));
		boost::proto::display_expr(d, std::cout);

		boost::proto::display_expr(d4, std::cout);
		GEOMETRIX_MEASURE_SCOPE_TIME("eval_grammar");
		for (int i = 0; i < nRuns; ++i)
			results[i] = d(7.7);
	}

	{
		GEOMETRIX_MEASURE_SCOPE_TIME("eval_byhand");
		double v = 7.7;
		for (int i = 0; i < nRuns; ++i)
			results1[i] = 1.0 + 2.0 * v + 3.0 * pow(v, 2) + 4.0 * pow(v, 3) + 5.0 * pow(v, 4) + 6.0 * pow(v, 5) + 7.0 * pow(v, 6) + 8.0 * pow(v, 7) + 9.0 * pow(v, 8);
	}

	EXPECT_EQ(results, results1);
}

void StandardExceptionTranslator(const std::exception& e)
{
	BOOST_TEST_MESSAGE(e.what());
}

boost::unit_test::test_suite* init_unit_test_suite(int, char*[])
{
	boost::unit_test::unit_test_log.set_threshold_level(boost::unit_test::log_messages);
	boost::unit_test::unit_test_monitor.register_exception_translator<std::exception>(&StandardExceptionTranslator);
	boost::unit_test::framework::master_test_suite().p_name.value = "Geometrix Testing Framework";
	return 0;
}


/*!
@file
Internal header to break cyclic dependencies.

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

#ifndef BOOST_HANA_GROUP_DETAIL_GROUP_FWD_HPP
#define BOOST_HANA_GROUP_DETAIL_GROUP_FWD_HPP

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

namespace boost { namespace hana {
    namespace group_detail { namespace operators { struct enable { }; }}

    //! @ingroup group-typeclasses
    //! The `Group` type class is used for `Monoid`s where all objects have
    //! an inverse w.r.t. the `Monoid`'s binary operation.
    //!
    //! The method names refer to the group of numbers under addition.
    //!
    //! ### Requires
    //! `Monoid`
    //!
    //! ### Laws
    //! For all objects `x` of a `Group` `G`, the following laws must be
    //! satisfied:
    //! @code
    //!     plus(x, negate(x)) == zero<G> // right inverse
    //!     plus(negate(x), x) == zero<G> // left inverse
    //! @endcode
    struct Group {
        BOOST_HANA_BINARY_TYPECLASS(Group);
        template <typename G1, typename G2>
        struct negate_mcd;
        template <typename G1, typename  G2>
        struct minus_mcd;
        struct laws;
        using operators = group_detail::operators::enable;
    };

    //! Subtract two elements of a group.
    //! @relates Group
    //!
    //! Specifically, this performs the `Monoid` operation on the first
    //! argument and on the inverse of the second argument, thus being
    //! equivalent to:
    //! @code
    //!     minus(x, y) == plus(x, negate(y))
    //! @endcode
    //!
    //! ### Example
    //! @snippet example/group/minus.cpp main
    BOOST_HANA_CONSTEXPR_LAMBDA auto minus = [](auto x, auto y) {
        return Group::instance<
            datatype_t<decltype(x)>, datatype_t<decltype(y)>
        >::minus_impl(x, y);
    };

    //! Return the inverse of an element of a group.
    //! @relates Group
    //!
    //! Since `Group` is a binary type class and `negate` is a unary method,
    //! `negate(x)` is dispatched to the type class instance for `G` and `G`,
    //! i.e. `Group::instance<G, G>`, where `G` is the data type of `x`.
    //!
    //! ### Example
    //! @snippet example/group/negate.cpp main
    BOOST_HANA_CONSTEXPR_LAMBDA auto negate = [](auto x) {
        return Group::instance<
            datatype_t<decltype(x)>, datatype_t<decltype(x)>
        >::negate_impl(x);
    };

    namespace group_detail { namespace operators {
        //! Equivalent to `minus`.
        //! @relates boost::hana::Group
        template <typename C1, typename C2>
        constexpr auto operator-(C1 c1, C2 c2)
        { return minus(c1, c2); }

        //! Equivalent to `negate`.
        //! @relates boost::hana::Group
        template <typename C1>
        constexpr auto operator-(C1 c1)
        { return negate(c1); }
    }}
}} // end namespace boost::hana

#endif // !BOOST_HANA_GROUP_DETAIL_GROUP_FWD_HPP


// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2022 Scipp contributors (https://github.com/scipp)
#include <gtest/gtest.h>

#include <Eigen/Geometry>

#include "scipp/common/constants.h"
#include "scipp/core/element/geometric_operations.h"
#include "scipp/units/unit.h"

#include "fix_typed_test_suite_warnings.h"

using namespace scipp;
using namespace scipp::core::element;

TEST(ElementPositionTest, unit_in) {
  const units::Unit m(units::m);
  const units::Unit s(units::s); // Not supported
  EXPECT_THROW(geometry::position(s, m, m), except::UnitError);
  EXPECT_THROW(geometry::position(m, s, m), except::UnitError);
  EXPECT_THROW(geometry::position(m, m, s), except::UnitError);
  EXPECT_NO_THROW(geometry::position(s, s, s));
  EXPECT_NO_THROW(geometry::position(m, m, m));
}

TEST(ElementPositionTest, unit_out) {
  const units::Unit m(units::m);
  EXPECT_EQ(geometry::position(m, m, m), m);
}

TEST(ElementPositionTest, zip_position_values) {
  EXPECT_EQ((Eigen::Vector3d(1, 2, 3)), geometry::position(1.0, 2.0, 3.0));
}

TEST(ElementPositionNTest, unzip_position) {
  Eigen::Vector3d a{1.0, 2.0, 3.0};
  EXPECT_EQ(geometry::x(a), a[0]);
  EXPECT_EQ(geometry::y(a), a[1]);
  EXPECT_EQ(geometry::z(a), a[2]);
  EXPECT_EQ(geometry::x(units::m), units::m);
  EXPECT_EQ(geometry::y(units::m), units::m);
  EXPECT_EQ(geometry::z(units::m), units::m);
}


#ifndef OTHELLO_AI_RANDOMPLAYER_HPP
#define OTHELLO_AI_RANDOMPLAYER_HPP

//Boost.random is used instead of the standard library <random>
//as mingw generates the same sequence of random numbers on my machine
//(see https://stackoverflow.com/questions/18880654/why-do-i-get-the-same-sequence-for-every-run-with-stdrandom-device-with-mingw)

//Boost headers:
#include <boost/random.hpp>
//Othello headers:
#include <othello/game/IPlayer.hpp>

namespace othello
{

    namespace ai
    {

        ////////////////////////////////////////////////////////////////
        /// \class RandomPlayer
        ///
        /// \brief An AI player that simply plays a random possible
        ///        move
        ///
        ////////////////////////////////////////////////////////////////
        class RandomPlayer : public game::IPlayer
        {
            private:

                ////////////////////////////////////////////////////////////////
                /// \brief A mersenne_twister_engine for generating random
                ///        numbers
                ///
                ////////////////////////////////////////////////////////////////
                boost::random::mt19937 randomNumberGenerator;

            public:

                ////////////////////////////////////////////////////////////////
                /// \brief Class constructor
                ///
                ////////////////////////////////////////////////////////////////
                explicit RandomPlayer(const unsigned int& seed)
                    : randomNumberGenerator(seed) {}

                ////////////////////////////////////////////////////////////////
                /// \brief Function that is called when the player should make a
                ///        move. This function simply picks a random move in
                ///        possibleMoves
                ///
                /// \param possibleMoves A vector of the possible moves
                ///
                /// \return A const pointer to a const move that will be played
                ///         by the current player. This pointer must point to a
                ///         move in possibleMoves
                ///
                ////////////////////////////////////////////////////////////////
                const game::Move* makeMove(const game::Game&, const uint8_t&,
                        const std::vector<game::Move>& possibleMoves) override
                {
                    //Create a uniform integer distribution
                    boost::random::uniform_int_distribution<> distribution(0, possibleMoves.size() - 1);
                    //Return a random move
                    return &possibleMoves.at(distribution(randomNumberGenerator));
                }

        };

    }

}

#endif //OTHELLO_AI_RANDOMPLAYER_HPP


//
// Licensed to Green Energy Corp (www.greenenergycorp.com) under one or
// more contributor license agreements. See the NOTICE file distributed
// with this work for additional information regarding copyright ownership.
// Green Energy Corp licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except in
// compliance with the License.  You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// This file was forked on 01/01/2013 by Automatak, LLC and modifications
// have been made to this file. Automatak, LLC licenses these modifications to
// you under the terms of the License.
//

#include <boost/test/unit_test.hpp>

#include <opendnp3/Types.h>
#include <limits>

using namespace std;
using namespace opendnp3;

#undef max
#undef min

template<class T>
void TestType(T min, T max, size_t aBytes)
{
	BOOST_CHECK_EQUAL(sizeof(T), aBytes);

	T val = min;
	T limit = numeric_limits<T>::min();
	BOOST_CHECK_EQUAL(val, limit);

	val = max;
	limit = numeric_limits<T>::max();
	BOOST_CHECK_EQUAL(val, limit);
}

BOOST_AUTO_TEST_SUITE(TestTypes)

BOOST_AUTO_TEST_CASE( UByte )
{
	TestType<uint8_t>(0, 255, 1);
}

BOOST_AUTO_TEST_CASE( Int16 )
{
	TestType<int16_t>(-32768, 32767, 2);
}

BOOST_AUTO_TEST_CASE( UInt16 )
{
	TestType<uint16_t>(0, 65535, 2);
}

BOOST_AUTO_TEST_CASE( Int32 )
{
	TestType<int32_t>(0x80000000, 2147483647L, 4);
}

BOOST_AUTO_TEST_CASE( UInt32 )
{
	TestType<uint32_t>(0, 4294967295UL, 4);
}

BOOST_AUTO_TEST_CASE( Int64)
{
	TestType<int64_t>(0x8000000000000000LL, 9223372036854775807LL, 8);
}

BOOST_AUTO_TEST_CASE(Float)
{
	BOOST_REQUIRE_EQUAL(4, sizeof(float));
}

BOOST_AUTO_TEST_CASE(Double)
{
	BOOST_REQUIRE_EQUAL(8, sizeof(double));
}

BOOST_AUTO_TEST_SUITE_END()