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

/**
 * \file libs/numeric/ublasx/test/reshape.cpp
 *
 * \brief Test suite for the \c reshape operation.
 *
 * \author Marco Guazzone (marco.guazzone@gmail.com)
 *
 * <hr/>
 *
 * Copyright (c) 2011, Marco Guazzone
 *
 * Distributed under the Boost Software License, Version 1.0. (See
 * accompanying file LICENSE_1_0.txt or copy at
 * http://www.boost.org/LICENSE_1_0.txt)
 */

#include <boost/numeric/ublas/io.hpp>
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/traits.hpp>
#include <boost/numeric/ublasx/detail/debug.hpp>
#include <boost/numeric/ublasx/operation/num_columns.hpp>
#include <boost/numeric/ublasx/operation/num_rows.hpp>
#include <boost/numeric/ublasx/operation/reshape.hpp>
#include <boost/numeric/ublasx/tags.hpp>
#include "libs/numeric/ublasx/test/utils.hpp"

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

static const double tol = 1e-5;

BOOST_UBLASX_TEST_DEF( reshape_by_dim1_col_major )
{
    typedef double value_type;
    typedef ublas::matrix<value_type,ublas::column_major> matrix_type;
    typedef typename ublas::matrix_traits<matrix_type>::size_type size_type;

    const size_type nr(3);
    const size_type nc(4);
    const size_type new_nr(2);
    const size_type new_nc(6);

    matrix_type A(nr,nc);

    A(0,0) =  1; A(0,1) =  4; A(0,2) =  7; A(0,3) = 10;
    A(1,0) =  2; A(1,1) =  5; A(1,2) =  8; A(1,3) = 11;
    A(2,0) =  3; A(2,1) =  6; A(2,2) =  9; A(2,3) = 12;

    matrix_type E(new_nr,new_nc);

    E(0,0) =  1; E(0,1) = 3; E(0,2) = 5; E(0,3) = 7; E(0,4) =  9; E(0,5) = 11;
    E(1,0) =  2; E(1,1) = 4; E(1,2) = 6; E(1,3) = 8; E(1,4) = 10; E(1,5) = 12;

    matrix_type X;
    X = ublasx::reshape<1>(A, new_nr, new_nc);
    BOOST_UBLASX_DEBUG_TRACE("A=" << A);
    BOOST_UBLASX_DEBUG_TRACE("reshape<1>(A," << new_nr << "," << new_nc << ")=" << X);
    BOOST_UBLASX_DEBUG_TRACE("Expected reshape(A," << new_nr << "," << new_nc << ")=" << E);
    BOOST_UBLASX_TEST_CHECK( ublasx::num_rows(X) == new_nr );
    BOOST_UBLASX_TEST_CHECK( ublasx::num_columns(X) == new_nc );
    BOOST_UBLASX_TEST_CHECK_MATRIX_CLOSE( X, E, new_nr, new_nc, tol );
}

BOOST_UBLASX_TEST_DEF( reshape_by_dim1_row_major )
{
    typedef double value_type;
    typedef ublas::matrix<value_type,ublas::row_major> matrix_type;
    typedef typename ublas::matrix_traits<matrix_type>::size_type size_type;

    const size_type nr(3);
    const size_type nc(4);
    const size_type new_nr(2);
    const size_type new_nc(6);

    matrix_type A(nr,nc);

    A(0,0) =  1; A(0,1) =  4; A(0,2) =  7; A(0,3) = 10;
    A(1,0) =  2; A(1,1) =  5; A(1,2) =  8; A(1,3) = 11;
    A(2,0) =  3; A(2,1) =  6; A(2,2) =  9; A(2,3) = 12;

    matrix_type E(new_nr,new_nc);

    E(0,0) =  1; E(0,1) = 3; E(0,2) = 5; E(0,3) = 7; E(0,4) =  9; E(0,5) = 11;
    E(1,0) =  2; E(1,1) = 4; E(1,2) = 6; E(1,3) = 8; E(1,4) = 10; E(1,5) = 12;

    matrix_type X;
    X = ublasx::reshape<1>(A, new_nr, new_nc);
    BOOST_UBLASX_DEBUG_TRACE("A=" << A);
    BOOST_UBLASX_DEBUG_TRACE("reshape<1>(A," << new_nr << "," << new_nc << ")=" << X);
    BOOST_UBLASX_DEBUG_TRACE("Expected reshape(A," << new_nr << "," << new_nc << ")=" << E);
    BOOST_UBLASX_TEST_CHECK( ublasx::num_rows(X) == new_nr );
    BOOST_UBLASX_TEST_CHECK( ublasx::num_columns(X) == new_nc );
    BOOST_UBLASX_TEST_CHECK_MATRIX_CLOSE( X, E, new_nr, new_nc, tol );
}

BOOST_UBLASX_TEST_DEF( reshape_by_dim2_col_major )
{
    typedef double value_type;
    typedef ublas::matrix<value_type,ublas::column_major> matrix_type;
    typedef typename ublas::matrix_traits<matrix_type>::size_type size_type;

    const size_type nr(3);
    const size_type nc(4);
    const size_type new_nr(2);
    const size_type new_nc(6);

    matrix_type A(nr,nc);

    A(0,0) =  1; A(0,1) =  4; A(0,2) =  7; A(0,3) = 10;
    A(1,0) =  2; A(1,1) =  5; A(1,2) =  8; A(1,3) = 11;
    A(2,0) =  3; A(2,1) =  6; A(2,2) =  9; A(2,3) = 12;

    matrix_type E(new_nr,new_nc);

    E(0,0) =  1; E(0,1) =  7; E(0,2) = 2; E(0,3) =  8; E(0,4) = 3; E(0,5) =  9;
    E(1,0) =  4; E(1,1) = 10; E(1,2) = 5; E(1,3) = 11; E(1,4) = 6; E(1,5) = 12;

    matrix_type X;
    X = ublasx::reshape<2>(A, new_nr, new_nc);
    BOOST_UBLASX_DEBUG_TRACE("A=" << A);
    BOOST_UBLASX_DEBUG_TRACE("reshape<2>(A," << new_nr << "," << new_nc << ")=" << X);
    BOOST_UBLASX_DEBUG_TRACE("Expected reshape(A," << new_nr << "," << new_nc << ")=" << E);
    BOOST_UBLASX_TEST_CHECK( ublasx::num_rows(X) == new_nr );
    BOOST_UBLASX_TEST_CHECK( ublasx::num_columns(X) == new_nc );
    BOOST_UBLASX_TEST_CHECK_MATRIX_CLOSE( X, E, new_nr, new_nc, tol );
}

BOOST_UBLASX_TEST_DEF( reshape_by_dim2_row_major )
{
    typedef double value_type;
    typedef ublas::matrix<value_type,ublas::row_major> matrix_type;
    typedef typename ublas::matrix_traits<matrix_type>::size_type size_type;

    const size_type nr(3);
    const size_type nc(4);
    const size_type new_nr(2);
    const size_type new_nc(6);

    matrix_type A(nr,nc);

    A(0,0) =  1; A(0,1) =  4; A(0,2) =  7; A(0,3) = 10;
    A(1,0) =  2; A(1,1) =  5; A(1,2) =  8; A(1,3) = 11;
    A(2,0) =  3; A(2,1) =  6; A(2,2) =  9; A(2,3) = 12;

    matrix_type E(new_nr,new_nc);

    E(0,0) =  1; E(0,1) =  7; E(0,2) = 2; E(0,3) =  8; E(0,4) = 3; E(0,5) =  9;
    E(1,0) =  4; E(1,1) = 10; E(1,2) = 5; E(1,3) = 11; E(1,4) = 6; E(1,5) = 12;

    matrix_type X;
    X = ublasx::reshape<2>(A, new_nr, new_nc);
    BOOST_UBLASX_DEBUG_TRACE("A=" << A);
    BOOST_UBLASX_DEBUG_TRACE("reshape<2>(A," << new_nr << "," << new_nc << ")=" << X);
    BOOST_UBLASX_DEBUG_TRACE("Expected reshape(A," << new_nr << "," << new_nc << ")=" << E);
    BOOST_UBLASX_TEST_CHECK( ublasx::num_rows(X) == new_nr );
    BOOST_UBLASX_TEST_CHECK( ublasx::num_columns(X) == new_nc );
    BOOST_UBLASX_TEST_CHECK_MATRIX_CLOSE( X, E, new_nr, new_nc, tol );
}

BOOST_UBLASX_TEST_DEF( inplace_reshape_by_dim1_col_major )
{
    typedef double value_type;
    typedef ublas::matrix<value_type,ublas::column_major> matrix_type;
    typedef typename ublas::matrix_traits<matrix_type>::size_type size_type;

    const size_type nr(3);
    const size_type nc(4);
    const size_type new_nr(2);
    const size_type new_nc(6);

    matrix_type A(nr,nc);

    A(0,0) =  1; A(0,1) =  4; A(0,2) =  7; A(0,3) = 10;
    A(1,0) =  2; A(1,1) =  5; A(1,2) =  8; A(1,3) = 11;
    A(2,0) =  3; A(2,1) =  6; A(2,2) =  9; A(2,3) = 12;

    matrix_type E(new_nr,new_nc);

    E(0,0) =  1; E(0,1) = 3; E(0,2) = 5; E(0,3) = 7; E(0,4) =  9; E(0,5) = 11;
    E(1,0) =  2; E(1,1) = 4; E(1,2) = 6; E(1,3) = 8; E(1,4) = 10; E(1,5) = 12;

    matrix_type X(A);
    ublasx::reshape_inplace<1>(X, new_nr, new_nc);
    BOOST_UBLASX_DEBUG_TRACE("A=" << A);
    BOOST_UBLASX_DEBUG_TRACE("reshape<1>(A," << new_nr << "," << new_nc << ")=" << X);
    BOOST_UBLASX_DEBUG_TRACE("Expected reshape(A," << new_nr << "," << new_nc << ")=" << E);
    BOOST_UBLASX_TEST_CHECK( ublasx::num_rows(X) == new_nr );
    BOOST_UBLASX_TEST_CHECK( ublasx::num_columns(X) == new_nc );
    BOOST_UBLASX_TEST_CHECK_MATRIX_CLOSE( X, E, new_nr, new_nc, tol );
}

BOOST_UBLASX_TEST_DEF( inplace_reshape_by_dim1_row_major )
{
    typedef double value_type;
    typedef ublas::matrix<value_type,ublas::row_major> matrix_type;
    typedef typename ublas::matrix_traits<matrix_type>::size_type size_type;

    const size_type nr(3);
    const size_type nc(4);
    const size_type new_nr(2);
    const size_type new_nc(6);

    matrix_type A(nr,nc);

    A(0,0) =  1; A(0,1) =  4; A(0,2) =  7; A(0,3) = 10;
    A(1,0) =  2; A(1,1) =  5; A(1,2) =  8; A(1,3) = 11;
    A(2,0) =  3; A(2,1) =  6; A(2,2) =  9; A(2,3) = 12;

    matrix_type E(new_nr,new_nc);

    E(0,0) =  1; E(0,1) = 3; E(0,2) = 5; E(0,3) = 7; E(0,4) =  9; E(0,5) = 11;
    E(1,0) =  2; E(1,1) = 4; E(1,2) = 6; E(1,3) = 8; E(1,4) = 10; E(1,5) = 12;

    matrix_type X(A);
    ublasx::reshape_inplace<1>(X, new_nr, new_nc);
    BOOST_UBLASX_DEBUG_TRACE("A=" << A);
    BOOST_UBLASX_DEBUG_TRACE("reshape<1>(A," << new_nr << "," << new_nc << ")=" << X);
    BOOST_UBLASX_DEBUG_TRACE("Expected reshape(A," << new_nr << "," << new_nc << ")=" << E);
    BOOST_UBLASX_TEST_CHECK( ublasx::num_rows(X) == new_nr );
    BOOST_UBLASX_TEST_CHECK( ublasx::num_columns(X) == new_nc );
    BOOST_UBLASX_TEST_CHECK_MATRIX_CLOSE( X, E, new_nr, new_nc, tol );
}

