/**
 * \file libs/numeric/ublasx/hold.cpp
 *
 * \brief Test the \c hold operation.
 *
 * Copyright (c) 2010, Marco Guazzone
 *
 * Distributed under the Boost Software License, Version 1.0. (See
 * accompwhiching file LICENSE_1_0.txt or copy at
 * http://www.boost.org/LICENSE_1_0.txt)
 *
 * \author Marco Guazzone, marco.guazzone@gmail.com
 */

#include <boost/numeric/ublas/fwd.hpp>
#include <boost/numeric/ublas/io.hpp>
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/matrix_expression.hpp>
#include <boost/numeric/ublas/vector.hpp>
#include <boost/numeric/ublas/vector_expression.hpp>
#include <boost/numeric/ublasx/detail/debug.hpp>
#include <boost/numeric/ublasx/operation/hold.hpp>
#include <boost/numeric/ublasx/tags.hpp>
#include <cstddef>
#include <functional>
#include <iostream>
#include "libs/numeric/ublasx/test/utils.hpp"

static const double tol = 1.0e-5;

namespace ublas = ::boost::numeric::ublas;
namespace ublasx = ::boost::numeric::ublasx;

BOOST_UBLASX_TEST_DEF( test_vector_container )
{
	BOOST_UBLASX_DEBUG_TRACE( "TEST Vector Container" );

	typedef double value_type;
	typedef ublas::vector<value_type> vector_type;
	typedef ublas::zero_vector<value_type> zero_vector_type;
	typedef ublas::vector<bool> out_vector_type;

	const std::size_t n = 5;

	vector_type v(n);

	v(0) = 0.0;
	v(1) = 0.108929;
	v(2) = 0.0;
	v(3) = 0.0;
	v(4) = 1.023787;

	zero_vector_type z(n);

	value_type val(0);
	out_vector_type expect;
	out_vector_type res;

	// hold(z)
	BOOST_UBLASX_DEBUG_TRACE( "NOTE: Expect to fail cause ublas::vector_assign assume the value type is a floating point" );
	expect = out_vector_type(n, false);
	res = ublasx::hold(z);
	BOOST_UBLASX_DEBUG_TRACE( "hold(" << z << ") = " << res << " ==> " << expect );
	BOOST_UBLASX_TEST_CHECK_VECTOR_EQ( res, expect, n );

	BOOST_UBLASX_DEBUG_TRACE( "HERE.2" );
	// hold(v)
	expect = out_vector_type(n);
	for (std::size_t i = 0; i < n; ++i)
	{
		expect(i) = v(i) != 0;
	}
	res = ublasx::hold(v);
	BOOST_UBLASX_DEBUG_TRACE( "hold(" << v << ") = " << res << " ==> " << expect );
	BOOST_UBLASX_TEST_CHECK_VECTOR_EQ( res, expect, n );

	BOOST_UBLASX_DEBUG_TRACE( "HERE.3" );
	// hold(v, > .5)
	val = 0.5;
	expect = out_vector_type(n);
	for (std::size_t i = 0; i < n; ++i)
	{
		expect(i) = v(i) > val;
	}
	res = ublasx::hold(v, ::std::bind2nd(::std::greater<value_type>(), val));
	BOOST_UBLASX_DEBUG_TRACE( "hold(" << v << ", > " << val << ") = " << res << " ==> " << expect );
	BOOST_UBLASX_TEST_CHECK_VECTOR_EQ( res, expect, n );
}

BOOST_UBLASX_TEST_DEF( test_vector_expression )
{
	BOOST_UBLASX_DEBUG_TRACE( "TEST Vector Expression" );

	typedef double value_type;
	typedef ublas::vector<value_type> vector_type;
	typedef ublas::vector<bool> out_vector_type;

	const std::size_t n = 5;

	vector_type v(n);

	v(0) = 0.0;
	v(1) = 0.108929;
	v(2) = 0.0;
	v(3) = 0.0;
	v(4) = 1.023787;

	value_type val(0);
	out_vector_type expect;
	out_vector_type res;

	// hold(-v)
	expect = out_vector_type(n);
	for (std::size_t i = 0; i < n; ++i)
	{
		expect(i) = (-v(i)) != 0;
	}
	res = ublasx::hold(-v);
	BOOST_UBLASX_DEBUG_TRACE( "hold(" << -v << ") = " << res << " ==> " << expect );
	BOOST_UBLASX_TEST_CHECK_VECTOR_EQ( res, expect, n );

	// hold(-v, > -.5)
	val = -0.5;
	expect = out_vector_type(n);
	for (std::size_t i = 0; i < n; ++i)
	{
		expect(i) = (-v(i)) > val;
	}
	res = ublasx::hold(-v, ::std::bind2nd(::std::greater<value_type>(), val));
	BOOST_UBLASX_DEBUG_TRACE( "hold(" << -v << ", > " << val << ") = " << res << " ==> " << expect );
	BOOST_UBLASX_TEST_CHECK_VECTOR_EQ( res, expect, n );
}

BOOST_UBLASX_TEST_DEF( test_vector_reference )
{
	BOOST_UBLASX_DEBUG_TRACE( "TEST Vector Reference" );

	typedef double value_type;
	typedef ublas::vector<value_type> vector_type;
	typedef ublas::vector_reference<vector_type> vector_reference_type;
	typedef ublas::vector<bool> out_vector_type;

	const std::size_t n = 5;

	vector_type v(n);

	v(0) = 0.0;
	v(1) = 0.108929;
	v(2) = 0.0;
	v(3) = 0.0;
	v(4) = 1.023787;

	value_type val(0);
	out_vector_type expect;
	out_vector_type res;

	// hold(ref(v))
	expect = out_vector_type(n);
	for (std::size_t i = 0; i < n; ++i)
	{
		expect(i) = v(i) != 0;
	}
	res = ublasx::hold(vector_reference_type(v));
	BOOST_UBLASX_DEBUG_TRACE( "hold(" << vector_reference_type(v) << ") = " << res << " ==> " << expect );
	BOOST_UBLASX_TEST_CHECK_VECTOR_EQ( res, expect, n );

	// which(ref(v), > .5)
	val = 0.5;
	expect = out_vector_type(n);
	for (std::size_t i = 0; i < n; ++i)
	{
		expect(i) = v(i) > val;
	}
	res = ublasx::hold(vector_reference_type(v), ::std::bind2nd(::std::greater<value_type>(), val));
	BOOST_UBLASX_DEBUG_TRACE( "hold(" << vector_reference_type(v) << ", > " << val << ") = " << res << " ==> " << expect );
	BOOST_UBLASX_TEST_CHECK_VECTOR_EQ( res, expect, n );
}

BOOST_UBLASX_TEST_DEF( test_row_major_matrix_container )
{
	BOOST_UBLASX_DEBUG_TRACE( "TEST Row-major Matrix Container" );

	typedef double value_type;
	typedef ublas::matrix<value_type, ublas::row_major> matrix_type;
	typedef ublas::zero_matrix<value_type, ublas::row_major> zero_matrix_type;
	typedef ublas::matrix<bool, ublas::row_major> out_matrix_type;

	const std::size_t nr(5);
	const std::size_t nc(4);

	matrix_type A(nr,nc);

	A(0,0) = 0.0;      A(0,1) = 0.274690; A(0,2) = 0.0;      A(0,3) = 0.798938;
	A(1,0) = 0.108929; A(1,1) = 0.0;      A(1,2) = 0.891726; A(1,3) = 0.0;
	A(2,0) = 0.0;      A(2,1) = 0.0;      A(2,2) = 0.0;      A(2,3) = 0.0;
	A(3,0) = 0.0;      A(3,1) = 0.675382; A(3,2) = 0.0;      A(3,3) = 0.450332;
	A(4,0) = 1.023787; A(4,1) = 1.0;      A(4,2) = 1.231751; A(4,3) = 1.0;

	zero_matrix_type Z(nr, nc);

	value_type val(0);
	out_matrix_type expect;
	out_matrix_type res;

	// hold(Z)
	expect = out_matrix_type(nr, nc, false);
	res = ublasx::hold(Z);
	BOOST_UBLASX_DEBUG_TRACE( "hold(" << Z << ") = " << ::std::boolalpha << res << " ==> " << expect );
	BOOST_UBLASX_TEST_CHECK_MATRIX_EQ( res, expect, nr, nc );

	// hold(A)
	expect = out_matrix_type(nr, nc);
	for (std::size_t r = 0; r < nr; ++r)
	{
		for (std::size_t c = 0; c < nc; ++c)
		{
			expect(r,c) = A(r,c) != 0;
		}
	}
	res = ublasx::hold(A);
	BOOST_UBLASX_DEBUG_TRACE( "hold(" << A << ") = " << ::std::boolalpha << res << " ==> " << expect );
	BOOST_UBLASX_TEST_CHECK_MATRIX_EQ( res, expect, nr, nc );

	// hold(A, > .5)
	val = 0.5;
	expect = out_matrix_type(nr, nc);
	for (std::size_t r = 0; r < nr; ++r)
	{
		for (std::size_t c = 0; c < nc; ++c)
		{
			expect(r,c) = A(r,c) > val;
		}
	}
	res = ublasx::hold(A, ::std::bind2nd(::std::greater<value_type>(), val));
	BOOST_UBLASX_DEBUG_TRACE( "hold(" << A << ", > " << val << ") = " << ::std::boolalpha << res << " ==> " << expect );
	BOOST_UBLASX_TEST_CHECK_MATRIX_EQ( res, expect, nr, nc );
}

BOOST_UBLASX_TEST_DEF( test_column_major_matrix_container )
{
	BOOST_UBLASX_DEBUG_TRACE( "TEST Column-major Matrix Container" );

	typedef double value_type;
	typedef ublas::matrix<value_type, ublas::column_major> matrix_type;
	typedef ublas::zero_matrix<value_type, ublas::column_major> zero_matrix_type;
	typedef ublas::matrix<bool, ublas::column_major> out_matrix_type;

	const std::size_t nr(5);
	const std::size_t nc(4);

	matrix_type A(nr,nc);

	A(0,0) = 0.0;      A(0,1) = 0.274690; A(0,2) = 0.0;      A(0,3) = 0.798938;
	A(1,0) = 0.108929; A(1,1) = 0.0;      A(1,2) = 0.891726; A(1,3) = 0.0;
	A(2,0) = 0.0;      A(2,1) = 0.0;      A(2,2) = 0.0;      A(2,3) = 0.0;
	A(3,0) = 0.0;      A(3,1) = 0.675382; A(3,2) = 0.0;      A(3,3) = 0.450332;
	A(4,0) = 1.023787; A(4,1) = 1.0;      A(4,2) = 1.231751; A(4,3) = 1.0;

	zero_matrix_type Z(nr, nc);

	value_type val(0);
	out_matrix_type expect;
	out_matrix_type res;

	// hold(Z)
	expect = out_matrix_type(nr, nc, false);
	res = ublasx::hold(Z);
	BOOST_UBLASX_DEBUG_TRACE( "hold(" << Z << ") = " << ::std::boolalpha << res << " ==> " << expect );
	BOOST_UBLASX_TEST_CHECK_MATRIX_EQ( res, expect, nr, nc );

	// hold(A)
	expect = out_matrix_type(nr, nc);
	for (std::size_t r = 0; r < nr; ++r)
	{
		for (std::size_t c = 0; c < nc; ++c)
		{
			expect(r,c) = A(r,c) != 0;
		}
	}
	res = ublasx::hold(A);
	BOOST_UBLASX_DEBUG_TRACE( "hold(" << A << ") = " << ::std::boolalpha << res << " ==> " << expect );
	BOOST_UBLASX_TEST_CHECK_MATRIX_EQ( res, expect, nr, nc );

	// hold(A, > .5)
	val = 0.5;
	expect = out_matrix_type(nr, nc);
	for (std::size_t r = 0; r < nr; ++r)
	{
		for (std::size_t c = 0; c < nc; ++c)
		{
			expect(r,c) = A(r,c) > val;
		}
	}
	res = ublasx::hold(A, ::std::bind2nd(::std::greater<value_type>(), val));
	BOOST_UBLASX_DEBUG_TRACE( "hold(" << A << ", > " << val << ") = " << ::std::boolalpha << res << " ==> " << expect );
	BOOST_UBLASX_TEST_CHECK_MATRIX_EQ( res, expect, nr, nc );
}

