/* =========================================================================
   Copyright (c) 2010-2014, Institute for Microelectronics,
                            Institute for Analysis and Scientific Computing,
                            TU Wien.
   Portions of this software are copyright by UChicago Argonne, LLC.

                            -----------------
                  ViennaCL - The Vienna Computing Library
                            -----------------

   Project Head:    Karl Rupp                   rupp@iue.tuwien.ac.at

   (A list of authors and contributors can be found in the PDF manual)

   License:         MIT (X11), see file LICENSE in the base directory
============================================================================= */

/** \file tests/src/vector_int.cpp  Tests vector operations (BLAS level 1) for signed integer arithmetic.
*   \test  Tests vector operations (BLAS level 1) for signed integer arithmetic.
**/

//
// *** System
//
#include <iostream>
#include <iomanip>

//
// *** Boost
//
#include <boost/numeric/ublas/io.hpp>
#include <boost/numeric/ublas/vector.hpp>
#include <boost/numeric/ublas/vector_proxy.hpp>

//
// *** ViennaCL
//
//#define VIENNACL_DEBUG_ALL
#define VIENNACL_WITH_UBLAS 1
#include "viennacl/vector.hpp"
#include "viennacl/vector_proxy.hpp"
#include "viennacl/linalg/inner_prod.hpp"
#include "viennacl/linalg/norm_1.hpp"
#include "viennacl/linalg/norm_2.hpp"
#include "viennacl/linalg/norm_inf.hpp"
#include "viennacl/linalg/maxmin.hpp"

#include "Random.hpp"

using namespace boost::numeric;

//
// -------------------------------------------------------------
//
template<typename ScalarType>
ScalarType diff(ScalarType const & s1, ScalarType const & s2)
{
  viennacl::backend::finish();
  return s1 - s2;
}
//
// -------------------------------------------------------------
//
template<typename ScalarType>
ScalarType diff(ScalarType const & s1, viennacl::scalar<ScalarType> const & s2)
{
  viennacl::backend::finish();
  return s1 - s2;
}
//
// -------------------------------------------------------------
//
template<typename ScalarType>
ScalarType diff(ScalarType const & s1, viennacl::entry_proxy<ScalarType> const & s2)
{
  viennacl::backend::finish();
  return s1 - s2;
}
//
// -------------------------------------------------------------
//
template<typename ScalarType, typename VCLVectorType>
ScalarType diff(ublas::vector<ScalarType> const & v1, VCLVectorType const & v2)
{
   ublas::vector<ScalarType> v2_cpu(v2.size());
   viennacl::backend::finish();  //workaround for a bug in APP SDK 2.7 on Trinity APUs (with Catalyst 12.8)
   viennacl::copy(v2.begin(), v2.end(), v2_cpu.begin());

   for (unsigned int i=0;i<v1.size(); ++i)
   {
      if (v2_cpu[i] != v1[i])
        return 1;
   }

   return 0;
}

template<typename T1, typename T2>
int check(T1 const & t1, T2 const & t2)
{
  int retval = EXIT_SUCCESS;

  if (diff(t1, t2) != 0)
  {
    std::cout << "# Error! Difference: " << std::abs(diff(t1, t2)) << std::endl;
    retval = EXIT_FAILURE;
  }
  return retval;
}