BOOST_UBLASX_TEST_DEF( inplace_reshape_by_dim2_col_major )
{
    typedef double value_type;
    typedef ublas::matrix<value_type,ublas::column_major> matrix_type;
    typedef typename ublas::matrix_traits<matrix_type>::size_type size_type;

    const size_type nr(3);
    const size_type nc(4);
    const size_type new_nr(2);
    const size_type new_nc(6);

    matrix_type A(nr,nc);

    A(0,0) =  1; A(0,1) =  4; A(0,2) =  7; A(0,3) = 10;
    A(1,0) =  2; A(1,1) =  5; A(1,2) =  8; A(1,3) = 11;
    A(2,0) =  3; A(2,1) =  6; A(2,2) =  9; A(2,3) = 12;

    matrix_type E(new_nr,new_nc);

    E(0,0) =  1; E(0,1) =  7; E(0,2) = 2; E(0,3) =  8; E(0,4) = 3; E(0,5) =  9;
    E(1,0) =  4; E(1,1) = 10; E(1,2) = 5; E(1,3) = 11; E(1,4) = 6; E(1,5) = 12;

    matrix_type X(A);
    ublasx::reshape_inplace<2>(X, new_nr, new_nc);
    BOOST_UBLASX_DEBUG_TRACE("A=" << A);
    BOOST_UBLASX_DEBUG_TRACE("reshape<2>(A," << new_nr << "," << new_nc << ")=" << X);
    BOOST_UBLASX_DEBUG_TRACE("Expected reshape(A," << new_nr << "," << new_nc << ")=" << E);
    BOOST_UBLASX_TEST_CHECK( ublasx::num_rows(X) == new_nr );
    BOOST_UBLASX_TEST_CHECK( ublasx::num_columns(X) == new_nc );
    BOOST_UBLASX_TEST_CHECK_MATRIX_CLOSE( X, E, new_nr, new_nc, tol );
}

BOOST_UBLASX_TEST_DEF( inplace_reshape_by_dim2_row_major )
{
    typedef double value_type;
    typedef ublas::matrix<value_type,ublas::row_major> matrix_type;
    typedef typename ublas::matrix_traits<matrix_type>::size_type size_type;

    const size_type nr(3);
    const size_type nc(4);
    const size_type new_nr(2);
    const size_type new_nc(6);

    matrix_type A(nr,nc);

    A(0,0) =  1; A(0,1) =  4; A(0,2) =  7; A(0,3) = 10;
    A(1,0) =  2; A(1,1) =  5; A(1,2) =  8; A(1,3) = 11;
    A(2,0) =  3; A(2,1) =  6; A(2,2) =  9; A(2,3) = 12;

    matrix_type E(new_nr,new_nc);

    E(0,0) =  1; E(0,1) =  7; E(0,2) = 2; E(0,3) =  8; E(0,4) = 3; E(0,5) =  9;
    E(1,0) =  4; E(1,1) = 10; E(1,2) = 5; E(1,3) = 11; E(1,4) = 6; E(1,5) = 12;

    matrix_type X(A);
    ublasx::reshape_inplace<2>(X, new_nr, new_nc);
    BOOST_UBLASX_DEBUG_TRACE("A=" << A);
    BOOST_UBLASX_DEBUG_TRACE("reshape<2>(A," << new_nr << "," << new_nc << ")=" << X);
    BOOST_UBLASX_DEBUG_TRACE("Expected reshape(A," << new_nr << "," << new_nc << ")=" << E);
    BOOST_UBLASX_TEST_CHECK( ublasx::num_rows(X) == new_nr );
    BOOST_UBLASX_TEST_CHECK( ublasx::num_columns(X) == new_nc );
    BOOST_UBLASX_TEST_CHECK_MATRIX_CLOSE( X, E, new_nr, new_nc, tol );
}

/*
 * BEGIN FIXME: Does Not Work!
 *
BOOST_UBLASX_TEST_DEF( reshape_by_tag_major_col_major )
{
    typedef double value_type;
    typedef ublas::matrix<value_type,ublas::column_major> matrix_type;
    typedef typename ublas::matrix_traits<matrix_type>::size_type size_type;

    const size_type nr(3);
    const size_type nc(4);
    const size_type new_nr(2);
    const size_type new_nc(6);

    matrix_type A(nr,nc);

    A(0,0) =  1; A(0,1) =  4; A(0,2) =  7; A(0,3) = 10;
    A(1,0) =  2; A(1,1) =  5; A(1,2) =  8; A(1,3) = 11;
    A(2,0) =  3; A(2,1) =  6; A(2,2) =  9; A(2,3) = 12;

    matrix_type E(new_nr,new_nc);

    E(0,0) =  1; E(0,1) = 3; E(0,2) = 5; E(0,3) = 7; E(0,4) =  9; E(0,5) = 11;
    E(1,0) =  2; E(1,1) = 4; E(1,2) = 6; E(1,3) = 8; E(1,4) = 10; E(1,5) = 12;

    matrix_type X;
    X = ublasx::reshape<ublas::tag::major>(A, new_nr, new_nc);
    BOOST_UBLASX_DEBUG_TRACE("A=" << A);
    BOOST_UBLASX_DEBUG_TRACE("reshape<major>(A," << new_nr << "," << new_nc << ")=" << X);
    BOOST_UBLASX_DEBUG_TRACE("Expected reshape(A," << new_nr << "," << new_nc << ")=" << E);
    BOOST_UBLASX_TEST_CHECK( ublasx::num_rows(X) == new_nr );
    BOOST_UBLASX_TEST_CHECK( ublasx::num_columns(X) == new_nc );
    BOOST_UBLASX_TEST_CHECK_MATRIX_CLOSE( X, E, new_nr, new_nc, tol );
}

BOOST_UBLASX_TEST_DEF( reshape_by_tag_major_row_major )
{
    typedef double value_type;
    typedef ublas::matrix<value_type,ublas::row_major> matrix_type;
    typedef typename ublas::matrix_traits<matrix_type>::size_type size_type;

    const size_type nr(3);
    const size_type nc(4);
    const size_type new_nr(2);
    const size_type new_nc(6);

    matrix_type A(nr,nc);

    A(0,0) =  1; A(0,1) =  4; A(0,2) =  7; A(0,3) = 10;
    A(1,0) =  2; A(1,1) =  5; A(1,2) =  8; A(1,3) = 11;
    A(2,0) =  3; A(2,1) =  6; A(2,2) =  9; A(2,3) = 12;

    matrix_type E(new_nr,new_nc);

    E(0,0) =  1; E(0,1) = 3; E(0,2) = 5; E(0,3) = 7; E(0,4) =  9; E(0,5) = 11;
    E(1,0) =  2; E(1,1) = 4; E(1,2) = 6; E(1,3) = 8; E(1,4) = 10; E(1,5) = 12;

    matrix_type X;
    X = ublasx::reshape<ublas::tag::major>(A, new_nr, new_nc);
    BOOST_UBLASX_DEBUG_TRACE("A=" << A);
    BOOST_UBLASX_DEBUG_TRACE("reshape<major>(A," << new_nr << "," << new_nc << ")=" << X);
    BOOST_UBLASX_DEBUG_TRACE("Expected reshape(A," << new_nr << "," << new_nc << ")=" << E);
    BOOST_UBLASX_TEST_CHECK( ublasx::num_rows(X) == new_nr );
    BOOST_UBLASX_TEST_CHECK( ublasx::num_columns(X) == new_nc );
    BOOST_UBLASX_TEST_CHECK_MATRIX_CLOSE( X, E, new_nr, new_nc, tol );
}

BOOST_UBLASX_TEST_DEF( reshape_by_tag_minor_col_major )
{
    typedef double value_type;
    typedef ublas::matrix<value_type,ublas::column_major> matrix_type;
    typedef typename ublas::matrix_traits<matrix_type>::size_type size_type;

    const size_type nr(3);
    const size_type nc(4);
    const size_type new_nr(2);
    const size_type new_nc(6);

    matrix_type A(nr,nc);

    A(0,0) =  1; A(0,1) =  4; A(0,2) =  7; A(0,3) = 10;
    A(1,0) =  2; A(1,1) =  5; A(1,2) =  8; A(1,3) = 11;
    A(2,0) =  3; A(2,1) =  6; A(2,2) =  9; A(2,3) = 12;

    matrix_type E(new_nr,new_nc);

    E(0,0) =  1; E(0,1) =  7; E(0,2) = 2; E(0,3) =  8; E(0,4) = 3; E(0,5) =  9;
    E(1,0) =  4; E(1,1) = 10; E(1,2) = 5; E(1,3) = 11; E(1,4) = 6; E(1,5) = 12;

    matrix_type X;
    X = ublasx::reshape<ublas::tag::minor>(A, new_nr, new_nc);
    BOOST_UBLASX_DEBUG_TRACE("A=" << A);
    BOOST_UBLASX_DEBUG_TRACE("reshape<minor>(A," << new_nr << "," << new_nc << ")=" << X);
    BOOST_UBLASX_DEBUG_TRACE("Expected reshape(A," << new_nr << "," << new_nc << ")=" << E);
    BOOST_UBLASX_TEST_CHECK( ublasx::num_rows(X) == new_nr );
    BOOST_UBLASX_TEST_CHECK( ublasx::num_columns(X) == new_nc );
    BOOST_UBLASX_TEST_CHECK_MATRIX_CLOSE( X, E, new_nr, new_nc, tol );
}

BOOST_UBLASX_TEST_DEF( reshape_by_tag_minor_row_major )
{
    typedef double value_type;
    typedef ublas::matrix<value_type,ublas::row_major> matrix_type;
    typedef typename ublas::matrix_traits<matrix_type>::size_type size_type;

    const size_type nr(3);
    const size_type nc(4);
    const size_type new_nr(2);
    const size_type new_nc(6);

    matrix_type A(nr,nc);

    A(0,0) =  1; A(0,1) =  4; A(0,2) =  7; A(0,3) = 10;
    A(1,0) =  2; A(1,1) =  5; A(1,2) =  8; A(1,3) = 11;
    A(2,0) =  3; A(2,1) =  6; A(2,2) =  9; A(2,3) = 12;

    matrix_type E(new_nr,new_nc);

    E(0,0) =  1; E(0,1) =  7; E(0,2) = 2; E(0,3) =  8; E(0,4) = 3; E(0,5) =  9;
    E(1,0) =  4; E(1,1) = 10; E(1,2) = 5; E(1,3) = 11; E(1,4) = 6; E(1,5) = 12;

    matrix_type X;
    X = ublasx::reshape<ublas::tag::minor>(A, new_nr, new_nc);
    BOOST_UBLASX_DEBUG_TRACE("A=" << A);
    BOOST_UBLASX_DEBUG_TRACE("reshape<minor>(A," << new_nr << "," << new_nc << ")=" << X);
    BOOST_UBLASX_DEBUG_TRACE("Expected reshape(A," << new_nr << "," << new_nc << ")=" << E);
    BOOST_UBLASX_TEST_CHECK( ublasx::num_rows(X) == new_nr );
    BOOST_UBLASX_TEST_CHECK( ublasx::num_columns(X) == new_nc );
    BOOST_UBLASX_TEST_CHECK_MATRIX_CLOSE( X, E, new_nr, new_nc, tol );
}

BOOST_UBLASX_TEST_DEF( reshape_by_tag_leading_col_major )
{
    typedef double value_type;
    typedef ublas::matrix<value_type,ublas::column_major> matrix_type;
    typedef typename ublas::matrix_traits<matrix_type>::size_type size_type;

    const size_type nr(3);
    const size_type nc(4);
    const size_type new_nr(2);
    const size_type new_nc(6);

    matrix_type A(nr,nc);

    A(0,0) =  1; A(0,1) =  4; A(0,2) =  7; A(0,3) = 10;
    A(1,0) =  2; A(1,1) =  5; A(1,2) =  8; A(1,3) = 11;
    A(2,0) =  3; A(2,1) =  6; A(2,2) =  9; A(2,3) = 12;

    matrix_type E(new_nr,new_nc);

    E(0,0) =  1; E(0,1) =  7; E(0,2) = 2; E(0,3) =  8; E(0,4) = 3; E(0,5) =  9;
    E(1,0) =  4; E(1,1) = 10; E(1,2) = 5; E(1,3) = 11; E(1,4) = 6; E(1,5) = 12;

    matrix_type X;
    X = ublasx::reshape<ublas::tag::leading>(A, new_nr, new_nc);
    BOOST_UBLASX_DEBUG_TRACE("A=" << A);
    BOOST_UBLASX_DEBUG_TRACE("reshape<leading>(A," << new_nr << "," << new_nc << ")=" << X);
    BOOST_UBLASX_DEBUG_TRACE("Expected reshape(A," << new_nr << "," << new_nc << ")=" << E);
    BOOST_UBLASX_TEST_CHECK( ublasx::num_rows(X) == new_nr );
    BOOST_UBLASX_TEST_CHECK( ublasx::num_columns(X) == new_nc );
    BOOST_UBLASX_TEST_CHECK_MATRIX_CLOSE( X, E, new_nr, new_nc, tol );
}

BOOST_UBLASX_TEST_DEF( reshape_by_tag_leading_row_major )
{
    typedef double value_type;
    typedef ublas::matrix<value_type,ublas::row_major> matrix_type;
    typedef typename ublas::matrix_traits<matrix_type>::size_type size_type;

    const size_type nr(3);
    const size_type nc(4);
    const size_type new_nr(2);
    const size_type new_nc(6);

    matrix_type A(nr,nc);

    A(0,0) =  1; A(0,1) =  4; A(0,2) =  7; A(0,3) = 10;
    A(1,0) =  2; A(1,1) =  5; A(1,2) =  8; A(1,3) = 11;
    A(2,0) =  3; A(2,1) =  6; A(2,2) =  9; A(2,3) = 12;

    matrix_type E(new_nr,new_nc);

    E(0,0) =  1; E(0,1) =  7; E(0,2) = 2; E(0,3) =  8; E(0,4) = 3; E(0,5) =  9;
    E(1,0) =  4; E(1,1) = 10; E(1,2) = 5; E(1,3) = 11; E(1,4) = 6; E(1,5) = 12;

    matrix_type X;
    X = ublasx::reshape<ublas::tag::leading>(A, new_nr, new_nc);
    BOOST_UBLASX_DEBUG_TRACE("A=" << A);
    BOOST_UBLASX_DEBUG_TRACE("reshape<leading>(A," << new_nr << "," << new_nc << ")=" << X);
    BOOST_UBLASX_DEBUG_TRACE("Expected reshape(A," << new_nr << "," << new_nc << ")=" << E);
    BOOST_UBLASX_TEST_CHECK( ublasx::num_rows(X) == new_nr );
    BOOST_UBLASX_TEST_CHECK( ublasx::num_columns(X) == new_nc );
    BOOST_UBLASX_TEST_CHECK_MATRIX_CLOSE( X, E, new_nr, new_nc, tol );
}
 * BEGIN FIXME: Does Not Work!
 *
 */