BOOST_UBLASX_TEST_DEF( test_matrix_expression )
{
	BOOST_UBLASX_DEBUG_TRACE( "TEST Matrix Expression" );

	typedef double value_type;
	typedef ublas::matrix<value_type> matrix_type;
	typedef ublas::matrix<bool, ublas::column_major> out_matrix_type;

	const std::size_t nr(5);
	const std::size_t nc(4);

	matrix_type A(nr,nc);

	A(0,0) = 0.0;      A(0,1) = 0.274690; A(0,2) = 0.0;      A(0,3) = 0.798938;
	A(1,0) = 0.108929; A(1,1) = 0.0;      A(1,2) = 0.891726; A(1,3) = 0.0;
	A(2,0) = 0.0;      A(2,1) = 0.0;      A(2,2) = 0.0;      A(2,3) = 0.0;
	A(3,0) = 0.0;      A(3,1) = 0.675382; A(3,2) = 0.0;      A(3,3) = 0.450332;
	A(4,0) = 1.023787; A(4,1) = 1.0;      A(4,2) = 1.231751; A(4,3) = 1.0;

	value_type val(0);
	out_matrix_type expect;
	out_matrix_type res;

	// all(A')
	expect = out_matrix_type(nc, nr);
	for (std::size_t r = 0; r < nc; ++r)
	{
		for (std::size_t c = 0; c < nr; ++c)
		{
			expect(r,c) = A(c,r) != 0;
		}
	}
	res = ublasx::hold(ublas::trans(A));
	BOOST_UBLASX_DEBUG_TRACE( "hold(" << A << "') = " << ::std::boolalpha << res << " ==> " << expect );
	BOOST_UBLASX_TEST_CHECK_MATRIX_EQ( res, expect, nc, nr );

	// hold(A', > .5)
	val = 0.5;
	expect = out_matrix_type(nc, nr);
	for (std::size_t r = 0; r < nc; ++r)
	{
		for (std::size_t c = 0; c < nr; ++c)
		{
			expect(r,c) = A(c,r) > val;
		}
	}
	res = ublasx::hold(ublas::trans(A), ::std::bind2nd(::std::greater<value_type>(), val));
	BOOST_UBLASX_DEBUG_TRACE( "hold(" << A << "', > " << val << ") = " << ::std::boolalpha << res << " ==> " << expect );
	BOOST_UBLASX_TEST_CHECK_MATRIX_EQ( res, expect, nc, nr );
}

BOOST_UBLASX_TEST_DEF( test_matrix_reference )
{
	BOOST_UBLASX_DEBUG_TRACE( "TEST Matrix Reference" );

	typedef double value_type;
	typedef ublas::matrix<value_type> matrix_type;
	typedef ublas::matrix_reference<matrix_type> matrix_reference_type;
	typedef ublas::matrix<bool, ublas::column_major> out_matrix_type;

	const std::size_t nr(5);
	const std::size_t nc(4);

	matrix_type A(nr,nc);

	A(0,0) = 0.0;      A(0,1) = 0.274690; A(0,2) = 0.0;      A(0,3) = 0.798938;
	A(1,0) = 0.108929; A(1,1) = 0.0;      A(1,2) = 0.891726; A(1,3) = 0.0;
	A(2,0) = 0.0;      A(2,1) = 0.0;      A(2,2) = 0.0;      A(2,3) = 0.0;
	A(3,0) = 0.0;      A(3,1) = 0.675382; A(3,2) = 0.0;      A(3,3) = 0.450332;
	A(4,0) = 1.023787; A(4,1) = 1.0;      A(4,2) = 1.231751; A(4,3) = 1.0;

	value_type val(0);
	out_matrix_type expect;
	out_matrix_type res;

	// hold(ref(A))
	expect = out_matrix_type(nr, nc);
	for (std::size_t r = 0; r < nr; ++r)
	{
		for (std::size_t c = 0; c < nc; ++c)
		{
			expect(r,c) = A(r,c) != 0;
		}
	}
	res = ublasx::hold(matrix_reference_type(A));
	BOOST_UBLASX_DEBUG_TRACE( "hold(reference(" << A << ")) = " << ::std::boolalpha << res << " ==> " << expect );
	BOOST_UBLASX_TEST_CHECK_MATRIX_EQ( res, expect, nr, nc );

	// hold(ref(A), > .5)
	val = 0.5;
	expect = out_matrix_type(nr, nc);
	for (std::size_t r = 0; r < nr; ++r)
	{
		for (std::size_t c = 0; c < nc; ++c)
		{
			expect(r,c) = A(r,c) > val;
		}
	}
	res = ublasx::hold(matrix_reference_type(A), ::std::bind2nd(::std::greater<value_type>(), val));
	BOOST_UBLASX_DEBUG_TRACE( "hold(reference(" << A << "), > " << val << ") = " << ::std::boolalpha << res << " ==> " << expect );
	BOOST_UBLASX_TEST_CHECK_MATRIX_EQ( res, expect, nr, nc );
}

int main()
{
	BOOST_UBLASX_DEBUG_TRACE("Test Suite: 'hold' operation");

	BOOST_UBLASX_TEST_BEGIN();

	BOOST_UBLASX_TEST_DO( test_vector_container );
	BOOST_UBLASX_TEST_DO( test_vector_expression );
	BOOST_UBLASX_TEST_DO( test_vector_reference );
	BOOST_UBLASX_TEST_DO( test_row_major_matrix_container );
	BOOST_UBLASX_TEST_DO( test_column_major_matrix_container );
	BOOST_UBLASX_TEST_DO( test_matrix_expression );
	BOOST_UBLASX_TEST_DO( test_matrix_reference );

	BOOST_UBLASX_TEST_END();
}


// Boost.Geometry (aka GGL, Generic Geometry Library)
// Unit Test

// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2008-2012 Bruno Lalande, Paris, France.
// Copyright (c) 2009-2012 Mateusz Loskot, London, UK.

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

#include <sstream>

#include <geometry_test_common.hpp>

#include <boost/geometry/geometries/geometries.hpp>
#include <boost/geometry/multi/io/dsv/write.hpp>
#include <boost/geometry/multi/geometries/multi_geometries.hpp>
#include <boost/geometry/multi/io/wkt/read.hpp>

template <typename Geometry>
void test_dsv(std::string const& wkt, std::string const& expected, bool json = false)
{
    Geometry geometry;
    bg::read_wkt(wkt, geometry);
    std::ostringstream out;
    if (json)
    {
        out << bg::dsv(geometry, ", ", "[", "]", ", ", "[ ", " ]", ", ");
    }
    else
    {
        out << bg::dsv(geometry);
    }
    BOOST_CHECK_EQUAL(out.str(), expected);
}

template <typename T>
void test_all()
{
    using namespace boost::geometry;
    typedef model::point<T, 2, cs::cartesian> point_type;
    typedef model::multi_point<point_type> mpoint;
    typedef model::multi_linestring<model::linestring<point_type> > mline;
    typedef model::multi_polygon<model::polygon<point_type> > mpoly;

    test_dsv<mpoint>
        (
            "multipoint((1 2),(3 4))",
            "((1, 2), (3, 4))"
        );
    test_dsv<mline>
        (
            "multilinestring((1 1,2 2,3 3),(4 4,5 5,6 6))",
            "(((1, 1), (2, 2), (3, 3)), ((4, 4), (5, 5), (6, 6)))"
        );
    test_dsv<mpoly>
        (
            // Multi with 2 poly's, first has hole, second is triangle
            "multipolygon(((0 0,0 4,4 4,4 0,0 0),(1 1,1 2,2 2,2 1,1 1)),((5 5,6 5,5 6,5 5)))",
            "((((0, 0), (0, 4), (4, 4), (4, 0), (0, 0)), ((1, 1), (1, 2), (2, 2), (2, 1), (1, 1))), (((5, 5), (6, 5), (5, 6), (5, 5))))"
        );

    // http://geojson.org/geojson-spec.html#id5
    test_dsv<mpoint>
        (
            "multipoint((1 2),(3 4))",
            "[ [1, 2], [3, 4] ]",
            true
        );

    // http://geojson.org/geojson-spec.html#id6
    test_dsv<mline>
        (
            "multilinestring((1 1,2 2,3 3),(4 4,5 5,6 6))",
            "[ [ [1, 1], [2, 2], [3, 3] ], [ [4, 4], [5, 5], [6, 6] ] ]",
            true
        );

    // http://geojson.org/geojson-spec.html#id7
    test_dsv<mpoly>
        (
            "multipolygon(((0 0,0 4,4 4,4 0,0 0),(1 1,1 2,2 2,2 1,1 1)),((5 5,6 5,5 6,5 5)))",
            "[ [ [ [0, 0], [0, 4], [4, 4], [4, 0], [0, 0] ], [ [1, 1], [1, 2], [2, 2], [2, 1], [1, 1] ] ], [ [ [5, 5], [6, 5], [5, 6], [5, 5] ] ] ]",
            true
        );

}

int test_main(int, char* [])
{
    test_all<double>();
    test_all<int>();

    return 0;
}


/*
 * @file
 * @author University of Warwick
 * @version 1.0
 *
 * @section LICENSE
 *
 * @section DESCRIPTION
 *
 */

#define BOOST_TEST_MODULE SparseMatrixHDF5File
#include <boost/test/unit_test.hpp>
#include <boost/test/output_test_stream.hpp>
#include "mpi.h"

#include "SparseMatrixHDF5File.h"
#include "Error.h"
#include "Communicator.h"
#include "SparseMatrixCOO.h"

using namespace cupcfd::fileformats::matrices;

// These tests require MPI
BOOST_AUTO_TEST_CASE(setup)
{
    int argc = boost::unit_test::framework::master_test_suite().argc;
    char ** argv = boost::unit_test::framework::master_test_suite().argv;
    MPI_Init(&argc, &argv);
}

// === Constructor ===
// Test 1:
BOOST_AUTO_TEST_CASE(constructor_test1)
{
	cupcfd::mpi::Communicator comm;
	cupcfd::mpi::drivers::set_comm(comm, MPI_COMM_WORLD);

	if(comm.rank == 0)
	{
		std::string fileName = "../tests/testdata/Matrix1SparseCOO.h5";
		SparseMatrixHDF5File<int ,double> file(fileName);
	}
}

// === getNNZ ===
// Test 1:
BOOST_AUTO_TEST_CASE(getNNZ_test1)
{
	cupcfd::mpi::Communicator comm;
	cupcfd::mpi::drivers::set_comm(comm, MPI_COMM_WORLD);
	cupcfd::error::eCodes status;

	if(comm.rank == 0)
	{
		std::string fileName = "../tests/testdata/Matrix1SparseCOO.h5";
		SparseMatrixHDF5File<int, double> file(fileName);

		int nnz;
		status = file.getNNZ(&nnz);
		BOOST_CHECK_EQUAL(nnz, 24);
		BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	}
}

// === getNRows ===
// Test 1:
BOOST_AUTO_TEST_CASE(getNRows_test1)
{
	cupcfd::mpi::Communicator comm;
	cupcfd::mpi::drivers::set_comm(comm, MPI_COMM_WORLD);
	cupcfd::error::eCodes status;

	if(comm.rank == 0)
	{
		std::string fileName = "../tests/testdata/Matrix1SparseCOO.h5";
		SparseMatrixHDF5File<int, double> file(fileName);

		int nRows;
		status = file.getNRows(&nRows);
		BOOST_CHECK_EQUAL(nRows, 8);
		BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	}
}

// === getNCols ===
// Test 1:
BOOST_AUTO_TEST_CASE(getNCols_test1)
{
	cupcfd::mpi::Communicator comm;
	cupcfd::mpi::drivers::set_comm(comm, MPI_COMM_WORLD);
	cupcfd::error::eCodes status;

	if(comm.rank == 0)
	{
		std::string fileName = "../tests/testdata/Matrix1SparseCOO.h5";
		SparseMatrixHDF5File<int, double> file(fileName);

		int nCols;
		status = file.getNCols(&nCols);
		BOOST_CHECK_EQUAL(nCols, 8);
		BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	}
}

// === getMatrixIndicesBase ===
// Test 1:
BOOST_AUTO_TEST_CASE(getMatrixIndicesBase_test1)
{
	cupcfd::mpi::Communicator comm;
	cupcfd::mpi::drivers::set_comm(comm, MPI_COMM_WORLD);
	cupcfd::error::eCodes status;

	if(comm.rank == 0)
	{
		std::string fileName = "../tests/testdata/Matrix1SparseCOO.h5";
		SparseMatrixHDF5File<int, double> file(fileName);

		int base;
		status = file.getMatrixIndicesBase(&base);
		BOOST_CHECK_EQUAL(base, 1);
		BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);
	}
}

// === getNNZRows ===
// Test 1:
BOOST_AUTO_TEST_CASE(getNNZRows_test1)
{
	cupcfd::mpi::Communicator comm;
	cupcfd::mpi::drivers::set_comm(comm, MPI_COMM_WORLD);
	cupcfd::error::eCodes status;

	if(comm.rank == 0)
	{
		std::string fileName = "../tests/testdata/Matrix1SparseCOO.h5";
		SparseMatrixHDF5File<int, double> file(fileName);

		// This test file is known in advance to have 8 rows
		int * nnzRows = (int *) malloc(sizeof(int) * 8);
		status = file.getNNZRows(nnzRows, 8);
		BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

		int cmp[8] = {3, 3, 3, 3, 3, 3, 4, 2};
		BOOST_CHECK_EQUAL_COLLECTIONS(cmp, cmp + 8, nnzRows, nnzRows + 8);
	}
}