//
// -------------------------------------------------------------
//
template< typename NumericT, typename UblasVectorType, typename ViennaCLVectorType1, typename ViennaCLVectorType2 >
int test(UblasVectorType     & ublas_v1, UblasVectorType     & ublas_v2,
         ViennaCLVectorType1 &   vcl_v1, ViennaCLVectorType2 &   vcl_v2)
{
  int retval = EXIT_SUCCESS;

  NumericT                    cpu_result = 42;
  viennacl::scalar<NumericT>  gpu_result = 43;

  //
  // Initializer:
  //
  std::cout << "Checking for zero_vector initializer..." << std::endl;
  //ublas_v1 = ublas::zero_vector<NumericT>(ublas_v1.size());
  for (std::size_t i=0; i<ublas_v1.size(); ++i)
    ublas_v1[i] = 0;
  vcl_v1 = viennacl::zero_vector<NumericT>(vcl_v1.size());
  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Checking for scalar_vector initializer..." << std::endl;
  //ublas_v1 = ublas::scalar_vector<NumericT>(ublas_v1.size(), cpu_result);
  for (std::size_t i=0; i<ublas_v1.size(); ++i)
    ublas_v1[i] = cpu_result;
  vcl_v1 = viennacl::scalar_vector<NumericT>(vcl_v1.size(), cpu_result);
  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  //ublas_v1 = ublas::scalar_vector<NumericT>(ublas_v1.size(), gpu_result);
  for (std::size_t i=0; i<ublas_v1.size(); ++i)
    ublas_v1[i] = cpu_result + 1;
  vcl_v1 = viennacl::scalar_vector<NumericT>(vcl_v1.size(), gpu_result);
  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Checking for unit_vector initializer..." << std::endl;
  //ublas_v1 = ublas::unit_vector<NumericT>(ublas_v1.size(), 5);
  for (std::size_t i=0; i<ublas_v1.size(); ++i)
    ublas_v1[i] = (i == 5) ? 1 : 0;
  vcl_v1 = viennacl::unit_vector<NumericT>(vcl_v1.size(), 5);
  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  for (std::size_t i=0; i<ublas_v1.size(); ++i)
  {
    ublas_v1[i] = NumericT(i);
    ublas_v2[i] = NumericT(i+42);
  }

  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());  //resync
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  std::cout << "Checking for successful copy..." << std::endl;
  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;
  if (check(ublas_v2, vcl_v2) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  //
  // Part 1: Norms and inner product
  //

  // --------------------------------------------------------------------------
  std::cout << "Testing inner_prod..." << std::endl;
  cpu_result = viennacl::linalg::inner_prod(ublas_v1, ublas_v2);
  NumericT cpu_result2 = viennacl::linalg::inner_prod(vcl_v1, vcl_v2);
  gpu_result = viennacl::linalg::inner_prod(vcl_v1, vcl_v2);

  if (check(cpu_result, cpu_result2) != EXIT_SUCCESS)
    return EXIT_FAILURE;
  if (check(cpu_result, gpu_result) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  cpu_result = inner_prod(ublas_v1 + ublas_v2, ublas_v2 - ublas_v1);
  NumericT cpu_result3 = viennacl::linalg::inner_prod(vcl_v1 + vcl_v2, vcl_v2 - vcl_v1);
  gpu_result = viennacl::linalg::inner_prod(vcl_v1 + vcl_v2, vcl_v2 - vcl_v1);

  if (check(cpu_result, cpu_result3) != EXIT_SUCCESS)
    return EXIT_FAILURE;
  if (check(cpu_result, gpu_result) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  // --------------------------------------------------------------------------
  std::cout << "Testing norm_1..." << std::endl;
  cpu_result = ublas::norm_1(ublas_v1);
  gpu_result = viennacl::linalg::norm_1(vcl_v1);

  if (check(cpu_result, gpu_result) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  gpu_result = 2 * cpu_result; //reset
  gpu_result = ublas::norm_1(ublas_v1);
  cpu_result = viennacl::linalg::norm_1(vcl_v1);

  if (check(cpu_result, gpu_result) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  gpu_result = ublas::norm_1(ublas_v1 + ublas_v2);
  cpu_result = viennacl::linalg::norm_1(vcl_v1 + vcl_v2);

  if (check(cpu_result, gpu_result) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  // --------------------------------------------------------------------------
  std::cout << "Testing norm_inf..." << std::endl;
  cpu_result = ublas::norm_inf(ublas_v1);
  gpu_result = viennacl::linalg::norm_inf(vcl_v1);

  if (check(cpu_result, gpu_result) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  gpu_result = 2 * cpu_result; //reset
  gpu_result = ublas::norm_inf(ublas_v1);
  cpu_result = viennacl::linalg::norm_inf(vcl_v1);

  if (check(cpu_result, gpu_result) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  gpu_result = ublas::norm_inf(ublas_v1 + ublas_v2);
  cpu_result = viennacl::linalg::norm_inf(vcl_v1 + vcl_v2);

  if (check(cpu_result, gpu_result) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  // --------------------------------------------------------------------------
  std::cout << "Testing index_norm_inf..." << std::endl;
  std::size_t cpu_index = ublas::index_norm_inf(ublas_v1);
  std::size_t gpu_index = viennacl::linalg::index_norm_inf(vcl_v1);

  if (check(static_cast<NumericT>(cpu_index), static_cast<NumericT>(gpu_index)) != EXIT_SUCCESS)
    return EXIT_FAILURE;
  // --------------------------------------------------------------------------
  cpu_result = ublas_v1[index_norm_inf(ublas_v1)];
  gpu_result = vcl_v1[viennacl::linalg::index_norm_inf(vcl_v1)];

  if (check(cpu_result, gpu_result) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  cpu_result = ublas_v1[index_norm_inf(ublas_v1 + ublas_v2)];
  gpu_result = vcl_v1[viennacl::linalg::index_norm_inf(vcl_v1 + vcl_v2)];

  if (check(cpu_result, gpu_result) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  // --------------------------------------------------------------------------
  std::cout << "Testing max..." << std::endl;
  cpu_result = ublas_v1[0];
  for (std::size_t i=0; i<ublas_v1.size(); ++i)
    cpu_result = std::max<NumericT>(cpu_result, ublas_v1[i]);
  gpu_result = viennacl::linalg::max(vcl_v1);

  if (check(cpu_result, gpu_result) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  cpu_result = ublas_v1[0];
  for (std::size_t i=0; i<ublas_v1.size(); ++i)
    cpu_result = std::max<NumericT>(cpu_result, ublas_v1[i]);
  gpu_result = cpu_result;
  cpu_result *= 2; //reset
  cpu_result = viennacl::linalg::max(vcl_v1);

  if (check(cpu_result, gpu_result) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  cpu_result = ublas_v1[0] + ublas_v2[0];
  for (std::size_t i=0; i<ublas_v1.size(); ++i)
    cpu_result = std::max<NumericT>(cpu_result, ublas_v1[i] + ublas_v2[i]);
  gpu_result = cpu_result;
  cpu_result *= 2; //reset
  cpu_result = viennacl::linalg::max(vcl_v1 + vcl_v2);

  if (check(cpu_result, gpu_result) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  // --------------------------------------------------------------------------
  std::cout << "Testing min..." << std::endl;
  cpu_result = ublas_v1[0];
  for (std::size_t i=0; i<ublas_v1.size(); ++i)
    cpu_result = std::min<NumericT>(cpu_result, ublas_v1[i]);
  gpu_result = viennacl::linalg::min(vcl_v1);

  if (check(cpu_result, gpu_result) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  cpu_result = ublas_v1[0];
  for (std::size_t i=0; i<ublas_v1.size(); ++i)
    cpu_result = std::min<NumericT>(cpu_result, ublas_v1[i]);
  gpu_result = cpu_result;
  cpu_result *= 2; //reset
  cpu_result = viennacl::linalg::min(vcl_v1);

  if (check(cpu_result, gpu_result) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  cpu_result = ublas_v1[0] + ublas_v2[0];
  for (std::size_t i=0; i<ublas_v1.size(); ++i)
    cpu_result = std::min<NumericT>(cpu_result, ublas_v1[i] + ublas_v2[i]);
  gpu_result = cpu_result;
  cpu_result *= 2; //reset
  cpu_result = viennacl::linalg::min(vcl_v1 + vcl_v2);

  if (check(cpu_result, gpu_result) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  //
  // Plane rotation and assignments
  //

  // --------------------------------------------------------------------------

  ublas::vector<NumericT> x = ublas_v1;
  ublas::vector<NumericT> y = ublas_v2;
  ublas::vector<NumericT> t = ublas_v1;
  t.assign (  NumericT(1) * x + NumericT(2) * y),
  y.assign (- NumericT(2) * x + NumericT(1) * y),
  x.assign (t);

  viennacl::linalg::plane_rotation(vcl_v1, vcl_v2, NumericT(1), NumericT(2));

  if (check(x, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;
  if (check(y, vcl_v2) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  // --------------------------------------------------------------------------

  std::cout << "Testing assignments..." << std::endl;
  NumericT val = static_cast<NumericT>(1);
  for (size_t i=0; i < ublas_v1.size(); ++i)
    ublas_v1(i) = val;

  for (size_t i=0; i < vcl_v1.size(); ++i)
    vcl_v1(i) = val;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  //
  // multiplication and division of vectors by scalars
  //
  std::cout << "Testing scaling with CPU scalar..." << std::endl;
  NumericT alpha = static_cast<NumericT>(3);
  viennacl::scalar<NumericT> gpu_alpha = alpha;

  ublas_v1  *= alpha;
  vcl_v1    *= alpha;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing scaling with GPU scalar..." << std::endl;
  ublas_v1  *= alpha;
  vcl_v1    *= gpu_alpha;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  NumericT beta  = static_cast<NumericT>(2);
  viennacl::scalar<NumericT> gpu_beta = beta;

  std::cout << "Testing shrinking with CPU scalar..." << std::endl;
  ublas_v1 /= beta;
  vcl_v1   /= beta;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing shrinking with GPU scalar..." << std::endl;
  ublas_v1 /= beta;
  vcl_v1   /= gpu_beta;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  //
  // add and inplace_add of vectors
  //
  for (size_t i=0; i < ublas_v1.size(); ++i)
    ublas_v1(i) = NumericT(i);
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());  //resync
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  std::cout << "Testing add on vector..." << std::endl;

  std::cout << "Checking for successful copy..." << std::endl;
  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;
  if (check(ublas_v2, vcl_v2) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  ublas_v1     = ublas_v1 + ublas_v2;
  vcl_v1       =   vcl_v1 +   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing add on vector with flipsign..." << std::endl;
  ublas_v1     = - ublas_v1 + ublas_v2;
  vcl_v1       = -   vcl_v1 +   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing inplace-add on vector..." << std::endl;
  ublas_v1 += ublas_v2;
  vcl_v1   +=   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  //
  // subtract and inplace_subtract of vectors
  //
  std::cout << "Testing sub on vector..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1     = ublas_v1 - ublas_v2;
  vcl_v1       =   vcl_v1 -   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing inplace-sub on vector..." << std::endl;
  ublas_v1 -= ublas_v2;
  vcl_v1   -= vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  //
  // multiply-add
  //
  std::cout << "Testing multiply-add on vector with CPU scalar (right)..." << std::endl;
  for (size_t i=0; i < ublas_v1.size(); ++i)
    ublas_v1(i) = NumericT(i);
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = ublas_v1 + alpha * ublas_v2;
  vcl_v1   = vcl_v1   + alpha *   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing multiply-add on vector with CPU scalar (left)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = alpha * ublas_v1 + ublas_v2;
  vcl_v1   = alpha *   vcl_v1 +   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing multiply-add on vector with CPU scalar (both)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = alpha * ublas_v1 + beta * ublas_v2;
  vcl_v1   = alpha *   vcl_v1 + beta *   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing inplace multiply-add on vector with CPU scalar..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 += alpha * ublas_v2;
  vcl_v1   += alpha *   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing multiply-add on vector with GPU scalar (right)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = ublas_v1 +     alpha * ublas_v2;
  vcl_v1   = vcl_v1   + gpu_alpha *   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing multiply-add on vector with GPU scalar (left)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = ublas_v1 +     alpha * ublas_v2;
  vcl_v1   = vcl_v1   + gpu_alpha *   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing multiply-add on vector with GPU scalar (both)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 =     alpha * ublas_v1 +     beta * ublas_v2;
  vcl_v1   = gpu_alpha *   vcl_v1 + gpu_beta *   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing inplace multiply-add on vector with GPU scalar (both, adding)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 +=     alpha * ublas_v1 +     beta * ublas_v2;
  vcl_v1   += gpu_alpha *   vcl_v1 + gpu_beta *   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing inplace multiply-add on vector with GPU scalar (both, subtracting)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 +=     alpha * ublas_v1 -     beta * ublas_v2;
  vcl_v1   += gpu_alpha *   vcl_v1 - gpu_beta *   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing inplace multiply-add on vector with GPU scalar..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 +=     alpha * ublas_v2;
  vcl_v1   += gpu_alpha *   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  //
  // division-add
  //
  std::cout << "Testing division-add on vector with CPU scalar (right)..." << std::endl;
  for (size_t i=0; i < ublas_v1.size(); ++i)
    ublas_v1(i) = NumericT(i);
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = ublas_v1 + ublas_v2 / alpha;
  vcl_v1   = vcl_v1   + vcl_v2 / alpha;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing division-add on vector with CPU scalar (left)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = ublas_v1 / alpha + ublas_v2;
  vcl_v1   =   vcl_v1 / alpha +   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing division-add on vector with CPU scalar (both)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = ublas_v1 / alpha + ublas_v2 / beta;
  vcl_v1   =   vcl_v1 / alpha +   vcl_v2 / beta;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing division-multiply-add on vector with CPU scalar..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = ublas_v1 / alpha + ublas_v2 * beta;
  vcl_v1   =   vcl_v1 / alpha +   vcl_v2 * beta;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing multiply-division-add on vector with CPU scalar..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = ublas_v1 * alpha + ublas_v2 / beta;
  vcl_v1   =   vcl_v1 * alpha +   vcl_v2 / beta;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing inplace division-add on vector with CPU scalar..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 += ublas_v2 / alpha;
  vcl_v1   += vcl_v2 / alpha;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing division-add on vector with GPU scalar (right)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = ublas_v1 + ublas_v2 / alpha;
  vcl_v1   = vcl_v1   +   vcl_v2 / gpu_alpha;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing division-add on vector with GPU scalar (left)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = ublas_v1 + ublas_v2 / alpha;
  vcl_v1   = vcl_v1   +   vcl_v2 / gpu_alpha;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing division-add on vector with GPU scalar (both)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = ublas_v1 / alpha     + ublas_v2 / beta;
  vcl_v1   =   vcl_v1 / gpu_alpha +   vcl_v2 / gpu_beta;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing inplace division-add on vector with GPU scalar (both, adding)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 += ublas_v1 / alpha     + ublas_v2 / beta;
  vcl_v1   +=   vcl_v1 / gpu_alpha +   vcl_v2 / gpu_beta;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing inplace division-add on vector with GPU scalar (both, subtracting)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 += ublas_v1 / alpha     - ublas_v2 / beta;
  vcl_v1   +=   vcl_v1 / gpu_alpha -   vcl_v2 / gpu_beta;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing inplace division-multiply-add on vector with GPU scalar (adding)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 += ublas_v1 / alpha     + ublas_v2 * beta;
  vcl_v1   +=   vcl_v1 / gpu_alpha +   vcl_v2 * gpu_beta;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing inplace multiply-division-add on vector with GPU scalar (subtracting)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 += ublas_v1 * alpha     - ublas_v2 / beta;
  vcl_v1   +=   vcl_v1 * gpu_alpha -   vcl_v2 / gpu_beta;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing inplace division-add on vector with GPU scalar..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 += ublas_v2 * alpha;
  vcl_v1   +=   vcl_v2 * gpu_alpha;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  //
  // multiply-subtract
  //
  std::cout << "Testing multiply-subtract on vector with CPU scalar (right)..." << std::endl;
  for (size_t i=0; i < ublas_v1.size(); ++i)
    ublas_v1(i) = NumericT(i);
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = ublas_v1 - alpha * ublas_v2;
  vcl_v1   = vcl_v1   - alpha *   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing multiply-subtract on vector with CPU scalar (left)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = alpha * ublas_v1 - ublas_v2;
  vcl_v1   = alpha * vcl_v1   -   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing multiply-subtract on vector with CPU scalar (both)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = alpha * ublas_v1 - beta * ublas_v2;
  vcl_v1   = alpha * vcl_v1   - beta *   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing inplace multiply-subtract on vector with CPU scalar..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 -= alpha * ublas_v2;
  vcl_v1   -= alpha *   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing multiply-subtract on vector with GPU scalar (right)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = ublas_v1 -     alpha * ublas_v2;
  vcl_v1   = vcl_v1   - gpu_alpha *   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing multiply-subtract on vector with GPU scalar (left)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = ublas_v1 -     alpha * ublas_v2;
  vcl_v1   = vcl_v1   - gpu_alpha *   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing multiply-subtract on vector with GPU scalar (both)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 =     alpha * ublas_v1 -     beta * ublas_v2;
  vcl_v1   = gpu_alpha * vcl_v1   - gpu_beta *   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing inplace multiply-subtract on vector with GPU scalar (both, adding)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 -=     alpha * ublas_v1 +     beta * ublas_v2;
  vcl_v1   -= gpu_alpha * vcl_v1   + gpu_beta *   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing inplace multiply-subtract on vector with GPU scalar (both, subtracting)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 -=     alpha * ublas_v1 -     beta * ublas_v2;
  vcl_v1   -= gpu_alpha * vcl_v1   - gpu_beta *   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing inplace multiply-subtract on vector with GPU scalar..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 -=     alpha * ublas_v2;
  vcl_v1   -= gpu_alpha *   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  //
  // division-subtract
  //
  std::cout << "Testing division-subtract on vector with CPU scalar (right)..." << std::endl;
  for (size_t i=0; i < ublas_v1.size(); ++i)
    ublas_v1(i) = NumericT(i);
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = ublas_v1 - ublas_v2 / alpha;
  vcl_v1   = vcl_v1   -   vcl_v2 / alpha;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing division-subtract on vector with CPU scalar (left)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = ublas_v1 / alpha - ublas_v2;
  vcl_v1   =   vcl_v1 / alpha -   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing division-subtract on vector with CPU scalar (both)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = ublas_v1 / alpha - ublas_v2 / alpha;
  vcl_v1   =   vcl_v1 / alpha -   vcl_v2 / alpha;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing inplace division-subtract on vector with CPU scalar..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 -= ublas_v2 / alpha;
  vcl_v1   -=   vcl_v2 / alpha;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing inplace division-subtract on vector with GPU scalar..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 -= ublas_v2 / alpha;
  vcl_v1   -=   vcl_v2 / gpu_alpha;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing division-subtract on vector with GPU scalar (right)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = ublas_v1 - ublas_v2 / alpha;
  vcl_v1   = vcl_v1   -   vcl_v2 / gpu_alpha;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing division-subtract on vector with GPU scalar (left)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = ublas_v1 - ublas_v2 / alpha;
  vcl_v1   = vcl_v1   -   vcl_v2 / gpu_alpha;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing division-subtract on vector with GPU scalar (both)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = ublas_v1 / alpha     - ublas_v2 / beta;
  vcl_v1   =   vcl_v1 / gpu_alpha -   vcl_v2 / gpu_beta;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing inplace division-subtract on vector with GPU scalar (both, adding)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 -= ublas_v1 / alpha     + ublas_v2 / beta;
  vcl_v1   -=   vcl_v1 / gpu_alpha +   vcl_v2 / gpu_beta;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing inplace division-subtract on vector with GPU scalar (both, subtracting)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 -= ublas_v1 / alpha     - ublas_v2 / beta;
  vcl_v1   -=   vcl_v1 / gpu_alpha -   vcl_v2 / gpu_beta;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing multiply-division-subtract on vector with GPU scalar..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = ublas_v1 * alpha     - ublas_v2 / beta;
  vcl_v1   =   vcl_v1 * gpu_alpha -   vcl_v2 / gpu_beta;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing division-multiply-subtract on vector with GPU scalar..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = ublas_v1 / alpha     - ublas_v2 * beta;
  vcl_v1   =   vcl_v1 / gpu_alpha -   vcl_v2 * gpu_beta;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing inplace multiply-division-subtract on vector with GPU scalar (adding)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 -= ublas_v1 * alpha     + ublas_v2 / beta;
  vcl_v1   -=   vcl_v1 * gpu_alpha +   vcl_v2 / gpu_beta;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing inplace division-multiply-subtract on vector with GPU scalar (adding)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 -= ublas_v1 / alpha     + ublas_v2 * beta;
  vcl_v1   -=   vcl_v1 / gpu_alpha +   vcl_v2 * gpu_beta;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing inplace multiply-division-subtract on vector with GPU scalar (subtracting)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 -= ublas_v1 * alpha     - ublas_v2 / beta;
  vcl_v1   -=   vcl_v1 * gpu_alpha -   vcl_v2 / gpu_beta;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing inplace division-multiply-subtract on vector with GPU scalar (subtracting)..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 -= ublas_v1 / alpha     - ublas_v2 * beta;
  vcl_v1   -=   vcl_v1 / gpu_alpha -   vcl_v2 * gpu_beta;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing inplace division-subtract on vector with GPU scalar..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 -=     alpha * ublas_v2;
  vcl_v1   -= gpu_alpha *   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  //
  // More complicated expressions (for ensuring the operator overloads work correctly)
  //
  for (size_t i=0; i < ublas_v1.size(); ++i)
    ublas_v1(i) = NumericT(i);
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  std::cout << "Testing three vector additions..." << std::endl;
  ublas_v1 = ublas_v2 + ublas_v1 + ublas_v2;
  vcl_v1   =   vcl_v2 +   vcl_v1 +   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  std::cout << "Testing complicated vector expression with CPU scalar..." << std::endl;
  ublas_v1 = beta * (ublas_v1 - alpha * ublas_v2);
  vcl_v1   = beta * (vcl_v1   - alpha * vcl_v2);

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing complicated vector expression with GPU scalar..." << std::endl;
  ublas_v1 =     beta * (ublas_v1 -     alpha * ublas_v2);
  vcl_v1   = gpu_beta * (vcl_v1   - gpu_alpha * vcl_v2);

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  // --------------------------------------------------------------------------
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  std::cout << "Testing swap..." << std::endl;
  swap(ublas_v1, ublas_v2);
  swap(vcl_v1, vcl_v2);

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  // --------------------------------------------------------------------------
  for (std::size_t i=0; i<ublas_v1.size(); ++i)
  {
    ublas_v1[i] = NumericT(1.0) + NumericT(i);
    ublas_v2[i] = NumericT(5.0) + NumericT(i);
  }

  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  std::cout << "Testing unary operator-..." << std::endl;
  ublas_v1 = - ublas_v2;
  vcl_v1   = -   vcl_v2;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing elementwise multiplication..." << std::endl;
  std::cout << " v1 = element_prod(v1, v2);" << std::endl;
  ublas_v1 = ublas::element_prod(ublas_v1, ublas_v2);
  vcl_v1 = viennacl::linalg::element_prod(vcl_v1, vcl_v2);

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << " v1 += element_prod(v1, v2);" << std::endl;
  ublas_v1 += ublas::element_prod(ublas_v1, ublas_v2);
  vcl_v1 += viennacl::linalg::element_prod(vcl_v1, vcl_v2);

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << " v1 -= element_prod(v1, v2);" << std::endl;
  ublas_v1 -= ublas::element_prod(ublas_v1, ublas_v2);
  vcl_v1 -= viennacl::linalg::element_prod(vcl_v1, vcl_v2);

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  ///////
  std::cout << " v1 = element_prod(v1 + v2, v2);" << std::endl;
  ublas_v1 = ublas::element_prod(ublas_v1 + ublas_v2, ublas_v2);
  vcl_v1 = viennacl::linalg::element_prod(vcl_v1 + vcl_v2, vcl_v2);

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << " v1 += element_prod(v1 + v2, v2);" << std::endl;
  ublas_v1 += ublas::element_prod(ublas_v1 + ublas_v2, ublas_v2);
  vcl_v1 += viennacl::linalg::element_prod(vcl_v1 + vcl_v2, vcl_v2);

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << " v1 -= element_prod(v1 + v2, v2);" << std::endl;
  ublas_v1 -= ublas::element_prod(ublas_v1 + ublas_v2, ublas_v2);
  vcl_v1 -= viennacl::linalg::element_prod(vcl_v1 + vcl_v2, vcl_v2);

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  ///////
  std::cout << " v1 = element_prod(v1, v2 + v1);" << std::endl;
  ublas_v1 = ublas::element_prod(ublas_v1, ublas_v2 + ublas_v1);
  vcl_v1 = viennacl::linalg::element_prod(vcl_v1, vcl_v2 + vcl_v1);

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << " v1 += element_prod(v1, v2 + v1);" << std::endl;
  ublas_v1 += ublas::element_prod(ublas_v1, ublas_v2 + ublas_v1);
  vcl_v1 += viennacl::linalg::element_prod(vcl_v1, vcl_v2 + vcl_v1);

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << " v1 -= element_prod(v1, v2 + v1);" << std::endl;
  ublas_v1 -= ublas::element_prod(ublas_v1, ublas_v2 + ublas_v1);
  vcl_v1 -= viennacl::linalg::element_prod(vcl_v1, vcl_v2 + vcl_v1);

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  ///////
  std::cout << " v1 = element_prod(v1 + v2, v2 + v1);" << std::endl;
  ublas_v1 = ublas::element_prod(ublas_v1 + ublas_v2, ublas_v2 + ublas_v1);
  vcl_v1 = viennacl::linalg::element_prod(vcl_v1 + vcl_v2, vcl_v2 + vcl_v1);

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << " v1 += element_prod(v1 + v2, v2 + v1);" << std::endl;
  ublas_v1 += ublas::element_prod(ublas_v1 + ublas_v2, ublas_v2 + ublas_v1);
  vcl_v1 += viennacl::linalg::element_prod(vcl_v1 + vcl_v2, vcl_v2 + vcl_v1);

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << " v1 -= element_prod(v1 + v2, v2 + v1);" << std::endl;
  ublas_v1 -= ublas::element_prod(ublas_v1 + ublas_v2, ublas_v2 + ublas_v1);
  vcl_v1 -= viennacl::linalg::element_prod(vcl_v1 + vcl_v2, vcl_v2 + vcl_v1);

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing elementwise division..." << std::endl;
  for (std::size_t i=0; i<ublas_v1.size(); ++i)
  {
    ublas_v1[i] = NumericT(1 + i);
    ublas_v2[i] = NumericT(5 + i);
  }

  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = ublas::element_div(ublas_v1, ublas_v2);
  vcl_v1 = viennacl::linalg::element_div(vcl_v1, vcl_v2);

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  ublas_v1 += ublas::element_div(ublas_v1, ublas_v2);
  vcl_v1 += viennacl::linalg::element_div(vcl_v1, vcl_v2);

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  ublas_v1 -= ublas::element_div(ublas_v1, ublas_v2);
  vcl_v1 -= viennacl::linalg::element_div(vcl_v1, vcl_v2);

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  ///////
  ublas_v1 = ublas::element_div(ublas_v1 + ublas_v2, ublas_v2);
  vcl_v1 = viennacl::linalg::element_div(vcl_v1 + vcl_v2, vcl_v2);

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  ublas_v1 += ublas::element_div(ublas_v1 + ublas_v2, ublas_v2);
  vcl_v1 += viennacl::linalg::element_div(vcl_v1 + vcl_v2, vcl_v2);

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  ublas_v1 -= ublas::element_div(ublas_v1 + ublas_v2, ublas_v2);
  vcl_v1 -= viennacl::linalg::element_div(vcl_v1 + vcl_v2, vcl_v2);

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  ///////
  ublas_v1 = ublas::element_div(ublas_v1, ublas_v2 + ublas_v1);
  vcl_v1 = viennacl::linalg::element_div(vcl_v1, vcl_v2 + vcl_v1);

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  ublas_v1 += ublas::element_div(ublas_v1, ublas_v2 + ublas_v1);
  vcl_v1 += viennacl::linalg::element_div(vcl_v1, vcl_v2 + vcl_v1);

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  ublas_v1 -= ublas::element_div(ublas_v1, ublas_v2 + ublas_v1);
  vcl_v1 -= viennacl::linalg::element_div(vcl_v1, vcl_v2 + vcl_v1);

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  ///////
  ublas_v1 = ublas::element_div(ublas_v1 + ublas_v2, ublas_v2 + ublas_v1);
  vcl_v1 = viennacl::linalg::element_div(vcl_v1 + vcl_v2, vcl_v2 + vcl_v1);

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  ublas_v1 += ublas::element_div(ublas_v1 + ublas_v2, ublas_v2 + ublas_v1);
  vcl_v1 += viennacl::linalg::element_div(vcl_v1 + vcl_v2, vcl_v2 + vcl_v1);

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  ublas_v1 -= ublas::element_div(ublas_v1 + ublas_v2, ublas_v2 + ublas_v1);
  vcl_v1 -= viennacl::linalg::element_div(vcl_v1 + vcl_v2, vcl_v2 + vcl_v1);

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << "Testing unary elementwise operations..." << std::endl;

#define GENERATE_UNARY_OP_TEST(FUNCNAME) \
  ublas_v2 = 3 * ublas_v1; \
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin()); \
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin()); \
  \
  for (std::size_t i=0; i<ublas_v1.size(); ++i) \
    ublas_v1[i] = std::FUNCNAME(ublas_v2[i]); \
  vcl_v1 = viennacl::linalg::element_##FUNCNAME(vcl_v2); \
 \
  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS) \
  { \
    std::cout << "Failure at v1 = " << #FUNCNAME << "(v2)" << std::endl; \
    return EXIT_FAILURE; \
  } \
 \
  for (std::size_t i=0; i<ublas_v1.size(); ++i) \
    ublas_v1[i] = std::FUNCNAME(ublas_v1[i] + ublas_v2[i]); \
  vcl_v1 = viennacl::linalg::element_##FUNCNAME(vcl_v1 + vcl_v2); \
 \
  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS) \
  { \
    std::cout << "Failure at v1 = " << #FUNCNAME << "(v1 + v2)" << std::endl; \
    return EXIT_FAILURE; \
  } \
 \
  for (std::size_t i=0; i<ublas_v1.size(); ++i) \
    ublas_v1[i] += std::FUNCNAME(ublas_v1[i]); \
  vcl_v1 += viennacl::linalg::element_##FUNCNAME(vcl_v1); \
 \
  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS) \
  { \
    std::cout << "Failure at v1 += " << #FUNCNAME << "(v2)" << std::endl; \
    return EXIT_FAILURE; \
  } \
 \
  for (std::size_t i=0; i<ublas_v1.size(); ++i) \
    ublas_v1[i] += std::FUNCNAME(ublas_v1[i] + ublas_v2[i]); \
  vcl_v1 += viennacl::linalg::element_##FUNCNAME(vcl_v1 + vcl_v2); \
 \
  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS) \
  { \
    std::cout << "Failure at v1 += " << #FUNCNAME << "(v1 + v2)" << std::endl; \
    return EXIT_FAILURE; \
  } \
 \
  for (std::size_t i=0; i<ublas_v1.size(); ++i) \
    ublas_v1[i] -= std::FUNCNAME(ublas_v2[i]); \
  vcl_v1 -= viennacl::linalg::element_##FUNCNAME(vcl_v2); \
 \
  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS) \
  { \
    std::cout << "Failure at v1 -= " << #FUNCNAME << "(v2)" << std::endl; \
    return EXIT_FAILURE; \
  } \
 \
  for (std::size_t i=0; i<ublas_v1.size(); ++i) \
    ublas_v1[i] -= std::FUNCNAME(ublas_v1[i] + ublas_v2[i]); \
  vcl_v1 -= viennacl::linalg::element_##FUNCNAME(vcl_v1 + vcl_v2); \
 \
  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS) \
  { \
    std::cout << "Failure at v1 -= " << #FUNCNAME << "(v1 + v2)" << std::endl; \
    return EXIT_FAILURE; \
  } \

  //GENERATE_UNARY_OP_TEST(cos);
  //GENERATE_UNARY_OP_TEST(cosh);
  //GENERATE_UNARY_OP_TEST(exp);
  //GENERATE_UNARY_OP_TEST(floor);
  //GENERATE_UNARY_OP_TEST(fabs);
  //GENERATE_UNARY_OP_TEST(log);
  //GENERATE_UNARY_OP_TEST(log10);
  //GENERATE_UNARY_OP_TEST(sin);
  //GENERATE_UNARY_OP_TEST(sinh);
  //GENERATE_UNARY_OP_TEST(fabs);
  GENERATE_UNARY_OP_TEST(abs);
  //GENERATE_UNARY_OP_TEST(sqrt);
  //GENERATE_UNARY_OP_TEST(tan);
  //GENERATE_UNARY_OP_TEST(tanh);

  std::cout << "Testing lenghty sum of scaled vectors..." << std::endl;
  ublas_v2 = 3 * ublas_v1;
  viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
  viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());

  ublas_v1 = ublas_v2 / alpha   +     beta * ublas_v1 - alpha * ublas_v2 + beta * ublas_v1 - alpha * ublas_v1;
  vcl_v1   = vcl_v2 / gpu_alpha + gpu_beta *   vcl_v1 - alpha *   vcl_v2 + beta *   vcl_v1 - alpha *   vcl_v1;

  if (check(ublas_v1, vcl_v1) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  // --------------------------------------------------------------------------
  return retval;
}

template< typename NumericT >
int test()
{
  int retval = EXIT_SUCCESS;
  std::size_t size = 12345;

  std::cout << "Running tests for vector of size " << size << std::endl;

  //
  // Set up UBLAS objects
  //
  ublas::vector<NumericT> ublas_full_vec(size);
  ublas::vector<NumericT> ublas_full_vec2(ublas_full_vec.size());

  for (std::size_t i=0; i<ublas_full_vec.size(); ++i)
  {
    ublas_full_vec[i]  = NumericT(1.0) + NumericT(i);
    ublas_full_vec2[i] = NumericT(2.0) + NumericT(i) / NumericT(2);
  }

  ublas::range r1(    ublas_full_vec.size() / 4, 2 * ublas_full_vec.size() / 4);
  ublas::range r2(2 * ublas_full_vec2.size() / 4, 3 * ublas_full_vec2.size() / 4);
  ublas::vector_range< ublas::vector<NumericT> > ublas_range_vec(ublas_full_vec, r1);
  ublas::vector_range< ublas::vector<NumericT> > ublas_range_vec2(ublas_full_vec2, r2);

  ublas::slice s1(    ublas_full_vec.size() / 4, 3, ublas_full_vec.size() / 4);
  ublas::slice s2(2 * ublas_full_vec2.size() / 4, 2, ublas_full_vec2.size() / 4);
  ublas::vector_slice< ublas::vector<NumericT> > ublas_slice_vec(ublas_full_vec, s1);
  ublas::vector_slice< ublas::vector<NumericT> > ublas_slice_vec2(ublas_full_vec2, s2);

  //
  // Set up ViennaCL objects
  //
  viennacl::vector<NumericT> vcl_full_vec(ublas_full_vec.size());
  viennacl::vector<NumericT> vcl_full_vec2(ublas_full_vec2.size());

  viennacl::fast_copy(ublas_full_vec.begin(), ublas_full_vec.end(), vcl_full_vec.begin());
  viennacl::copy(ublas_full_vec2.begin(), ublas_full_vec2.end(), vcl_full_vec2.begin());

  viennacl::range vcl_r1(    vcl_full_vec.size() / 4, 2 * vcl_full_vec.size() / 4);
  viennacl::range vcl_r2(2 * vcl_full_vec2.size() / 4, 3 * vcl_full_vec2.size() / 4);
  viennacl::vector_range< viennacl::vector<NumericT> > vcl_range_vec(vcl_full_vec, vcl_r1);
  viennacl::vector_range< viennacl::vector<NumericT> > vcl_range_vec2(vcl_full_vec2, vcl_r2);

  {
    viennacl::vector<NumericT> vcl_short_vec(vcl_range_vec);
    viennacl::vector<NumericT> vcl_short_vec2 = vcl_range_vec2;

    ublas::vector<NumericT> ublas_short_vec(ublas_range_vec);
    ublas::vector<NumericT> ublas_short_vec2(ublas_range_vec2);

    std::cout << "Testing creation of vectors from range..." << std::endl;
    if (check(ublas_short_vec, vcl_short_vec) != EXIT_SUCCESS)
      return EXIT_FAILURE;
    if (check(ublas_short_vec2, vcl_short_vec2) != EXIT_SUCCESS)
      return EXIT_FAILURE;
  }

  viennacl::slice vcl_s1(    vcl_full_vec.size() / 4, 3, vcl_full_vec.size() / 4);
  viennacl::slice vcl_s2(2 * vcl_full_vec2.size() / 4, 2, vcl_full_vec2.size() / 4);
  viennacl::vector_slice< viennacl::vector<NumericT> > vcl_slice_vec(vcl_full_vec, vcl_s1);
  viennacl::vector_slice< viennacl::vector<NumericT> > vcl_slice_vec2(vcl_full_vec2, vcl_s2);

  viennacl::vector<NumericT> vcl_short_vec(vcl_slice_vec);
  viennacl::vector<NumericT> vcl_short_vec2 = vcl_slice_vec2;

  ublas::vector<NumericT> ublas_short_vec(ublas_slice_vec);
  ublas::vector<NumericT> ublas_short_vec2(ublas_slice_vec2);

  std::cout << "Testing creation of vectors from slice..." << std::endl;
  if (check(ublas_short_vec, vcl_short_vec) != EXIT_SUCCESS)
    return EXIT_FAILURE;
  if (check(ublas_short_vec2, vcl_short_vec2) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  //
  // Now start running tests for vectors, ranges and slices:
  //

  std::cout << " ** vcl_v1 = vector, vcl_v2 = vector **" << std::endl;
  retval = test<NumericT>(ublas_short_vec, ublas_short_vec2,
                          vcl_short_vec, vcl_short_vec2);
  if (retval != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << " ** vcl_v1 = vector, vcl_v2 = range **" << std::endl;
  retval = test<NumericT>(ublas_short_vec, ublas_short_vec2,
                          vcl_short_vec, vcl_range_vec2);
  if (retval != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << " ** vcl_v1 = vector, vcl_v2 = slice **" << std::endl;
  retval = test<NumericT>(ublas_short_vec, ublas_short_vec2,
                          vcl_short_vec, vcl_slice_vec2);
  if (retval != EXIT_SUCCESS)
    return EXIT_FAILURE;

  ///////

  std::cout << " ** vcl_v1 = range, vcl_v2 = vector **" << std::endl;
  retval = test<NumericT>(ublas_short_vec, ublas_short_vec2,
                          vcl_range_vec, vcl_short_vec2);
  if (retval != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << " ** vcl_v1 = range, vcl_v2 = range **" << std::endl;
  retval = test<NumericT>(ublas_short_vec, ublas_short_vec2,
                          vcl_range_vec, vcl_range_vec2);
  if (retval != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << " ** vcl_v1 = range, vcl_v2 = slice **" << std::endl;
  retval = test<NumericT>(ublas_short_vec, ublas_short_vec2,
                          vcl_range_vec, vcl_slice_vec2);
  if (retval != EXIT_SUCCESS)
    return EXIT_FAILURE;

  ///////

  std::cout << " ** vcl_v1 = slice, vcl_v2 = vector **" << std::endl;
  retval = test<NumericT>(ublas_short_vec, ublas_short_vec2,
                          vcl_slice_vec, vcl_short_vec2);
  if (retval != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << " ** vcl_v1 = slice, vcl_v2 = range **" << std::endl;
  retval = test<NumericT>(ublas_short_vec, ublas_short_vec2,
                          vcl_slice_vec, vcl_range_vec2);
  if (retval != EXIT_SUCCESS)
    return EXIT_FAILURE;

  std::cout << " ** vcl_v1 = slice, vcl_v2 = slice **" << std::endl;
  retval = test<NumericT>(ublas_short_vec, ublas_short_vec2,
                          vcl_slice_vec, vcl_slice_vec2);
  if (retval != EXIT_SUCCESS)
    return EXIT_FAILURE;

  return EXIT_SUCCESS;
}

//
// -------------------------------------------------------------
//
int main()
{
  std::cout << std::endl;
  std::cout << "----------------------------------------------" << std::endl;
  std::cout << "----------------------------------------------" << std::endl;
  std::cout << "## Test :: Vector with Integer types" << std::endl;
  std::cout << "----------------------------------------------" << std::endl;
  std::cout << "----------------------------------------------" << std::endl;
  std::cout << std::endl;

  int retval = EXIT_SUCCESS;

  std::cout << std::endl;
  std::cout << "----------------------------------------------" << std::endl;
  std::cout << std::endl;
  {
    std::cout << "# Testing setup:" << std::endl;
    std::cout << "  numeric: int" << std::endl;
    retval = test<int>();
    if ( retval == EXIT_SUCCESS )
      std::cout << "# Test passed" << std::endl;
    else
      return retval;
  }
  std::cout << std::endl;
  std::cout << "----------------------------------------------" << std::endl;
  std::cout << std::endl;
  {
    std::cout << "# Testing setup:" << std::endl;
    std::cout << "  numeric: long" << std::endl;
    retval = test<long>();
    if ( retval == EXIT_SUCCESS )
      std::cout << "# Test passed" << std::endl;
    else
      return retval;
  }
  std::cout << std::endl;
  std::cout << "----------------------------------------------" << std::endl;
  std::cout << std::endl;

  std::cout << std::endl;
  std::cout << "------- Test completed --------" << std::endl;
  std::cout << std::endl;

  return retval;
}


//==============================================================================
//         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
//==============================================================================
#include <nt2/trigonometric/include/functions/sincosd.hpp>
#include <boost/simd/sdk/simd/native.hpp>
#include <boost/simd/sdk/simd/io.hpp>

#include <nt2/include/constants/pio2_3.hpp>
#include <nt2/include/constants/pi.hpp>
#include <nt2/include/constants/zero.hpp>
#include <nt2/include/constants/one.hpp>
#include <nt2/include/constants/pio_2.hpp>
#include <nt2/include/constants/inf.hpp>
#include <nt2/include/constants/minf.hpp>
#include <nt2/include/constants/nan.hpp>
#include <nt2/include/functions/sind.hpp>
#include <nt2/include/functions/cosd.hpp>
#include <nt2/include/functions/splat.hpp>
#include <boost/dispatch/functor/meta/call.hpp>
#include <boost/fusion/include/vector_tie.hpp>

#include <nt2/sdk/unit/module.hpp>
#include <nt2/sdk/unit/tests/relation.hpp>
#include <nt2/sdk/unit/tests/type_expr.hpp>

NT2_TEST_CASE_TPL( sincosd, BOOST_SIMD_SIMD_REAL_TYPES)
{
  using nt2::sincosd;
  using nt2::tag::sincosd_;
  using boost::simd::native;

  typedef native<T,BOOST_SIMD_DEFAULT_EXTENSION>            vT;
  vT a[] = {nt2::Zero<vT>(), nt2::One<vT>(), nt2::splat<vT>(120), nt2::splat<vT>(180),
            nt2::splat<vT>(90), nt2::Inf<vT>(), nt2::Minf<vT>(), nt2::Nan<vT>()};
  size_t N =  sizeof(a)/sizeof(vT);

  NT2_TEST_TYPE_IS( (typename boost::dispatch::meta::call<sincosd_(vT)>::type)
                  , (std::pair<vT,vT>)
                  );

  {
    vT s, c;
    for(size_t i=0; i < N; ++i)
    {
      sincosd(a[i], s, c);
      NT2_TEST_EQUAL(s, nt2::sind(a[i]));
      NT2_TEST_EQUAL(c, nt2::cosd(a[i]));
    }
  }

  {
    vT s, c;
    for(size_t i=0; i < N; ++i)
    {
      s = sincosd(a[i], c);
      NT2_TEST_EQUAL(s, nt2::sind(a[i]));
      NT2_TEST_EQUAL(c, nt2::cosd(a[i]));
    }
  }

  {
    vT s, c;
    for(size_t i=0; i < N; ++i)
    {
      boost::fusion::vector_tie(s, c) = sincosd(a[i]);
      NT2_TEST_EQUAL(s, nt2::sind(a[i]));
      NT2_TEST_EQUAL(c, nt2::cosd(a[i]));
    }
  }

  {
    for(size_t i=0; i < N; ++i)
    {
      std::pair<vT,vT> p = sincosd(a[i]);
      NT2_TEST_EQUAL(p.first,  nt2::sind(a[i]));
      NT2_TEST_EQUAL(p.second, nt2::cosd(a[i]));
    }
  }
}


// Copyright © 2016-2019 Thomas Nagler and Thibault Vatter
//
// This file is part of the vinecopulib library and licensed under the terms of
// the MIT license. For a copy, see the LICENSE file in the root directory of
// vinecopulib or https://vinecopulib.github.io/vinecopulib/.

#pragma once

#include <Eigen/Dense>
#include <vinecopulib/vinecop/fit_controls.hpp>
#include <boost/property_tree/ptree.hpp>
#include <vinecopulib/vinecop/rvine_structure.hpp>

namespace vinecopulib {

// forward declarations
class Bicop;
namespace tools_select {
    class VinecopSelector;
}

//! @brief A class for vine copula models
//!
//! A vine copula model is characterized by the structure matrix (see
//! TriangularArray) and the pair-copulas.
class Vinecop
{
public:

    // default constructors
    Vinecop() {}

    Vinecop(size_t d);

    // Constructors with structure only
    Vinecop(const RVineStructure &vine_struct);

    Vinecop(const Eigen::Matrix <size_t, Eigen::Dynamic, Eigen::Dynamic> &matrix,
            const bool check_matrix = true);

    Vinecop(const std::vector<size_t> &order,
            const TriangularArray<size_t> &struct_array,
            const bool check_array = true);

    // Constructors with pair_copulas + structure
    Vinecop(const std::vector<std::vector<Bicop>> &pair_copulas,
            const RVineStructure &vine_struct);

    Vinecop(const std::vector <std::vector<Bicop>> &pair_copulas,
            const Eigen::Matrix <size_t, Eigen::Dynamic, Eigen::Dynamic> &matrix,
            const bool check_matrix = true);

    Vinecop(const std::vector<std::vector<Bicop>> &pair_copulas,
            const std::vector<size_t> &order,
            const TriangularArray<size_t> &struct_array,
            const bool check_array = true);

    // Constructors from data
    Vinecop(const Eigen::MatrixXd &data,
            const FitControlsVinecop &controls = FitControlsVinecop());

    Vinecop(const Eigen::MatrixXd &data,
            const RVineStructure &vine_struct,
            FitControlsVinecop controls = FitControlsVinecop());

    Vinecop(const Eigen::MatrixXd &data,
            const Eigen::Matrix <size_t, Eigen::Dynamic, Eigen::Dynamic> &matrix,
            FitControlsVinecop controls = FitControlsVinecop(),
            const bool check_matrix = true);

    Vinecop(const Eigen::MatrixXd &data,
            const std::vector<size_t> &order,
            const TriangularArray<size_t> &struct_array,
            FitControlsVinecop controls = FitControlsVinecop(),
            const bool check_array = true);

    // Constructors from files/serialized objects
    Vinecop(const char *filename, const bool check_matrix = true);

    Vinecop(const boost::property_tree::ptree input,
            const bool check_matrix = true);

    // Serialize
    boost::property_tree::ptree to_ptree() const;

    void to_json(const char *filename) const;

    // Methods modifying structure and/or families and parameters
    void select_all(const Eigen::MatrixXd &data,
                    const FitControlsVinecop &controls = FitControlsVinecop());

    void select_families(const Eigen::MatrixXd &data,
                         const FitControlsVinecop &controls = FitControlsVinecop());

    // Getters for a single pair copula
    Bicop get_pair_copula(size_t tree, size_t edge) const;

    BicopFamily get_family(size_t tree, size_t edge) const;

    int get_rotation(size_t tree, size_t edge) const;

    Eigen::MatrixXd get_parameters(size_t tree, size_t edge) const;

    double get_tau(size_t tree, size_t edge) const;

    size_t get_trunc_lvl() const;

    // Getters for all pair copulas
    std::vector <std::vector<Bicop>> get_all_pair_copulas() const;

    std::vector <std::vector<BicopFamily>> get_all_families() const;

    std::vector <std::vector<int>> get_all_rotations() const;

    std::vector <std::vector<Eigen::MatrixXd>> get_all_parameters() const;

    std::vector <std::vector<double>> get_all_taus() const;

    // Getters for the structure
    size_t get_dim() const;

    std::vector<size_t> get_order() const;

    RVineStructure get_rvine_structure() const;

    Eigen::Matrix<size_t, Eigen::Dynamic, Eigen::Dynamic> get_matrix() const;

    TriangularArray<size_t> get_struct_array() const;

    // getters for fit statistics
    double get_threshold() const;
    double get_loglik() const;
    size_t get_nobs() const;
    double get_aic() const;
    double get_bic() const;
    double get_mbicv(const double psi0) const;

    // Stats methods
    Eigen::VectorXd pdf(const Eigen::MatrixXd &u,
                        const size_t num_threads = 1) const;

    Eigen::VectorXd cdf(const Eigen::MatrixXd &u,
                        const size_t N = 1e4,
                        const size_t num_threads = 1,
                        std::vector<int> seeds = std::vector<int>()) const;

    Eigen::MatrixXd simulate(const size_t n,
                             const bool qrng = false,
                             const size_t num_threads = 1,
                             const std::vector<int>& seeds = std::vector<int>()) const;

    Eigen::MatrixXd rosenblatt(const Eigen::MatrixXd &u,
                               const size_t num_threads = 1) const;
    Eigen::MatrixXd inverse_rosenblatt(const Eigen::MatrixXd &u,
                                       const size_t num_threads = 1) const;

    // Fit statistics
    double calculate_npars() const;

    double loglik(const Eigen::MatrixXd &u, const size_t num_threads = 1) const;

    double aic(const Eigen::MatrixXd &u, const size_t num_threads = 1) const;

    double bic(const Eigen::MatrixXd &u, const size_t num_threads = 1) const;

    double mbicv(const Eigen::MatrixXd &u, const double psi0, const size_t num_threads = 1) const;

    // Misc methods
    static std::vector<std::vector<Bicop>>
    make_pair_copula_store(const size_t d,
                           const size_t trunc_lvl = std::numeric_limits<size_t>::max());
    void truncate(size_t trunc_lvl);

    std::string str() const;

protected:
    size_t d_;
    RVineStructure vine_struct_;
    std::vector<std::vector<Bicop>> pair_copulas_;
    double threshold_;
    double loglik_;
    size_t nobs_;

    void check_data_dim(const Eigen::MatrixXd &data) const;
    void check_pair_copulas_rvine_structure(
        const std::vector<std::vector<Bicop>> &pair_copulas) const;
    double calculate_mbicv_penalty(const size_t nobs, const double psi0) const;
    void finalize_fit(const tools_select::VinecopSelector& selector);
    void check_weights_size(const Eigen::VectorXd& weights,
                            const Eigen::MatrixXd& data) const;
    void check_enough_data(const Eigen::MatrixXd& data) const;
    void check_fitted() const;
};

}

#include <vinecopulib/vinecop/implementation/class.ipp>


//
//=======================================================================
// Copyright 2002 Marc Wintermantel (wintermantel@imes.mavt.ethz.ch)
// ETH Zurich, Center of Structure Technologies (www.imes.ethz.ch/st)
//
// This file is part of the Boost Graph Library
//
// You should have received a copy of the License Agreement for the
// Boost Graph Library along with the software; see the file LICENSE.
// If not, contact Office of Research, University of Notre Dame, Notre
// Dame, IN 46556.
//
// Permission to modify the code and to distribute modified code is
// granted, provided the text of this NOTICE is retained, a notice that
// the code was modified is included with the above COPYRIGHT NOTICE and
// with the COPYRIGHT NOTICE in the LICENSE file, and that the LICENSE
// file is distributed with the modified code.
//
// LICENSOR MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.
// By way of example, but not limitation, Licensor MAKES NO
// REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
// PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE COMPONENTS
// OR DOCUMENTATION WILL NOT INFRINGE ANY PATENTS, COPYRIGHTS, TRADEMARKS
// OR OTHER RIGHTS.
//=======================================================================
//

#ifndef BOOST_GRAPH_PROFILE_HPP
#define BOOST_GRAPH_PROFILE_HPP

#include <boost/graph/graph_traits.hpp>
#include <boost/detail/numeric_traits.hpp>
#include <boost/graph/bandwidth.hpp>

namespace boost {

  template <typename Graph, typename VertexIndexMap>
  typename graph_traits<Graph>::vertices_size_type
  profile(const Graph& g, VertexIndexMap index)
  {
    typename graph_traits<Graph>::vertices_size_type b = 0;
    typename graph_traits<Graph>::vertex_iterator i, end;
    for (tie(i, end) = vertices(g); i != end; ++i){
      b += ith_bandwidth(*i, g, index) + 1;
    }

    return b;
  }

  template <typename Graph>
  typename graph_traits<Graph>::vertices_size_type
  profile(const Graph& g)
  {
    return profile(g, get(vertex_index, g));
  }

} // namespace boost

#endif // BOOST_GRAPH_PROFILE_HPP


#ifndef SUPERGENIUS_SRC_CRYPTO_MP_UTILS_HPP
#define SUPERGENIUS_SRC_CRYPTO_MP_UTILS_HPP

#include <boost/multiprecision/cpp_int.hpp>
#include <gsl/span>

namespace sgns::base {

  namespace detail {
    template <size_t size, typename uint>
    std::array<uint8_t, size> uint_to_bytes(uint &&i);

    template <size_t size, typename uint>
    uint bytes_to_uint(gsl::span<uint8_t, size> bytes);
  }  // namespace detail

  std::array<uint8_t, 8> uint64_t_to_bytes(uint64_t number);

  uint64_t bytes_to_uint64_t(gsl::span<uint8_t, 8> bytes);

  std::array<uint8_t, 16> uint128_t_to_bytes(
      const boost::multiprecision::uint128_t &i);

  boost::multiprecision::uint128_t bytes_to_uint128_t(
      gsl::span<uint8_t, 16> bytes);

  std::array<uint8_t, 32> uint256_t_to_bytes(
      const boost::multiprecision::uint256_t &i);

  boost::multiprecision::uint256_t bytes_to_uint256_t(
      gsl::span<uint8_t, 32> bytes);

}  // namespace sgns::base

#endif  // SUPERGENIUS_SRC_CRYPTO_MP_UTILS_HPP


#include <unistd.h>

#include <algorithm>
#include <chrono>
#include <cmath>
#include <cstdint>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <random>
#include <sstream>   // std::stringstream
#include <stdexcept> // std::runtime_error
#include <string>
#include <thread>
#include <vector>

#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>

#include "array.h"
#include "func.h"
#include "perceptron.h"
#include "utility.h"

/**
 * Read Data
 * Read the test or training data from a CSV File, and apply a * preprocessing
 * function to the data.
 *
 * The data is always expected to be:
 *    Label, Feature1, Feature2, Feature3, ...
 *
 * @param fname File of input data
 * @param features The number of features in the dataset
 * @param process
 * @return Dataset
 */
Array2D read_data(std::string fname, size_t features, preprocess_t process) {
  std::cout << "Reading vectors from " << fname << "\n";
  Array2D ret;
  std::string file_line;
  std::string col_value;
  std::ifstream infile(fname);

  if (!infile.is_open()) {
    throw std::runtime_error("Could not open file");
  }

  size_t i = 0;
  while (infile.good()) {
    getline(infile, file_line);
    std::stringstream ss(file_line);

    // checking if its an empty line
    if (!(std::getline(ss, col_value, ','))) {
      continue;
    }

    // Create a New Entry:
    ret.data.push_back(std::vector<double>());
    ret.data[i].push_back((double)std::stoi(col_value));

    size_t count = 0;
    while (std::getline(ss, col_value, ',')) {
      if (count >= features) {
        break;
      }
      count++;
      ret.data[i].push_back((double)std::stoi(col_value));
    }
    i++;
    if (count != features) {
      throw std::runtime_error(
          "Number of Events specified is less than the number that exist");
    }
  }
  infile.close();
  if (ret.data.size()) {
    ret.width = ret.data[0].size();
  }
  ret.height = ret.data.size();

  Array2D temp;

  // Apply input formatting
  switch (process) {
    case RAW: {
      // Do nothing
      break;
    }
    case STANDARDIZE: {
      temp = ret.get_subset(ret.height, ret.width - 1, 0, 1);
      temp = standardize(temp);
      ret.apply_subset(temp, 0, 1);
      break;
    }
    case NORMALIZE: {
      temp = ret.get_subset(ret.height, ret.width - 1, 0, 1);
      temp = normalize(temp);
      ret.apply_subset(temp, 0, 1);
      break;
    }
    default: {
      break;
    }
  }

  std::cout << "Read " << i << " vectors from file: " << fname << "\n";
  return ret;
}

/**
 * train_dnn
 * Minibatch Train the DNN
 * @param dnn The network to train
 * @param training_data
 * @param epochs The number of training intervals
 * @param minibatch_size The size of the minibatch, 1 = sgd
 */
void train_dnn(DNN &dnn,
               Array2D &train_data,
               size_t epochs,
               size_t minibatch_size) {
  Array2D input;
  Array2D label;
  int int_label;
  double loss = 0;
  double t_correct = 0;
  double t_total = 0;
  std::vector<double> correct;
  std::vector<double> total;
  correct.resize(dnn.get_actions(), 0.0);
  total.resize(dnn.get_actions(), 0.0);
  for (size_t i = 0; i < epochs; i++) {
    train_data.apply_shuffle();
    input =
        train_data.get_subset(train_data.height, train_data.width - 1, 0, 1);
    label = train_data.get_subset(train_data.height, 1, 0, 0);
    for (size_t j = 0; j < correct.size(); j++) {
      correct[j] = 0.0;
      total[j] = 0.0;
    }
    t_correct = 0.0;
    t_total = 0.0;
    loss = 0.0;
    for (size_t j = 0; j < train_data.height - minibatch_size;
         j += minibatch_size) {
      loss += dnn.train(input.get_subset(minibatch_size, input.width, j, 0),
                        label.get_subset(minibatch_size, label.width, j, 0));
    }
    for (size_t j = 0; j < train_data.height; j++) {
      int_label = (int)label.get_subset(1, label.width, j, 0).data[0][0];
      if (int_label == dnn.eval(input.get_subset(1, input.width, j, 0))) {
        t_correct += 1.0;
        correct[int_label] += 1.0;
      }
      total[int_label] += 1.0;
      t_total += 1.0;
    }
    std::cout << i << "," << loss << "," << t_correct << "," << t_total << ","
              << t_correct / t_total;
    for (size_t k = 0; k < correct.size(); k++) {
      std::cout << "," << correct[k] / total[k];
    }
    std::cout << "\n";
  }
}

/**
 * train_dnn
 * Test the DNN
 * @param dnn The network to test
 * @param test_data, Array of Test Data
 */
void test_dnn(DNN &dnn, Array2D &test_data) {
  Array2D input;
  Array2D label;
  int int_label;
  double t_correct = 0;
  double t_total = 0;
  std::vector<double> correct;
  std::vector<double> total;
  correct.resize(dnn.get_actions(), 0.0);
  total.resize(dnn.get_actions(), 0.0);
  test_data.apply_shuffle();
  input = test_data.get_subset(test_data.height, test_data.width - 1, 0, 1);
  label = test_data.get_subset(test_data.height, 1, 0, 0);
  correct.resize(dnn.get_actions(), 0.0);
  total.resize(dnn.get_actions(), 0.0);
  t_correct = 0.0;
  t_total = 0.0;
  for (size_t j = 0; j < test_data.height; j++) {
    int_label = (int)label.get_subset(1, label.width, j, 0).data[0][0];
    if (int_label == dnn.eval(input.get_subset(1, input.width, j, 0))) {
      t_correct += 1.0;
      correct[int_label] += 1.0;
    }
    total[int_label] += 1.0;
    t_total += 1.0;
  }
  std::cout << "Test DNN " << t_correct << "," << t_total << ","
            << t_correct / t_total;
  for (size_t k = 0; k < correct.size(); k++) {
    std::cout << "," << correct[k] / total[k];
  }
  std::cout << "\n";
}

/**
 * train_classifer
 * Train the Perceptron Classifier
 * @param a Classifier to train
 * @param training_data
 * @param epochs The number of training intervals
 */
void train_classifier(Classifier &a, Array2D &train_data, size_t epochs) {
  Array2D input;
  Array2D label;
  int int_label;
  double t_correct = 0;
  double t_total = 0;
  std::vector<double> correct;
  std::vector<double> total;
  correct.resize(a.get_actions(), 0.0);
  total.resize(a.get_actions(), 0.0);
  for (size_t i = 0; i < epochs; i++) {
    train_data.apply_shuffle();
    input =
        train_data.get_subset(train_data.height, train_data.width - 1, 0, 1);
    label = train_data.get_subset(train_data.height, 1, 0, 0);
    t_correct = 0.0;
    t_total = 0.0;
    for (size_t j = 0; j < correct.size(); j++) {
      correct[j] = 0.0;
      total[j] = 0.0;
    }
    for (size_t j = 0; j < train_data.height; j++) {
      int_label = (int)label.get_subset(1, label.width, j, 0).data[0][0];
      if (a.train(input.get_subset(1, input.width, j, 0),
                  label.get_subset(1, label.width, j, 0))) {
        t_correct += 1.0;
        correct[int_label] += 1.0;
      }
      total[int_label] += 1.0;
      t_total += 1.0;
    }
    std::cout << i << "," << t_correct << "," << t_total << ","
              << t_correct / t_total;
    for (size_t k = 0; k < correct.size(); k++) {
      std::cout << "," << correct[k] / total[k];
    }
    std::cout << "\n";
  }
  std::cout << "Train Classifier " << t_correct << "," << t_total << ","
            << t_correct / t_total;
  for (size_t k = 0; k < correct.size(); k++) {
    std::cout << "," << correct[k] / total[k];
  }
  std::cout << "\n";
}

/**
 * test classifier
 * test the perceptron classifer on the input data
 * @param a Classifier to train
 * @param training_data
 * @param epochs The number of training intervals
 */
void test_classifier(Classifier &a, Array2D &test_data) {
  Array2D input;
  Array2D label;
  int int_label;
  double t_correct = 0;
  double t_total = 0;
  std::vector<double> correct;
  std::vector<double> total;
  correct.resize(a.get_actions(), 0.0);
  total.resize(a.get_actions(), 0.0);
  input = test_data.get_subset(test_data.height, test_data.width - 1, 0, 1);
  label = test_data.get_subset(test_data.height, 1, 0, 0);
  for (size_t j = 0; j < test_data.height; j++) {
    int_label = (int)label.get_subset(1, label.width, j, 0).data[0][0];
    if (int_label == a.eval(input.get_subset(1, input.width, j, 0))) {
      t_correct += 1.0;
      correct[int_label] += 1.0;
    }
    total[int_label] += 1.0;
    t_total += 1.0;
  }
  std::cout << "Test Classifier " << t_correct << "," << t_total << ","
            << t_correct / t_total;
  for (size_t k = 0; k < correct.size(); k++) {
    std::cout << "," << correct[k] / total[k];
  }
  std::cout << "\n";
}

void save_classifier(Classifier &a, std::string fname) {
  std::ofstream ofs(fname.c_str());
  boost::archive::text_oarchive oa(ofs);
  oa << a;
}

void save_dnn(DNN &a, std::string fname) {
  std::ofstream ofs(fname.c_str());
  boost::archive::text_oarchive oa(ofs);
  oa << a;
}

void restore_classifier(Classifier &a, std::string fname) {
  std::ifstream ifs(fname.c_str());
  boost::archive::text_iarchive ia(ifs);
  ia >> a;
}

void restore_dnn(DNN &a, std::string fname) {
  std::ifstream ifs(fname.c_str());
  boost::archive::text_iarchive ia(ifs);
  ia >> a;
}

/**
 * dnn_unit_tests
 * Unit test some of the functionality of the Array2D and Layer Classes
 */
void dnn_unit_test() {
#ifdef DEBUG
  int i = 0;

  /*
   * Test the Default Contructor of Array2D
   */
  i++;
  std::cout << "-----------------------------------------------------\n";
  std::cout << "Test " << i << ": Create Array2D\n";
  std::cout << "-----------------------------------------------------\n";
  Array2D A = Array2D();
  std::cout << A << "\n";
  std::cout << "\n\n";

  /*
   * Test the NxM constructor & random Init
   */
  i++;
  std::cout << "-----------------------------------------------------\n";
  std::cout << "Test " << i << ": Create Array2D; Height=4, Width=6\n";
  std::cout << "-----------------------------------------------------\n";
  A = Array2D(4, 6);
  std::cout << A << "\n";
  std::cout << "\n\n";

  /*
   * Test Multiplication of Array and Scalar
   */
  i++;
  std::cout << "-----------------------------------------------------\n";
  std::cout << "Test " << i << ": Multiply Array2D*double\n";
  std::cout << "-----------------------------------------------------\n";
  A = Array2D(4, 4, 1.0);
  Array2D B = A * 100.0;
  std::cout << A << "\n";
  std::cout << B << "\n";
  std::cout << "\n\n";

  /*
   * Test Multiplication of Array and Array
   */
  i++;
  std::cout << "-----------------------------------------------------\n";
  std::cout << "Test " << i << ": Multiply Array2D 2x2, 2x2\n";
  std::cout << "-----------------------------------------------------\n";
  A = Array2D(2, 2, 1.0);
  B = Array2D(2, 2, 1.0);
  Array2D C = A * B;
  std::cout << A << "\n";
  std::cout << B << "\n";
  std::cout << C << "\n";
  std::cout << "\n\n";

  /*
   * Test Multiplication of Array and Array
   */
  i++;
  std::cout << "-----------------------------------------------------\n";
  std::cout << "Test " << i << ": Multiply Array2D 2x5, 5x2\n";
  std::cout << "-----------------------------------------------------\n";
  A = Array2D(2, 5, 1.0);
  B = Array2D(5, 2, 1.0);
  C = A * B;
  std::cout << A << "\n";
  std::cout << B << "\n";
  std::cout << C << "\n";
  std::cout << "\n\n";

  /*
   * Test identity matrix
   */
  i++;
  std::cout << "-----------------------------------------------------\n";
  std::cout << "Test " << i << ": Identity 5x5\n";
  std::cout << "-----------------------------------------------------\n";
  A = Array2D(5, 5, 0.0, true);
  std::cout << A << "\n";
  std::cout << "\n\n";

  /*
   * Test Transpose of Array and Array
   */
  i++;
  std::cout << "-----------------------------------------------------\n";
  std::cout << "Test " << i << ": Transpose Array2D 2x5\n";
  std::cout << "-----------------------------------------------------\n";
  A = Array2D(2, 5, 1.0);
  B = A.transpose();
  std::cout << A << "\n";
  std::cout << B << "\n";
  std::cout << "\n\n";

  /*
   * Test Addition of Array and Bias Vector
   */
  i++;
  std::cout << "-----------------------------------------------------\n";
  std::cout << "Test " << i << ": Add bias 2x5, 5x1\n";
  std::cout << "-----------------------------------------------------\n";
  A = Array2D(2, 5, 1.0);
  B = Array2D(5, 1, 1.0);
  C = A.add_bias(B);
  std::cout << A << "\n";
  std::cout << B << "\n";
  std::cout << C << "\n";
  std::cout << "\n\n";

  /*
   * Test Subtraction of Array and Array
   */
  i++;
  std::cout << "-----------------------------------------------------\n";
  std::cout << "Test " << i << ": Subtract Array2D 8x8, 8x8\n";
  std::cout << "-----------------------------------------------------\n";
  A = Array2D(8, 8, 1.0);
  B = A;
  C = A - B;
  std::cout << A << "\n";
  std::cout << B << "\n";
  std::cout << C << "\n";
  std::cout << "\n\n";

  /*
   * Test Addition of Array and Array
   */
  i++;
  std::cout << "-----------------------------------------------------\n";
  std::cout << "Test " << i << ": Subtract Array2D 8x8, 8x8\n";
  std::cout << "-----------------------------------------------------\n";
  A = Array2D(8, 8, 1.0);
  B = A;
  C = A + B;
  std::cout << A << "\n";
  std::cout << B << "\n";
  std::cout << C << "\n";
  std::cout << "\n\n";

  /*
   * Test ReLU Function
   */
  i++;
  std::cout << "-----------------------------------------------------\n";
  std::cout << "Test " << i << ": Array2D Apply relu()\n";
  std::cout << "-----------------------------------------------------\n";
  A = Array2D(4, 4, 10.0);
  B = A.apply(relu);
  std::cout << A << "\n";
  std::cout << B << "\n";
  std::cout << "\n\n";

  /*
   * Test Argmax()
   */
  i++;
  std::cout << "-----------------------------------------------------\n";
  std::cout << "Test " << i << ": argmax(Array2D)\n";
  std::cout << "-----------------------------------------------------\n";
  A = Array2D(1, 10, 10.0);
  std::cout << A << "\n";
  std::cout << argmax(A) << "\n";
  std::cout << "\n\n";

  /*
   * Test normalize
   */
  i++;
  std::cout << "-----------------------------------------------------\n";
  std::cout << "Test " << i << ": normalize(Array2D) 1x10\n";
  std::cout << "-----------------------------------------------------\n";
  A = Array2D(1, 10, 10.0);
  std::cout << A << "\n";
  std::cout << normalize(A) << "\n";
  std::cout << "\n\n";

  /*
   * Test normalize
   */
  i++;
  std::cout << "-----------------------------------------------------\n";
  std::cout << "Test " << i << ": normalize(Array2D) 10x1\n";
  std::cout << "-----------------------------------------------------\n";
  A = Array2D(10, 1, 10.0);
  std::cout << A << "\n";
  std::cout << normalize(A) << "\n";
  std::cout << "\n\n";

  /*
   * Test normalize
   */
  i++;
  std::cout << "-----------------------------------------------------\n";
  std::cout << "Test " << i << ": normalize(Array2D) 8x8\n";
  std::cout << "-----------------------------------------------------\n";
  A = Array2D(8, 8, 10.0);
  std::cout << A << "\n";
  std::cout << normalize(A) << "\n";
  std::cout << "\n\n";

  /*
   * Test standardize
   */
  i++;
  std::cout << "-----------------------------------------------------\n";
  std::cout << "Test " << i << ": standardize(Array2D) 1x10\n";
  std::cout << "-----------------------------------------------------\n";
  A = Array2D(1, 10, 10.0);
  std::cout << A << "\n";
  std::cout << standardize(A) << "\n";
  std::cout << "\n\n";

  /*
   * Test standardize
   */
  i++;
  std::cout << "-----------------------------------------------------\n";
  std::cout << "Test " << i << ": standardize(Array2D) 10x1\n";
  std::cout << "-----------------------------------------------------\n";
  A = Array2D(10, 1, 10.0);
  std::cout << A << "\n";
  std::cout << standardize(A) << "\n";
  std::cout << "\n\n";

  /*
   * Test standardize
   */
  i++;
  std::cout << "-----------------------------------------------------\n";
  std::cout << "Test " << i << ": standardize(Array2D) 8x8\n";
  std::cout << "-----------------------------------------------------\n";
  A = Array2D(8, 8, 10.0);
  std::cout << A << "\n";
  std::cout << standardize(A) << "\n";
  std::cout << "\n\n";

  /*
   * Test get_subset
   */
  i++;
  std::cout << "-----------------------------------------------------\n";
  std::cout << "Test " << i << ": get_subset 8x8 -> 8x7\n";
  std::cout << "-----------------------------------------------------\n";
  A = Array2D(8, 8, 10.0);
  B = A.get_subset(8, 7, 0, 1);
  std::cout << A << "\n";
  std::cout << B << "\n";
  std::cout << "\n\n";

  /*
   * Test get_subset
   */
  i++;
  std::cout << "-----------------------------------------------------\n";
  std::cout << "Test " << i << ": get_subset 8x8 -> 8x1\n";
  std::cout << "-----------------------------------------------------\n";
  A = Array2D(8, 8, 10.0);
  B = A.get_subset(8, 1, 0, 0);
  std::cout << A << "\n";
  std::cout << B << "\n";
  std::cout << "\n\n";

  /*
   * Test shuffle
   */
  i++;
  std::cout << "-----------------------------------------------------\n";
  std::cout << "Test " << i << ": shuffle 8x4\n";
  std::cout << "-----------------------------------------------------\n";
  A = Array2D(8, 4, 10.0);
  std::cout << A << "\n";
  A.apply_shuffle();
  std::cout << A << "\n";
  std::cout << "\n\n";

  /*
   * Test apply subset
   */
  i++;
  std::cout << "-----------------------------------------------------\n";
  std::cout << "Test " << i << ": apply_subset 8x4\n";
  std::cout << "-----------------------------------------------------\n";
  A = Array2D(8, 4, 10.0);
  B = A.get_subset(8, 3, 0, 1);
  B = normalize(B);
  std::cout << A << "\n";
  A.apply_subset(B, 0, 1);
  std::cout << A << "\n";
  std::cout << "\n\n";

  /*
   * Test read_file, raw
   */
  i++;
  std::cout << "-----------------------------------------------------\n";
  std::cout << "Test " << i << ": read_file, RAW\n";
  std::cout << "-----------------------------------------------------\n";
  A = read_data("./unit_test/input.csv", 4, RAW);
  std::cout << A << "\n";
  std::cout << "\n\n";

  /*
   * Test read_file, STANDARDIZE
   */
  i++;
  std::cout << "-----------------------------------------------------\n";
  std::cout << "Test " << i << ": read_file, STANDARDIZE\n";
  std::cout << "-----------------------------------------------------\n";
  A = read_data("./unit_test/input.csv", 4, STANDARDIZE);
  std::cout << A << "\n";
  std::cout << "\n\n";

  /*
   * Test read_file, NORMALIZE
   */
  i++;
  std::cout << "-----------------------------------------------------\n";
  std::cout << "Test " << i << ": read_file, NORMALIZE\n";
  std::cout << "-----------------------------------------------------\n";
  A = read_data("./unit_test/input.csv", 4, NORMALIZE);
  std::cout << A << "\n";
  std::cout << "\n\n";

  /*
   * Test Classifer Train
   */
  i++;
  std::cout << "-----------------------------------------------------\n";
  std::cout << "Test " << i << ": classifier train\n";
  std::cout << "-----------------------------------------------------\n";
  A = read_data("./unit_test/input.csv", 17, NORMALIZE);
  std::cout << A << "\n";
  Classifier classifier = Classifier(2, A.width - 1);
  train_classifier(classifier, A, 1000);
  std::cout << "\n\n";

  /*
   * Test Classifer Test
   */
  i++;
  std::cout << "-----------------------------------------------------\n";
  std::cout << "Test " << i << ": classifier test\n";
  std::cout << "-----------------------------------------------------\n";
  test_classifier(classifier, A);
  std::cout << "\n\n";
#endif
}


/*
 * test_ALIGN.cpp
 *
 *  Copyright (c) 2019 hikyuu.org
 *
 *  Created on: 2019-5-1
 *      Author: fasiondog
 */

#ifdef TEST_ALL_IN_ONE
    #include <boost/test/unit_test.hpp>
#else
    #define BOOST_TEST_MODULE test_hikyuu_indicator_suite
    #include <boost/test/unit_test.hpp>
#endif

#include <fstream>
#include <hikyuu/StockManager.h>
#include <hikyuu/indicator/crt/ALIGN.h>
#include <hikyuu/indicator/crt/KDATA.h>
#include <hikyuu/indicator/crt/PRICELIST.h>

using namespace hku;

/**
 * @defgroup test_indicator_ALIGN test_indicator_ALIGN
 * @ingroup test_hikyuu_indicator_suite
 * @{
 */

/** @par 检测点 */
BOOST_AUTO_TEST_CASE( test_ALIGN ) {
    Indicator result;
    Stock stk = getStock("sh000001");

    PriceList a;
    for (int i = 0; i < 10; i++) {
        a.push_back(i);
    }

    /** @arg 输入指标本身和上下文无关，参考日期长度为0 */
    DatetimeList ref;
    Indicator data = PRICELIST(a);
    result = ALIGN(data, ref);
    BOOST_CHECK(result.name() == "ALIGN");
    BOOST_CHECK(ref.size() == 0);
    BOOST_CHECK(result.size() == 0);
    BOOST_CHECK(result.discard() == 0);

    /** @arg 输入指标本身和上下文无关，和参考日期列表等长 */
    ref = stk.getDatetimeList(KQuery(-10));
    data = PRICELIST(a);
    result = ALIGN(data, ref);
    BOOST_CHECK(result.name() == "ALIGN");
    BOOST_CHECK(result.size() == ref.size());
    BOOST_CHECK(result.discard() == 0);
    DatetimeList result_dates = result.getDatetimeList();
    for (int i = 0; i < result.size(); i++) {
        BOOST_CHECK(result[i] == data[i]);
        BOOST_CHECK(result_dates[i] == ref[i]);
    }

    /** @arg 输入指标本身和上下文无关，且长度长于参考日期列表 */
    a.push_back(11);
    data = PRICELIST(a);
    result = ALIGN(data, ref);
    BOOST_CHECK(result.name() == "ALIGN");
    BOOST_CHECK(result.size() == ref.size());
    BOOST_CHECK(result.discard() == 0);
    result_dates = result.getDatetimeList();
    for (int i = 0; i < result.size(); i++) {
        BOOST_CHECK(result[i] == data[i+1]);
        BOOST_CHECK(result_dates[i] == ref[i]);
    }

    /** @arg 输入指标本身和上下文无关，且长度小于参考日期列表 */
    a.clear();
    a.push_back(1);
    data = PRICELIST(a);
    result = ALIGN(data, ref);
    BOOST_CHECK(result.name() == "ALIGN");
    BOOST_CHECK(result.size() == ref.size());
    BOOST_CHECK(result.discard() == 9);
    result_dates = result.getDatetimeList();
    for (int i = 0; i < result.discard(); i++) {
        BOOST_CHECK(std::isnan(result[i]));
        BOOST_CHECK(result_dates[i] == ref[i]);
    }
    BOOST_CHECK(result[9] == 1);
    BOOST_CHECK(result_dates[9] == ref[9]);

    /** @arg 输入指标本身和上下文无关，且长度为0 */
    a.clear();
    data = PRICELIST(a);
    result = ALIGN(data, ref);
    BOOST_CHECK(result.name() == "ALIGN");
    BOOST_CHECK(result.size() == ref.size());
    BOOST_CHECK(result.discard() == ref.size());
    result_dates = result.getDatetimeList();
    for (int i = 0; i < result.discard(); i++) {
        BOOST_CHECK(std::isnan(result[i]));
        BOOST_CHECK(result_dates[i] == ref[i]);
    }

    /** @arg ind对应日期全部大于参考日期 */
    KData k = stk.getKData(KQuery(-10));
    ref.clear();
    ref.push_back(Datetime(201901010000));
    ref.push_back(Datetime(201901020000));
    ref.push_back(Datetime(201901030000));
    data = CLOSE(k);
    result = ALIGN(data, ref);
    BOOST_CHECK(result.name() == "ALIGN");
    BOOST_CHECK(result.size() == ref.size());
    BOOST_CHECK(result.discard() == ref.size());
    result_dates = result.getDatetimeList();
    for (int i = 0; i < result.discard(); i++) {
        BOOST_CHECK(std::isnan(result[i]));
        BOOST_CHECK(result_dates[i] == ref[i]);
    }

    /** @arg ind对应日期全部小于参考日期 */
    ref.clear();
    ref.push_back(Datetime(191901010000));
    ref.push_back(Datetime(191901020000));
    ref.push_back(Datetime(191901030000));
    data = CLOSE(k);
    result = ALIGN(data, ref);
    BOOST_CHECK(result.name() == "ALIGN");
    BOOST_CHECK(result.size() == ref.size());
    BOOST_CHECK(result.discard() == ref.size());
    result_dates = result.getDatetimeList();
    for (int i = 0; i < result.discard(); i++) {
        BOOST_CHECK(std::isnan(result[i]));
        BOOST_CHECK(result_dates[i] == ref[i]);
    }

    /** @arg ind对应日期等于参考日期 */
    ref = k.getDatetimeList();
    data = CLOSE(k);
    result = ALIGN(data, ref);
    BOOST_CHECK(result.name() == "ALIGN");
    BOOST_CHECK(result.size() == ref.size());
    BOOST_CHECK(result.discard() == 0);
    result_dates = result.getDatetimeList();
    for (int i = 0; i < result.size(); i++) {
        BOOST_CHECK(result[i] == data[i]);
        BOOST_CHECK(result_dates[i] == ref[i]);
    }

    /** @arg ind对应部分日期 */
    ref.clear();
    ref.push_back(Datetime(201111220000));
    ref.push_back(Datetime(201111230000));
    ref.push_back(Datetime(201111240000));
    ref.push_back(Datetime(201111260000));
    ref.push_back(Datetime(201112060000));
    ref.push_back(Datetime(201112070000));
    ref.push_back(Datetime(201112100000));
    data = CLOSE(k);
    result = ALIGN(data, ref);
    BOOST_CHECK(result.name() == "ALIGN");
    BOOST_CHECK(result.size() == ref.size());
    BOOST_CHECK(result.discard() == 1);
    BOOST_CHECK(std::isnan(result[0]));
    BOOST_CHECK_CLOSE(result[1], 2395.07, 0.01);
    BOOST_CHECK_CLOSE(result[2], 2397.55, 0.01);
    BOOST_CHECK(std::isnan(result[3]));
    BOOST_CHECK_CLOSE(result[4], 2325.91, 0.01);
    BOOST_CHECK(std::isnan(result[5]));
    BOOST_CHECK(std::isnan(result[6]));
    for (int i = 0; i < result.size(); i++) {
        BOOST_CHECK(result.getDatetime(i) == ref[i]);
    }
}

//-----------------------------------------------------------------------------
// test export
//-----------------------------------------------------------------------------
#if HKU_SUPPORT_SERIALIZATION

/** @par 检测点 */
BOOST_AUTO_TEST_CASE( test_ALIGN_export ) {
    StockManager& sm = StockManager::instance();
    string filename(sm.tmpdir());
    filename += "/ALIGN.xml";

    Stock stock = sm.getStock("sh000001");
    KData kdata = stock.getKData(KQuery(-20));
    Indicator x1 = ALIGN(CLOSE(kdata), sm.getStock("sh600004").getDatetimeList(KQuery(-20)));
    {
        std::ofstream ofs(filename);
        boost::archive::xml_oarchive oa(ofs);
        oa << BOOST_SERIALIZATION_NVP(x1);
    }

    Indicator x2;
    {
        std::ifstream ifs(filename);
        boost::archive::xml_iarchive ia(ifs);
        ia >> BOOST_SERIALIZATION_NVP(x2);
    }

    BOOST_CHECK(x2.name() == "ALIGN");
    BOOST_CHECK(x1.size() == x2.size());
    BOOST_CHECK(x1.discard() == x2.discard());
    BOOST_CHECK(x1.getResultNumber() == x2.getResultNumber());
    for (size_t i = 0; i < x1.size(); ++i) {
        BOOST_CHECK_CLOSE(x1[i], x2[i], 0.00001);
    }
}
#endif /* #if HKU_SUPPORT_SERIALIZATION */

/** @} */


//---------------------------------------------------------------------------//
//!
//! \file   tstInverseLengthConversion.cpp
//! \author Alex Robinson
//! \brief  The inverse length conversion unit tests
//!
//---------------------------------------------------------------------------//

// Std Lib Includes
#include <iostream>

// Boost Includes
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
#include <boost/test/tools/floating_point_comparison.hpp>
#include <boost/units/quantity.hpp>

// FRENSIE Includes
#include "Utility_InverseAngstromUnit.hpp"
#include "Utility_InverseCentimeterUnit.hpp"

using namespace Utility::Units;

//---------------------------------------------------------------------------//
// Tests
//---------------------------------------------------------------------------//
// Check that the inverse length units can be converted
BOOST_AUTO_TEST_CASE( convert )
{
  boost::units::quantity<Utility::Units::InverseAngstrom> inverse_angstrom_q( 1.0*inverse_centimeter );

  boost::units::quantity<Utility::Units::InverseCentimeter> inverse_centimeter_q( 1.0*inverse_angstrom );

  BOOST_CHECK_EQUAL( inverse_angstrom_q.value(), 1e-8 );
  BOOST_CHECK_EQUAL( inverse_centimeter_q.value(), 1e8 );
}

//---------------------------------------------------------------------------//
// end tstInverseLengthConversion.cpp
//---------------------------------------------------------------------------//