BOOST_UBLASX_TEST_DEF( reshape_col_major )
{
    typedef double value_type;
    typedef ublas::matrix<value_type,ublas::column_major> matrix_type;
    typedef typename ublas::matrix_traits<matrix_type>::size_type size_type;

    const size_type nr(3);
    const size_type nc(4);
    const size_type new_nr(2);
    const size_type new_nc(6);

    matrix_type A(nr,nc);

    A(0,0) =  1; A(0,1) =  4; A(0,2) =  7; A(0,3) = 10;
    A(1,0) =  2; A(1,1) =  5; A(1,2) =  8; A(1,3) = 11;
    A(2,0) =  3; A(2,1) =  6; A(2,2) =  9; A(2,3) = 12;

    matrix_type E(new_nr,new_nc);

    E(0,0) =  1; E(0,1) = 3; E(0,2) = 5; E(0,3) = 7; E(0,4) =  9; E(0,5) = 11;
    E(1,0) =  2; E(1,1) = 4; E(1,2) = 6; E(1,3) = 8; E(1,4) = 10; E(1,5) = 12;

    matrix_type X;
    X = ublasx::reshape(A, new_nr, new_nc);
    BOOST_UBLASX_DEBUG_TRACE("A=" << A);
    BOOST_UBLASX_DEBUG_TRACE("reshape(A," << new_nr << "," << new_nc << ")=" << X);
    BOOST_UBLASX_DEBUG_TRACE("Expected reshape(A," << new_nr << "," << new_nc << ")=" << E);
    BOOST_UBLASX_TEST_CHECK( ublasx::num_rows(X) == new_nr );
    BOOST_UBLASX_TEST_CHECK( ublasx::num_columns(X) == new_nc );
    BOOST_UBLASX_TEST_CHECK_MATRIX_CLOSE( X, E, new_nr, new_nc, tol );
}

BOOST_UBLASX_TEST_DEF( reshape_row_major )
{
    typedef double value_type;
    typedef ublas::matrix<value_type,ublas::row_major> matrix_type;
    typedef typename ublas::matrix_traits<matrix_type>::size_type size_type;

    const size_type nr(3);
    const size_type nc(4);
    const size_type new_nr(2);
    const size_type new_nc(6);

    matrix_type A(nr,nc);

    A(0,0) =  1; A(0,1) =  4; A(0,2) =  7; A(0,3) = 10;
    A(1,0) =  2; A(1,1) =  5; A(1,2) =  8; A(1,3) = 11;
    A(2,0) =  3; A(2,1) =  6; A(2,2) =  9; A(2,3) = 12;

    matrix_type E(new_nr,new_nc);

    E(0,0) =  1; E(0,1) = 3; E(0,2) = 5; E(0,3) = 7; E(0,4) =  9; E(0,5) = 11;
    E(1,0) =  2; E(1,1) = 4; E(1,2) = 6; E(1,3) = 8; E(1,4) = 10; E(1,5) = 12;

    matrix_type X;
    X = ublasx::reshape(A, new_nr, new_nc);
    BOOST_UBLASX_DEBUG_TRACE("A=" << A);
    BOOST_UBLASX_DEBUG_TRACE("reshape(A," << new_nr << "," << new_nc << ")=" << X);
    BOOST_UBLASX_DEBUG_TRACE("Expected reshape(A," << new_nr << "," << new_nc << ")=" << E);
    BOOST_UBLASX_TEST_CHECK( ublasx::num_rows(X) == new_nr );
    BOOST_UBLASX_TEST_CHECK( ublasx::num_columns(X) == new_nc );
    BOOST_UBLASX_TEST_CHECK_MATRIX_CLOSE( X, E, new_nr, new_nc, tol );
}

BOOST_UBLASX_TEST_DEF( inplace_reshape_col_major )
{
    typedef double value_type;
    typedef ublas::matrix<value_type,ublas::column_major> matrix_type;
    typedef typename ublas::matrix_traits<matrix_type>::size_type size_type;

    const size_type nr(3);
    const size_type nc(4);
    const size_type new_nr(2);
    const size_type new_nc(6);

    matrix_type A(nr,nc);

    A(0,0) =  1; A(0,1) =  4; A(0,2) =  7; A(0,3) = 10;
    A(1,0) =  2; A(1,1) =  5; A(1,2) =  8; A(1,3) = 11;
    A(2,0) =  3; A(2,1) =  6; A(2,2) =  9; A(2,3) = 12;

    matrix_type E(new_nr,new_nc);

    E(0,0) =  1; E(0,1) = 3; E(0,2) = 5; E(0,3) = 7; E(0,4) =  9; E(0,5) = 11;
    E(1,0) =  2; E(1,1) = 4; E(1,2) = 6; E(1,3) = 8; E(1,4) = 10; E(1,5) = 12;

    matrix_type X(A);
    ublasx::reshape_inplace(X, new_nr, new_nc);
    BOOST_UBLASX_DEBUG_TRACE("A=" << A);
    BOOST_UBLASX_DEBUG_TRACE("reshape(A," << new_nr << "," << new_nc << ")=" << X);
    BOOST_UBLASX_DEBUG_TRACE("Expected reshape(A," << new_nr << "," << new_nc << ")=" << E);
    BOOST_UBLASX_TEST_CHECK( ublasx::num_rows(X) == new_nr );
    BOOST_UBLASX_TEST_CHECK( ublasx::num_columns(X) == new_nc );
    BOOST_UBLASX_TEST_CHECK_MATRIX_CLOSE( X, E, new_nr, new_nc, tol );
}

BOOST_UBLASX_TEST_DEF( inplace_reshape_row_major )
{
    typedef double value_type;
    typedef ublas::matrix<value_type,ublas::row_major> matrix_type;
    typedef typename ublas::matrix_traits<matrix_type>::size_type size_type;

    const size_type nr(3);
    const size_type nc(4);
    const size_type new_nr(2);
    const size_type new_nc(6);

    matrix_type A(nr,nc);

    A(0,0) =  1; A(0,1) =  4; A(0,2) =  7; A(0,3) = 10;
    A(1,0) =  2; A(1,1) =  5; A(1,2) =  8; A(1,3) = 11;
    A(2,0) =  3; A(2,1) =  6; A(2,2) =  9; A(2,3) = 12;

    matrix_type E(new_nr,new_nc);

    E(0,0) =  1; E(0,1) = 3; E(0,2) = 5; E(0,3) = 7; E(0,4) =  9; E(0,5) = 11;
    E(1,0) =  2; E(1,1) = 4; E(1,2) = 6; E(1,3) = 8; E(1,4) = 10; E(1,5) = 12;

    matrix_type X(A);
    ublasx::reshape_inplace(X, new_nr, new_nc);
    BOOST_UBLASX_DEBUG_TRACE("A=" << A);
    BOOST_UBLASX_DEBUG_TRACE("reshape(A," << new_nr << "," << new_nc << ")=" << X);
    BOOST_UBLASX_DEBUG_TRACE("Expected reshape(A," << new_nr << "," << new_nc << ")=" << E);
    BOOST_UBLASX_TEST_CHECK( ublasx::num_rows(X) == new_nr );
    BOOST_UBLASX_TEST_CHECK( ublasx::num_columns(X) == new_nc );
    BOOST_UBLASX_TEST_CHECK_MATRIX_CLOSE( X, E, new_nr, new_nc, tol );
}

BOOST_UBLASX_TEST_DEF( reshape_vec )
{
    BOOST_UBLASX_DEBUG_TRACE("Test Case: Reshape Vector");

    typedef double value_type;
    typedef ublas::vector<value_type> vector_type;
    typedef ublas::matrix<value_type> matrix_type;
    typedef typename ublas::matrix_traits<matrix_type>::size_type size_type;

    const size_type n(6);

    vector_type v(n);

    v(0) =  1;
    v(1) =  2;
    v(2) =  3;
    v(3) =  4;
    v(4) =  5;
    v(5) =  6;

    size_type new_nr;
    size_type new_nc;
    matrix_type E;
    matrix_type X;

    // vector(n) => matrix(n,1)
    new_nr = 6;
    new_nc = 1;
    E = matrix_type(new_nr,new_nc);
    E(0,0) =  1;
    E(1,0) =  2;
    E(2,0) =  3;
    E(3,0) =  4;
    E(4,0) =  5;
    E(5,0) =  6;
    X = ublasx::reshape(v, new_nr, new_nc);
    BOOST_UBLASX_DEBUG_TRACE("v=" << v);
    BOOST_UBLASX_DEBUG_TRACE("reshape(v," << new_nr << "," << new_nc << ")=" << X);
    BOOST_UBLASX_DEBUG_TRACE("Expected reshape(v," << new_nr << "," << new_nc << ")=" << E);
    BOOST_UBLASX_TEST_CHECK( ublasx::num_rows(X) == new_nr );
    BOOST_UBLASX_TEST_CHECK( ublasx::num_columns(X) == new_nc );
    BOOST_UBLASX_TEST_CHECK_MATRIX_CLOSE( X, E, new_nr, new_nc, tol );

    // vector(n) => matrix(1,n)
    new_nr = 1;
    new_nc = 6;
    E = matrix_type(new_nr,new_nc);
    E(0,0) =  1; E(0,1) =  2; E(0,2) =  3; E(0,3) =  4; E(0,4) =  5; E(0,5) = 6;
    X = ublasx::reshape(v, new_nr, new_nc);
    BOOST_UBLASX_DEBUG_TRACE("v=" << v);
    BOOST_UBLASX_DEBUG_TRACE("reshape(v," << new_nr << "," << new_nc << ")=" << X);
    BOOST_UBLASX_DEBUG_TRACE("Expected reshape(v," << new_nr << "," << new_nc << ")=" << E);
    BOOST_UBLASX_TEST_CHECK( ublasx::num_rows(X) == new_nr );
    BOOST_UBLASX_TEST_CHECK( ublasx::num_columns(X) == new_nc );
    BOOST_UBLASX_TEST_CHECK_MATRIX_CLOSE( X, E, new_nr, new_nc, tol );

    // vector(n) => matrix(n1,n2)
    new_nr = 3;
    new_nc = 2;
    E = matrix_type(new_nr,new_nc);
    E(0,0) = 1; E(0,1) = 4;
    E(1,0) = 2; E(1,1) = 5;
    E(2,0) = 3; E(2,1) = 6;
    X = ublasx::reshape(v, new_nr, new_nc);
    BOOST_UBLASX_DEBUG_TRACE("v=" << v);
    BOOST_UBLASX_DEBUG_TRACE("reshape(v," << new_nr << "," << new_nc << ")=" << X);
    BOOST_UBLASX_DEBUG_TRACE("Expected reshape(v," << new_nr << "," << new_nc << ")=" << E);
    BOOST_UBLASX_TEST_CHECK( ublasx::num_rows(X) == new_nr );
    BOOST_UBLASX_TEST_CHECK( ublasx::num_columns(X) == new_nc );
    BOOST_UBLASX_TEST_CHECK_MATRIX_CLOSE( X, E, new_nr, new_nc, tol );

    // vector(n) => matrix(n1,n2)
    new_nr = 2;
    new_nc = 3;
    E = matrix_type(new_nr,new_nc);
    E(0,0) = 1; E(0,1) = 3; E(0,2) = 5;
    E(1,0) = 2; E(1,1) = 4; E(1,2) = 6;
    X = ublasx::reshape(v, new_nr, new_nc);
    BOOST_UBLASX_DEBUG_TRACE("v=" << v);
    BOOST_UBLASX_DEBUG_TRACE("reshape(v," << new_nr << "," << new_nc << ")=" << X);
    BOOST_UBLASX_DEBUG_TRACE("Expected reshape(v," << new_nr << "," << new_nc << ")=" << E);
    BOOST_UBLASX_TEST_CHECK( ublasx::num_rows(X) == new_nr );
    BOOST_UBLASX_TEST_CHECK( ublasx::num_columns(X) == new_nc );
    BOOST_UBLASX_TEST_CHECK_MATRIX_CLOSE( X, E, new_nr, new_nc, tol );
}