// === getSparseMatrix (All) ===
// Test 1: Test we can read into a matrix with the correct values for a base 0 Sparsematrix object
BOOST_AUTO_TEST_CASE(getSparseMatrix_full_test1)
{
	cupcfd::mpi::Communicator comm;
	cupcfd::mpi::drivers::set_comm(comm, MPI_COMM_WORLD);
	cupcfd::error::eCodes status;

	if(comm.rank == 0)
	{
		std::string fileName = "../tests/testdata/Matrix1SparseCOO.h5";
		SparseMatrixHDF5File<int, double> file(fileName);

		cupcfd::data_structures::matrices::SparseMatrixCOO<int, double> matrix(8, 8, 0);

		// Test and Check
		status = file.getSparseMatrix(matrix);
		BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

		// Should probably use the matrix functions for this test, but can just check on
		// internal data structures for now
		int rowCmp[24] = {0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 7, 7};
		int colCmp[24] = {0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, 5, 6, 7, 4, 5, 6, 7, 6, 7};
		double valCmp[24] = {0.1, 0.2, 0.3, 0.4, 0.1, 0.2, 0.3, 0.4, 0.1, 0.2, 0.3, 0.4, 0.1, 0.2, 0.3, 0.4, 0.1, 0.2, 0.3, 0.4, 0.1, 0.2, 0.3, 0.4};

		BOOST_CHECK_EQUAL_COLLECTIONS(rowCmp, rowCmp + 24, &matrix.row[0], &matrix.row[0] + 24);
		BOOST_CHECK_EQUAL_COLLECTIONS(colCmp, colCmp + 24, &matrix.col[0], &matrix.col[0] + 24);
		BOOST_CHECK_EQUAL_COLLECTIONS(valCmp, valCmp + 24, &matrix.val[0], &matrix.val[0] + 24);
	}
}

// Test 1: Test we can read into a matrix with the correct values for a base 4 SparseMatrix object
BOOST_AUTO_TEST_CASE(getSparseMatrix_full_test2)
{
	cupcfd::mpi::Communicator comm;
	cupcfd::mpi::drivers::set_comm(comm, MPI_COMM_WORLD);
	cupcfd::error::eCodes status;

	if(comm.rank == 0)
	{
		std::string fileName = "../tests/testdata/Matrix1SparseCOO.h5";
		SparseMatrixHDF5File<int, double> file(fileName);

		cupcfd::data_structures::matrices::SparseMatrixCOO<int, double> matrix(8, 8, 4);

		// Test and Check
		status = file.getSparseMatrix(matrix);
		BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

		// Should probably use the matrix functions for this test, but can just check on
		// internal data structures for now
		int rowCmp[24] = {4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 10, 11, 11};
		int colCmp[24] = {4, 5, 6, 5, 6, 7, 6, 7, 8, 7, 8, 9, 8, 9, 10, 9, 10, 11, 8, 9, 10, 11, 10, 11};
		double valCmp[24] = {0.1, 0.2, 0.3, 0.4, 0.1, 0.2, 0.3, 0.4, 0.1, 0.2, 0.3, 0.4, 0.1, 0.2, 0.3, 0.4, 0.1, 0.2, 0.3, 0.4, 0.1, 0.2, 0.3, 0.4};

		BOOST_CHECK_EQUAL_COLLECTIONS(rowCmp, rowCmp + 24, &matrix.row[0], &matrix.row[0] + 24);
		BOOST_CHECK_EQUAL_COLLECTIONS(colCmp, colCmp + 24, &matrix.col[0], &matrix.col[0] + 24);
		BOOST_CHECK_EQUAL_COLLECTIONS(valCmp, valCmp + 24, &matrix.val[0], &matrix.val[0] + 24);
	}
}

// === getSparseMatrix (Partial) ===
//ToDo: The multile pranges aspect has the potential for a lot of edge cases.
// This will probably need more expansive testing - this one test tries too many things at once
// Test 1:
BOOST_AUTO_TEST_CASE(getSparseMatrix_partial_test1)
{
	cupcfd::mpi::Communicator comm;
	cupcfd::mpi::drivers::set_comm(comm, MPI_COMM_WORLD);
	cupcfd::error::eCodes status;

	if(comm.rank == 0)
	{
		// === Setup ===
		std::string fileName = "../tests/testdata/Matrix1SparseCOO.h5";
		SparseMatrixHDF5File<int, double> file(fileName);
		cupcfd::data_structures::matrices::SparseMatrixCOO<int, double> matrix(8, 8, 4);

		// Speciy which rows we want
		int desiredRows[4] = {2, 3, 5, 7};

		// === Test and Check ===
		status = file.getSparseMatrix(matrix, desiredRows, 4, 1);
		BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

		// Should probably use the matrix functions for this test, but can just check on
		// internal data structures for now
		int rowCmp[13] = {5, 5, 5, 6, 6, 6, 8, 8, 8, 10, 10, 10, 10};
		int colCmp[13] = {5, 6, 7, 6, 7, 8, 8, 9, 10, 8, 9, 10, 11};
		double valCmp[13] = {0.4, 0.1, 0.2, 0.3, 0.4, 0.1, 0.1, 0.2, 0.3, 0.3, 0.4, 0.1, 0.2};

		BOOST_CHECK_EQUAL_COLLECTIONS(rowCmp, rowCmp + 13, &matrix.row[0], &matrix.row[0] + 13);
		BOOST_CHECK_EQUAL_COLLECTIONS(colCmp, colCmp + 13, &matrix.col[0], &matrix.col[0] + 13);
		BOOST_CHECK_EQUAL_COLLECTIONS(valCmp, valCmp + 13, &matrix.val[0], &matrix.val[0] + 13);
	}
}

// Test 2: Single row
BOOST_AUTO_TEST_CASE(getSparseMatrix_partial_test2)
{
	cupcfd::mpi::Communicator comm;
	cupcfd::mpi::drivers::set_comm(comm, MPI_COMM_WORLD);
	cupcfd::error::eCodes status;

	if(comm.rank == 0)
	{
		// === Setup ===
		std::string fileName = "../tests/testdata/Matrix1SparseCOO.h5";
		SparseMatrixHDF5File<int, double> file(fileName);
		cupcfd::data_structures::matrices::SparseMatrixCOO<int, double> matrix(8, 8, 4);

		// Speciy which rows we want
		int desiredRows[1] = {5};

		// === Test and Check ===
		status = file.getSparseMatrix(matrix, desiredRows, 1, 2);
		BOOST_CHECK_EQUAL(status, cupcfd::error::E_SUCCESS);

		// Should probably use the matrix functions for this test, but can just check on
		// internal data structures for now
		int rowCmp[3] = {7, 7, 7};
		int colCmp[3] = {7, 8, 9};
		double valCmp[3] = {0.2, 0.3, 0.4};

		BOOST_CHECK_EQUAL_COLLECTIONS(rowCmp, rowCmp + 3, &matrix.row[0], &matrix.row[0] + 3);
		BOOST_CHECK_EQUAL_COLLECTIONS(colCmp, colCmp + 3, &matrix.col[0], &matrix.col[0] + 3);
		BOOST_CHECK_EQUAL_COLLECTIONS(valCmp, valCmp + 3, &matrix.val[0], &matrix.val[0] + 3);
	}
}

// Finalize MPI
BOOST_AUTO_TEST_CASE(cleanup)
{
    // Cleanup MPI Environment
    MPI_Finalize();
}


#include <vector>
#include <list>
#include <set>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <deque>
#include <forward_list>
#include <iostream>
#include <boost/tti/tti.hpp>

template< typename T, typename ... Types> struct IsMap{
 static constexpr bool value = false;
};

template< typename ... Types> struct IsMap<std::map<Types ...>>{
  static constexpr bool value = true;
};

template< typename ... Types> struct IsMap<std::unordered_map<Types ...>>{
  static constexpr bool value = true;
};

int main(){

  std::set<int> s;  std::map<int,int> m;  std::unordered_map<int,int> um;

  bool b1 =  IsMap<decltype(s)>::value;
  bool b2 =  IsMap<decltype(m)>::value;
  bool b3 =  IsMap<decltype(um)>::value;

  return 0;
  }


// https://launchpad.net/libmct

//#define GOOGLE_HASH_MAPS

#include "details/io_details.h"
#include "details/fp_details.h"

#include <gtest/gtest.h>

//#include <ext/hash_set>
#include <boost/unordered_set.hpp>
#include <boost/unordered_map.hpp>
#ifdef GOOGLE_HASH_MAPS
// DANGER: Кажется с пользовательскими типами как-то не очень. Что то похоже и про строки слышал, кажется.
// Exsist depend on K and V type
#  include <google/dense_hash_map>
#endif
#include <boost/foreach.hpp>

#include <cassert>
#include <vector>
#include <iostream>
#include <fstream>
#include <algorithm>
#include <stdexcept>
#include <unordered_map>

using namespace std;

using std::hash;  // C++11
//using boost::hash;  // нужно точечно указать

namespace {
struct TaskId {
  TaskId() : v(0), w(0) {}
  TaskId(int _v, int _w) : v(_v), w(_w) {}

  int v;
  int w;
};

struct KeyHash {
 std::size_t operator()(const TaskId& k) const
 {
   // Watch "Eff. Java."
   // Проблема в том как скомбинировать.
   return boost::hash<int>()(k.v) ^ (boost::hash<int>()(k.w) << 1);
 }
};

struct KeyEqual {
 bool operator()(const TaskId& lhs, const TaskId& rhs) const
 {
    return lhs.v == rhs.v && lhs.w == rhs.w;
 }
};

}

/// HashTables
// TODO: а есть ли адаптивные хэш-таблицы?
//
// Используется рандомизация, но как потом искать?
//
// см. effective Java - там есть про хэш коды - если переорп. equal then переорп. hashCode!
// Равные объекты должны иметь равные хэши.
//
// DANGER: Good hashtable:
// - good hash functon - равномерно разбрасывает по бакетам
//   && good load factor - n/(count_buckets) - при первом условии делает связанные списки (или аналог) как можно короче
//   && O(1) calc hash value
//
// Pro:
//
//
// Cons:
//   - для больших объемов данных?
//   - O(1) при вычислении это да, но константы могут быть большими
//   - нельзя делать сложные выборки
//
// Java:
//  http://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html
//
// C++:
// TODO: как переопределение хэш функции влияет на работу? Может быть использовать двумерную таблицу?
//   Похоже по получнному ключу таблица еще раз считает хэш.
//
// Benchmarks:
//   http://research.neustar.biz/tag/unordered_map/
//   http://preshing.com/20110603/hash-table-performance-tests/
//
// Hash functions:
//   http://programmers.stackexchange.com/questions/49550/which-hashing-algorithm-is-best-for-uniqueness-and-speed
//
TEST(DataStructures, HashTables) {
  // http://stackoverflow.com/questions/2179946/i-would-like-to-see-a-hash-map-example-in-c
  //
  // http://msdn.microsoft.com/en-us/library/1s1byw77.aspx
  // Если уточнять ключ, то как быть с коллизиями - в задаче при поиске нужно быть уверенным.
  // Хотя... Вообще два разных объекта с равными кешами очень вероятны. Еще передается функция эквивалетности.
  //
  //
  //unordered_map<TaskId, int> table;  // not compiled - можно, но нужно уточнить операции с ключами
  unordered_map<int, int> table;

  boost::unordered_map<TaskId, int, KeyHash, KeyEqual> htbl;

#ifdef GOOGLE_HASH_MAPS
  google::dense_hash_map<TaskId, int, KeyHash, KeyEqual> g_tbl;
  g_tbl.set_empty_key(TaskId(0, 0));
  g_tbl[TaskId(1, 5)] = 9;

  cout << g_tbl[TaskId(1, 5)] << endl;
  assert(g_tbl.end() != g_tbl.find(TaskId(1, 5)));
#endif

}

TEST(DataStructures, BloomFilter) {
  // Bloom filter:
  //   http://code.google.com/p/bloom/
  //
  // Pro:
  //   - more then hashtable space efficient - DANGER: не всегда можно проверить ложноположительное срабатывание
  // Cons:
  //   - can't store value
  //   - can't delete
  //   - ложно положительные срабатывания
}

// Хранение в разнобой
// http://preshing.com/20130107/this-hash-table-is-faster-than-a-judy-array/
// TODO: See sparese arrays, skeep list
TEST(DataStructures, JudyArrays) {
  // http://judy.sourceforge.net/
  // Cons:
  //   - похоже запатентовано
}


//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2012   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#define NT2_UNIT_MODULE "nt2 boost.simd.arithmetic toolbox - idivround2even/simd Mode"

//////////////////////////////////////////////////////////////////////////////
// unit test behavior of boost.simd.arithmetic components in simd mode
//////////////////////////////////////////////////////////////////////////////
/// created by jt the 01/12/2010
///
#include <boost/simd/arithmetic/include/functions/idivround2even.hpp>
#include <boost/simd/sdk/simd/io.hpp>
#include <boost/dispatch/meta/as_integer.hpp>
#include <nt2/sdk/unit/tests.hpp>
#include <nt2/sdk/unit/module.hpp>
#include <nt2/sdk/unit/tests/type_expr.hpp>
#include <boost/simd/sdk/simd/native.hpp>
#include <boost/simd/constant/constant.hpp>
#include <boost/simd/include/constants/one.hpp>
#include <boost/simd/include/constants/inf.hpp>
#include <boost/simd/include/constants/zero.hpp>
#include <boost/simd/include/constants/minf.hpp>
#include <boost/simd/include/constants/mone.hpp>
#include <boost/simd/include/constants/nan.hpp>