int main()
{
    BOOST_UBLASX_TEST_BEGIN();

    BOOST_UBLASX_TEST_DO( reshape_by_dim1_col_major );
    BOOST_UBLASX_TEST_DO( reshape_by_dim1_row_major );
    BOOST_UBLASX_TEST_DO( reshape_by_dim2_col_major );
    BOOST_UBLASX_TEST_DO( reshape_by_dim2_row_major );
    BOOST_UBLASX_TEST_DO( inplace_reshape_by_dim1_col_major );
    BOOST_UBLASX_TEST_DO( inplace_reshape_by_dim1_row_major );
    BOOST_UBLASX_TEST_DO( inplace_reshape_by_dim2_col_major );
    BOOST_UBLASX_TEST_DO( inplace_reshape_by_dim2_row_major );

//FIXME: does not work
//  BOOST_UBLASX_TEST_DO( reshape_by_tag_major_col_major );
//  BOOST_UBLASX_TEST_DO( reshape_by_tag_major_row_major );
//  BOOST_UBLASX_TEST_DO( reshape_by_tag_minor_col_major );
//  BOOST_UBLASX_TEST_DO( reshape_by_tag_minor_row_major );
//  BOOST_UBLASX_TEST_DO( reshape_by_tag_leading_col_major );
//  BOOST_UBLASX_TEST_DO( reshape_by_tag_leading_row_major );

    BOOST_UBLASX_TEST_DO( reshape_col_major );
    BOOST_UBLASX_TEST_DO( reshape_row_major );
    BOOST_UBLASX_TEST_DO( inplace_reshape_col_major );
    BOOST_UBLASX_TEST_DO( inplace_reshape_row_major );

    BOOST_UBLASX_TEST_DO( reshape_vec );

    BOOST_UBLASX_TEST_END();
}


#include "test_helpers/test_assertions.hpp"

#include <vector>

#include <deal.II/base/mpi.h>
#include <deal.II/dofs/dof_tools.h>
#include <deal.II/dofs/dof_handler.h>
#include <deal.II/distributed/tria.h>
#include <deal.II/fe/fe_q.h>
#include <deal.II/grid/grid_generator.h>
#include <deal.II/grid/grid_tools.h>
#include <deal.II/lac/dynamic_sparsity_pattern.h>
#include <deal.II/lac/petsc_sparse_matrix.h>
#include <deal.II/base/tensor.h>

#include "test_helpers/dealii_test_domain.h"
#include "test_helpers/test_helper_functions.h"
#include "test_helpers/gmock_wrapper.h"

namespace  {

namespace test_helpers = bart::test_helpers;

using ::testing::AssertionResult, ::testing::AssertionFailure, ::testing::AssertionSuccess;

class TestAssertionsTest : public ::testing::Test {
 protected:
  std::vector<double> vector_1, vector_2;
  dealii::Vector<double> dealii_vector_1, dealii_vector_2;
  void SetUp() override;
};

void TestAssertionsTest::SetUp() {
  const auto vector_size = test_helpers::RandomInt(5, 10);
  vector_1 = test_helpers::RandomVector(vector_size, 0, 1.0);
  vector_2 = test_helpers::RandomVector(vector_size, 1.0, 2.0);
  dealii_vector_1.reinit(vector_size);
  dealii_vector_2.reinit(vector_size);

  for (int i = 0; i < vector_size; ++i) {
    dealii_vector_1[i] = vector_1[i];
    dealii_vector_2[i] = vector_2[i];
  }
}

TEST_F(TestAssertionsTest, GoodComparisonStdVector) {
  EXPECT_EQ(AssertionSuccess(), bart::test_helpers::AreEqual(vector_1, vector_1));
}

TEST_F(TestAssertionsTest, BadComparisonStdVector) {
  EXPECT_EQ(AssertionFailure(), bart::test_helpers::AreEqual(vector_1, vector_2));
  EXPECT_EQ(AssertionFailure(), bart::test_helpers::AreEqual(vector_2, vector_1));
}

TEST_F(TestAssertionsTest, GoodComparisonDealiiVector) {
  EXPECT_EQ(AssertionSuccess(), bart::test_helpers::AreEqual(dealii_vector_1, dealii_vector_1));
  EXPECT_EQ(AssertionSuccess(), bart::test_helpers::AreEqual(dealii_vector_2, dealii_vector_2));
}

TEST_F(TestAssertionsTest, BadComparisonDealiiVector) {
  EXPECT_EQ(AssertionFailure(), bart::test_helpers::AreEqual(dealii_vector_1, dealii_vector_2));
  EXPECT_EQ(AssertionFailure(), bart::test_helpers::AreEqual(dealii_vector_2, dealii_vector_1));
}

class TestAssertionsMatrixTests : public ::testing::Test {
 public:
  dealii::FullMatrix<double> matrix_1, matrix_2, matrix_bad_columns, matrix_bad_rows;
  void SetUp() override;
};

void TestAssertionsMatrixTests::SetUp() {
  const auto matrix_rows{ test_helpers::RandomInt(5, 10) }, matrix_cols{ matrix_rows + 1 };
  matrix_1.reinit(matrix_rows, matrix_cols);
  matrix_2.reinit(matrix_rows, matrix_cols);
  matrix_bad_columns.reinit(matrix_rows, matrix_rows);
  matrix_bad_rows.reinit(matrix_cols, matrix_cols);

  for (int i = 0; i < matrix_rows; ++i) {
    for (int j = 0; j < matrix_cols; ++j) {
      matrix_1.set(i, j, test_helpers::RandomDouble(-100, 100));
      matrix_2.set(i, j, test_helpers::RandomDouble(-100, 100));
    }
  }

  for (int i = 0; i < matrix_rows; ++i) {
    for (int j = 0; j < matrix_rows; ++j) {
      matrix_bad_columns.set(i, j, test_helpers::RandomDouble(-100, 100));
    }
  }

  for (int i = 0; i < matrix_cols; ++i) {
    for (int j = 0; j < matrix_cols; ++j) {
      matrix_bad_rows.set(i, j, test_helpers::RandomDouble(-100, 100));
    }
  }
}

TEST_F(TestAssertionsMatrixTests, GoodComparison) {
  EXPECT_TRUE(test_helpers::AreEqual(matrix_1, matrix_1));
  EXPECT_TRUE(test_helpers::AreEqual(matrix_2, matrix_2));
  EXPECT_TRUE(test_helpers::AreEqual(matrix_bad_columns, matrix_bad_columns));
  EXPECT_TRUE(test_helpers::AreEqual(matrix_bad_rows, matrix_bad_rows));
}

TEST_F(TestAssertionsMatrixTests, BadComparison) {
  EXPECT_FALSE(test_helpers::AreEqual(matrix_1, matrix_2));
  EXPECT_FALSE(test_helpers::AreEqual(matrix_2, matrix_1));
}

TEST_F(TestAssertionsMatrixTests, BadSizeComparison) {
  EXPECT_FALSE(test_helpers::AreEqual(matrix_1, matrix_bad_columns));
  EXPECT_FALSE(test_helpers::AreEqual(matrix_2, matrix_bad_columns));
  EXPECT_FALSE(test_helpers::AreEqual(matrix_1, matrix_bad_rows));
  EXPECT_FALSE(test_helpers::AreEqual(matrix_2, matrix_bad_rows));
  EXPECT_FALSE(test_helpers::AreEqual(matrix_bad_rows, matrix_bad_columns));
}

TEST_F(TestAssertionsMatrixTests, GoodComparisonWithinTolerance) {
  auto matrix_3 = matrix_1;
  for (auto entry : matrix_3) {
    entry += test_helpers::RandomDouble(1e-6, 1e-5);
  }
  EXPECT_FALSE(test_helpers::AreEqual(matrix_1, matrix_3));
  EXPECT_TRUE(test_helpers::AreEqual(matrix_1, matrix_3, 1e-4));
}

template <typename DimensionWrapper>
class TestAssertionsTensorsAreEqual : public ::testing::Test {
 public:
  static constexpr int dim = DimensionWrapper::value;
  dealii::Tensor<1, dim> tensor_1_, tensor_2_;

  auto SetUp() -> void override;
};

template <typename DimensionWrapper>
auto TestAssertionsTensorsAreEqual<DimensionWrapper>::SetUp() -> void {
  for (int i = 0; i < dim; ++i) {
    tensor_1_[i] = test_helpers::RandomDouble(-100, 100);
    tensor_2_[i] = test_helpers::RandomDouble(-100, 100);
  }
}

TYPED_TEST_SUITE(TestAssertionsTensorsAreEqual, bart::testing::AllDimensions);

TYPED_TEST(TestAssertionsTensorsAreEqual, GoodComparison) {
  EXPECT_TRUE(test_helpers::AreEqual(this->tensor_1_, this->tensor_1_));
  EXPECT_TRUE(test_helpers::AreEqual(this->tensor_2_, this->tensor_2_));
}

TYPED_TEST(TestAssertionsTensorsAreEqual, BadComparison) {
  EXPECT_FALSE(test_helpers::AreEqual(this->tensor_1_, this->tensor_2_));
  EXPECT_FALSE(test_helpers::AreEqual(this->tensor_2_, this->tensor_1_));
}

TYPED_TEST(TestAssertionsTensorsAreEqual, Tolerance) {
  auto tensor_3 = this->tensor_1_;
  tensor_3 *= (1 + 1e-8);
  EXPECT_TRUE(test_helpers::AreEqual(this->tensor_1_, tensor_3));
  tensor_3 *= (1 + 1e-5);
  EXPECT_FALSE(test_helpers::AreEqual(this->tensor_1_, tensor_3));
}

class TestAssertionsMPIMatricesTests : public ::testing::Test, public bart::testing::DealiiTestDomain<2> {
 protected:
  void SetUp() override;
};

void TestAssertionsMPIMatricesTests::SetUp() {
  SetUpDealii();
  std::vector<dealii::types::global_dof_index> local_dof_indices(fe_.dofs_per_cell);

  for (const auto cell : cells_) {
    cell->get_dof_indices(local_dof_indices);
    for (const auto index_i : local_dof_indices) {
      for (const auto index_j : local_dof_indices) {
        matrix_1.add(index_i, index_j, 1);
        matrix_2.add(index_i, index_j, 2);
        matrix_3.add(index_i, index_j, 1);
      }
    }
  }

  matrix_1.compress(dealii::VectorOperation::add);
  matrix_2.compress(dealii::VectorOperation::add);
  matrix_3.compress(dealii::VectorOperation::add);
}

TEST_F(TestAssertionsMPIMatricesTests, CompareMPIMatrices) {
  EXPECT_EQ(AssertionSuccess(), bart::test_helpers::AreEqual(matrix_2, matrix_2));
  EXPECT_EQ(AssertionSuccess(), bart::test_helpers::AreEqual(matrix_1, matrix_3));
  EXPECT_EQ(AssertionFailure(), bart::test_helpers::AreEqual(matrix_1, matrix_2));

  const int random_cell = test_helpers::RandomInt(0, cells_.size());
  std::vector<dealii::types::global_dof_index> local_dof_indices(fe_.dofs_per_cell);
  cells_[random_cell]->get_dof_indices(local_dof_indices);

  for (const auto index_i : local_dof_indices) {
    for (const auto index_j : local_dof_indices) {
      matrix_3.add(index_i, index_j, 1);
    }
  }
  matrix_3.compress(dealii::VectorOperation::add);
  EXPECT_EQ(AssertionFailure(), bart::test_helpers::AreEqual(matrix_1, matrix_3));
}

class TestAssertionsMPIVectorTests : public ::testing::Test, public bart::testing::DealiiTestDomain<2> {
 protected:
  void SetUp() override;
};

void TestAssertionsMPIVectorTests::SetUp() {
  SetUpDealii();
  std::vector<dealii::types::global_dof_index> local_dof_indices(fe_.dofs_per_cell);

  for (const auto cell : cells_) {
    cell->get_dof_indices(local_dof_indices);
    for (const auto index_i : local_dof_indices) {
      vector_1(index_i) += 1;
      vector_2(index_i) += 2;
      vector_3(index_i) += 1;
    }
  }

  vector_1.compress(dealii::VectorOperation::add);
  vector_2.compress(dealii::VectorOperation::add);
  vector_3.compress(dealii::VectorOperation::add);
}

TEST_F(TestAssertionsMPIVectorTests, CompareMPIVectors) {
  EXPECT_EQ(AssertionSuccess(), bart::test_helpers::AreEqual(vector_1, vector_1));
  EXPECT_EQ(AssertionSuccess(), bart::test_helpers::AreEqual(vector_1, vector_3));
  EXPECT_EQ(AssertionFailure(), bart::test_helpers::AreEqual(vector_1, vector_2));

  const int random_cell = test_helpers::RandomInt(0, cells_.size());
  std::vector<dealii::types::global_dof_index> local_dof_indices(fe_.dofs_per_cell);
  cells_[random_cell]->get_dof_indices(local_dof_indices);

  for (auto index_i : local_dof_indices) {
    vector_3(index_i) += 1;
  }

  EXPECT_EQ(AssertionFailure(), bart::test_helpers::AreEqual(vector_1, vector_3));
}

} // namespace


//------------------------------------------------------------------------------
/// \file FunctionObject_test.cpp
/// \ref Vandevoorde, Josuttis, Gregor. C++ Templates: The Complete Guide.
/// Addison-Wesley Professional; 2nd edition. 2017
//------------------------------------------------------------------------------

#include <boost/test/unit_test.hpp>
//#include <functional> // std::functional
#include <vector>

using std::vector;

BOOST_AUTO_TEST_SUITE(Cpp)
BOOST_AUTO_TEST_SUITE(Utilities)
BOOST_AUTO_TEST_SUITE(FunctionObject_tests)

//------------------------------------------------------------------------------
/// \ref Vandevoorde, Josuttis, Gregor (2017). pp. 517, 22.1
/// \brief Demonstrate Function template that enumerates integer values from 0
/// up to some value, providing each value to given function object f.
/// bridge/forupto3.hpp from Vandevoorde, Josuttis, Gregor (2017)
//------------------------------------------------------------------------------
template <typename F>
void for_up_to(const int n, F f)
{
  for (int i {0}; i < n; ++i)
  {
    f(i); // call passed function f for i
  }
}

void for_up_to_3(const int n, std::function<void(int)> f)
{
  for (int i {0}; i < n; ++i)
  {
    f(i); // call passed function f for i
  }
}