NT2_TEST_CASE_TPL ( idivround2even_real__2_0,  BOOST_SIMD_SIMD_REAL_TYPES)
{
  using boost::simd::idivround2even;
  using boost::simd::tag::idivround2even_;
  using boost::simd::native;
  typedef BOOST_SIMD_DEFAULT_EXTENSION  ext_t;
  typedef native<T,ext_t>                  vT;
  typedef typename boost::dispatch::meta::as_integer<T>::type iT;
  typedef native<iT,ext_t>                                   ivT;
  typedef typename boost::dispatch::meta::call<idivround2even_(vT,vT)>::type r_t;
  typedef ivT wished_r_t;

  NT2_TEST_TYPE_IS( r_t, wished_r_t );
  // specific values tests
  NT2_TEST_ULP_EQUAL(idivround2even(boost::simd::Inf<vT>(), boost::simd::Inf<vT>()), boost::simd::Zero<r_t>(), 0);
  NT2_TEST_ULP_EQUAL(idivround2even(boost::simd::Minf<vT>(), boost::simd::Minf<vT>()), boost::simd::Zero<r_t>(), 0);
  NT2_TEST_ULP_EQUAL(idivround2even(boost::simd::Mone<vT>(), boost::simd::Mone<vT>()), boost::simd::One<r_t>(), 0);
  NT2_TEST_ULP_EQUAL(idivround2even(boost::simd::Nan<vT>(), boost::simd::Nan<vT>()), boost::simd::Zero<r_t>(), 0);
  NT2_TEST_ULP_EQUAL(idivround2even(boost::simd::One<vT>(), boost::simd::One<vT>()), boost::simd::One<r_t>(), 0);
} // end of test for floating_

NT2_TEST_CASE_TPL ( idivround2even_unsigned_int__2_0,  BOOST_SIMD_SIMD_UNSIGNED_TYPES)
{

  using boost::simd::idivround2even;
  using boost::simd::tag::idivround2even_;
  using boost::simd::native;
  typedef BOOST_SIMD_DEFAULT_EXTENSION  ext_t;
  typedef native<T,ext_t>                  vT;
  typedef typename boost::dispatch::meta::as_integer<T>::type iT;
  typedef native<iT,ext_t>                                   ivT;
  typedef typename boost::dispatch::meta::call<idivround2even_(vT,vT)>::type r_t;
  typedef ivT wished_r_t;

  // return type conformity test
  NT2_TEST_TYPE_IS( r_t, wished_r_t );

  // specific values tests
  NT2_TEST_ULP_EQUAL(idivround2even(boost::simd::One<vT>(), boost::simd::One<vT>()), boost::simd::One<ivT>(), 0);
} // end of test for unsigned_int_

NT2_TEST_CASE_TPL ( idivround2even_signed_int__2_0,  BOOST_SIMD_SIMD_INTEGRAL_SIGNED_TYPES)
{

  using boost::simd::idivround2even;
  using boost::simd::tag::idivround2even_;
  using boost::simd::native;
  typedef BOOST_SIMD_DEFAULT_EXTENSION  ext_t;
  typedef native<T,ext_t>                  vT;
  typedef typename boost::dispatch::meta::as_integer<T>::type iT;
  typedef native<iT,ext_t>                                   ivT;
  typedef typename boost::dispatch::meta::call<idivround2even_(vT,vT)>::type r_t;
  typedef ivT wished_r_t;

  // return type conformity test
  NT2_TEST_TYPE_IS( r_t, wished_r_t );

  // specific values tests
  NT2_TEST_ULP_EQUAL(idivround2even(boost::simd::Mone<vT>(), boost::simd::Mone<vT>()), boost::simd::One<ivT>(), 0);
  NT2_TEST_ULP_EQUAL(idivround2even(boost::simd::One<vT>(), boost::simd::One<vT>()), boost::simd::One<ivT>(), 0);
} // end of test for signed_int_


//------------------------------------------------------------------------------
// \file ErrorHandling_tests.cpp
//------------------------------------------------------------------------------
#include "Utilities/ErrorHandling/ErrorHandling.h"

#include "Cpp/Utilities/TypeSupport/UnderlyingTypes.h"

#include <boost/test/unit_test.hpp>
#include <cmath>

using Cpp::Utilities::TypeSupport::get_underlying_value;
using Utilities::ErrorHandling::ErrorCodeNumber;
using Utilities::ErrorHandling::HandleReturnValuePassively;
using OptionalErrorNumber =
	Utilities::ErrorHandling::HandleReturnValuePassively::OptionalErrorNumber;

BOOST_AUTO_TEST_SUITE(Utilities)
BOOST_AUTO_TEST_SUITE(ErrorHandling_tests)

// cf. https://en.cppreference.com/w/cpp/error/errno
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(HandleReturnValuePassivelyGetsLatestErrno)
{
  double not_a_number {std::log(-1.0)};

	const OptionalErrorNumber result {HandleReturnValuePassively()(-1)};

	BOOST_TEST(static_cast<bool>(result));
	BOOST_TEST((*result).error_number() ==
		get_underlying_value(ErrorCodeNumber::argument_out_of_domain));
}

BOOST_AUTO_TEST_SUITE_END() // ErrorHandling_tests
BOOST_AUTO_TEST_SUITE_END() // Utilities


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*                                                                           */
/*  This file is part of the library KASKADE 7                               */
/*    see http://www.zib.de/projects/kaskade7-finite-element-toolbox         */
/*                                                                           */
/*  Copyright (C) 2002-2016 Zuse Institute Berlin                            */
/*                                                                           */
/*  KASKADE 7 is distributed under the terms of the ZIB Academic License.    */
/*    see $KASKADE/academic.txt                                              */
/*                                                                           */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include <iostream>

#ifndef GRIDTYPE
#define GRIDTYPE 1 // UGGrid
#endif

#if ( GRIDTYPE < 1 ) || ( GRIDTYPE > 4 )
#error "GRIDTYPE must be 1 (UGGrid), 2 (ALUSImplexGrid), 3 (ALUConformGrid) or 4 (AlbertaGrid)"
#endif

#if GRIDTYPE==1
#define CHARGRIDTYPE "UGGrid"
#elif  GRIDTYPE==2
#define CHARGRIDTYPE "ALUSimplexGrid"
#elif  GRIDTYPE==3
#define CHARGRIDTYPE "ALUConformGrid"
#elif  GRIDTYPE==4
#define CHARGRIDTYPE "AlbertaGrid"
#endif

#include <boost/timer/timer.hpp>

#include "dune/grid/config.h"
#include "dune/grid/uggrid.hh"

#ifndef SPACEDIM
#define SPACEDIM 2
#endif

#if ( SPACEDIM < 2 ) || ( SPACEDIM > 3 )
#error 'Dimension SPACEDIM must be 2 or 3'
#endif

#if ( GRIDTYPE==2 ) || ( GRIDTYPE==3 )
#define HAVE_DUNE_ALUGRID 1
#define ENABLE_DUNE_ALUGRID 1
#if SPACEDIM==2
#include "dune/alugrid/2d/alu2dinclude.hh"
#include "dune/alugrid/2d/alugrid.hh"
#include "dune/alugrid/2d/gridfactory.hh"
#else
#include "dune/alugrid/3d/alu3dinclude.hh"
#include "dune/alugrid/3d/alugrid.hh"
#include "dune/alugrid/3d/gridfactory.hh"
#endif
#endif

#if GRIDTYPE==4
#define ENABLE_ALBERTA 1
#define ALBERTA_DIM SPACEDIM
#include "dune/grid/albertagrid/agrid.hh"
#include "dune/grid/albertagrid/gridfactory.hh"
#endif

#include "utilities/enums.hh"

#include "fem/assemble.hh"
#include "fem/norms.hh"
#include "fem/lagrangespace.hh"
//#include "fem/hierarchicspace.hh"   // ContinuousHierarchicMapper
#include "linalg/direct.hh"
#include "linalg/trivialpreconditioner.hh"
//#include "linalg/partialDirectPreconditioner.hh"
#include "linalg/additiveschwarz.hh"
#include "linalg/iluprecond.hh"      // ILUT, ILUK, ARMS
#include "linalg/iccprecond.hh"
#include "linalg/icc0precond.hh"
#include "linalg/hyprecond.hh"       // BoomerAMG, Euclid
#include "linalg/jacobiPreconditioner.hh"
#include "linalg/cg.hh"
#include "mg/hb.hh"
#include "io/vtk.hh"
//#include "io/amira.hh"
#include "utilities/kaskopt.hh"

using namespace Kaskade;
#include "ht.hh"

#if SPACEDIM==3
#include "cubus.hh"
#endif

#if GRIDTYPE==4
#define DEFAULT_REFINEMENTS 14
#else
#if GRIDTYPE==3
#define DEFAULT_REFINEMENTS 7
#else
#if SPACEDIM==2
#define DEFAULT_REFINEMENTS 5
#endif
#if SPACEDIM==3
#define DEFAULT_REFINEMENTS 3
#endif
#endif
#endif

int main(int argc, char *argv[])
{
  using namespace boost::fusion;

  std::cout << "Start heat transfer tutorial program (with GridType=" << CHARGRIDTYPE <<
              " and SpaceDimension=" << SPACEDIM << ")" << std::endl;

  boost::timer::cpu_timer totalTimer;

  int verbosityOpt = 1;
  bool dump = true;
  std::unique_ptr<boost::property_tree::ptree> pt = getKaskadeOptions(argc, argv, verbosityOpt, dump);

  int  refinements = getParameter(pt, "refinements", DEFAULT_REFINEMENTS),
       order       =  getParameter(pt, "order", 2),
       verbosity   = getParameter(pt, "verbosity", 1);
  std::cout << "original mesh shall be refined : " << refinements << " times" << std::endl;
  std::cout << "discretization order           : " << order << std::endl;
  std::cout << "output level (verbosity)       : " << verbosity << std::endl;

  int  direct, onlyLowerTriangle = false;

  DirectType directType;
//  IterateType iterateType = IterateType::CG;
  MatrixProperties property = MatrixProperties::SYMMETRIC;
  PrecondType precondType = PrecondType::NONE;
  std::string empty;

  std::string s("names.type.");
  s += getParameter(pt, "solver.type", empty);
  direct = getParameter(pt, s, 0);

  s = "names.direct." + getParameter(pt, "solver.direct", empty);
  directType = static_cast<DirectType>(getParameter(pt, s, 0));

//  s = "names.iterate." + getParameter(pt, "solver.iterate", empty);
//  iterateType = static_cast<IterateType>(getParameter(pt, s, 0));
  s = "names.preconditioner." + getParameter(pt, "solver.preconditioner", empty);
  precondType = static_cast<PrecondType>(getParameter(pt, s, 0));

  int blocks = getParameter(pt,"blocks",40);
  int nthreads = getParameter(pt,"threads",4);
  double rowBlockFactor = getParameter(pt,"rowBlockFactor",2.0);

  property = MatrixProperties::SYMMETRIC;

  if ( (directType == DirectType::MUMPS)||(directType == DirectType::PARDISO) || ( (precondType == PrecondType::ICC) && !direct ) )
  {
    onlyLowerTriangle = true;
    std::cout <<
      "Note: direct solver MUMPS/PARADISO or PrecondType::ICC preconditioner ===> onlyLowerTriangle is set to true!"
      << std::endl;
  }

  boost::timer::cpu_timer gridTimer;
#if SPACEDIM==2
  //   two-dimensional space: dim=2
  constexpr int dim=2;
#if GRIDTYPE==1
  using Grid = Dune::UGGrid<dim>;
#endif
  // There are alternatives to UGGrid: ALUSimplexGrid (red refinement), ALUConformGrid (bisection)
  // and AlbertaGrid
#if GRIDTYPE==2
  using Grid = Dune::ALUGrid<dim,dim,Dune::ALUGridElementType::simplex,Dune::ALUGridRefinementType::nonconforming>;
#endif
#if GRIDTYPE==3
  using Grid = Dune::ALUGrid<dim,dim,Dune::ALUGridElementType::simplex,Dune::ALUGridRefinementType::conforming>;
#endif
#if GRIDTYPE==4
  using Grid = Dune::AlbertaGrid<dim,dim>;
#endif
  Dune::GridFactory<Grid> factory;

  // vertex coordinates v[0], v[1]
  Dune::FieldVector<double,dim> v;
  v[0]=0; v[1]=0; factory.insertVertex(v);
  v[0]=1; v[1]=0; factory.insertVertex(v);
  v[0]=1; v[1]=1; factory.insertVertex(v);
  v[0]=0; v[1]=1; factory.insertVertex(v);
  // triangle defined by 3 vertex indices
  std::vector<unsigned int> vid(3);
  Dune::GeometryType gt(Dune::GeometryType::simplex,2);
  vid[0]=0; vid[1]=1; vid[2]=2; factory.insertElement(gt,vid);
  vid[0]=0; vid[1]=2; vid[2]=3; factory.insertElement(gt,vid);
  std::unique_ptr<Grid> grid( factory.createGrid() ) ;
  // the coarse grid will be refined three times
  grid->globalRefine(refinements);
  // some information on the refined mesh
  std::cout << std::endl << "Grid: " << grid->size(0) << " triangles, " << std::endl;
  std::cout << "      " << grid->size(1) << " edges, " << std::endl;
  std::cout << "      " << grid->size(2) << " points" << std::endl;

  // a gridmanager is constructed
  // as connector between geometric and algebraic information
  GridManager<Grid> gridManager(std::move(grid));
#else
  //  three-dimensional space: dim=3
  //  we offer 2 geometries:
  //  - very simple:  1 tetrahedron defined by 4 vertices
  //  - more complex: 1 cube defined by 48 tetrahedra, provided in cubus.hh
  constexpr int dim=3;

  //
  //definition of 1 tetrahedron by 4 vertices
#if GRIDTYPE==4
  using Grid = Dune::AlbertaGrid<dim,dim>;
  Dune::GridFactory<Grid> factory;
  // vertex coordinates v[0], v[1]
  Dune::FieldVector<double,dim> v;
  v[0]=0; v[1]=0; v[2]=0; factory.insertVertex(v);
  v[0]=1; v[1]=0; v[2]=0; factory.insertVertex(v);
  v[0]=0; v[1]=1; v[2]=0; factory.insertVertex(v);
  v[0]=0; v[1]=0; v[2]=1; factory.insertVertex(v);
  // tetrahedron defined by 4 vertex indices
  std::vector<unsigned int> vid(4);
  Dune::GeometryType gt(Dune::GeometryType::simplex,dim);
  vid[0]=0; vid[1]=1; vid[2]=2; vid[3]=3; factory.insertElement(gt,vid);
  std::unique_ptr<Grid> grid( factory.createGrid() ) ;
  // the coarse grid will be refined three times
  grid->globalRefine(refinements);
  //
#else
  // definition of a more complex mesh using cubus.hh
  // note: trying to use the following code with AlbertaGrid will lead to a crash
  // during runtime due to a bug in the AlbertaGrid refinement routine
  int heapSize=1024;
#if GRIDTYPE==1
  using Grid = Dune::UGGrid<dim>;
#endif
  // There are alternatives to UGGrid: ALUSimplexGrid (red refinement)
#if GRIDTYPE==2
  using Grid = Dune::ALUGrid<dim,dim,Dune::ALUGridElementType::simplex,Dune::ALUGridRefinementType::nonconforming>;
#endif
#if GRIDTYPE==3
#error ALUCONFORM GridType not supported by DUNE for Dimension=3
#endif
  std::unique_ptr<Grid> grid( RefineGrid<Grid>(refinements, heapSize) );
#endif

  // some information on the refined mesh
  std::cout << std::endl << "Grid: " << grid->size(0) << " tetrahedra, " << std::endl;
  std::cout << "      " << grid->size(1) << " triangles, " << std::endl;
  std::cout << "      " << grid->size(dim-1) << " edges, " << std::endl;
  std::cout << "      " << grid->size(dim) << " points" << std::endl;
  // a gridmanager is constructed
  // as connector between geometric and algebraic information
  GridManager<Grid> gridManager(std::move(grid));
#endif
  std::cout << "computing time for generation of initial mesh: " << boost::timer::format(gridTimer.elapsed()) << "\n";

  using LeafView = Grid::LeafGridView;

  // construction of finite element space for the scalar solution T.
  using H1Space = FEFunctionSpace<ContinuousLagrangeMapper<double,LeafView> >;
  // using H1Space = FEFunctionSpace<ContinuousHierarchicMapper<double,LeafView> >;
  using Spaces = boost::fusion::vector<H1Space const*>;
  using VariableDescriptions = boost::fusion::vector<Variable<SpaceIndex<0>,Components<1>,VariableId<0> > >;
  using VariableSet = VariableSetDescription<Spaces,VariableDescriptions>;
  using Functional = HeatFunctional<double,VariableSet>;
  using Assembler = VariationalFunctionalAssembler<LinearizationAt<Functional> >;
  constexpr int neq = Functional::TestVars::noOfVariables;
  using CoefficientVectors = VariableSet::CoefficientVectorRepresentation<0,neq>::type;
  using LinearSpace = VariableSet::CoefficientVectorRepresentation<0,neq>::type;
  // avoid collision of reference to the Kaskade CG with an equal named enum value in the Alberta headers
  using CG = Kaskade::CG<LinearSpace,LinearSpace>;

  // construction of finite element space for the scalar solution T.
  H1Space temperatureSpace(gridManager,gridManager.grid().leafGridView(),order);

  Spaces spaces(&temperatureSpace);

  // construct variable list.
  // VariableDescription<int spaceId, int components, int Id>
  // spaceId: number of associated FEFunctionSpace
  // components: number of components in this variable
  // Id: number of this variable

  std::string varNames[1] = { "u" };

  VariableSet variableSet(spaces,varNames);

  // construct variational functional

  double kappa = 1.0;
  double q = 1.0;
  Functional F(kappa,q);
  constexpr int nvars = Functional::AnsatzVars::noOfVariables;
  std::cout << std::endl << "no of variables = " << nvars << std::endl;
  std::cout << "no of equations = " << neq   << std::endl;
  size_t dofs = variableSet.degreesOfFreedom(0,nvars);
  std::cout << "number of degrees of freedom = " << dofs   << std::endl;

  //construct Galerkin representation
  Assembler assembler(gridManager,spaces);
  VariableSet::VariableSet u(variableSet);
  VariableSet::VariableSet du(variableSet);

  size_t nnz = assembler.nnz(0,neq,0,nvars,onlyLowerTriangle);
  std::cout << "number of nonzero elements in the stiffness matrix: " << nnz << std::endl << std::endl;

  boost::timer::cpu_timer assembTimer;
  CoefficientVectors solution(VariableSet::CoefficientVectorRepresentation<0,neq>::init(spaces));
  solution = 0;

  // UG seems to admit concurrent reads while claiming not to be thread safe. In this case we enforce multithreading during assembly.
  gridManager.enforceConcurrentReads(std::is_same<Grid,Dune::UGGrid<dim> >::value);
  assembler.setNSimultaneousBlocks(blocks);
  assembler.setRowBlockFactor(rowBlockFactor);
  assembler.assemble(linearization(F,u),assembler.MATRIX|assembler.RHS|assembler.VALUE,nthreads,verbosity);
  std::cout << "computing time for assemble: " << boost::timer::format(assembTimer.elapsed()) << "\n";

  CoefficientVectors rhs(assembler.rhs());
  AssembledGalerkinOperator<Assembler,0,neq,0,nvars> A(assembler, onlyLowerTriangle);

  // matrix may be used in triplet format, e.g.,
  // MatrixAsTriplet<double> tri = A.get<MatrixAsTriplet<double> >();
  //     for (k=0; k< nnz; k++)
  //       {
  //         printf("%3d %3d %e\n", tri.ridx[k], tri.cidx[k], tri.data[k]);
  //       }

  if (direct)
  {
    boost::timer::cpu_timer directTimer;
    directInverseOperator(A,directType,property).applyscaleadd(-1.0,rhs,solution);
    u.data = solution.data;
    std::cout << "computing time for direct solve: " << boost::timer::format(directTimer.elapsed()) << "\n";
  }
  else
  {
    boost::timer::cpu_timer iteTimer;
    Dune::InverseOperatorResult res;
    const DefaultDualPairing<LinearSpace,LinearSpace> defaultScalarProduct{};
    int iteSteps = getParameter(pt, "solver.iteMax", 2000);
    double iteEps = getParameter(pt, "solver.iteEps", 1.0e-10);
    StrakosTichyPTerminationCriterion<double> termination(iteEps,iteSteps);
    int lookAhead;
    switch (precondType)
    {
      case PrecondType::NONE:
      case PrecondType::HB:   lookAhead=50; break;
      default:                lookAhead=3; break;
    }
    lookAhead = getParameter(pt, "solver.lookAhead", lookAhead);
    termination.setLookAhead(lookAhead);

    switch (precondType)
    {
      case PrecondType::NONE:
      {
        std::cout << "selected preconditioner: NONE" << std::endl;
        TrivialPreconditioner<AssembledGalerkinOperator<Assembler,0,neq,0,nvars> > trivial;
        CG cg(A,trivial,defaultScalarProduct,termination,verbosity);
        cg.apply(solution,rhs,res);
      }
      break;
      case PrecondType::ADDITIVESCHWARZ:
      {
        std::cout << "selected preconditioner: ADDITIVESCHWARZ" << std::endl;
        std::pair<size_t,size_t> idx = temperatureSpace.mapper().globalIndexRange(gridManager.grid().leafIndexSet().geomTypes(dim)[0]);
        AdditiveSchwarzPreconditioner<AssembledGalerkinOperator<Assembler,0,neq,0,nvars> > addschwarz(A,idx.first,idx.second,verbosity);
        CG cg(A,addschwarz,defaultScalarProduct,termination,verbosity);
        cg.apply(solution,rhs,res);
      }
      break;
      case PrecondType::ILUT:
      {
        std::cout << "selected preconditioner: ILUT" << std::endl;
//        std::cout << "Note that this preconditioner combined with the BICGSTAB solver" << std::endl;
        std::cout << "needs matrix.property = GENERAL" << std::endl;
        int lfil = getParameter(pt, "solver.ILUT.lfil", 140);
        double dropTol = getParameter(pt, "solver.ILUT.dropTol", 0.01);
        ILUTPreconditioner<AssembledGalerkinOperator<Assembler,0,neq,0,nvars> > ilut(A,lfil,dropTol,verbosity);
        CG cg(A,ilut,defaultScalarProduct,termination,verbosity);
        cg.apply(solution,rhs,res);
//         Dune::BiCGSTABSolver<LinearSpace> cg(A,ilut,iteEps,iteSteps,verbosity);
//         cg.apply(solution,rhs,res);
      }
      break;
      case PrecondType::ILUK:
      {
        std::cout << "selected preconditioner: ILUK" << std::endl;
//        std::cout << "Note that this preconditioner combined with the BICGSTAB solver" << std::endl;
        std::cout << "needs matrix.property = GENERAL" << std::endl;
        int fill_lev = getParameter(pt, "solver.ILUK.fill_lev", 3);
        ILUKPreconditioner<AssembledGalerkinOperator<Assembler,0,neq,0,nvars> > iluk(A,fill_lev,verbosity);
        CG cg(A,iluk,defaultScalarProduct,termination,verbosity);
        cg.apply(solution,rhs,res);
//         Dune::BiCGSTABSolver<LinearSpace> cg(A,iluk,iteEps,iteSteps,verbosity);
//         cg.apply(solution,rhs,res);
      }
      break;
      case PrecondType::ARMS:
      {
        int lfil = getParameter(pt, "solver.ARMS.lfil", 140);
        int lev_reord = getParameter(pt, "solver.ARMS.lev_reord", 1);
        double dropTol = getParameter(pt, "solver.ARMS.dropTol", 0.01);
        double tolind = getParameter(pt, "solver.ARMS.tolind", 0.2);
        ARMSPreconditioner<AssembledGalerkinOperator<Assembler,0,neq,0,nvars> > iluk(A,lfil,dropTol,lev_reord,tolind,verbosity);
        CG cg(A,iluk,defaultScalarProduct,termination,verbosity);
        cg.apply(solution,rhs,res);
      }
      break;
      case PrecondType::ICC:
      {
        std::cout << "selected preconditioner: ICC" << std::endl;
        if (property != MatrixProperties::SYMMETRIC)
        {
          std::cout << "PrecondType::ICC preconditioner of TAUCS lib has to be used with matrix.property==MatrixProperties::SYMMETRIC\n";
          std::cout << "i.e., call the executable with option --solver.property MatrixProperties::SYMMETRIC\n\n";
        }
        double dropTol = getParameter(pt, "solver.ICC.dropTol", 0.01);;
        ICCPreconditioner<AssembledGalerkinOperator<Assembler,0,neq,0,nvars> > icc(A,dropTol);
        CG cg(A,icc,defaultScalarProduct,termination,verbosity);
        cg.apply(solution,rhs,res);
      }
      break;
      case PrecondType::ICC0:
      {
        std::cout << "selected preconditioner: ICC0" << std::endl;
        ICC_0Preconditioner<AssembledGalerkinOperator<Assembler,0,neq,0,nvars> > icc0(A);
        CG cg(A,icc0,defaultScalarProduct,termination,verbosity);
        cg.apply(solution,rhs,res);
      }
      break;
      case PrecondType::HB:
      {
        std::cout << "selected preconditioner: HB" << std::endl;
        HierarchicalBasisPreconditioner<Grid,AssembledGalerkinOperator<Assembler,0,neq,0,nvars>::range_type, AssembledGalerkinOperator<Assembler,0,neq,0,nvars>::range_type > hb(gridManager.grid());
        CG cg(A,hb,defaultScalarProduct,termination,verbosity);
        cg.apply(solution,rhs,res);
      }
      break;
      case PrecondType::BOOMERAMG:
      {
        int steps = getParameter(pt, "solver.BOOMERAMG.steps", iteSteps);
        int coarsentype = getParameter(pt, "solver.BOOMERAMG.coarsentype", 21);
        int interpoltype = getParameter(pt, "solver.BOOMERAMG.interpoltype", 0);
        int cycleType = getParameter(pt, "solver.BOOMERAMG.cycleType", 1);
        int relaxType = getParameter(pt, "solver.BOOMERAMG.relaxType", 3);
        int variant = getParameter(pt, "solver.BOOMERAMG.variant", 0);
        int overlap = getParameter(pt, "solver.BOOMERAMG.overlap", 1);
        double tol = getParameter(pt, "solver.BOOMERAMG.tol", iteEps);
        double strongThreshold = getParameter(pt, "solver.BOOMERAMG.strongThreshold", (dim==2)?0.25:0.6);
        BoomerAMG<AssembledGalerkinOperator<Assembler,0,neq,0,nvars> >
                  boomerAMGPrecon(A,steps,coarsentype,interpoltype,tol,cycleType,relaxType,
                  strongThreshold,variant,overlap,1,verbosity);
        CG cg(A,boomerAMGPrecon,defaultScalarProduct,termination,verbosity);
        cg.apply(solution,rhs,res);
//         Dune::LoopSolver<LinearSpace> cg(A,boomerAMGPrecon,iteEps,iteSteps,verbosity);
//         cg.apply(solution,rhs,res);
      }
      break;
      case PrecondType::EUCLID:
      {
        std::cout << "selected preconditioner: EUCLID" << std::endl;
        int level      = getParameter(pt, "solver.EUCLID.level",1);
        double droptol = getParameter(pt, "solver.EUCLID.droptol",0.01);
        int printlevel = 0;
        if (verbosity>2) printlevel=verbosity-2;
        printlevel = getParameter(pt,"solver.EUCLID.printlevel",printlevel);
        int bj = getParameter(pt, "solver.EUCLID.bj",0);
        Euclid<AssembledGalerkinOperator<Assembler,0,neq,0,nvars> > EuclidPrecon(A,level,droptol,printlevel,bj,verbosity);
        CG cg(A,EuclidPrecon,defaultScalarProduct,termination,verbosity);
        cg.apply(solution,rhs,res);
     }
      break;
      case PrecondType::JACOBI:
      default:
      {
        std::cout << "selected preconditioner: JACOBI" << std::endl;
        JacobiPreconditioner<AssembledGalerkinOperator<Assembler,0,neq,0,nvars> > jacobi(A,1.0);
        CG cg(A,jacobi,defaultScalarProduct,termination,verbosity);
        cg.apply(solution,rhs,res);
      }
      break;
    }
    solution *= -1.0;
    u.data = solution.data;

    std::cout << "iterative solve eps= " << iteEps << ": "
              << (res.converged?"converged":"failed") << " after "
              << res.iterations << " steps, rate="
              << res.conv_rate << ", computing time=" << (double)(iteTimer.elapsed().user)/1e9 << "s\n";
  }

  // compute L2 norm of the solution
  boost::timer::cpu_timer outputTimer;
  L2Norm l2Norm;
  std::cout << "L2norm(solution) = " << l2Norm(boost::fusion::at_c<0>(u.data)) << std::endl;

  // output of solution in VTK format for visualization,
  // the data are written as ascii stream into file temperature.vtu,
  // possible is also binary
  writeVTKFile(u,"temperature",IoOptions().setOrder(order).setPrecision(7));

  std::cout << "graphical output finished, data in VTK format is written into file temperature.vtu \n";

  // output of solution for Amira visualization,
  // the data are written in binary format into file temperature.am,
  // possible is also ascii
  // IoOptions options;
  // options.outputType = IoOptions::ascii;
  // LeafView leafGridView = gridManager.grid().leafGridView();
  // writeAMIRAFile(leafGridView,variableSet,u,"temperature",options);

  std::cout << "computing time for output: " << boost::timer::format(outputTimer.elapsed()) << "\n";

  std::cout << "total computing time: " << boost::timer::format(totalTimer.elapsed()) << "\n";
  std::cout << "End heat transfer tutorial program" << std::endl;
}