class TestOutputStringStream
{
  private:

};

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(FunctionTemplateForStaticPolymorphism)
{
  constexpr int n {5};

  vector<int> values;
  for_up_to(n, [&values](int i) { values.push_back(i); });

  for (int i {0}; i < n; ++i)
  {
    BOOST_TEST(values.at(i) == i);
  }
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(StdFunctionForTypeErasure)
{
  constexpr int n {5};

  vector<int> values;
  for_up_to_3(n, [&values](int i) { values.push_back(i); });

  for (int i {0}; i < n; ++i)
  {
    BOOST_TEST(values.at(i) == i);
  }
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(StdFunctionTypesMatchOnArgumentsNotOnReturnType)
{
  std::function<void(double)> f = [](double x) -> double { return x*x; };
  f(2);
  // Void has incomplete type.
  //auto result = f(2);
  BOOST_TEST(true);
}

BOOST_AUTO_TEST_SUITE_END() // FunctionObject_tests
BOOST_AUTO_TEST_SUITE_END() // Utilities
BOOST_AUTO_TEST_SUITE_END() // Cpp


//! c/c++ system headers
//! other headers
#include <class_def.hpp>
#include <boost/python.hpp>

// create class that will wrap our c++ code into python using boost
struct BoostPyWrapper {
    // we can expose either constructor from class_def, as well
    // as both.  Let's expose both.  This just means that we can init
    // python version using either an input argument or not.
    BoostPyWrapper() : e_(new Exponentiate()) {}
    BoostPyWrapper(double base) : e_(new Exponentiate(base)) {}

    // create a python-callable method to raise base to an input power
    double raise_to_power(double in) {
        return e_->RaiseToPower(in);
    }

    // create a shared pointer to our Exponentiate instance
    boost::shared_ptr<Exponentiate> e_;
};

// define boost python module
BOOST_PYTHON_MODULE(pyRTP) {
    using namespace boost::python;
    // this is where the magic happens
    // here is where we define what is actually exposed to python
    // and how to reference it
    class_<BoostPyWrapper>("Exponentiate", init<>())  // default constructor
        .def(init<double>())  // constructor that takes a single argument
        .def("raise_to_power", &BoostPyWrapper::raise_to_power, "perform the computation");  // ref to our single method

}


/// @file
/// @brief

#include <ostream>
#include <vector>
#include <boost/bind/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/test/data/test_case.hpp>
#include <boost/test/data/monomorphic.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/transformed.hpp>

template< typename T >
auto makeSequence( std::initializer_list< T >&& il )
{
     return std::vector< T, std::allocator< T > >{ il.begin(), il.end() };
}

namespace std {
template< typename T >
ostream& boost_test_print_type( ostream& os, const vector< T, allocator< T > >& v )
{
     const char* sep = "";
     os << '{';
     for( auto&& el: v )
     {
          os << sep << el;
          sep = ",";
     }
     os << '}';
     return os;
}
} // namespace std

struct InfiniteSequence
{
     enum { arity = 1 };

     struct iterator
     {
          iterator() {}

          int operator*() const { return current_; }
          void operator++() { ++current_; }
     private:
          unsigned current_ = 0u;
     };

     InfiniteSequence() {}

     boost::unit_test::data::size_t size() const
     {
          return boost::unit_test::data::BOOST_TEST_DS_INFINITE_SIZE;
     }

     iterator begin() const
     {
          return iterator{};
     }
};

namespace boost {
namespace unit_test {
namespace data {
namespace monomorphic {

template<> struct is_dataset< InfiniteSequence > : mpl::true_ {};

} // namespace monomorphic
} // namespace data
} // namespace unit_test
} // namespace boost

BOOST_AUTO_TEST_SUITE(
     DatasetTestExamples,
     * boost::unit_test::label( "dataset" )
     * boost::unit_test::description( "Tests with datasets for example" )
     )
BOOST_DATA_TEST_CASE(
     TestInfiniteSequence
     , InfiniteSequence() ^ boost::unit_test::data::make({ 0,1,2,3,4,5,6,7,8,9,10,11,12 })
     , generatedValue
     , expectedValue
     )
{
     BOOST_TEST( expectedValue == generatedValue );
}

static constexpr int INTEGERS[] = {
     0,1,2,3,4,5,6,7,8,9
};

BOOST_DATA_TEST_CASE(
     TestInfiniteSequenceUsingArray
     , InfiniteSequence() ^ boost::unit_test::data::make( INTEGERS )
     , generatedValue
     , expectedValue
     )
{
     BOOST_TEST( expectedValue == generatedValue );
}

static constexpr int XRANGE_5 = 5;

BOOST_DATA_TEST_CASE(
     TestUsingXrange
     , boost::unit_test::data::xrange( XRANGE_5 )
     , value
     )
{
     BOOST_TEST( ((0 <= value) && (value < XRANGE_5)) );
}

static constexpr int XRANGE_3 = 3;
static const char* const CSTRS_3[] = { "zero", "one", "two" };

BOOST_DATA_TEST_CASE(
     TestUsingZip
     , boost::unit_test::data::xrange( XRANGE_3 ) ^ boost::unit_test::data::make( CSTRS_3 )
     , index
     , text
     )
{
     BOOST_TEST( (0 <= index && index < XRANGE_3 ) );
     BOOST_TEST( std::string{ CSTRS_3[ index ] } == text );
}

BOOST_DATA_TEST_CASE(
     TestUsingJoin
     , boost::unit_test::data::xrange( XRANGE_3 ) + boost::unit_test::data::make({ 66, 55, 44, 99, 12 })
     , value
     )
{
     BOOST_TEST((
          value == 0
          || value == 1
          || value == 2
          || value == 66
          || value == 55
          || value == 44
          || value == 99
          || value == 12
          ));
}

static const char* CSTRS_4[] = { "cake", "owl", "radio", "top" };

BOOST_DATA_TEST_CASE(
     TestUsingCartesianProduct
     , boost::unit_test::data::xrange( 3 ) * boost::unit_test::data::make( CSTRS_4 )
     , first
     , second
     )
{
     static const auto isPresent =
          []( const std::string& str )
          {
               static constexpr auto end = CSTRS_4 + 4;
               return end != std::find( CSTRS_4, end, str );
          };

     switch( first )
     {
          case 0:
               BOOST_TEST( isPresent( second ) );
               break;
          case 1:
               BOOST_TEST( isPresent( second ) );
               break;
          case 2:
               BOOST_TEST( isPresent( second ) );
               break;
          default:
               BOOST_TEST( false );
     }
}

BOOST_DATA_TEST_CASE(
     TestDatasetSingleton
     , boost::unit_test::data::make( 2 )
     , singleton
     )
{
     BOOST_TEST( (singleton == 2) );
}

BOOST_DATA_TEST_CASE(
     TestDatasetSingletonZip
     , boost::unit_test::data::xrange( 3 ) ^ boost::unit_test::data::make( 2 )
     , value
     , singleton
     )
{
     BOOST_TEST( ((0 <= value && value < 3) && (singleton == 2)) );
}

BOOST_DATA_TEST_CASE(
     TestDatasetOneToManyMapping
     , boost::unit_test::data::xrange( 3 )
          ^ boost::unit_test::data::make({
               makeSequence({ 1,2,3 })
               , makeSequence({ 9,7,3,5 })
               , makeSequence({ 4,0 })
          })
     , index
     , value
     )
{
     boost::ignore_unused( index, value );
     BOOST_TEST( true );
}

BOOST_DATA_TEST_CASE(
     TestDatasetRandomReal
     , boost::unit_test::data::xrange( 3 ) ^ boost::unit_test::data::random()
     , index
     , random
     )
{
     BOOST_TEST( (0 <= index && index < 3) );
     BOOST_TEST( (random != 0.538256) ); /// I feel lucky!
}

BOOST_DATA_TEST_CASE(
     TestDatasetRandomDiceRoll
     , boost::unit_test::data::xrange( 5 ) ^ boost::unit_test::data::random( 1, 6 )
     , step
     , dice
     )
{
     BOOST_TEST( (0 <= step && step < 5) );
     BOOST_TEST( (1 <= dice && dice <= 6) );
}

BOOST_AUTO_TEST_SUITE_END()


/**
 * @file lecture111.cpp
 * @brief Code demonstrating the program_options library and the chrono library
 *
 * @author Henry Chronowski
 * @assignment Lab 11.2
 * @date 19/11/2020
 * @credits Lecture 11.2
 * https://www.boost.org/doc/libs/1_71_0/doc/html/chrono.html
 * https://www.boost.org/doc/libs/1_71_0/doc/html/program_options.html
 *
 **/

#include <boost/program_options.hpp>
#include <boost/chrono/include.hpp>
#include <iostream>
#include <iomanip>
#include <chrono>

using namespace boost::program_options;

const double NUMBER = 289.7773;
const double TO_SECONDS = 0.000000001;

int main(int argc, const char *argv[])
{
  try
  {
    options_description desc{"Options"};
    desc.add_options()
      ("help,h", "Help screen")
      ("it", value<long>()->default_value(100), "Iterations");

    variables_map vm;
    store(parse_command_line(argc, argv, desc), vm);
    notify(vm);

    if (vm.count("help"))
      std::cout << desc << '\n';
    else if (vm.count("it"))
    {
      long i;

      // Run a for loop and time it with the stl timer
      auto startTime = std::chrono::high_resolution_clock::now();
      for( i = 0; i < vm["it"].as<long>(); i++)
      {
        std::sqrt(NUMBER);
      }
      auto secondsPassed = std::chrono::high_resolution_clock::now() - startTime;

      // Run a for loop and time it with the chrono timer
      boost::chrono::system_clock::time_point start = boost::chrono::system_clock::now();
      for( i = 0; i < vm["it"].as<long>(); i++)
      {
        std::sqrt(NUMBER);
      }
      boost::chrono::duration<double> dt = boost::chrono::system_clock::now() - start;

      // Output results
      std::cout << "Iterations: " << vm["it"].as<long>() << '\n';
      std::cout << "Boost timer: " << dt << std::endl;
      std::cout << "STL timer: " << std::fixed << std::setprecision(5) << secondsPassed.count() * TO_SECONDS << " seconds\n";
    }

  }
  catch (const error &ex)
  {
    std::cerr << ex.what() << '\n';
  }
}


/*! \file auto_boxplot.cpp

   \brief An example to demonstrate boxplot settings, including auto-scaling.
   \details See also:
     example @c auto_1d_containers.cpp for an example autoscaling with multiple data-series.
     example @c demo_boxplot.cpp for a wider range of use.

// Copyright Jacob Voytko 2007
// Copyright Paul A Bristow 2008, 2020

// Use, modification and distribution are subject to the
// Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)

// This file is written to be included from a Quickbook .qbk document.
// It can be compiled by the C++ compiler, and run. Any output can
// also be added here as comment or included or pasted in elsewhere.

// Caution: this file contains Quickbook markup as well as code
// and comments: don't change any of the special comment markups!

//[auto_boxplot_1

//!`First we need a few includes to use Boost.Plot
//! (and some others only needed for this example).
*/

#include <boost/quan/meas.hpp> // measurement class
#include <boost/quan/unc.hpp> // uncertain class

#include <boost/svg_plot/svg_boxplot.hpp>
  using boost::svg::svg_boxplot;

#include <boost/svg_plot/detail/pair.hpp>
// (Provides operator<< for std::pair).

#include <boost/algorithm/minmax.hpp>
 using boost::minmax;
#include <boost/algorithm/minmax_element.hpp>
 using boost::minmax_element;

#include <boost/svg_plot/detail/auto_axes.hpp>
  using boost::svg::show; // A single STL container.
  using boost::svg::show_all; // Multiple STL containers.
 // using boost::svg::range; // Find min and max of a STL container.
  using boost::svg::range_all;// Find min and max of multipler STL containers.

#include <iostream> // for debugging.
  using std::cout;
  using std::endl;
  using std::boolalpha;

#include <limits>
  using std::numeric_limits;

#include <vector>
  using std::vector;
#include <set>
  using std::multiset;

#include <utility>
  using std::pair;

//] [/auto_boxplot_1]

void scale_axis(double min_value, double max_value, // input
               double* axis_min_value,  double* axis_max_value, double* axis_tick_increment, // updated.
               bool origin, double tight, int min_ticks, int steps);

constexpr double tol100eps = 1000 * numeric_limits<double>::epsilon(); // Suitable tight value.

int main()
{
  using namespace boost::svg; // Especially convenient for SVG colors.

  //[auto_boxplot_2
  /*`
  This example uses containers to demonstrate autoscaling.
  Autoscaling must inspect the container in order to find axis ranges that will be suitable.
  First we create a container and fill with some fictional data.
  */
  vector<double> my_data;
  // Initialize my_data with some entirely fictional data.
  my_data.push_back(0.2); // [0]
  my_data.push_back(1.1); // [1]
  my_data.push_back(4.2); // [2]
  my_data.push_back(3.3); // [3]
  my_data.push_back(5.4); // [4]
  my_data.push_back(6.5); // [5]
  my_data.push_back(6.8); // [6]
  my_data.push_back(6.9); // [7]
  my_data.push_back(7.2); // [8]
  my_data.push_back(7.3); // [9]
  my_data.push_back(8.1); // [10]
  my_data.push_back(8.5); // [11]

  /*`Not included is an 'at limit' value that could confuse autoscaling.
  Obviously we do not want the plot range to include infinity.
  // my_data.push_back(numeric_limits<double>::infinity()); // [12]
  */
  try
  { // Ensure error, warning and information messages from svg_plot are displayed by the catch block.

    double mn; // Ready to be updated by mnmx;
    double mx;
    int good = mnmx(my_data.begin(), my_data.end(), &mn, &mx);
    cout << good << " good values, " << my_data.size() - good << " limit values."
      << " min value = " << mn << ", max = " << mx << std::endl;
    // 12 good values, 0 limit values. min value = 0.2, max = 8.5

    svg_boxplot my_boxplot; // Construct a plot with all the default constructor values.
    my_boxplot.title("Auto boxplot");
    my_boxplot.y_label("Values");
    my_boxplot.y_autoscale(my_data);  // Compute autoscale values for the plot.
    //my_boxplot.y_autoscale(my_data.begin(), my_data.end());  // Compute autoscale values for the plot.
    // my_boxplot.y_autoscale(std::make_pair(0., 10.));
    //my_boxplot.y_autoscale(0., 9.);  // Compute autoscale values for the plot.
    cout << boolalpha << "Use y autoscale " << my_boxplot.y_autoscale() << "."<< std::endl;
    my_boxplot.plot(my_data, "Auto boxplot"); // Add the one data-series, and give it a title.
    my_boxplot.write("auto_boxplot.svg"); // Write the plot to file.

    /*`It may be useful to display that range chosen by autoscaling. */
    using boost::svg::detail::operator<<; // For displaying std::pair.
    cout << "y_range() " << my_boxplot.y_range() << std::endl; // x_range()
  }
  catch(const std::exception& e)
  { // Error, warning and information messages are displayed by the catch block.
    std::cout <<
      "\n""Message from thrown exception was:\n   " << e.what() << std::endl;
  }
  //] [/auto_boxplot_2]

  return 0;
} // int main()

/*

//[auto_boxplot_output

Compiling...
auto_boxplot.cpp
Linking...
Embedding manifest...
Autorun "j:\Cpp\SVG\debug\auto_boxplot.exe"
12 good values, 0 limit values. min value = 0.2, max = 8.5
Use y autoscale true.
Min outlier fences <-1.7 or >12.6333
Min extreme fences <-7.075 or >18.0083
y_range() 0, 9
Build Time 0:02
Build log was saved at "file://j:\Cpp\SVG\auto_boxplot\Debug\BuildLog.htm"

//] [auto_boxplot_output]

*/


/**
 * \file boost/numeric/ublasx/operation/for_each.hpp
 *
 * The \c for_each operation.
 *
 * Copyright (c) 2010, Marco Guazzone
 *
 * 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)
 *
 * \author Marco Guazzone, marco.guazzone@gmail.com
 */

#ifndef BOOST_NUMERIC_UBLASX_OPERATION_FOR_EACH_HPP
#define BOOST_NUMERIC_UBLASX_OPERATION_FOR_EACH_HPP

#include <boost/numeric/ublas/fwd.hpp>
#include <boost/numeric/ublas/traits.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 <boost/numeric/ublasx/tags.hpp>
#include <cstddef>

namespace boost { namespace numeric { namespace ublasx {

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

namespace detail {

template <std::size_t Dim>
struct for_each_by_dim_impl;

template <>
struct for_each_by_dim_impl<1>
{
	template <typename MatrixExprT, typename UnaryFunctorT>
	static void apply(matrix_expression<MatrixExprT> const& me, UnaryFunctorT f)
	{
		typedef typename matrix_traits<MatrixExprT>::size_type size_type;

		size_type nr = num_rows(me);
		size_type nc = num_columns(me);
		for (size_type r = 0; r < nr; ++r)
		{
			for (size_type c = 0; c < nc; ++c)
			{
				f(me()(r,c));
			}
		}
	}
};

template <>
struct for_each_by_dim_impl<2>
{
	template <typename MatrixExprT, typename UnaryFunctorT>
	static void apply(matrix_expression<MatrixExprT> const& me, UnaryFunctorT f)
	{
		typedef typename matrix_traits<MatrixExprT>::size_type size_type;

		size_type nr = num_rows(me);
		size_type nc = num_columns(me);
		for (size_type c = 0; c < nc; ++c)
		{
			for (size_type r = 0; r < nr; ++r)
			{
				f(me()(r,c));
			}
		}
	}
};

template <typename TagT, typename OrientationT>
struct for_each_by_tag_impl;

template <>
struct for_each_by_tag_impl<tag::major, row_major_tag>
{
	template <typename MatrixExprT, typename UnaryFunctorT>
	static void apply(matrix_expression<MatrixExprT> const& me, UnaryFunctorT f)
	{
		typedef typename matrix_traits<MatrixExprT>::size_type size_type;

		size_type nr = num_rows(me);
		size_type nc = num_columns(me);
		for (size_type r = 0; r < nr; ++r)
		{
			for (size_type c = 0; c < nc; ++c)
			{
				f(me()(r,c));
			}
		}
	}
};

template <>
struct for_each_by_tag_impl<tag::major, column_major_tag>
{
	template <typename MatrixExprT, typename UnaryFunctorT>
	static void apply(matrix_expression<MatrixExprT> const& me, UnaryFunctorT f)
	{
		typedef typename matrix_traits<MatrixExprT>::size_type size_type;

		size_type nr = num_rows(me);
		size_type nc = num_columns(me);
		for (size_type c = 0; c < nc; ++c)
		{
			for (size_type r = 0; r < nr; ++r)
			{
				f(me()(r,c));
			}
		}
	}
};

template <>
struct for_each_by_tag_impl<tag::minor, row_major_tag>
{
	template <typename MatrixExprT, typename UnaryFunctorT>
	static void apply(matrix_expression<MatrixExprT> const& me, UnaryFunctorT f)
	{
		typedef typename matrix_traits<MatrixExprT>::size_type size_type;

		size_type nr = num_rows(me);
		size_type nc = num_columns(me);
		for (size_type c = 0; c < nc; ++c)
		{
			for (size_type r = 0; r < nr; ++r)
			{
				f(me()(r,c));
			}
		}
	}
};

template <>
struct for_each_by_tag_impl<tag::minor, column_major_tag>
{
	template <typename MatrixExprT, typename UnaryFunctorT>
	static void apply(matrix_expression<MatrixExprT> const& me, UnaryFunctorT f)
	{
		typedef typename matrix_traits<MatrixExprT>::size_type size_type;

		size_type nr = num_rows(me);
		size_type nc = num_columns(me);
		for (size_type r = 0; r < nr; ++r)
		{
			for (size_type c = 0; c < nc; ++c)
			{
				f(me()(r,c));
			}
		}
	}
};

template <>
struct for_each_by_tag_impl<tag::leading, row_major_tag>
{
	template <typename MatrixExprT, typename UnaryFunctorT>
	static void apply(matrix_expression<MatrixExprT> const& me, UnaryFunctorT f)
	{
		typedef typename matrix_traits<MatrixExprT>::size_type size_type;

		size_type nr = num_rows(me);
		size_type nc = num_columns(me);
		for (size_type c = 0; c < nc; ++c)
		{
			for (size_type r = 0; r < nr; ++r)
			{
				f(me()(r,c));
			}
		}
	}
};

template <>
struct for_each_by_tag_impl<tag::leading, column_major_tag>
{
	template <typename MatrixExprT, typename UnaryFunctorT>
	static void apply(matrix_expression<MatrixExprT> const& me, UnaryFunctorT f)
	{
		typedef typename matrix_traits<MatrixExprT>::size_type size_type;

		size_type nr = num_rows(me);
		size_type nc = num_columns(me);
		for (size_type r = 0; r < nr; ++r)
		{
			for (size_type c = 0; c < nc; ++c)
			{
				f(me()(r,c));
			}
		}
	}
};

} // Namespace detail

/**
 * \brief Apply a function to a vector expression.
 *
 * \tparam VectorExprT The type of input vector expression.
 * \tparam UnaryFunctorT The type of the function to be applied.
 *
 * \param ve The input vector expression.
 * \param f The unary function to be applied to the vector expression.
 *
 * \author Marco Guazzone, marco.guazzone@gmail.com
 */
template <typename VectorExprT, typename UnaryFunctorT>
void for_each(vector_expression<VectorExprT> const& ve, UnaryFunctorT f)
{
	typedef typename vector_traits<VectorExprT>::size_type size_type;

	size_type n = size(ve);
	for (size_type i = 0; i < n; ++i)
	{
		f(ve()(i));
	}
}

/**
 * \brief Apply a function to a matrix expression.
 *
 * \tparam MatrixExprT The type of input matrix expression.
 * \tparam UnaryFunctorT The type of the function to be applied.
 *
 * \param me The input matrix expression.
 * \param f The unary function to be applied to the matrix expression.
 *
 * \author Marco Guazzone, marco.guazzone@gmail.com
 */
template <typename MatrixExprT, typename UnaryFunctorT>
void for_each(matrix_expression<MatrixExprT> const& me, UnaryFunctorT f)
{
	typedef typename matrix_traits<MatrixExprT>::size_type size_type;

	size_type nr = num_rows(me);
	size_type nc = num_columns(me);
	for (size_type r = 0; r < nr; ++r)
	{
		for (size_type c = 0; c < nc; ++c)
		{
			f(me()(r,c));
		}
	}
}

/**
 * \brief Apply a function to a matrix expression along the given dimension.
 *
 * \tparam Dim The dimension to follow when applying the function.
 *  Valid values are: 1 (by rows), and 2 (by columns).
 * \tparam MatrixExprT The type of input matrix expression.
 * \tparam UnaryFunctorT The type of the function to be applied.
 *
 * \param me The input matrix expression.
 * \param f The unary function to be applied to the matrix expression.
 *
 * \author Marco Guazzone, marco.guazzone@gmail.com
 */
template <std::size_t Dim, typename MatrixExprT, typename UnaryFunctorT>
void for_each(matrix_expression<MatrixExprT> const& me, UnaryFunctorT f)
{
	detail::for_each_by_dim_impl<Dim>::template apply(me, f);
}

/**
 * \brief Apply a function to a matrix expression along the given dimension.
 *
 * \tparam TagT The dimension to follow when applying the function.
 *  Valid values are \c tag::major, \c tag::minor, and \c tag::leading.
 * \tparam MatrixExprT The type of input matrix expression.
 * \tparam UnaryFunctorT The type of the function to be applied.
 *
 * \param me The input matrix expression.
 * \param f The unary function to be applied to the matrix expression.
 *
 * \author Marco Guazzone, marco.guazzone@gmail.com
 */
template <typename TagT, typename MatrixExprT, typename UnaryFunctorT>
void for_each_by_tag(matrix_expression<MatrixExprT> const& me, UnaryFunctorT f)
{
	detail::for_each_by_tag_impl<TagT, typename matrix_traits<MatrixExprT>::orientation_category>::template apply(me, f);
}

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

#endif // BOOST_NUMERIC_UBLASX_OPERATION_FOR_EACH_HPP


/*!
  \file gpp_random_test.cpp
  \rst
  This file contains functions for testing the functions and classes in gpp_random.hpp.  There are also a number of simple
  supporting routines.  See header for comments on the general layout of these tests.
\endrst*/

#include "gpp_random_test.hpp"

#include <algorithm>
#include <limits>
#include <unordered_set>
#include <vector>

#include <boost/random/uniform_int.hpp>  // NOLINT(build/include_order)
#include <boost/random/uniform_real.hpp>  // NOLINT(build/include_order)

#include "gpp_common.hpp"
#include "gpp_exception.hpp"
#include "gpp_geometry.hpp"
#include "gpp_logging.hpp"
#include "gpp_random.hpp"
#include "gpp_test_utils.hpp"

namespace optimal_learning {

namespace {

/*!\rst
  Randomly generates points in a domain and ensures that the results are valid.  ComputeRandomPointInDomain guarantees
  nothing further about the distribution of its outputs.

  \return
    number of randomly generated points that were not inside the domain
\endrst*/
OL_WARN_UNUSED_RESULT int RandomPointInDomainTest() {
  static const int kDim = 5;
  const int num_tests = 50;
  ClosedInterval domain[kDim];
  double random_point[kDim];
  int total_errors = 0;

  UniformRandomGenerator uniform_generator(314);
  boost::uniform_real<double> uniform_double_domain_lower_bound(-5.0, -0.01);
  boost::uniform_real<double> uniform_double_domain_upper_bound(0.02, 4.0);

  // domain w/min edge length 0.03 and max edge length 9
  for (int i = 0; i < kDim; ++i) {
    domain[i].min = uniform_double_domain_lower_bound(uniform_generator.engine);
    domain[i].max = uniform_double_domain_upper_bound(uniform_generator.engine);
  }

  for (int i = 0; i < num_tests; ++i) {
    ComputeRandomPointInDomain(domain, kDim, &uniform_generator, random_point);

    if (!CheckPointInHypercube(domain, random_point, kDim)) {
      ++total_errors;
    }
  }

  if (total_errors != 0) {
    OL_PARTIAL_FAILURE_PRINTF("random_point_in_domain generated points outside the domain\n");
  }

  return total_errors;
}

/*!\rst
  Just your basic bubble sort.

  Need the ability to sort matrices ``A_{ij}`` in blocks of ``A_{i*}``, doing comparisons only on
  ``j``-th entries.  This is (as far as I know) awkward with STL vectors/sort.
\endrst*/
OL_NONNULL_POINTERS void bubble_sort(int dim_to_sort, int dim, int num_points, double * restrict points) noexcept {
  int newi;

  for (int i = num_points - 1; i > 0; --i) {
    newi = 0;
    for (int j = 1; j <= i; ++j) {
      if (points[(j-1)*dim + dim_to_sort] > points[j*dim + dim_to_sort]) {
        double * restrict point_one = points + (j-1)*dim;
        double * restrict point_two = points + j*dim;
        std::swap_ranges(point_one, point_one + dim, point_two);
        newi = j;
      }
    }
    // after a given pass (loop over j), all elements after the most recent swap are
    // already sorted.  skip over them.
    i = newi;
  }
}

/*!\rst
  Check that the latin hypercube point generation routine generates points in that are:

  1. in the domain
  2. properly distributed

  Latin hypercube sampling with N points divides a d-dimensional domain into N subranges in
  each ordinate direction.  For example, in the 2D domain [0,1]x[0,1], with N=8, the square
  is divided up like a chess board.

  Then lathin hypercube sampling guarantees that there can only be ONE point per row and per column.  In
  the chess analogy, this sampling places N rooks so that no 2 attack each other.  Since placing each point
  eliminates 1 row and 1 column, this is always possible (let the rooks be pidgeons).

  So the test is as follows:

  1. use latin hypercube sampling to sample N points
  2. for each spatial dimension d
  3. sort the points along their d-th coordinate
  4. check that each subrange only contains 1 point

  \return
    number of LHC points that were improperly distributed
\endrst*/
OL_WARN_UNUSED_RESULT int HypercubePointInDomainTest() {
  static const int kDim = 5;
  const int num_tests = 50;
  static const int kNumberOfSamples = 30;
  ClosedInterval domain[kDim];
  double random_points[kDim*kNumberOfSamples];
  double subcube_edge_length, min_val, max_val;
  int errors_this_iteration;
  int total_errors = 0;

  UniformRandomGenerator uniform_generator(314);
  boost::uniform_real<double> uniform_double_domain_lower_bound(-5.0, -0.01);
  boost::uniform_real<double> uniform_double_domain_upper_bound(0.02, 4.0);

  // domain w/min edge length 0.03 and max edge length 9
  for (int i = 0; i < kDim; ++i) {
    domain[i].min = uniform_double_domain_lower_bound(uniform_generator.engine);
    domain[i].max = uniform_double_domain_upper_bound(uniform_generator.engine);
  }

  for (int i = 0; i < num_tests; ++i) {
    ComputeLatinHypercubePointsInDomain(domain, kDim, kNumberOfSamples, &uniform_generator, random_points);

    for (int j = 0; j < kNumberOfSamples; ++j) {
      if (!CheckPointInHypercube(domain, random_points + j*kDim, kDim)) {
        ++total_errors;
      }
    }

    for (int k = 0; k < kDim; ++k) {
      subcube_edge_length = (domain[k].Length())/static_cast<double>(kNumberOfSamples);
      bubble_sort(k, kDim, kNumberOfSamples, random_points);

      // i-th point (sorted) must fall somewhere in the i-th slice of the hypercube
      for (int j = 0; j < kNumberOfSamples; ++j) {
        min_val = domain[k].min + subcube_edge_length*j;
        max_val = min_val + subcube_edge_length;

        errors_this_iteration = 0;
        if (random_points[j*kDim + k] > max_val) {
          ++errors_this_iteration;
        }
        if (random_points[j*kDim + k] < min_val) {
          ++errors_this_iteration;
        }
        total_errors += errors_this_iteration;
      }
    }
  }

  if (total_errors != 0) {
    OL_PARTIAL_FAILURE_PRINTF("hypercube_point_in_domain generated invalid point distributions\n");
  }

  return total_errors;
}

/*!\rst
  Test random point generation in a unit simplex.

  \return
    number of randomly generated points that were not in the unit simplex
\endrst*/
OL_WARN_UNUSED_RESULT int RandomPointInUnitSimplexTest() {
  static const int kDim = 5;
  const int num_tests = 50;
  static const int kNumberOfSamples = 30;
  double random_points[kDim*kNumberOfSamples];
  int total_errors = 0;

  UniformRandomGenerator uniform_generator(314);

  for (int i = 0; i < num_tests; ++i) {
    ComputeUniformPointsInUnitSimplex(kDim, kNumberOfSamples, &uniform_generator, random_points);

    for (int j = 0; j < kNumberOfSamples; ++j) {
      if (!CheckPointInUnitSimplex(random_points + j*kDim, kDim)) {
        ++total_errors;
      }
    }
  }

  if (total_errors != 0) {
    OL_PARTIAL_FAILURE_PRINTF("RandomPointInUnitSimplex generated invalid point distributions\n");
  }

  return total_errors;
}

}  // end unnamed namespace

int RunRandomPointGeneratorTests() {
  int current_errors;
  int total_errors = 0;

  current_errors = RandomPointInDomainTest();
  total_errors += current_errors;
  if (current_errors != 0) {
    OL_PARTIAL_FAILURE_PRINTF("Random Point in Domain errors = %d\n", current_errors);
  }

  current_errors = HypercubePointInDomainTest();
  total_errors += current_errors;
  if (current_errors != 0) {
    OL_PARTIAL_FAILURE_PRINTF("Latin Hypercube Points in Domain errors = %d\n", current_errors);
  }

  current_errors = RandomPointInUnitSimplexTest();
  total_errors += current_errors;
  if (current_errors != 0) {
    OL_PARTIAL_FAILURE_PRINTF("Random Point in Unit Simplex errors = %d\n", current_errors);
  }

  return total_errors;
}

namespace {

/*!\rst
  Checks that all elements of a vector are unique.

  Modifies the vector (by sorting).

  \input
    :input_vector: vector to be checked
  \return
    true if all elements are distinct
\endrst*/
template <typename T>
OL_WARN_UNUSED_RESULT bool CheckAllElementsUnique(const std::vector<T>& input_vector) {
  return std::unordered_set<T>(input_vector.begin(), input_vector.end()).size() == input_vector.size();
}

/*!\rst
  Fill a vector with all unique random elements.  Random elements are meant
  to potentially be fake process IDs for non-system processes.  I'm assuming
  those probably lie in ``(100, 2^32)`` or so.

  \input
    :thread_ids[1]: allocated vector of thread ids, already set to desired size
  \output
    :thread_ids[1]: overwrites all thread_ids entries with unique values
\endrst*/
void GenerateUniqueRandomVector(std::vector<int> * thread_ids) {
  UniformRandomGenerator uniform_generator(314, 0);  // single instance, so thread_id = 0
  boost::uniform_int<int> uniform_int_distribution(100, std::numeric_limits<int>::max());

  // generate seeds
  for (auto& entry : (*thread_ids)) {
    entry = uniform_int_distribution(uniform_generator.engine);
  }

  while (false == CheckAllElementsUnique(*thread_ids)) {
    // could be smarter about this and only regen non-unique elements
    for (auto& entry : (*thread_ids)) {
      entry = uniform_int_distribution(uniform_generator.engine);
    }
  }

  // re-randomize ordering
  std::shuffle((*thread_ids).begin(), (*thread_ids).end(), uniform_generator.engine);
}

/*!\rst
  Test the features of random number generator containers.

  1. Check that explicitly setting the seed works and sets "last_seed" properly
  2. Verify that the reset functionality properly resets to the last seed
  3. verify that with different input thread ids, container will generate will
     generate a unique seed per thread

  \return
    number of test failures
\endrst*/
template <typename RNGContainer>
OL_WARN_UNUSED_RESULT int RandomNumberGeneratorContainerTestCore() {
  int total_errors = 0;
  int current_errors = 0;

  // set seed manually; verify that last seed is set appropriately
  {
    current_errors = 0;
    const typename RNGContainer::EngineType::result_type seed1 = 31415;
    const typename RNGContainer::EngineType::result_type seed2 = 27182;
    RNGContainer test_rng(seed1);
    if (!CheckIntEquals(test_rng.last_seed(), seed1)) {
      ++current_errors;
    }

    test_rng.SetExplicitSeed(seed2);
    if (!CheckIntEquals(test_rng.last_seed(), seed2)) {
      ++current_errors;
    }
    total_errors += current_errors;
  }

  // verify last seed reset: set seed and save off PRNG state.  Generate a few randoms
  // and check that state changed; then reset to last seed and verify against the original state
  {
    current_errors = 0;
    RNGContainer rng;

    typename RNGContainer::EngineType original_engine(rng.GetEngine());  // copy ctor
    rng.GetEngine().discard(13);
    if (rng.GetEngine() == original_engine) {
      ++current_errors;  // engine state should have changed
    }

    rng.ResetToMostRecentSeed();
    if (rng.GetEngine() != original_engine) {
      ++current_errors;  // engine state should have been reset
    }

    total_errors += current_errors;
  }

  // multi-threaded seeding check
  // build several RNGContainer objects w/different thread ids (ensure ids are all different)
  // verify that objects' last_seed() values are all different
  {
    current_errors = 0;
    const int max_num_threads = 11;

    std::vector<RNGContainer> normal_rng_vec(max_num_threads);
    std::vector<typename RNGContainer::EngineType::result_type> seed_values(max_num_threads);
    std::vector<int> thread_ids(max_num_threads);
    GenerateUniqueRandomVector(&thread_ids);

    for (int i = 0; i < max_num_threads; ++i) {
      normal_rng_vec[i].SetRandomizedSeed(38970, thread_ids[i]);
      seed_values[i] = normal_rng_vec[i].last_seed();
    }

    bool all_seeds_different = CheckAllElementsUnique(seed_values);

    if (all_seeds_different == false) {
      OL_PARTIAL_FAILURE_PRINTF("seed values are not all different!\n");
      for (const auto& value : seed_values) {
        OL_ERROR_PRINTF("%d ", value);
      }
      OL_ERROR_PRINTF("\n");
    }

    if (!all_seeds_different) {
      ++current_errors;
    }
    total_errors += current_errors;
  }

  return total_errors;
}

/*!\rst
  Checks that NormalRNGSimulator is behaving correctly:

  * Tests index increments as expected
  * Tests ResetToMostRecentSeed reset index to 0
  * Tests exception handling when number of queries of random numbers exceeds
  * size of the random table

  \return
    number of test failures: 0 if NormalRNGSimulator behaving correctly
\endrst*/
int NormalRNGSimulatorTest() {
  int total_errors = 0;
  int random_table_size = 500;
  std::vector<double> random_table(random_table_size);
  for (int i = 0; i < random_table_size; ++i) {
    random_table[i] = static_cast<double>(i);
  }
  NormalRNGSimulator rng_simulator(random_table);

  for (int n = 0; n < 40; ++n) {
    int current_idx = rng_simulator.index();
    rng_simulator();
    int next_idx = rng_simulator.index();
    total_errors = ((next_idx - current_idx) == 1) ? total_errors : (total_errors+1);
  }

  rng_simulator.ResetToMostRecentSeed();
  total_errors = (rng_simulator.index() == 0) ? total_errors : (total_errors+1);

  for (int n = 0; n < random_table_size; ++n) {
    rng_simulator();
  }

  ++total_errors;

  try {
    rng_simulator();
  } catch (const InvalidValueException<int>& exception) {
    if ((exception.value() == random_table_size) && (exception.truth() == random_table_size)) {
      --total_errors;
    }
  }

  return total_errors;
}

}  // end unnamed namespace

/*!\rst
  .. Note:: only NormalRNG is meant to be used multi-threaded, so UniformRandomGenerator
      is not tested for generating unique seeds in a multi-threaded environment
\endrst*/
int RandomNumberGeneratorContainerTest() {
  int total_errors = 0;
  int current_errors = 0;

  current_errors = RandomNumberGeneratorContainerTestCore<UniformRandomGenerator>();
  if (current_errors != 0) {
    OL_PARTIAL_FAILURE_PRINTF("UniformRandomGenerator failed with %d errors\n", current_errors);
  } else {
    OL_PARTIAL_SUCCESS_PRINTF("UniformRandomGenerator passed all tests\n");
  }
  total_errors += current_errors;

  current_errors = RandomNumberGeneratorContainerTestCore<NormalRNG>();
  if (current_errors != 0) {
    OL_PARTIAL_FAILURE_PRINTF("NormalRNG failed with %d errors\n", current_errors);
  } else {
    OL_PARTIAL_SUCCESS_PRINTF("NormalRNG passed all tests\n");
  }
  total_errors += current_errors;

  current_errors = NormalRNGSimulatorTest();
  if (current_errors != 0) {
    OL_PARTIAL_FAILURE_PRINTF("NormalRNGSimulator failed with %d errors\n", current_errors);
  } else {
    OL_PARTIAL_SUCCESS_PRINTF("NormalRNGSimulator passed all tests\n");
  }
  total_errors += current_errors;

  return total_errors;
}

}  // end namespace optimal_learning


// Copyright Louis Dionne 2013-2017
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)

#include <boost/hana/functional/iterate.hpp>

#include <boost/hana/assert.hpp>
#include <boost/hana/equal.hpp>

#include <laws/base.hpp>

#include <vector>
namespace hana = boost::hana;
using hana::test::ct_eq;

struct undefined { };

constexpr int incr(int i) { return i + 1; }

int main() {
    hana::test::_injection<0> f{};

    // "real usage" tests
    static_assert(hana::iterate<3>(incr, 0) == 3, "");
    {
        std::vector<int> vec;
        hana::iterate<10>([&](int i) { vec.push_back(i); return i + 1; }, 0);
        BOOST_HANA_RUNTIME_CHECK(
            vec == std::vector<int>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
        );
    }

    // equivalence between iterate<n>(f, x) and iterate<n>(f)(x)
    BOOST_HANA_CONSTANT_CHECK(hana::equal(
        hana::iterate<0>(undefined{})(ct_eq<0>{}),
        hana::iterate<0>(undefined{}, ct_eq<0>{})
    ));

    BOOST_HANA_CONSTANT_CHECK(hana::equal(
        hana::iterate<4>(f)(ct_eq<0>{}),
        hana::iterate<4>(f, ct_eq<0>{})
    ));

    BOOST_HANA_CONSTANT_CHECK(hana::equal(
        hana::iterate<10>(f)(ct_eq<0>{}),
        hana::iterate<10>(f, ct_eq<0>{})
    ));

    // systematic tests
    BOOST_HANA_CONSTANT_CHECK(hana::equal(
        hana::iterate<0>(undefined{}, ct_eq<0>{}),
        ct_eq<0>{}
    ));

    BOOST_HANA_CONSTANT_CHECK(hana::equal(
        hana::iterate<1>(f, ct_eq<0>{}),
        f(ct_eq<0>{})
    ));

    BOOST_HANA_CONSTANT_CHECK(hana::equal(
        hana::iterate<2>(f, ct_eq<0>{}),
        f(f(ct_eq<0>{}))
    ));

    BOOST_HANA_CONSTANT_CHECK(hana::equal(
        hana::iterate<3>(f, ct_eq<0>{}),
        f(f(f(ct_eq<0>{})))
    ));

    BOOST_HANA_CONSTANT_CHECK(hana::equal(
        hana::iterate<4>(f, ct_eq<0>{}),
        f(f(f(f(ct_eq<0>{}))))
    ));

    BOOST_HANA_CONSTANT_CHECK(hana::equal(
        hana::iterate<5>(f, ct_eq<0>{}),
        f(f(f(f(f(ct_eq<0>{})))))
    ));

    BOOST_HANA_CONSTANT_CHECK(hana::equal(
        hana::iterate<6>(f, ct_eq<0>{}),
        f(f(f(f(f(f(ct_eq<0>{}))))))
    ));

    BOOST_HANA_CONSTANT_CHECK(hana::equal(
        hana::iterate<7>(f, ct_eq<0>{}),
        f(f(f(f(f(f(f(ct_eq<0>{})))))))
    ));

    BOOST_HANA_CONSTANT_CHECK(hana::equal(
        hana::iterate<11>(f, ct_eq<0>{}),
        f(f(f(f(f(f(f(f(f(f(f(ct_eq<0>{})))))))))))
    ));

    BOOST_HANA_CONSTANT_CHECK(hana::equal(
        hana::iterate<12>(f, ct_eq<0>{}),
        f(f(f(f(f(f(f(f(f(f(f(f(ct_eq<0>{}))))))))))))
    ));

    BOOST_HANA_CONSTANT_CHECK(hana::equal(
        hana::iterate<13>(f, ct_eq<0>{}),
        f(f(f(f(f(f(f(f(f(f(f(f(f(ct_eq<0>{})))))))))))))
    ));

    // We can't nest too many calls to f, because that uses a hana::tuple
    // internally and some implementation (libstdc++) have trouble with
    // deeply-nested calls to `std::is_constructible`, which is required by
    // hana::tuple. Hence, we use an homogeneous function for the remaining
    // tests.
    static_assert(hana::iterate<23>(incr, 0) == 23, "");
    static_assert(hana::iterate<24>(incr, 0) == 24, "");
    static_assert(hana::iterate<25>(incr, 0) == 25, "");
    static_assert(hana::iterate<26>(incr, 0) == 26, "");
    static_assert(hana::iterate<27>(incr, 0) == 27, "");
    static_assert(hana::iterate<28>(incr, 0) == 28, "");
    static_assert(hana::iterate<29>(incr, 0) == 29, "");

    static_assert(hana::iterate<30>(incr, 0) == 30, "");
    static_assert(hana::iterate<31>(incr, 0) == 31, "");
    static_assert(hana::iterate<32>(incr, 0) == 32, "");
    static_assert(hana::iterate<33>(incr, 0) == 33, "");
    static_assert(hana::iterate<34>(incr, 0) == 34, "");
    static_assert(hana::iterate<35>(incr, 0) == 35, "");
    static_assert(hana::iterate<36>(incr, 0) == 36, "");
    static_assert(hana::iterate<37>(incr, 0) == 37, "");
    static_assert(hana::iterate<38>(incr, 0) == 38, "");
    static_assert(hana::iterate<39>(incr, 0) == 39, "");

    static_assert(hana::iterate<40>(incr, 0) == 40, "");
    static_assert(hana::iterate<41>(incr, 0) == 41, "");
    static_assert(hana::iterate<42>(incr, 0) == 42, "");
    static_assert(hana::iterate<43>(incr, 0) == 43, "");
    static_assert(hana::iterate<44>(incr, 0) == 44, "");
    static_assert(hana::iterate<45>(incr, 0) == 45, "");
    static_assert(hana::iterate<46>(incr, 0) == 46, "");
    static_assert(hana::iterate<47>(incr, 0) == 47, "");
    static_assert(hana::iterate<48>(incr, 0) == 48, "");
    static_assert(hana::iterate<49>(incr, 0) == 49, "");

    static_assert(hana::iterate<50>(incr, 0) == 50, "");
    static_assert(hana::iterate<51>(incr, 0) == 51, "");
    static_assert(hana::iterate<52>(incr, 0) == 52, "");
    static_assert(hana::iterate<53>(incr, 0) == 53, "");
    static_assert(hana::iterate<54>(incr, 0) == 54, "");
    static_assert(hana::iterate<55>(incr, 0) == 55, "");
    static_assert(hana::iterate<56>(incr, 0) == 56, "");
    static_assert(hana::iterate<57>(incr, 0) == 57, "");
    static_assert(hana::iterate<58>(incr, 0) == 58, "");
    static_assert(hana::iterate<59>(incr, 0) == 59, "");
}


// Group B - Perpetual American Options
//
// by Scott Sidoli
//
// 6-10-19
//
// Types.hpp
//
// Create new type names for parameters in option data.

#ifndef Types_hpp
#define Types_hpp

#include <boost/serialization/strong_typedef.hpp>

BOOST_STRONG_TYPEDEF(double, Strike_Price);
BOOST_STRONG_TYPEDEF(double, Volatility);
BOOST_STRONG_TYPEDEF(double, rate);
BOOST_STRONG_TYPEDEF(double, cost_of_carry);
BOOST_STRONG_TYPEDEF(double, curr_stock_price);

#endif


#include "testsuite.h"

#include <blitz/array.h>

BZ_USING_NAMESPACE(blitz)

Array< double, 1> a(100);

int main(){

  BZTEST(a.size() == 100);
  return 0;
}


//==================================================================================================
/*!
  @file

  @copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_CONSTANT_ZERO_HPP_INCLUDED
#define BOOST_SIMD_CONSTANT_ZERO_HPP_INCLUDED

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{
  /*!
    @ingroup group-constant

    Generate  value 0

    @par Header <boost/simd/constant/zero.hpp>

    @par Semantic:

    @code
    T r = Zero<T>();
    @endcode

    is similar to:

    @code
    T r = T(0);
    @endcode

    @return The Zero constant for the proper type
  **/
  template<typename T> T Zero();

  namespace functional
  {
    /*!
      @ingroup group-callable-constant
      Generate the  constant zero.

      @return The Zero constant for the proper type
    **/
    Value Zero();
  }
} }
#endif

#include <boost/simd/constant/scalar/zero.hpp>
#include <boost/simd/constant/simd/zero.hpp>

#endif