/*!
 * @file right_hand_side.hpp
 * @brief Contains implementation of right-hand side.
 * @author Konrad Simon
 * @date August 2019
 */

#ifndef INCLUDE_RIGHT_HAND_SIDE_HPP_
#define INCLUDE_RIGHT_HAND_SIDE_HPP_

// Deal.ii
#include <deal.II/base/function.h>

// STL
#include <cmath>
#include <fstream>

// My Headers
#include "coefficients.h"

namespace Coefficients
{
using namespace dealii;

/*!
 * @class RightHandSide
 * @brief Class implements scalar right-hand side function.
 *
 * The right-hand side represents some external forcing parameter.
 */
template <int dim>
class RightHandSide : public Function<dim>
{
public:
	RightHandSide() : Function<dim>() {}

	virtual double value(const Point<dim> &p,
						const unsigned int component = 0) const override;
	virtual void value_list(const std::vector<Point<dim>> &points,
							std::vector<double>  &values,
							const unsigned int component = 0) const override;
};

template <int dim>
double
RightHandSide<dim>::value(const Point<dim>& /*p*/,
							   const unsigned int /*component*/) const
{
	double return_value = 2.0;

	return return_value;
}

template <int dim>
void
RightHandSide<dim>::value_list(const std::vector<Point<dim>> &points,
								std::vector<double>  &values,
								const unsigned int /*component = 0*/) const
{
	Assert (points.size() == values.size(),
			ExcDimensionMismatch (points.size(), values.size()) );

	for ( unsigned int p=0; p<points.size(); ++p)
	{
		values[p] = 2.0;
	} // end ++p
}

} // end namespace Coefficients

#endif /* INCLUDE_RIGHT_HAND_SIDE_HPP_ */


/***
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF 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.
 */

#include <boost/ut.hpp>

#include <mpp/mat.hpp>

#include "../include/utils.hpp"

#include <initializer_list>
#include <vector>

using namespace boost::ut;
using namespace boost::ut::bdd;
using namespace mpp;

namespace
{
	template<typename Mats, bool Move>
	void test_assign_rng(std::string_view test_name)
	{
		test(test_name.data()) = [test_name]<typename Mat, typename Mat2>(
									 std::tuple<std::type_identity<Mat>, std::type_identity<Mat2>>) {
			auto [mat, vec2d, expected_mat] =
				parse_test(test_name, parse_mat<Mat>, parse_rng2d<typename Mat::value_type>, parse_mat<Mat2>);

			if constexpr (Move)
			{
				mat = std::move(vec2d);
			}
			else
			{
				mat = vec2d;
			}

			cmp_mat_to_expr_like(mat, expected_mat);
		} | Mats{};
	}

	template<typename Mats, bool Move>
	void test_assign_mat(std::string_view test_name)
	{
		test(test_name.data()) =
			[test_name]<typename Mat, typename Mat2, typename Mat3>(
				std::tuple<std::type_identity<Mat>, std::type_identity<Mat2>, std::type_identity<Mat3>>) {
				auto [mat, mat2, expected_mat] =
					parse_test(test_name, parse_mat<Mat>, parse_mat<Mat2>, parse_mat<Mat3>);

				if constexpr (Move)
				{
					mat = std::move(mat2);
				}
				else
				{
					mat = mat2;
				}

				cmp_mat_to_expr_like(mat, expected_mat);
			} |
			Mats{};
	}
} // namespace

int main()
{
	// @NOTE: This covers copy and move assignment from rule of five (2/5)

	feature("Assigning a 2D range with same dimensions") = []() {
		using mats = join_mats<all_mats<int, 2, 3>, all_mats<int, 2, 3>>;

		test_assign_rng<mats, false>("assign/2x3_same_dims.txt");
		test_assign_rng<mats, true>("assign/2x3_same_dims.txt");
	};

	feature("Assigning a matrix with same dimensions") = []() {
		using mats = join_mats<all_mats<int, 2, 3>, all_mats<int, 2, 3>, all_mats<int, 2, 3>>;

		test_assign_mat<mats, false>("assign/2x3_same_dims.txt");
		test_assign_mat<mats, true>("assign/2x3_same_dims.txt");
	};

	feature("Assigning a matrix with same dimensions but different types") = []() {
		using mats = join_mats<all_mats<int, 2, 3>, all_mats_reverse<int, 2, 3>, all_mats<int, 2, 3>>;

		test_assign_mat<mats, false>("assign/2x3_same_dims.txt");
		test_assign_mat<mats, true>("assign/2x3_same_dims.txt");
	};

	feature("Expanding dynamic matrices by 2D range (dynamic matrices only)") = []() {
		using mats = join_mats<dyn_mat<int>, dyn_mat<int>>;

		test_assign_rng<mats, false>("assign/2x3_10x10_expand.txt");
		test_assign_rng<mats, true>("assign/2x3_10x10_expand.txt");
		test_assign_rng<mats, false>("assign/2x3_10x3_expand.txt");
		test_assign_rng<mats, true>("assign/2x3_10x3_expand.txt");
		test_assign_rng<mats, false>("assign/2x3_2x10_expand.txt");
		test_assign_rng<mats, true>("assign/2x3_2x10_expand.txt");
	};

	feature("Shrinking dynamic matrices by 2D range (dynamic matrices only)") = []() {
		using mats = join_mats<dyn_mat<int>, dyn_mat<int>>;

		test_assign_rng<mats, false>("assign/10x10_2x3_shrink.txt");
		test_assign_rng<mats, true>("assign/10x10_2x3_shrink.txt");
		test_assign_rng<mats, false>("assign/10x3_2x3_shrink.txt");
		test_assign_rng<mats, true>("assign/10x3_2x3_shrink.txt");
		test_assign_rng<mats, false>("assign/2x10_2x3_shrink.txt");
		test_assign_rng<mats, true>("assign/2x10_2x3_shrink.txt");
	};

	feature("Expanding dynamic matrices by another matrix (dynamic matrices only)") = []() {
		using mats = join_mats<dyn_mat<int>, dyn_mat<int>, dyn_mat<int>>;

		test_assign_mat<mats, false>("assign/2x3_10x10_expand.txt");
		test_assign_mat<mats, true>("assign/2x3_10x10_expand.txt");
		test_assign_mat<mats, false>("assign/2x3_10x3_expand.txt");
		test_assign_mat<mats, true>("assign/2x3_10x3_expand.txt");
		test_assign_mat<mats, false>("assign/2x3_2x10_expand.txt");
		test_assign_mat<mats, true>("assign/2x3_2x10_expand.txt");
	};

	feature("Shrinking dynamic matrices by another matrix (dynamic matrices only)") = []() {
		using mats = join_mats<dyn_mat<int>, dyn_mat<int>, dyn_mat<int>>;

		test_assign_mat<mats, false>("assign/10x10_2x3_shrink.txt");
		test_assign_mat<mats, true>("assign/10x10_2x3_shrink.txt");
		test_assign_mat<mats, false>("assign/10x3_2x3_shrink.txt");
		test_assign_mat<mats, true>("assign/10x3_2x3_shrink.txt");
		test_assign_mat<mats, false>("assign/2x10_2x3_shrink.txt");
		test_assign_mat<mats, true>("assign/2x10_2x3_shrink.txt");
	};

	return 0;
}


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

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

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

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

    }
}

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

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

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

            cv_type cv2;
            rv_type rv2;
            typedef typename matrix_type::type scalar_type;
            scalar_type val;

            counter_gemv() = 0;
            cv2 = m*cv;
            DLIB_TEST(counter_gemv() == 1);

            counter_gemv() = 0;
            cv2 = m*2*cv;
            DLIB_TEST(counter_gemv() == 1);

            counter_gemv() = 0;
            cv2 = m*2*trans(rv);
            DLIB_TEST(counter_gemv() == 1);

            counter_gemv() = 0;
            rv2 = trans(m*2*cv);
            DLIB_TEST(counter_gemv() == 1);

            counter_gemv() = 0;
            rv2 = rv*m;
            DLIB_TEST(counter_gemv() == 1);

            counter_gemv() = 0;
            rv2 = (rv + rv)*m;
            DLIB_TEST(counter_gemv() == 1);

            counter_gemv() = 0;
            rv2 = trans(cv)*m;
            DLIB_TEST(counter_gemv() == 1);
            dlog << dlib::LTRACE << 1;

            counter_gemv() = 0;
            rv2 = trans(cv)*trans(m) + rv*trans(m);
            DLIB_TEST(counter_gemv() == 2);
            dlog << dlib::LTRACE << 2;

            counter_gemv() = 0;
            cv2 = m*trans(trans(cv)*trans(m) + 3*rv*trans(m));
            DLIB_TEST(counter_gemv() == 3);

            // This does one dot and one gemv
            counter_gemv() = 0;
            val = trans(cv)*m*trans(rv);
            DLIB_TEST_MSG(counter_gemv() == 1, counter_gemv());

            // This does one dot and two gemv
            counter_gemv() = 0;
            val = (trans(cv)*m)*(m*trans(rv));
            DLIB_TEST_MSG(counter_gemv() == 2, counter_gemv());

            // This does one dot and two gemv
            counter_gemv() = 0;
            val = trans(cv)*m*trans(m)*trans(rv);
            DLIB_TEST_MSG(counter_gemv() == 2, counter_gemv());
        }

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

            cv_type cv2;
            rv_type rv2;

            counter_gemv() = 0;
            cv2 = trans(cv)*conj(m);
            DLIB_TEST(counter_gemv() == 1);

            counter_gemv() = 0;
            cv2 = conj(trans(m))*rv;
            DLIB_TEST(counter_gemv() == 1);

            counter_gemv() = 0;
            cv2 = conj(trans(m))*trans(cv);
            DLIB_TEST(counter_gemv() == 1);

            counter_gemv() = 0;
            cv2 = trans(trans(cv)*conj(2*m) + conj(3*trans(m))*rv + conj(trans(m)*3)*trans(cv));
            DLIB_TEST(counter_gemv() == 3);

        }

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

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

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

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

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

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

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

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

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

            print_spinner();
        }
    };

    blas_bindings_gemv_tester a;

}


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

// Copyright (c) 2015, Oracle and/or its affiliates

// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle

// Licensed under the Boost Software License version 1.0.
// http://www.boost.org/users/license.html

//[is_empty
//` Check if a geometry is the empty set

#include <iostream>

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>

int main()
{
    boost::geometry::model::multi_linestring
        <
            boost::geometry::model::linestring
                <
                    boost::geometry::model::d2::point_xy<double>
                >
        > mls;
    boost::geometry::read_wkt("MULTILINESTRING((0 0,0 10,10 0),(1 1,8 1,1 8))", mls);
    std::cout << "Is empty? " << (boost::geometry::is_empty(mls) ? "yes" : "no") << std::endl;
    boost::geometry::clear(mls);
    std::cout << "Is empty (after clearing)? " << (boost::geometry::is_empty(mls) ? "yes" : "no") << std::endl;
    return 0;
}

//]

//[is_empty_output
/*`
Output:
[pre
Is empty? no
Is empty (after clearing)? yes
]
*/
//]


/**
 *
 * Copyright (C) 2021 Mohammad Javad Dousti, Qing Xie, Mahdi Nazemi,
 * and Massoud Pedram. All rights reserved.
 *
 * Please refer to the LICENSE file for terms of use.
 *
 */

#pragma once

#include <Eigen/SparseCore>
#include <Eigen/Core>

#include "general.hpp"

// OpenMP header file
#include <omp.h>

#define NEGLIGIBLE_EPSILON 1e-5

namespace utils {
VALUE KtoC(VALUE temp);

void dumpMatrix(const Eigen::SparseMatrix<VALUE>& matrix, const string& file_output);
void dumpVector(const Eigen::Matrix<VALUE, Eigen::Dynamic, 1>& vec, const string& file_output);

bool fileExists(const std::string &name);

VALUE calcElapsedTime(
    const std::chrono::high_resolution_clock::time_point &start,
    const std::chrono::high_resolution_clock::time_point &end);

// Inline function for speed up.
inline bool eq(VALUE a, VALUE b) { return fabs(a - b) < NEGLIGIBLE_EPSILON; }
inline bool neq(VALUE a, VALUE b) { return !eq(a, b); }
inline bool less(VALUE a, VALUE b) { return (b - a) > NEGLIGIBLE_EPSILON; }
inline bool le(VALUE a, VALUE b) { return ((a < b) || eq(a, b)); }
inline bool ge(VALUE a, VALUE b) { return ((a > b) || eq(a, b)); }
inline bool greater(VALUE a, VALUE b) { return (a - b) > NEGLIGIBLE_EPSILON; }

}; // namespace utils


/*
 * @file
 *
 *  Created on: Oct 8, 2018
 *      Author: koldar
 */

#ifndef XYLOC_H_
#define XYLOC_H_

#include <boost/functional/hash.hpp>
#include <cstdint>
#include <iostream>
#include "types.hpp"

namespace pathfinding {

	/**
	 * A direction of a movement within the grid
	 */
	enum class Direction {
		NORTH,
		SOUTH,
		EAST,
		WEST,
		NORTHWEST,
		NORTHEAST,
		SOUTHWEST,
		SOUTHEAST
	};

	namespace DirectionMethods {

		/**
		 * @brief
		 *
		 * @param dir  the direction involved
		 * @return true if dir is either north, south, east or west
		 * @return false otherwise
		 */
		bool isStraight(Direction dir);

		/**
		 * @brief
		 *
		 * @param dir the direction involved
		 * @return true if dir is not straight
		 * @return false otherwise
		 */
		bool isDiagonal(Direction dir);

		/**
		 * @param[in] dir a direction
		 * @return the label associated
		 * ted to a direction
		 */
		const char* getLabel(const Direction& dir);

	}

	std::ostream& operator <<(std::ostream& ss, const Direction& dir);

	/**
	 * represent a position in the grid.
	 *
	 * Inside the grid, "x" is the column while "y" is the row.
	 *
	 */
	struct xyLoc {
		///x coordinate
		ucood_t x;
		///y coordinate
		ucood_t y;

		xyLoc(ucood_t x, ucood_t y) : x{x}, y{y} {

		}

		xyLoc(ucood_t t): x{t}, y{t} {

		}

		xyLoc(): x{0}, y{0} {

		}

		xyLoc(const xyLoc& other): x{other.x}, y{other.y} {

		}

		xyLoc(xyLoc&& other): x{other.x}, y{other.y} {

		}

		xyLoc& operator =(const xyLoc& other) {
			this->x = other.x;
			this->y = other.y;
			return *this;
		};
		xyLoc& operator =(xyLoc&& other) {
			this->x = other.x;
			this->y = other.y;
			return *this;
		};
		xyLoc& operator +=(const xyLoc& other) = delete;
		xyLoc& operator -=(const xyLoc& other) = delete;
		xyLoc& operator *=(const xyLoc& other) = delete;
		xyLoc& operator /=(const xyLoc& other) = delete;

		/**
		 * The direction a cell is relative to another one
		 *
		 * for example if the current location is <tt>5,5</tt> and @c to is <tt>5,4</tt> the direction will be @c west because
		 * the second cell is on the west of the first one.
		 *
		 * @param[in] to the cell (other than this one) where we need to look at
		 * @return the direction between this cell and @c
		 */
		Direction getDirectionTo(const xyLoc& to) const;

		/**
		 * like ::xyLoc::getDirectionTo but we can specific 2 location, not just one
		 *
		 * @param[in] from the first cell to consider
		 * @param[in] to the second cell to consider
		 * @return the direction @c to is relative to @c from
		 */
		static Direction getDirection(const xyLoc& from, const xyLoc& to);

		/**
		 *
		 * 2 locations are immediately adjacent one to the other iff they shares a side or a point.
		 *
		 * for example \f$(4,5)\f$ is adjacent with \f$(4,6)\f$ or \f$(3,4)\f$ but is not adjacent with \f$(100, 400)\f$.
		 *
		 * If @c US represents this xyLoc location, "OK" stands for location immediately adjacent while "KO" stands for location not immediately adjacent.
		 *
		 *
		 * |--|--|--|--|
		 * |OK|OK|OK|KO|
		 * |OK|US|OK|KO|
		 * |OK|OK|OK|KO|
		 *
		 * @note
		 * This definition does not take into consideration if the underlying map is traversable or not in the involved locations
		 *
		 *
		 * @param[in] other the location to test against
		 * @return
		 *  @li true if this location is immediately adjacent to @c other;
		 *  @li false otherwise
		 */
		bool isAdjacentTo(const xyLoc& other) const;

		/**
		 * @brief check if, starting from this, we can go in a particular direction
		 *
		 * @code
		 * 	{5,3}.isThereLocationInDirectionOf(LEFT, {5,5}); //yes
		 *  {5,3}.isThereLocationInDirectionOf(RIGHT, {5,5}); //no
		 * @endcode
		 *
		 * @param dir the direction we should follow to check if there is another xyLoc.
		 * @param maxPoint coordinates representing the bottom right corner of an invisible rectangle we cannot escape from. You can have a location set to this point
		 * @param minPoint coordinates representing the top left corner of an invisible rectangle we cannot escape from. You can have a location set to this point
		 * @return true if there is a clocation adjacent to self by following @c dir
		 * @return false
		 */
		bool isThereLocationInDirectionOf(Direction dir, xyLoc maxPoint, xyLoc minPoint = {0, 0}) const;

		/**
		 * @brief Get the Nearby Diagonale Cells object
		 *
		 * If `loc1` and `loc2` are marked as `1` and `2` the out locations will be "a" and "b"
		 * @code
		 * 1|a
		 * -|-
		 * b|2
		 * @endcode
		 *
		 * @pre
		 *  @li loc1 adjacent to loc2
		 *
		 * @param loc1 first location
		 * @param loc2 second location
		 * @return a pair representing the adjacent cells
		 */
		static std::pair<xyLoc, xyLoc> getNearbyDiagonalCells(const xyLoc& loc1, const xyLoc& loc2);

		/**
		 * The coordiante system is in the topLeft corner (0,0) while the infinity is in the bottomRight
		 *
		 * locationns within the border of the rectangle are considered as well
		 *
		 * @param[in] topLeft the point representing a point of the rectangle
		 * @param[in] bottomRight the point representing a point of the rectangle
		 * @return
		 *  @li true if the point is inside the rectangle generated by @c topLeft and @c bottomRight
		 */
		bool isInside(const xyLoc& topLeft, const xyLoc& bottomRight) const {
			return (topLeft.x <= this->x)&&(this->x <= bottomRight.x) &&
					(topLeft.y <= this->y)&&(this->y <= bottomRight.y);
		}

		/**
		 * @brief return the distance between 2 points, ignoring sign
		 *
		 * For example here:
		 *
		 * ```
		 * | |B| | |
		 * | | | | |
		 * | | | | |
		 * | | | |A|
		 * ```
		 *
		 * it would be `<-2, -3>` but with this method it will return `<2, 3>`
		 *
		 * @param other the other point
		 * @return xyLoc
		 */
		xyLoc getDistance(const xyLoc& other) const {
			cood_t ax = static_cast<cood_t>(this->x);
			cood_t ay = static_cast<cood_t>(this->y);
			cood_t bx = static_cast<cood_t>(other.x);
			cood_t by = static_cast<cood_t>(other.y);

			return xyLoc{
				static_cast<ucood_t>(std::abs(ax - bx)),
				static_cast<ucood_t>(std::abs(ay - by))
			};
		}

		/**
		 * @brief get the adjacent cell of self by following a direction
		 *
		 * @note
		 * UB if you don't check with ::isThereLocationInDirectionOf if the direction might generate a result
		 *
		 * @param dir ther direction to follow to obtain a new location
		 * @return xyLoc the adjacent location by following a particular direction
		 */
		xyLoc getAdjacent(Direction dir) const;

		/**
		 * @brief Get the Min Coordinate object
		 *
		 * @return ucood_t the coordinate which has the least value between x and y
		 */
		ucood_t getMinCoordinate() const {
			return this->x < this->y ? this->x : this->y;
		}

		/**
		 * @brief Get the Max Coordinate object
		 *
		 * @return ucood_t the coordinate which has the greatest value between x and y
		 */
		ucood_t getMaxCoordinate() const {
			return this->x > this->y ? this->x : this->y;
		}

		friend std::size_t hash_value(const xyLoc& p) {
			std::size_t seed = 0;
			boost::hash_combine(seed, p.x);
			boost::hash_combine(seed, p.y);

			return seed;
		}
	};

	std::ostream& operator<<(std::ostream& str, const xyLoc& v);
	bool operator==(const xyLoc& a, const xyLoc& b);
	bool operator!=(const xyLoc& a, const xyLoc& b);
	xyLoc operator +(const xyLoc& a, const xyLoc& b);
	xyLoc operator +(const xyLoc& a, int value);
	xyLoc operator -(const xyLoc& a, const xyLoc& b);
	xyLoc operator -(const xyLoc& a, int value);

	/**
	 * @brief anew xyLoc containing the minimum of the coordinates of 2 xyLoc
	 *
	 * @code
	 * 	min.x = min(a.x, b.x);
	 *  min.y = min(a.y, b.y);
	 * @endcode
	 *
	 * @param a first location
	 * @param b second location
	 * @return xyLoc minimum of coordinates
	 */
	xyLoc min(const xyLoc& a, const xyLoc& b);
	/**
	 * @brief a new xyLoc containing the maximum of the coordinates of 2 xyLoc
	 *
	 * @code
	 * 	max.x = max(a.x, b.x);
	 *  max.y = max(a.y, b.y);
	 * @endcode
	 *
	 * @param a first location
	 * @param b second location
	 * @return xyLoc maximum of coordinates
	 */
	xyLoc max(const xyLoc& a, const xyLoc& b);

}

namespace std {

	template <>
	struct hash<pathfinding::xyLoc> {
	public:
		size_t operator()(const pathfinding::xyLoc& l) const {
			size_t seed = 0;
			boost::hash_combine(seed, l.x);
			boost::hash_combine(seed, l.y);
			return seed;
		}
	};

}

#endif /* XYLOC_H_ */


/* vim: set tabstop=4 expandtab shiftwidth=4 softtabstop=4: */

/**
 * \file boost/numeric/ublasx/operation/which.hpp
 *
 * \brief Find the positions of the elments of a given container which satisfy
 *  a given unary predicate.
 *
 * \author Marco Guazzone (marco.guazzone@gmail.com)
 *
 * <hr/>
 *
 * Copyright (c) 2010, Marco Guazzone
 *
 * Distributed under the Boost Software License, Version 1.0. (See
 * accompwhiching file LICENSE_1_0.txt or copy at
 * http://www.boost.org/LICENSE_1_0.txt)
 */

#ifndef BOOST_NUMERIC_UBLASX_OPERATION_WHICH_HPP
#define BOOST_NUMERIC_UBLASX_OPERATION_WHICH_HPP

#include <boost/numeric/ublas/detail/config.hpp>
#include <boost/numeric/ublas/expression_types.hpp>
#include <boost/numeric/ublas/traits.hpp>
#include <boost/numeric/ublas/vector.hpp>
#include <boost/numeric/ublasx/operation/num_columns.hpp>
#include <boost/numeric/ublasx/operation/num_rows.hpp>
#include <boost/numeric/ublasx/operation/size.hpp>
#include <functional>

//TODO: implement the 'which' operation for matrix expressions

namespace boost { namespace numeric { namespace ublasx {

using namespace ::boost::numeric::ublas;

//@{ Declarations

/**
 * \brief Find the positions of the elments of the given vector expression
 *  which satisfy the given unary predicate.
 * \tparam VectorExprT The type of the vector expression.
 * \tparam UnaryPredicateT The type of the unary predicate functor.
 * \param ve The vector expression over which check for the predicate.
 * \param p The unary predicate functor: must accept one argument and return
 *  a boolean value.
 * \return A vector of positions of the elements of the given vector
 *  expression which satisfy the given predicate; an empty vector, if no element
 *  satisfies the given predicate.
 *
 * \author Marco Guazzone, <marco.guazzone@gmail.com>
 */
template <typename VectorExprT, typename UnaryPredicateT>
vector<typename vector_traits<VectorExprT>::size_type> which(vector_expression<VectorExprT> const& ve, UnaryPredicateT p);

/**
 * \brief Find the positions of the non-zero elments of the given vector
 *  expression.
 * \tparam VectorExprT The type of the vector expression.
 * \param ve The vector expression over which check for existence of non-zero
 *  elements.
 * \return A vector of positions of the non-zero elements of the given vector
 *  expression; an empty vector, if no non-zero element is found.
 *
 * \note The test for zero equality is done in the strong sense, that is by not
 *  using which tolerance.
 *  For checking for "weak" zero equality between a given tolerance use the
 *  two-argument version of this function with an appropriate predicate.
 *
 * \author Marco Guazzone, <marco.guazzone@gmail.com>
 */
template <typename VectorExprT>
vector<typename vector_traits<VectorExprT>::size_type> which(vector_expression<VectorExprT> const& ve);

//@} Declarations

//@{ Definitions

template <typename VectorExprT, typename UnaryPredicateT>
BOOST_UBLAS_INLINE
vector<typename vector_traits<VectorExprT>::size_type> which(vector_expression<VectorExprT> const& ve, UnaryPredicateT p)
{
    typedef typename vector_traits<VectorExprT>::size_type size_type;

    vector<size_type> res;
    size_type n = size(ve);
    size_type j = 0;
    for (size_type i = 0; i < n; ++i)
    {
        if (p(ve()(i)))
        {
            res.resize(res.size()+1);
            res(j++) = i;
        }
    }

    return res;
}

template <typename VectorExprT>
BOOST_UBLAS_INLINE
vector<typename vector_traits<VectorExprT>::size_type> which(vector_expression<VectorExprT> const& ve)
{
    typedef typename vector_traits<VectorExprT>::value_type value_type;

    return which(ve, ::std::bind2nd(::std::not_equal_to<value_type>(), 0));
}

//@} Definitions

}}} // Namespace boost::numeric::ublasx

#endif // BOOST_NUMERIC_UBLASX_OPERATION_WHICH_HPP


/*
*   back up Eigen vector of matrix to CSV file
*   by R. Falque
*   30/07/2019
*/

#ifndef EIGEN_WRITE_TO_CSV_HPP
#define EIGEN_WRITE_TO_CSV_HPP

#include <Eigen/Core>
#include <iostream>
#include <fstream>
#include <string>

const static Eigen::IOFormat CSVFormat(Eigen::StreamPrecision, Eigen::DontAlignCols, ", ", "\n");

template <typename T>
inline bool EigenWriteToCSVfile(Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> matrix, std::string name)
{
    std::ofstream file(name.c_str());
    file << matrix.format(CSVFormat);
    file.close();

    return true;
};

inline bool EigenWriteToCSVfile(Eigen::VectorXi matrix, std::string name)
{
    std::ofstream file(name.c_str());
    file << matrix.format(CSVFormat);
    file.close();

    return true;
};

#endif


//
// Copyright (c) 2009 Rutger ter Borg
//
// 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_NUMERIC_BINDINGS_STD_VALARRAY_HPP
#define BOOST_NUMERIC_BINDINGS_STD_VALARRAY_HPP

#include <boost/numeric/bindings/detail/adaptor.hpp>
#include <valarray>

namespace boost {
namespace numeric {
namespace bindings {
namespace detail {

template< typename T, typename Id, typename Enable >
struct adaptor< std::valarray< T >, Id, Enable > {

    typedef typename copy_const< Id, T >::type value_type;
    typedef mpl::map<
        mpl::pair< tag::value_type, value_type >,
        mpl::pair< tag::entity, tag::vector >,
        mpl::pair< tag::size_type<1>, std::ptrdiff_t >,
        mpl::pair< tag::data_structure, tag::linear_array >,
        mpl::pair< tag::stride_type<1>, tag::contiguous >
    > property_map;

    static std::ptrdiff_t size1( const Id& id ) {
        return id.size();
    }

    static value_type* begin_value( Id& id ) {
        return &const_cast< std::valarray< T >& >( id )[0];
    }

    static value_type* end_value( Id& id ) {
        return &const_cast< std::valarray< T >& >( id )[0] + id.size();
    }

};

} // namespace detail
} // namespace bindings
} // namespace numeric
} // namespace boost

#endif


/*    Copyright (c) 2010-2019, Delft University of Technology
 *    All rigths reserved
 *
 *    This file is part of the Tudat. Redistribution and use in source and
 *    binary forms, with or without modification, are permitted exclusively
 *    under the terms of the Modified BSD license. You should have received
 *    a copy of the license with this file. If not, please or visit:
 *    http://tudat.tudelft.nl/LICENSE.
 *
 *    Notes
 *      If tabs are used as spaces, it doesn't work. The seperator should also be tabs then.
 *
 */

#define BOOST_TEST_MAIN

#include <cmath>
#include <limits>
#include <stdexcept>
#include <iostream>

#include <boost/test/floating_point_comparison.hpp>
#include <boost/test/unit_test.hpp>

#include <Eigen/Core>

#include "Tudat/Basics/testMacros.h"
#include "Tudat/InputOutput/basicInputOutput.h"
#include "Tudat/InputOutput/mapTextFileReader.h"

namespace tudat
{

namespace unit_tests
{

// Test if matrix text file reader is working correctly.
BOOST_AUTO_TEST_CASE( testMatrixTextFileReader )
{

    // Test 1
    {
        // Set expected matrix.
        std::map< int, std::vector< int > > expectedMap = {
            { 1, { 1, 2 } },
            { 2, { 1, 2, 4 } },
            { 3, { 1, 2, 4, 8 } },
            { 4, { 1, 2, 4, 8, 16 } }
        };

        // Read input file and store data in matrix.
        auto inputFileMap = input_output::readStlVectorMapFromFile< int, int >(
                    input_output::getTudatRootPath( ) + "/InputOutput/UnitTests/testMap1.txt" );

        bool allEqual = true;
        // Check if data input file matrix matches expected matrix.
        try
        {
            for ( auto ent: inputFileMap )
            {
                auto key = ent.first;
                auto values = ent.second;
                for ( unsigned int i = 0; i < values.size(); i++ )
                {
                    if ( inputFileMap[ key ][ i ] != expectedMap[ key ][ i ] )
                    {
                        std::cout << "Element at " << i << " for key " << key << " ("
                                  << inputFileMap[ key ][ i ] << ") does not match expected value of "
                                  << "(" << expectedMap[ key ][ i ] << ")." << std::endl;
                        allEqual = false;
                        break;
                    }
                }
                if ( ! allEqual )
                {
                    break;
                }
            }
        }
        catch( std::runtime_error &inconsistentSizes )
        {
            std::cout << "Maps have different keys or inconsistent sizes." << std::endl;
            allEqual = false;
        }

        BOOST_CHECK( allEqual );

    }

    // Test 2
    {
        // Set expected matrix.
        std::map< double, std::vector< float > > expectedMap = {
            { 0.0, {  0.5, 0.8 } },
            { 0.1, { -0.5, 1.0 } },
            { 0.2, {  0.5 } },
            { 0.3, {  1e6, 1.0, 1.0 } }
        };

        // Read input file and store data in matrix.
        auto inputFileMap = input_output::readStlVectorMapFromFile< double, float >(
                    input_output::getTudatRootPath( ) + "/InputOutput/UnitTests/testMap2.txt" );

        bool allEqual = true;
        // Check if data input file matrix matches expected matrix.
        try
        {
            for ( auto ent: inputFileMap )
            {
                auto key = ent.first;
                auto values = ent.second;
                for ( unsigned int i = 0; i < values.size(); i++ )
                {
                    if ( inputFileMap[ key ][ i ] != expectedMap[ key ][ i ] )
                    {
                        std::cout << "Element at " << i << " for key " << key << " ("
                                  << inputFileMap[ key ][ i ] << ") does not match expected value of "
                                  << "(" << expectedMap[ key ][ i ] << ")." << std::endl;
                        allEqual = false;
                        break;
                    }
                }
                if ( ! allEqual )
                {
                    break;
                }
            }
        }
        catch( std::runtime_error &inconsistentSizes )
        {
            std::cout << "Maps have different keys or inconsistent sizes." << std::endl;
            allEqual = false;
        }

        BOOST_CHECK( allEqual );

    }

    // Test 1
    {
        // Set expected matrix.
        std::map< std::string, std::vector< std::string > > expectedMap = {
            { "x", { "one", "two" } },
            { "y", { "three", "four", "five" } },
            { "z", { "six" } }
        };

        // Read input file and store data in matrix.
        auto inputFileMap = input_output::readStlVectorMapFromFile< std::string, std::string >(
                    input_output::getTudatRootPath( ) + "/InputOutput/UnitTests/testMap3.txt" );

        bool allEqual = true;
        // Check if data input file matrix matches expected matrix.
        try
        {
            for ( auto ent: inputFileMap )
            {
                auto key = ent.first;
                auto values = ent.second;
                for ( unsigned int i = 0; i < values.size(); i++ )
                {
                    if ( inputFileMap[ key ][ i ] != expectedMap[ key ][ i ] )
                    {
                        std::cout << "Element at " << i << " for key " << key << " ("
                                  << inputFileMap[ key ][ i ] << ") does not match expected value of "
                                  << "(" << expectedMap[ key ][ i ] << ")." << std::endl;
                        allEqual = false;
                        break;
                    }
                }
                if ( ! allEqual )
                {
                    break;
                }
            }
        }
        catch( std::runtime_error &inconsistentSizes )
        {
            std::cout << "Maps have different keys or inconsistent sizes." << std::endl;
            allEqual = false;
        }

        BOOST_CHECK( allEqual );

    }

}

} // namespace unit_tests

} // namespace tudat


#define BOOST_TEST_MODULE factorial shared library test
#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>

#include "factorial.cpp"

BOOST_AUTO_TEST_CASE(Factorials_for_zero) {
    BOOST_TEST( Factorial(0) == 1 );
}

BOOST_AUTO_TEST_CASE(Factorials_for_positive_numbers) {
    BOOST_TEST( Factorial(1) == 1 );
    BOOST_TEST( Factorial(2) == 2 );
    BOOST_TEST( Factorial(3) == 6 );
    BOOST_TEST( Factorial(10) == 3628800 );
}


/**
 * @file laxwendroffscheme_main.cc
 * @brief NPDE homework "LaxWendroffScheme" code
 * @author Oliver Rietmann
 * @date 29.04.2019
 * @copyright Developed at ETH Zurich
 */

#include <Eigen/Core>
#include <fstream>
#include <iostream>

#include "laxwendroffscheme.h"

using namespace LaxWendroffScheme;

const static Eigen::IOFormat CSVFormat(Eigen::FullPrecision,
                                       Eigen::DontAlignCols, ", ", "\n");

int main() {
  Eigen::VectorXi M(6);
  M << 20, 40, 80, 160, 320, 640;

  Eigen::VectorXd error_LaxWendroffRP = numexpLaxWendroffRP(M);
  Eigen::VectorXd error_LaxWendroffSmoothU0 = numexpLaxWendroffSmoothU0(M);
  Eigen::VectorXd error_GodunovSmoothU0 = numexpGodunovSmoothU0(M);

  std::ofstream file;
  file.open("convergence.csv");
  file << M.transpose().format(CSVFormat) << std::endl;
  file << error_LaxWendroffRP.transpose().format(CSVFormat) << std::endl;
  file << error_LaxWendroffSmoothU0.transpose().format(CSVFormat) << std::endl;
  file << error_GodunovSmoothU0.transpose().format(CSVFormat) << std::endl;
  file.close();
  std::cout << "Generated " CURRENT_BINARY_DIR "/convergence.csv" << std::endl;

  // To plot from convergence.csv uncomment this:
  // std::system("python3 " CURRENT_SOURCE_DIR "/plot.py " CURRENT_BINARY_DIR
  // "/convergence.csv " CURRENT_BINARY_DIR "/convergence.eps");

  return 0;
}


#include <frovedis.hpp>

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

using namespace frovedis;
using namespace std;

int sum(int a, int b) {return a + b;}

BOOST_AUTO_TEST_CASE( frovedis_test )
{
  int argc = 1;
  char** argv = NULL;
  use_frovedis use(argc, argv);

  // filling sample input vector and computing expected output
  std::vector<int> v;
  int ref_out = 0;
  for(size_t i = 1; i <= 8; i++) {
    v.push_back(i);
    ref_out += i;
  }

  // testing on dvector::reduce
  auto d1 = frovedis::make_dvector_scatter(v);
  auto r = d1.reduce(sum);

  // confirming whether reduced result matched with expected output
  BOOST_CHECK(r == ref_out);
}