/* =========================================================================
   Copyright (c) 2010-2012, 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
============================================================================= */

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

//
// *** Boost
//
#include <boost/numeric/ublas/io.hpp>
#include <boost/numeric/ublas/triangular.hpp>
#include <boost/numeric/ublas/matrix_sparse.hpp>
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/matrix_proxy.hpp>
#include <boost/numeric/ublas/lu.hpp>
#include <boost/numeric/ublas/io.hpp>

//
// *** ViennaCL
//
//#define VIENNACL_DEBUG_ALL
#define VIENNACL_HAVE_UBLAS 1
#include "viennacl/scalar.hpp"
#include "viennacl/matrix.hpp"
#include "viennacl/vector.hpp"
#include "viennacl/linalg/prod.hpp"
#include "viennacl/linalg/norm_2.hpp"
#include "viennacl/linalg/direct_solve.hpp"
#include "viennacl/linalg/lu.hpp"
#include "examples/tutorial/Random.hpp"

//
// -------------------------------------------------------------
//
using namespace boost::numeric;
//
// -------------------------------------------------------------
//
template <typename ScalarType>
ScalarType diff(ScalarType & s1, viennacl::scalar<ScalarType> & s2)
{
   viennacl::backend::finish();
   if (s1 != s2)
      return (s1 - s2) / std::max(fabs(s1), fabs(s2));
   return 0;
}

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 ( std::max( fabs(v2_cpu[i]), fabs(v1[i]) ) > 0 )
         v2_cpu[i] = fabs(v2_cpu[i] - v1[i]) / std::max( fabs(v2_cpu[i]), fabs(v1[i]) );
      else
         v2_cpu[i] = 0.0;
   }

   return norm_inf(v2_cpu);
}

template <typename ScalarType, typename VCLMatrixType>
ScalarType diff(ublas::matrix<ScalarType> const & mat1, VCLMatrixType const & mat2)
{
   ublas::matrix<ScalarType> mat2_cpu(mat2.size1(), mat2.size2());
   viennacl::backend::finish();  //workaround for a bug in APP SDK 2.7 on Trinity APUs (with Catalyst 12.8)
   viennacl::copy(mat2, mat2_cpu);
   ScalarType ret = 0;
   ScalarType act = 0;

    for (unsigned int i = 0; i < mat2_cpu.size1(); ++i)
    {
      for (unsigned int j = 0; j < mat2_cpu.size2(); ++j)
      {
         act = fabs(mat2_cpu(i,j) - mat1(i,j)) / std::max( fabs(mat2_cpu(i, j)), fabs(mat1(i,j)) );
         if (act > ret)
           ret = act;
      }
    }
   //std::cout << ret << std::endl;
   return ret;
}
//
// -------------------------------------------------------------
//

template <typename NumericT, typename Epsilon,
          typename UblasMatrixType, typename UblasVectorType,
          typename VCLMatrixType, typename VCLVectorType1, typename VCLVectorType2>
int test_prod_rank1(Epsilon const & epsilon,
                    UblasMatrixType & ublas_m1, UblasVectorType & ublas_v1, UblasVectorType & ublas_v2,
                    VCLMatrixType & vcl_m1, VCLVectorType1 & vcl_v1, VCLVectorType2 & vcl_v2)
{
   int retval = EXIT_SUCCESS;

   // sync data:
   viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
   viennacl::copy(ublas_v2.begin(), ublas_v2.end(), vcl_v2.begin());
   viennacl::copy(ublas_m1, vcl_m1);

   // --------------------------------------------------------------------------
   std::cout << "Rank 1 update" << std::endl;

   ublas_m1 += ublas::outer_prod(ublas_v1, ublas_v2);
   vcl_m1 += viennacl::linalg::outer_prod(vcl_v1, vcl_v2);
   if( fabs(diff(ublas_m1, vcl_m1)) > epsilon )
   {
      std::cout << "# Error at operation: rank 1 update" << std::endl;
      std::cout << "  diff: " << fabs(diff(ublas_m1, vcl_m1)) << std::endl;
      return EXIT_FAILURE;
   }

   // --------------------------------------------------------------------------
   std::cout << "Scaled rank 1 update" << std::endl;
   ublas_m1 += NumericT(4.2) * ublas::outer_prod(ublas_v1, ublas_v2);
   vcl_m1 += NumericT(2.1) * viennacl::linalg::outer_prod(vcl_v1, vcl_v2);
   vcl_m1 += viennacl::linalg::outer_prod(vcl_v1, vcl_v2) * NumericT(2.1);  //check proper compilation
   if( fabs(diff(ublas_m1, vcl_m1)) > epsilon )
   {
      std::cout << "# Error at operation: scaled rank 1 update" << std::endl;
      std::cout << "  diff: " << fabs(diff(ublas_m1, vcl_m1)) << std::endl;
      return EXIT_FAILURE;
   }

   //reset vcl_matrix:
   viennacl::copy(ublas_m1, vcl_m1);

   // --------------------------------------------------------------------------
   std::cout << "Matrix-Vector product" << std::endl;
   ublas_v1 = viennacl::linalg::prod(ublas_m1, ublas_v2);
   vcl_v1   = viennacl::linalg::prod(vcl_m1, vcl_v2);

   if( fabs(diff(ublas_v1, vcl_v1)) > epsilon )
   {
      std::cout << "# Error at operation: matrix-vector product" << std::endl;
      std::cout << "  diff: " << fabs(diff(ublas_v1, vcl_v1)) << std::endl;
      retval = EXIT_FAILURE;
   }
   // --------------------------------------------------------------------------
   std::cout << "Matrix-Vector product with scaled add" << std::endl;
   NumericT alpha = static_cast<NumericT>(2.786);
   NumericT beta = static_cast<NumericT>(1.432);
   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 * viennacl::linalg::prod(ublas_m1, ublas_v2) + beta * ublas_v1;
   vcl_v1   = alpha * viennacl::linalg::prod(vcl_m1, vcl_v2) + beta * vcl_v1;

   if( fabs(diff(ublas_v1, vcl_v1)) > epsilon )
   {
      std::cout << "# Error at operation: matrix-vector product with scaled additions" << std::endl;
      std::cout << "  diff: " << fabs(diff(ublas_v1, vcl_v1)) << std::endl;
      retval = EXIT_FAILURE;
   }
   // --------------------------------------------------------------------------

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

   std::cout << "Transposed Matrix-Vector product" << std::endl;
   ublas_v2     = alpha * viennacl::linalg::prod(trans(ublas_m1), ublas_v1);
   vcl_v2 = alpha * viennacl::linalg::prod(trans(vcl_m1), vcl_v1);

   if( fabs(diff(ublas_v2, vcl_v2)) > epsilon )
   {
      std::cout << "# Error at operation: transposed matrix-vector product" << std::endl;
      std::cout << "  diff: " << fabs(diff(ublas_v2, vcl_v2)) << std::endl;
      retval = EXIT_FAILURE;
   }

   std::cout << "Transposed Matrix-Vector product with scaled add" << std::endl;
   ublas_v2 = alpha * viennacl::linalg::prod(trans(ublas_m1), ublas_v1) + beta * ublas_v2;
   vcl_v2   = alpha * viennacl::linalg::prod(trans(vcl_m1), vcl_v1) + beta * vcl_v2;

   if( fabs(diff(ublas_v2, vcl_v2)) > epsilon )
   {
      std::cout << "# Error at operation: transposed matrix-vector product with scaled additions" << std::endl;
      std::cout << "  diff: " << fabs(diff(ublas_v2, vcl_v2)) << std::endl;
      retval = EXIT_FAILURE;
   }
   // --------------------------------------------------------------------------

   return retval;
}

template <typename NumericT, typename Epsilon,
          typename UblasMatrixType, typename UblasVectorType,
          typename VCLMatrixType, typename VCLVectorType1>
int test_solve(Epsilon const & epsilon,
               UblasMatrixType & ublas_m1, UblasVectorType & ublas_v1,
               VCLMatrixType & vcl_m1, VCLVectorType1 & vcl_v1)
{
   int retval = EXIT_SUCCESS;

   // sync data:
   //viennacl::copy(ublas_v1.begin(), ublas_v1.end(), vcl_v1.begin());
   viennacl::copy(ublas_v1, vcl_v1);
   viennacl::copy(ublas_m1, vcl_m1);

   /////////////////// test direct solvers ////////////////////////////

   //upper triangular:
   std::cout << "Upper triangular solver" << std::endl;
   ublas_v1 = ublas::solve(ublas_m1, ublas_v1, ublas::upper_tag());
   vcl_v1 = viennacl::linalg::solve(vcl_m1, vcl_v1, viennacl::linalg::upper_tag());
   if( fabs(diff(ublas_v1, vcl_v1)) > epsilon )
   {
      std::cout << "# Error at operation: upper triangular solver" << std::endl;
      std::cout << "  diff: " << fabs(diff(ublas_v1, vcl_v1)) << std::endl;
      retval = EXIT_FAILURE;
   }

   //upper unit triangular:
   std::cout << "Upper unit triangular solver" << std::endl;
   viennacl::copy(ublas_v1, vcl_v1);
   ublas_v1 = ublas::solve(ublas_m1, ublas_v1, ublas::unit_upper_tag());
   vcl_v1 = viennacl::linalg::solve(vcl_m1, vcl_v1, viennacl::linalg::unit_upper_tag());
   if( fabs(diff(ublas_v1, vcl_v1)) > epsilon )
   {
      std::cout << "# Error at operation: unit upper triangular solver" << std::endl;
      std::cout << "  diff: " << fabs(diff(ublas_v1, vcl_v1)) << std::endl;
      retval = EXIT_FAILURE;
   }

   //lower triangular:
   std::cout << "Lower triangular solver" << std::endl;
   viennacl::copy(ublas_v1, vcl_v1);
   ublas_v1 = ublas::solve(ublas_m1, ublas_v1, ublas::lower_tag());
   vcl_v1 = viennacl::linalg::solve(vcl_m1, vcl_v1, viennacl::linalg::lower_tag());
   if( fabs(diff(ublas_v1, vcl_v1)) > epsilon )
   {
      std::cout << "# Error at operation: lower triangular solver" << std::endl;
      std::cout << "  diff: " << fabs(diff(ublas_v1, vcl_v1)) << std::endl;
      retval = EXIT_FAILURE;
   }

   //lower unit triangular:
   std::cout << "Lower unit triangular solver" << std::endl;
   viennacl::copy(ublas_v1, vcl_v1);
   ublas_v1 = ublas::solve(ublas_m1, ublas_v1, ublas::unit_lower_tag());
   vcl_v1 = viennacl::linalg::solve(vcl_m1, vcl_v1, viennacl::linalg::unit_lower_tag());
   if( fabs(diff(ublas_v1, vcl_v1)) > epsilon )
   {
      std::cout << "# Error at operation: unit lower triangular solver" << std::endl;
      std::cout << "  diff: " << fabs(diff(ublas_v1, vcl_v1)) << std::endl;
      retval = EXIT_FAILURE;
   }

   //transposed upper triangular:
   std::cout << "Transposed upper triangular solver" << std::endl;
   viennacl::copy(ublas_v1, vcl_v1);
   ublas_v1 = ublas::solve(trans(ublas_m1), ublas_v1, ublas::upper_tag());
   vcl_v1 = viennacl::linalg::solve(trans(vcl_m1), vcl_v1, viennacl::linalg::upper_tag());
   if( fabs(diff(ublas_v1, vcl_v1)) > epsilon )
   {
      std::cout << "# Error at operation: upper triangular solver" << std::endl;
      std::cout << "  diff: " << fabs(diff(ublas_v1, vcl_v1)) << std::endl;
      retval = EXIT_FAILURE;
   }

   //transposed upper unit triangular:
   std::cout << "Transposed unit upper triangular solver" << std::endl;
   viennacl::copy(ublas_v1, vcl_v1);
   ublas_v1 = ublas::solve(trans(ublas_m1), ublas_v1, ublas::unit_upper_tag());
   vcl_v1 = viennacl::linalg::solve(trans(vcl_m1), vcl_v1, viennacl::linalg::unit_upper_tag());
   if( fabs(diff(ublas_v1, vcl_v1)) > epsilon )
   {
      std::cout << "# Error at operation: unit upper triangular solver" << std::endl;
      std::cout << "  diff: " << fabs(diff(ublas_v1, vcl_v1)) << std::endl;
      retval = EXIT_FAILURE;
   }

   //transposed lower triangular:
   std::cout << "Transposed lower triangular solver" << std::endl;
   viennacl::copy(ublas_v1, vcl_v1);
   ublas_v1 = ublas::solve(trans(ublas_m1), ublas_v1, ublas::lower_tag());
   vcl_v1 = viennacl::linalg::solve(trans(vcl_m1), vcl_v1, viennacl::linalg::lower_tag());
   if( fabs(diff(ublas_v1, vcl_v1)) > epsilon )
   {
      std::cout << "# Error at operation: lower triangular solver" << std::endl;
      std::cout << "  diff: " << fabs(diff(ublas_v1, vcl_v1)) << std::endl;
      retval = EXIT_FAILURE;
   }

   //transposed lower unit triangular:
   std::cout << "Transposed unit lower triangular solver" << std::endl;
   viennacl::copy(ublas_v1, vcl_v1);
   ublas_v1 = ublas::solve(trans(ublas_m1), ublas_v1, ublas::unit_lower_tag());
   vcl_v1 = viennacl::linalg::solve(trans(vcl_m1), vcl_v1, viennacl::linalg::unit_lower_tag());
   if( fabs(diff(ublas_v1, vcl_v1)) > epsilon )
   {
      std::cout << "# Error at operation: unit lower triangular solver" << std::endl;
      std::cout << "  diff: " << fabs(diff(ublas_v1, vcl_v1)) << std::endl;
      retval = EXIT_FAILURE;
   }

   return retval;
}

//
// -------------------------------------------------------------
//
template< typename NumericT, typename F, typename Epsilon >
int test(Epsilon const& epsilon)
{
   int retval = EXIT_SUCCESS;

   std::size_t num_rows = 141;
   std::size_t num_cols = 103;

   // --------------------------------------------------------------------------
   ublas::vector<NumericT> ublas_v1(num_rows);
   for (std::size_t i = 0; i < ublas_v1.size(); ++i)
     ublas_v1(i) = random<NumericT>();
   ublas::vector<NumericT> ublas_v2 = ublas::scalar_vector<NumericT>(num_cols, NumericT(3.1415));

   ublas::matrix<NumericT> ublas_m1(ublas_v1.size(), ublas_v2.size());

   for (std::size_t i = 0; i < ublas_m1.size1(); ++i)
      for (std::size_t j = 0; j < ublas_m1.size2(); ++j)
         ublas_m1(i,j) = static_cast<NumericT>(0.1) * random<NumericT>();

   ublas::matrix<NumericT> ublas_m2(ublas_v1.size(), ublas_v1.size());

   for (std::size_t i = 0; i < ublas_m2.size1(); ++i)
   {
      for (std::size_t j = 0; j < ublas_m2.size2(); ++j)
         ublas_m2(i,j) = static_cast<NumericT>(-0.1) * random<NumericT>();
      ublas_m2(i, i) = static_cast<NumericT>(2) + random<NumericT>();
   }

   viennacl::vector<NumericT> vcl_v1_native(ublas_v1.size());
   viennacl::vector<NumericT> vcl_v1_large(4 * ublas_v1.size());
   viennacl::vector_range< viennacl::vector<NumericT> > vcl_v1_range(vcl_v1_large, viennacl::range(3, ublas_v1.size() + 3));
   viennacl::vector_slice< viennacl::vector<NumericT> > vcl_v1_slice(vcl_v1_large, viennacl::slice(2, 3, ublas_v1.size()));

   viennacl::vector<NumericT> vcl_v2_native(ublas_v2.size());
   viennacl::vector<NumericT> vcl_v2_large(4 * ublas_v2.size());
   viennacl::vector_range< viennacl::vector<NumericT> > vcl_v2_range(vcl_v2_large, viennacl::range(8, ublas_v2.size() + 8));
   viennacl::vector_slice< viennacl::vector<NumericT> > vcl_v2_slice(vcl_v2_large, viennacl::slice(6, 2, ublas_v2.size()));

   viennacl::matrix<NumericT, F> vcl_m1_native(ublas_m1.size1(), ublas_m1.size2());
   viennacl::matrix<NumericT, F> vcl_m1_large(4 * ublas_m1.size1(), 4 * ublas_m1.size2());
   viennacl::matrix_range< viennacl::matrix<NumericT, F> > vcl_m1_range(vcl_m1_large,
                                                                        viennacl::range(8, ublas_m1.size1() + 8),
                                                                        viennacl::range(ublas_m1.size2(), 2 * ublas_m1.size2()) );
   viennacl::matrix_slice< viennacl::matrix<NumericT, F> > vcl_m1_slice(vcl_m1_large,
                                                                        viennacl::slice(6, 2, ublas_m1.size1()),
                                                                        viennacl::slice(ublas_m1.size2(), 2, ublas_m1.size2()) );

   viennacl::matrix<NumericT, F> vcl_m2_native(ublas_m2.size1(), ublas_m2.size2());
   viennacl::matrix<NumericT, F> vcl_m2_large(4 * ublas_m2.size1(), 4 * ublas_m2.size2());
   viennacl::matrix_range< viennacl::matrix<NumericT, F> > vcl_m2_range(vcl_m2_large,
                                                                        viennacl::range(8, ublas_m2.size1() + 8),
                                                                        viennacl::range(ublas_m2.size2(), 2 * ublas_m2.size2()) );
   viennacl::matrix_slice< viennacl::matrix<NumericT, F> > vcl_m2_slice(vcl_m2_large,
                                                                        viennacl::slice(6, 2, ublas_m2.size1()),
                                                                        viennacl::slice(ublas_m2.size2(), 2, ublas_m2.size2()) );

/*   std::cout << "Matrix resizing (to larger)" << std::endl;
   matrix.resize(2*num_rows, 2*num_cols, true);
   for (unsigned int i = 0; i < matrix.size1(); ++i)
   {
      for (unsigned int j = (i<result.size() ? rhs.size() : 0); j < matrix.size2(); ++j)
         matrix(i,j) = 0;
   }
   vcl_matrix.resize(2*num_rows, 2*num_cols, true);
   viennacl::copy(vcl_matrix, matrix);
   if( fabs(diff(matrix, vcl_matrix)) > epsilon )
   {
      std::cout << "# Error at operation: matrix resize (to larger)" << std::endl;
      std::cout << "  diff: " << fabs(diff(matrix, vcl_matrix)) << std::endl;
      return EXIT_FAILURE;
   }

   matrix(12, 14) = NumericT(1.9);
   matrix(19, 16) = NumericT(1.0);
   matrix (13, 15) =  NumericT(-9);
   vcl_matrix(12, 14) = NumericT(1.9);
   vcl_matrix(19, 16) = NumericT(1.0);
   vcl_matrix (13, 15) =  NumericT(-9);

   std::cout << "Matrix resizing (to smaller)" << std::endl;
   matrix.resize(result.size(), rhs.size(), true);
   vcl_matrix.resize(result.size(), rhs.size(), true);
   if( fabs(diff(matrix, vcl_matrix)) > epsilon )
   {
      std::cout << "# Error at operation: matrix resize (to smaller)" << std::endl;
      std::cout << "  diff: " << fabs(diff(matrix, vcl_matrix)) << std::endl;
      return EXIT_FAILURE;
   }
   */

   //
   // Run a bunch of tests for rank-1-updates, matrix-vector products
   //
   std::cout << "------------ Testing rank-1-updates and matrix-vector products ------------------" << std::endl;

   std::cout << "* m = full, v1 = full, v2 = full" << std::endl;
   retval = test_prod_rank1<NumericT>(epsilon,
                                      ublas_m1, ublas_v1, ublas_v2,
                                      vcl_m1_native, vcl_v1_native, vcl_v2_native);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   std::cout << "* m = full, v1 = full, v2 = range" << std::endl;
   retval = test_prod_rank1<NumericT>(epsilon,
                                      ublas_m1, ublas_v1, ublas_v2,
                                      vcl_m1_native, vcl_v1_native, vcl_v2_range);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   std::cout << "* m = full, v1 = full, v2 = slice" << std::endl;
   retval = test_prod_rank1<NumericT>(epsilon,
                                      ublas_m1, ublas_v1, ublas_v2,
                                      vcl_m1_native, vcl_v1_native, vcl_v2_slice);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   // v1 = range

   std::cout << "* m = full, v1 = range, v2 = full" << std::endl;
   retval = test_prod_rank1<NumericT>(epsilon,
                                      ublas_m1, ublas_v1, ublas_v2,
                                      vcl_m1_native, vcl_v1_range, vcl_v2_native);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   std::cout << "* m = full, v1 = range, v2 = range" << std::endl;
   retval = test_prod_rank1<NumericT>(epsilon,
                                      ublas_m1, ublas_v1, ublas_v2,
                                      vcl_m1_native, vcl_v1_range, vcl_v2_range);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   std::cout << "* m = full, v1 = range, v2 = slice" << std::endl;
   retval = test_prod_rank1<NumericT>(epsilon,
                                      ublas_m1, ublas_v1, ublas_v2,
                                      vcl_m1_native, vcl_v1_range, vcl_v2_slice);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   // v1 = slice

   std::cout << "* m = full, v1 = slice, v2 = full" << std::endl;
   retval = test_prod_rank1<NumericT>(epsilon,
                                      ublas_m1, ublas_v1, ublas_v2,
                                      vcl_m1_native, vcl_v1_slice, vcl_v2_native);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   std::cout << "* m = full, v1 = slice, v2 = range" << std::endl;
   retval = test_prod_rank1<NumericT>(epsilon,
                                      ublas_m1, ublas_v1, ublas_v2,
                                      vcl_m1_native, vcl_v1_slice, vcl_v2_range);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   std::cout << "* m = full, v1 = slice, v2 = slice" << std::endl;
   retval = test_prod_rank1<NumericT>(epsilon,
                                      ublas_m1, ublas_v1, ublas_v2,
                                      vcl_m1_native, vcl_v1_slice, vcl_v2_slice);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   ///////////////////////////// matrix_range

   std::cout << "* m = range, v1 = full, v2 = full" << std::endl;
   retval = test_prod_rank1<NumericT>(epsilon,
                                      ublas_m1, ublas_v1, ublas_v2,
                                      vcl_m1_range, vcl_v1_native, vcl_v2_native);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   std::cout << "* m = range, v1 = full, v2 = range" << std::endl;
   retval = test_prod_rank1<NumericT>(epsilon,
                                      ublas_m1, ublas_v1, ublas_v2,
                                      vcl_m1_range, vcl_v1_native, vcl_v2_range);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   std::cout << "* m = range, v1 = full, v2 = slice" << std::endl;
   retval = test_prod_rank1<NumericT>(epsilon,
                                      ublas_m1, ublas_v1, ublas_v2,
                                      vcl_m1_range, vcl_v1_native, vcl_v2_slice);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   // v1 = range

   std::cout << "* m = range, v1 = range, v2 = full" << std::endl;
   retval = test_prod_rank1<NumericT>(epsilon,
                                      ublas_m1, ublas_v1, ublas_v2,
                                      vcl_m1_range, vcl_v1_range, vcl_v2_native);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   std::cout << "* m = range, v1 = range, v2 = range" << std::endl;
   retval = test_prod_rank1<NumericT>(epsilon,
                                      ublas_m1, ublas_v1, ublas_v2,
                                      vcl_m1_range, vcl_v1_range, vcl_v2_range);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   std::cout << "* m = range, v1 = range, v2 = slice" << std::endl;
   retval = test_prod_rank1<NumericT>(epsilon,
                                      ublas_m1, ublas_v1, ublas_v2,
                                      vcl_m1_range, vcl_v1_range, vcl_v2_slice);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   // v1 = slice

   std::cout << "* m = range, v1 = slice, v2 = full" << std::endl;
   retval = test_prod_rank1<NumericT>(epsilon,
                                      ublas_m1, ublas_v1, ublas_v2,
                                      vcl_m1_range, vcl_v1_slice, vcl_v2_native);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   std::cout << "* m = range, v1 = slice, v2 = range" << std::endl;
   retval = test_prod_rank1<NumericT>(epsilon,
                                      ublas_m1, ublas_v1, ublas_v2,
                                      vcl_m1_range, vcl_v1_slice, vcl_v2_range);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   std::cout << "* m = range, v1 = slice, v2 = slice" << std::endl;
   retval = test_prod_rank1<NumericT>(epsilon,
                                      ublas_m1, ublas_v1, ublas_v2,
                                      vcl_m1_range, vcl_v1_slice, vcl_v2_slice);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   ///////////////////////////// matrix_slice

   std::cout << "* m = slice, v1 = full, v2 = full" << std::endl;
   retval = test_prod_rank1<NumericT>(epsilon,
                                      ublas_m1, ublas_v1, ublas_v2,
                                      vcl_m1_slice, vcl_v1_native, vcl_v2_native);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   std::cout << "* m = slice, v1 = full, v2 = range" << std::endl;
   retval = test_prod_rank1<NumericT>(epsilon,
                                      ublas_m1, ublas_v1, ublas_v2,
                                      vcl_m1_slice, vcl_v1_native, vcl_v2_range);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   std::cout << "* m = slice, v1 = full, v2 = slice" << std::endl;
   retval = test_prod_rank1<NumericT>(epsilon,
                                      ublas_m1, ublas_v1, ublas_v2,
                                      vcl_m1_slice, vcl_v1_native, vcl_v2_slice);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   // v1 = range

   std::cout << "* m = slice, v1 = range, v2 = full" << std::endl;
   retval = test_prod_rank1<NumericT>(epsilon,
                                      ublas_m1, ublas_v1, ublas_v2,
                                      vcl_m1_slice, vcl_v1_range, vcl_v2_native);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   std::cout << "* m = slice, v1 = range, v2 = range" << std::endl;
   retval = test_prod_rank1<NumericT>(epsilon,
                                      ublas_m1, ublas_v1, ublas_v2,
                                      vcl_m1_slice, vcl_v1_range, vcl_v2_range);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   std::cout << "* m = slice, v1 = range, v2 = slice" << std::endl;
   retval = test_prod_rank1<NumericT>(epsilon,
                                      ublas_m1, ublas_v1, ublas_v2,
                                      vcl_m1_slice, vcl_v1_range, vcl_v2_slice);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   // v1 = slice

   std::cout << "* m = slice, v1 = slice, v2 = full" << std::endl;
   retval = test_prod_rank1<NumericT>(epsilon,
                                      ublas_m1, ublas_v1, ublas_v2,
                                      vcl_m1_slice, vcl_v1_slice, vcl_v2_native);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   std::cout << "* m = slice, v1 = slice, v2 = range" << std::endl;
   retval = test_prod_rank1<NumericT>(epsilon,
                                      ublas_m1, ublas_v1, ublas_v2,
                                      vcl_m1_slice, vcl_v1_slice, vcl_v2_range);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   std::cout << "* m = slice, v1 = slice, v2 = slice" << std::endl;
   retval = test_prod_rank1<NumericT>(epsilon,
                                      ublas_m1, ublas_v1, ublas_v2,
                                      vcl_m1_slice, vcl_v1_slice, vcl_v2_slice);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   //
   // Testing triangular solve() routines
   //

   std::cout << "------------ Testing triangular solves ------------------" << std::endl;

   std::cout << "* m = full, v1 = full" << std::endl;
   retval = test_solve<NumericT>(epsilon,
                                 ublas_m2, ublas_v1,
                                 vcl_m2_native, vcl_v1_native);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   std::cout << "* m = full, v1 = range" << std::endl;
   retval = test_solve<NumericT>(epsilon,
                                 ublas_m2, ublas_v1,
                                 vcl_m2_native, vcl_v1_range);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   std::cout << "* m = full, v1 = slice" << std::endl;
   retval = test_solve<NumericT>(epsilon,
                                 ublas_m2, ublas_v1,
                                 vcl_m2_native, vcl_v1_slice);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   ///////// matrix_range

   std::cout << "* m = range, v1 = full" << std::endl;
   retval = test_solve<NumericT>(epsilon,
                                 ublas_m2, ublas_v1,
                                 vcl_m2_range, vcl_v1_native);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   std::cout << "* m = range, v1 = range" << std::endl;
   retval = test_solve<NumericT>(epsilon,
                                 ublas_m2, ublas_v1,
                                 vcl_m2_range, vcl_v1_range);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   std::cout << "* m = range, v1 = slice" << std::endl;
   retval = test_solve<NumericT>(epsilon,
                                 ublas_m2, ublas_v1,
                                 vcl_m2_range, vcl_v1_slice);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   //////// matrix_slice

   std::cout << "* m = slice, v1 = full" << std::endl;
   retval = test_solve<NumericT>(epsilon,
                                 ublas_m2, ublas_v1,
                                 vcl_m2_slice, vcl_v1_native);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   std::cout << "* m = slice, v1 = range" << std::endl;
   retval = test_solve<NumericT>(epsilon,
                                 ublas_m2, ublas_v1,
                                 vcl_m2_slice, vcl_v1_range);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   std::cout << "* m = slice, v1 = slice" << std::endl;
   retval = test_solve<NumericT>(epsilon,
                                 ublas_m2, ublas_v1,
                                 vcl_m2_slice, vcl_v1_slice);
   if (retval == EXIT_FAILURE)
   {
     std::cout << " --- FAILED! ---" << std::endl;
     return retval;
   }
   else
     std::cout << " --- PASSED ---" << std::endl;

   ////////////// Final test for full LU decomposition:

   //full solver:
   std::cout << "Full solver" << std::endl;
   unsigned int lu_dim = 100;
   ublas::matrix<NumericT> square_matrix(lu_dim, lu_dim);
   ublas::vector<NumericT> lu_rhs(lu_dim);
   viennacl::matrix<NumericT, F> vcl_square_matrix(lu_dim, lu_dim);
   viennacl::vector<NumericT> vcl_lu_rhs(lu_dim);

   for (std::size_t i=0; i<lu_dim; ++i)
     for (std::size_t j=0; j<lu_dim; ++j)
       square_matrix(i,j) = -static_cast<NumericT>(0.5) * random<NumericT>();

   //put some more weight on diagonal elements:
   for (std::size_t j=0; j<lu_dim; ++j)
   {
     square_matrix(j,j) = static_cast<NumericT>(20.0) + random<NumericT>();
     lu_rhs(j) = random<NumericT>();
   }

   viennacl::copy(square_matrix, vcl_square_matrix);
   viennacl::copy(lu_rhs, vcl_lu_rhs);

   //ublas::
   ublas::lu_factorize(square_matrix);
   ublas::inplace_solve (square_matrix, lu_rhs, ublas::unit_lower_tag ());
   ublas::inplace_solve (square_matrix, lu_rhs, ublas::upper_tag ());

   // ViennaCL:
   viennacl::linalg::lu_factorize(vcl_square_matrix);
   //viennacl::copy(square_matrix, vcl_square_matrix);
   viennacl::linalg::lu_substitute(vcl_square_matrix, vcl_lu_rhs);

   if( fabs(diff(lu_rhs, vcl_lu_rhs)) > epsilon )
   {
      std::cout << "# Error at operation: dense solver" << std::endl;
      std::cout << "  diff: " << fabs(diff(lu_rhs, vcl_lu_rhs)) << std::endl;
      retval = EXIT_FAILURE;
   }

   return retval;
}
//
// -------------------------------------------------------------
//
int main()
{
   std::cout << std::endl;
   std::cout << "----------------------------------------------" << std::endl;
   std::cout << "----------------------------------------------" << std::endl;
   std::cout << "## Test :: Matrix" << 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;
   {
      typedef float NumericT;
      NumericT epsilon = NumericT(1.0E-3);
      std::cout << "# Testing setup:" << std::endl;
      std::cout << "  eps:     " << epsilon << std::endl;
      std::cout << "  numeric: float" << std::endl;
      std::cout << "  layout: row-major" << std::endl;
      retval = test<NumericT, viennacl::row_major>(epsilon);
      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;
   {
      typedef float NumericT;
      NumericT epsilon = NumericT(1.0E-3);
      std::cout << "# Testing setup:" << std::endl;
      std::cout << "  eps:     " << epsilon << std::endl;
      std::cout << "  numeric: float" << std::endl;
      std::cout << "  layout: column-major" << std::endl;
      retval = test<NumericT, viennacl::column_major>(epsilon);
      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;

#ifdef VIENNACL_HAVE_OPENCL
   if( viennacl::ocl::current_device().double_support() )
#endif
   {
      {
         typedef double NumericT;
         NumericT epsilon = 1.0E-11;
         std::cout << "# Testing setup:" << std::endl;
         std::cout << "  eps:     " << epsilon << std::endl;
         std::cout << "  numeric: double" << std::endl;
         std::cout << "  layout: row-major" << std::endl;
         retval = test<NumericT, viennacl::row_major>(epsilon);
            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;
      {
         typedef double NumericT;
         NumericT epsilon = 1.0E-11;
         std::cout << "# Testing setup:" << std::endl;
         std::cout << "  eps:     " << epsilon << std::endl;
         std::cout << "  numeric: double" << std::endl;
         std::cout << "  layout: column-major" << std::endl;
         retval = test<NumericT, viennacl::column_major>(epsilon);
            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 2019 GridGain Systems, Inc. and Contributors.
 *
 * Licensed under the GridGain Community Edition License (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.gridgain.com/products/software/community-edition/gridgain-community-edition-license
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifdef _WIN32
#   include <windows.h>
#endif

#include <vector>
#include <string>

#include <boost/test/unit_test.hpp>

#include "test_type.h"
#include "sql_test_suite_fixture.h"
#include <ignite/common/decimal.h>

using namespace ignite;

BOOST_FIXTURE_TEST_SUITE(SqlOperatorTestSuite, ignite::SqlTestSuiteFixture)

BOOST_AUTO_TEST_CASE(TestOperatorAddInt)
{
    CheckSingleResult<SQLINTEGER>("SELECT 123 + 51", 123 + 51);
};

BOOST_AUTO_TEST_CASE(TestOperatorSubInt)
{
    CheckSingleResult<SQLINTEGER>("SELECT 123 - 51", 123 - 51);
};

BOOST_AUTO_TEST_CASE(TestOperatorDivInt)
{
    CheckSingleResult<SQLINTEGER>("SELECT 123 / 51", 123 / 51);
};

BOOST_AUTO_TEST_CASE(TestOperatorModInt)
{
    CheckSingleResult<SQLINTEGER>("SELECT 123 % 51", 123 % 51);
};

BOOST_AUTO_TEST_CASE(TestOperatorMultInt)
{
    CheckSingleResult<SQLINTEGER>("SELECT 123 * 51", 123 * 51);
};

BOOST_AUTO_TEST_CASE(TestOperatorAddDouble)
{
    CheckSingleResult<double>("SELECT 123.0 + 51.0", 123.0 + 51.0);
};

BOOST_AUTO_TEST_CASE(TestOperatorSubDouble)
{
    CheckSingleResult<double>("SELECT 123.0 - 51.0", 123.0 - 51.0);
};

BOOST_AUTO_TEST_CASE(TestOperatorDivDouble)
{
    CheckSingleResult<double>("SELECT 123.0 / 51.0", 123.0 / 51.0);
};

BOOST_AUTO_TEST_CASE(TestOperatorModDouble)
{
    CheckSingleResult<double>("SELECT 123.0 % 51.0", 123 % 51);
};

BOOST_AUTO_TEST_CASE(TestOperatorMultDouble)
{
    CheckSingleResult<double>("SELECT 123.0 * 51.0", 123.0 * 51.0);
};

BOOST_AUTO_TEST_CASE(TestOperatorConcatString)
{
    CheckSingleResult<std::string>("SELECT 'Hello' || ' ' || 'World' || '!'", "Hello World!");
};

BOOST_AUTO_TEST_CASE(TestOperatorGreaterInt)
{
    CheckSingleResult<bool>("SELECT 2 > 3", false);
    CheckSingleResult<bool>("SELECT 3 > 3", false);
    CheckSingleResult<bool>("SELECT 34 > 3", true);
};

BOOST_AUTO_TEST_CASE(TestOperatorLessInt)
{
    CheckSingleResult<bool>("SELECT 4 < 4", false);
    CheckSingleResult<bool>("SELECT 4 < 4", false);
    CheckSingleResult<bool>("SELECT 8 < 42", true);
};

BOOST_AUTO_TEST_CASE(TestOperatorGreaterEquealInt)
{
    CheckSingleResult<bool>("SELECT 2 >= 3", false);
    CheckSingleResult<bool>("SELECT 3 >= 3", true);
    CheckSingleResult<bool>("SELECT 34 >= 3", true);
};

BOOST_AUTO_TEST_CASE(TestOperatorLessEquealInt)
{
    CheckSingleResult<bool>("SELECT 4 <= 3", false);
    CheckSingleResult<bool>("SELECT 4 <= 4", true);
    CheckSingleResult<bool>("SELECT 8 <= 42", true);
};

BOOST_AUTO_TEST_CASE(TestOperatorEquealInt)
{
    CheckSingleResult<bool>("SELECT 4 = 3", false);
    CheckSingleResult<bool>("SELECT 4 = 4", true);
};

BOOST_AUTO_TEST_CASE(TestOperatorNotEquealInt)
{
    CheckSingleResult<bool>("SELECT 4 != 3", true);
    CheckSingleResult<bool>("SELECT 4 != 4", false);
};

BOOST_AUTO_TEST_CASE(TestOperatorGreaterDouble)
{
    CheckSingleResult<bool>("SELECT 2 > 3", false);
    CheckSingleResult<bool>("SELECT 3 > 3", false);
    CheckSingleResult<bool>("SELECT 34 > 3", true);
};

BOOST_AUTO_TEST_CASE(TestOperatorLessDouble)
{
    CheckSingleResult<bool>("SELECT 4.0 < 4.0", false);
    CheckSingleResult<bool>("SELECT 4.0 < 4.0", false);
    CheckSingleResult<bool>("SELECT 8.0 < 42.0", true);
};

BOOST_AUTO_TEST_CASE(TestOperatorGreaterEquealDouble)
{
    CheckSingleResult<bool>("SELECT 2.0 >= 3.0", false);
    CheckSingleResult<bool>("SELECT 3.0 >= 3.0", true);
    CheckSingleResult<bool>("SELECT 34.0 >= 3.0", true);
};

BOOST_AUTO_TEST_CASE(TestOperatorLessEquealDouble)
{
    CheckSingleResult<bool>("SELECT 4.0 <= 3.0", false);
    CheckSingleResult<bool>("SELECT 4.0 <= 4.0", true);
    CheckSingleResult<bool>("SELECT 8.0 <= 42.0", true);
};

BOOST_AUTO_TEST_CASE(TestOperatorEquealDouble)
{
    CheckSingleResult<bool>("SELECT 4.0 = 3.0", false);
    CheckSingleResult<bool>("SELECT 4.0 = 4.0", true);
};

BOOST_AUTO_TEST_CASE(TestOperatorNotEquealDouble)
{
    CheckSingleResult<bool>("SELECT 4.0 != 3.0", true);
    CheckSingleResult<bool>("SELECT 4.0 != 4.0", false);
};

BOOST_AUTO_TEST_CASE(TestOperatorGreaterString)
{
    CheckSingleResult<bool>("SELECT 'abc' > 'bcd'", false);
    CheckSingleResult<bool>("SELECT 'abc' > 'abc'", false);
    CheckSingleResult<bool>("SELECT 'bcd' > 'abc'", true);
};

BOOST_AUTO_TEST_CASE(TestOperatorLessString)
{
    CheckSingleResult<bool>("SELECT 'bcd' < 'abc'", false);
    CheckSingleResult<bool>("SELECT 'abc' < 'abc'", false);
    CheckSingleResult<bool>("SELECT 'abc' < 'bcd'", true);
};

BOOST_AUTO_TEST_CASE(TestOperatorGreaterEquealString)
{
    CheckSingleResult<bool>("SELECT 'abc' >= 'bcd'", false);
    CheckSingleResult<bool>("SELECT 'abc' >= 'abc'", true);
    CheckSingleResult<bool>("SELECT 'bcd' >= 'abc'", true);
};

BOOST_AUTO_TEST_CASE(TestOperatorLessEquealString)
{
    CheckSingleResult<bool>("SELECT 'bcd' <= 'abc'", false);
    CheckSingleResult<bool>("SELECT 'abc' <= 'bcd'", true);
    CheckSingleResult<bool>("SELECT 'abc' <= 'abc'", true);
};

BOOST_AUTO_TEST_CASE(TestOperatorEquealString)
{
    CheckSingleResult<bool>("SELECT 'bcd' = 'abc'", false);
    CheckSingleResult<bool>("SELECT 'abc' = 'abc'", true);
};

BOOST_AUTO_TEST_CASE(TestOperatorNotEquealString)
{
    CheckSingleResult<bool>("SELECT 'abc' != 'abc'", false);
    CheckSingleResult<bool>("SELECT 'bcd' != 'abc'", true);
};

BOOST_AUTO_TEST_SUITE_END()


#ifndef CMBML__PLACE_INTEGRAL_TYPE__HPP_
#define CMBML__PLACE_INTEGRAL_TYPE__HPP_

#include <climits>
#include <type_traits>

#include <boost/hana/at_key.hpp>
#include <boost/hana/map.hpp>
#include <boost/hana/type.hpp>
/*
#include <boost/hana.hpp>
*/

namespace hana = boost::hana;

// I suspect these functions don't work for signed values
namespace cmbml {

// I couldn't find a compile-time association like this in the standard library.
constexpr auto max_value_map = hana::make_map(
  hana::make_pair(hana::type_c<char>,  CHAR_MAX),
  hana::make_pair(hana::type_c<uint8_t>,  UINT8_MAX),
  hana::make_pair(hana::type_c<uint16_t>, UINT16_MAX),
  hana::make_pair(hana::type_c<uint32_t>, UINT32_MAX),
  hana::make_pair(hana::type_c<uint64_t>, UINT64_MAX),
  hana::make_pair(hana::type_c<int8_t>,  INT8_MAX),
  hana::make_pair(hana::type_c<int16_t>, INT16_MAX),
  hana::make_pair(hana::type_c<int32_t>, INT32_MAX),
  hana::make_pair(hana::type_c<int64_t>, INT64_MAX)
);

template<typename T>
constexpr size_t number_of_bits() {
  return sizeof(T)*CHAR_BIT;
}

// place an integral type into a long
// i represents the bitwise placement index of src
// start at low index, MSBs
//
// For example, suppose we are placing the byte 0x42 into a uint32 at bit index 8
// that would result in a long with the following layout:
// byte 0          | byte 1        | byte 2       | byte 3
// 0...2...........7...............15.............23...............31
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 0 0 0 0 0 0 0 0 | 1 0 0 0 0 1 0 | ...
//
// Preserve the value of dst: only overwrite the bits we need to replace
// TODO Unit test me
// TODO template specialization for "narrow", or make different f'ns for "widen" and "narrow"
template<
  typename SrcT,
  typename DstT,
  typename std::enable_if<std::is_integral<SrcT>::value &&
      std::is_integral<DstT>::value &&
      !std::is_same<SrcT, bool>::value && !std::is_same<DstT, bool>::value
      >::type * = nullptr>
void place_integral_type(const SrcT src, DstT & dst, const size_t i) {
  if (sizeof(SrcT) <= sizeof(DstT)) {
    // We need to fit something of size src into the long at the specified index
    // We expect src to span from index i to index "i + sizeof(IntegralType)"
    assert(i <= number_of_bits<DstT>() - number_of_bits<SrcT>());

    DstT widened_src = src;
    // Bitshift src to the index which it will land at
    widened_src = widened_src << i;

    // zero the part of dst which we plan to replace: i to (i + bits(SrcT))
    // dst may have critical

    DstT mask = max_value_map[hana::type_c<DstT>] << (i + number_of_bits<SrcT>());
    mask |= max_value_map[hana::type_c<DstT>] >> (number_of_bits<DstT>() - i);
    dst &= mask;

    // OR dst with the widened src
    dst |= widened_src;
    // i += number_of_bits<SrcT>();
  } else {  // Narrow
    // TODO
    dst = 0;
    assert(i <= number_of_bits<SrcT>() - number_of_bits<DstT>());
    // i refers to the index of src from which we will extract the bits to place into dst
    // Make a copy of src, bitshift, then mask
    SrcT copy = src;
    copy = copy >> i;
    SrcT mask = max_value_map[hana::type_c<SrcT>];
    mask = mask >> (number_of_bits<SrcT>() - number_of_bits<DstT>());

    copy &= mask;
    dst = copy;
    // index is incremented outside of this scope
    // i += number_of_bits<DstT>();
  }
}

// Bool specialization for flags
// first of all, partial specialization doesn't work :(
// Need a separate specialization for metadata/flags vs data serialization for uint8 size
template<typename DstT>
void place_integral_type(const bool src, DstT & dst, const size_t i) {
  assert(i < number_of_bits<DstT>());
  DstT mask = 1;
  mask = mask << i;

  if (src) {
    // OR with mask
      dst |= mask;
  } else {
    // Invert mask and AND
    mask = ~mask;
    dst &= mask;
  }
}

template<typename SrcT>
void place_integral_type(const SrcT src, bool & dst, const size_t i) {
  assert(i < number_of_bits<SrcT>());
  // TODO Reverse
  SrcT copy = src;
  copy = copy << i;
  copy = copy >> (number_of_bits<SrcT>() - 1);
  dst = copy;
}

// Specialization for when SrcT == DstT
template<typename T>
void place_integral_type(const T src, T & dst, const size_t i) {
  assert(i == 0);
  dst = src;
}

}

#endif  // CMBML__PLACE_INTEGRAL_TYPE__HPP_


//
//=======================================================================
// Copyright 2012-2014
// Author: Alex Hagen-Zanker
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================
//
// Two specialized relax functions that, when appropriate, are more efficient
// than the boost relax function. relax_target (skips relaxing the origin
// of a vertex). relax_target_confident (assumes that the target distance
// decreases).
//
// This file can be deprecated once the following patch is applied:
// https://svn.boost.org/trac/boost/ticket/7387
//=======================================================================
//
#ifndef BLINK_GRAPH_RELAX_HPP
#define BLINK_GRAPH_RELAX_HPP

#include <functional>

#include <boost/limits.hpp> // for numeric limits
#include <boost/graph/graph_traits.hpp>
#include <boost/property_map/property_map.hpp>

namespace blink {

template <class Graph, class WeightMap,
    class PredecessorMap, class DistanceMap,
    class BinaryFunction, class BinaryPredicate>
  bool relax_target(const typename boost::graph_traits<Graph>::edge_descriptor e,
    const Graph& g, const WeightMap& w,
    PredecessorMap& p, DistanceMap& d,
    const BinaryFunction& combine, const BinaryPredicate& compare)
  {
    typedef typename boost::graph_traits<Graph>::vertex_descriptor Vertex;
    typedef typename boost::property_traits<DistanceMap>::value_type D;
    typedef typename boost::property_traits<WeightMap>::value_type W;
    const Vertex u = source(e, g);
    const Vertex v = target(e, g);
    const D& d_u = get(d, u);
    const D d_v = get(d, v);
    const W& w_e = get(w, e);

    // The redundant gets in the return statements are to ensure that extra
    // floating-point precision in x87 registers does not lead to relax_target()
    // returning true when the distance did not actually change.
    if ( compare(combine(d_u, w_e), d_v) ) {
      put(d, v, combine(d_u, w_e));
      if(compare(get(d, v), d_v) ) {
        put(p, v, u);
        return true;
      };
    }
    return false;
  }

  template <class Graph, class WeightMap, class PredecessorMap, class DistanceMap,
    class BinaryFunction>
  void relax_target_confident(
    const typename boost::graph_traits<Graph>::edge_descriptor e,
    const Graph& g, const WeightMap& w, PredecessorMap& p, DistanceMap& d,
    const BinaryFunction& combine)
  {
    typedef typename boost::graph_traits<Graph>::vertex_descriptor Vertex;
    typedef typename boost::property_traits<DistanceMap>::value_type D;
    typedef typename boost::property_traits<WeightMap>::value_type W;
    const Vertex u = source(e, g);
    const Vertex v = target(e, g);
    const D& d_u = get(d, source(e, g));
    const W& w_e = get(w, e);

    put(d, v, combine(d_u, w_e));
    put(p, v, u);
  }

} // namespace blink

#endif // BLINK_GRAPH_RELAX_HPP


#define BOOST_TEST_MODULE sub_graph_iso_test

#include <boost/test/included/unit_test.hpp> // Testing
#include <lemon/list_graph.h> // LEMON Graph

#include "lib/complete_graph.hpp" // Helper to create complete graphs.

using namespace lemon;

BOOST_AUTO_TEST_SUITE( test_sub_graph_induced )

BOOST_AUTO_TEST_CASE( small_sub_graph_finds_iso )
{

	// Build graph
	ListGraph g;
	for (int i = 0; i <= 4; i++) {
		g.addNode();
	}

	g.addEdge(g.nodeFromId(0), g.nodeFromId(1));
	g.addEdge(g.nodeFromId(1), g.nodeFromId(2));
	g.addEdge(g.nodeFromId(2), g.nodeFromId(0));
	g.addEdge(g.nodeFromId(2), g.nodeFromId(3));
	g.addEdge(g.nodeFromId(3), g.nodeFromId(4));

	ListGraph s;
	for (int i = 0; i<= 3; i++) {
		s.addNode();
	}

	s.addEdge(s.nodeFromId(0), s.nodeFromId(1));
	s.addEdge(s.nodeFromId(1), s.nodeFromId(2));
	s.addEdge(s.nodeFromId(2), s.nodeFromId(3));

	// TODO: Call algorithm
	BOOST_TEST(false);

}

// Test complete graph K4 embedding in K7.
BOOST_AUTO_TEST_CASE( K4_K7_finds_iso )
{

	ListGraph graph_K7 = *lib::complete_undirected(7);
	ListGraph graph_K4 = *lib::complete_undirected(4);

	// TODO: Call algorithm
	BOOST_TEST(false);
}

// Test medium-sized embedding of K6 into K9.
BOOST_AUTO_TEST_CASE ( K6_K9_finds_iso )
{

	ListGraph graph_K9 = *lib::complete_undirected(9);
	ListGraph graph_K6 = *lib::complete_undirected(6);

	// TODO: Call algorithm
	BOOST_TEST(false);
}

// Test no embedding of K4 into complete bipartite graph KB3,4.
BOOST_AUTO_TEST_CASE ( K4_bipartite_no_iso )
{

	ListGraph graph_KB3c4;

	for (int i = 0; i <= 6; i++) {
		graph_KB3c4.addNode();
	}

	for (int i = 0; i <= 3; i++) {
		for (int j = 4; j <= 7; j++) {
			graph_KB3c4.addEdge(graph_KB3c4.nodeFromId(i), graph_KB3c4.nodeFromId(j));
		}
	}

	ListGraph graph_K4 = *lib::complete_undirected(4);

	// TODO: Call algorithm
	BOOST_TEST(false); // Should not find embedding.

}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE( test_sub_graph_non_induced )

// Test sqaure embeds in K4.
BOOST_AUTO_TEST_CASE( square_K4_finds_iso )
{

	ListGraph graph_square;
	graph_square.addEdge(graph_square.nodeFromId(0), graph_square.nodeFromId(1));
	graph_square.addEdge(graph_square.nodeFromId(1), graph_square.nodeFromId(2));
	graph_square.addEdge(graph_square.nodeFromId(2), graph_square.nodeFromId(3));
	graph_square.addEdge(graph_square.nodeFromId(3), graph_square.nodeFromId(0));

	ListGraph graph_K4 = *lib::complete_undirected(4);

	// TODO: Call algorithm
	BOOST_TEST(false);

}

// Test almost complete graph K4 embedding in K7.
BOOST_AUTO_TEST_CASE( almost_K4_K7_finds_iso )
{

	ListGraph graph_K7 = *lib::complete_undirected(7);

	ListGraph graph_almost_K4 = *lib::complete_undirected(4);
	graph_almost_K4.erase(graph_almost_K4.edgeFromId(0));

	// TODO: Call algorithm
	BOOST_TEST(false);

}

BOOST_AUTO_TEST_SUITE_END()


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

#include <pch.hpp>

#include <boost/math/concepts/real_concept.hpp>
#include <boost/test/test_exec_monitor.hpp>
#include <boost/test/floating_point_comparison.hpp>
#include <boost/math/special_functions/round.hpp>
#include <boost/math/special_functions/trunc.hpp>
#include <boost/math/special_functions/modf.hpp>
#include <boost/math/special_functions/sign.hpp>
#include <boost/random/mersenne_twister.hpp>

boost::mt19937 rng;

template <class T>
T get_random()
{
   //
   // Fill all the bits in T with random values,
   // likewise set the exponent to a random value
   // that will still fit inside a T, and always
   // have a remainder as well as an integer part.
   //
   int bits = boost::math::tools::digits<T>();
   int shift = 0;
   int exponent = rng() % (bits - 4);
   T result = 0;
   while(bits > 0)
   {
      result += ldexp(static_cast<T>(rng()), shift);
      shift += std::numeric_limits<int>::digits;
      bits -= std::numeric_limits<int>::digits;
   }
   return rng() & 1u ? -ldexp(frexp(result, &bits), exponent) : ldexp(frexp(result, &bits), exponent);
}

template <class T, class U>
void check_within_half(T a, U u)
{
   BOOST_MATH_STD_USING
   if(fabs(a-u) > 0.5f)
   {
      BOOST_ERROR("Rounded result differed by more than 0.5 from the original");
      std::cerr << "Values were: " << std::setprecision(35) << std::setw(40)
         << std::left << a << u << std::endl;
   }
   if((fabs(a - u) == 0.5f) && (fabs(static_cast<T>(u)) < fabs(a)))
   {
      BOOST_ERROR("Rounded result was towards zero with boost::round");
      std::cerr << "Values were: " << std::setprecision(35) << std::setw(40)
         << std::left << a << u << std::endl;
   }
}

//
// We may not have an abs overload for long long so provide a fall back:
//
template <class T>
inline T safe_abs(T const& v ...)
{
   return v < 0 ? -v : v;
}

template <class T, class U>
void check_trunc_result(T a, U u)
{
   BOOST_MATH_STD_USING
   if(fabs(a-u) >= 1)
   {
      BOOST_ERROR("Rounded result differed by more than 1 from the original");
      std::cerr << "Values were: " << std::setprecision(35) << std::setw(40)
         << std::left << a << u << std::endl;
   }
   if(abs(a) < safe_abs(u))
   {
      BOOST_ERROR("Truncated result had larger absolute value than the original");
      std::cerr << "Values were: " << std::setprecision(35) << std::setw(40)
         << std::left << a << u << std::endl;
   }
   if(fabs(static_cast<T>(u)) > fabs(a))
   {
      BOOST_ERROR("Rounded result was away from zero with boost::trunc");
      std::cerr << "Values were: " << std::setprecision(35) << std::setw(40)
         << std::left << a << u << std::endl;
   }
}

template <class T, class U>
void check_modf_result(T a, T fract, U ipart)
{
   BOOST_MATH_STD_USING
   if(fract + ipart != a)
   {
      BOOST_ERROR("Fractional and integer results do not add up to the original value");
      std::cerr << "Values were: " << std::setprecision(35) << " "
         << std::left << a << ipart << " " << fract << std::endl;
   }
   if((boost::math::sign(a) != boost::math::sign(fract)) && boost::math::sign(fract))
   {
      BOOST_ERROR("Original and fractional parts have differing signs");
      std::cerr << "Values were: " << std::setprecision(35) << " "
         << std::left << a << ipart << " " << fract << std::endl;
   }
   if((boost::math::sign(a) != boost::math::sign(ipart)) && boost::math::sign(ipart))
   {
      BOOST_ERROR("Original and integer parts have differing signs");
      std::cerr << "Values were: " << std::setprecision(35) << " "
         << std::left << a << ipart << " " << ipart << std::endl;
   }
   if(fabs(a-ipart) >= 1)
   {
      BOOST_ERROR("Rounded result differed by more than 1 from the original");
      std::cerr << "Values were: " << std::setprecision(35) << std::setw(40)
         << std::left << a << ipart << std::endl;
   }
}

template <class T>
void test_round(T, const char* name )
{
   BOOST_MATH_STD_USING

   std::cout << "Testing rounding with type " << name << std::endl;

   for(int i = 0; i < 1000; ++i)
   {
      T arg = get_random<T>();
      T r = boost::math::round(arg);
      check_within_half(arg, r);
      r = boost::math::trunc(arg);
      check_trunc_result(arg, r);
      T frac = boost::math::modf(arg, &r);
      check_modf_result(arg, frac, r);

      if(abs(r) < (std::numeric_limits<int>::max)())
      {
         int i = boost::math::iround(arg);
         check_within_half(arg, i);
         i = boost::math::itrunc(arg);
         check_trunc_result(arg, i);
         r = boost::math::modf(arg, &i);
         check_modf_result(arg, r, i);
      }

      if(abs(r) < (std::numeric_limits<long>::max)())
      {
         long l = boost::math::lround(arg);
         check_within_half(arg, l);
         l = boost::math::ltrunc(arg);
         check_trunc_result(arg, l);
         r = boost::math::modf(arg, &l);
         check_modf_result(arg, r, l);
      }

#ifdef BOOST_HAS_LONG_LONG
      if(abs(r) < (std::numeric_limits<boost::long_long_type>::max)())
      {
         boost::long_long_type ll = boost::math::llround(arg);
         check_within_half(arg, ll);
         ll = boost::math::lltrunc(arg);
         check_trunc_result(arg, ll);
         r = boost::math::modf(arg, &ll);
         check_modf_result(arg, r, ll);
      }
#endif
   }
   //
   // Finish off by testing the error handlers:
   //
   BOOST_CHECK_THROW(boost::math::iround(static_cast<T>(1e20)), boost::math::rounding_error);
   BOOST_CHECK_THROW(boost::math::iround(static_cast<T>(-1e20)), boost::math::rounding_error);
   BOOST_CHECK_THROW(boost::math::lround(static_cast<T>(1e20)), boost::math::rounding_error);
   BOOST_CHECK_THROW(boost::math::lround(static_cast<T>(-1e20)), boost::math::rounding_error);
#ifdef BOOST_HAS_LONG_LONG
   BOOST_CHECK_THROW(boost::math::llround(static_cast<T>(1e20)), boost::math::rounding_error);
   BOOST_CHECK_THROW(boost::math::llround(static_cast<T>(-1e20)), boost::math::rounding_error);
#endif
   if(std::numeric_limits<T>::has_infinity)
   {
      BOOST_CHECK_THROW(boost::math::round(std::numeric_limits<T>::infinity()), boost::math::rounding_error);
      BOOST_CHECK_THROW(boost::math::iround(std::numeric_limits<T>::infinity()), boost::math::rounding_error);
      BOOST_CHECK_THROW(boost::math::iround(-std::numeric_limits<T>::infinity()), boost::math::rounding_error);
      BOOST_CHECK_THROW(boost::math::lround(std::numeric_limits<T>::infinity()), boost::math::rounding_error);
      BOOST_CHECK_THROW(boost::math::lround(-std::numeric_limits<T>::infinity()), boost::math::rounding_error);
   #ifdef BOOST_HAS_LONG_LONG
      BOOST_CHECK_THROW(boost::math::llround(std::numeric_limits<T>::infinity()), boost::math::rounding_error);
      BOOST_CHECK_THROW(boost::math::llround(-std::numeric_limits<T>::infinity()), boost::math::rounding_error);
   #endif
   }
   if(std::numeric_limits<T>::has_quiet_NaN)
   {
      BOOST_CHECK_THROW(boost::math::round(std::numeric_limits<T>::quiet_NaN()), boost::math::rounding_error);
      BOOST_CHECK_THROW(boost::math::iround(std::numeric_limits<T>::quiet_NaN()), boost::math::rounding_error);
      BOOST_CHECK_THROW(boost::math::lround(std::numeric_limits<T>::quiet_NaN()), boost::math::rounding_error);
   #ifdef BOOST_HAS_LONG_LONG
      BOOST_CHECK_THROW(boost::math::llround(std::numeric_limits<T>::quiet_NaN()), boost::math::rounding_error);
   #endif
   }
   BOOST_CHECK_THROW(boost::math::itrunc(static_cast<T>(1e20)), boost::math::rounding_error);
   BOOST_CHECK_THROW(boost::math::itrunc(static_cast<T>(-1e20)), boost::math::rounding_error);
   BOOST_CHECK_THROW(boost::math::ltrunc(static_cast<T>(1e20)), boost::math::rounding_error);
   BOOST_CHECK_THROW(boost::math::ltrunc(static_cast<T>(-1e20)), boost::math::rounding_error);
#ifdef BOOST_HAS_LONG_LONG
   BOOST_CHECK_THROW(boost::math::lltrunc(static_cast<T>(1e20)), boost::math::rounding_error);
   BOOST_CHECK_THROW(boost::math::lltrunc(static_cast<T>(-1e20)), boost::math::rounding_error);
#endif
   if(std::numeric_limits<T>::has_infinity)
   {
      BOOST_CHECK_THROW(boost::math::trunc(std::numeric_limits<T>::infinity()), boost::math::rounding_error);
      BOOST_CHECK_THROW(boost::math::itrunc(std::numeric_limits<T>::infinity()), boost::math::rounding_error);
      BOOST_CHECK_THROW(boost::math::itrunc(-std::numeric_limits<T>::infinity()), boost::math::rounding_error);
      BOOST_CHECK_THROW(boost::math::ltrunc(std::numeric_limits<T>::infinity()), boost::math::rounding_error);
      BOOST_CHECK_THROW(boost::math::ltrunc(-std::numeric_limits<T>::infinity()), boost::math::rounding_error);
   #ifdef BOOST_HAS_LONG_LONG
      BOOST_CHECK_THROW(boost::math::lltrunc(std::numeric_limits<T>::infinity()), boost::math::rounding_error);
      BOOST_CHECK_THROW(boost::math::lltrunc(-std::numeric_limits<T>::infinity()), boost::math::rounding_error);
   #endif
   }
   if(std::numeric_limits<T>::has_quiet_NaN)
   {
      BOOST_CHECK_THROW(boost::math::trunc(std::numeric_limits<T>::quiet_NaN()), boost::math::rounding_error);
      BOOST_CHECK_THROW(boost::math::itrunc(std::numeric_limits<T>::quiet_NaN()), boost::math::rounding_error);
      BOOST_CHECK_THROW(boost::math::ltrunc(std::numeric_limits<T>::quiet_NaN()), boost::math::rounding_error);
   #ifdef BOOST_HAS_LONG_LONG
      BOOST_CHECK_THROW(boost::math::lltrunc(std::numeric_limits<T>::quiet_NaN()), boost::math::rounding_error);
   #endif
   }
}

int test_main(int, char* [])
{
   test_round(0.1F, "float");
   test_round(0.1, "double");
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
   test_round(0.1L, "long double");
   test_round(boost::math::concepts::real_concept(0.1), "real_concept");
#else
   std::cout << "<note>The long double tests have been disabled on this platform "
      "either because the long double overloads of the usual math functions are "
      "not available at all, or because they are too inaccurate for these tests "
      "to pass.</note>" << std::cout;
#endif
   return 0;
}


#ifndef HEADER_FACET
#define HEADER_FACET
#include <armadillo>
#include <ShapeUQLib/ControlPoint.hpp>
#include <ShapeUQLib/Element.hpp>

#include <memory>
#include <iostream>

#include <set>

class ControlPoint;
template <class PointType> class ShapeModel;

class Facet : public Element{

public:

	/**
	Constructor
	@param vertices pointer to vector storing the vertices owned by this facet
	*/
	Facet( std::vector<int> & vertices,ShapeModel<ControlPoint> * owning_shape);

	/**
	Get neighbors
	@param if false, only return neighbors sharing an edge. Else, returns all neighbords
	@return Pointer to neighboring facets, plus the calling facet
	*/
	virtual std::set< int > get_neighbors(bool all_neighbors) const;

	const arma::vec::fixed<3> & get_normal_coordinates() const;

	/**
	Returns pointer to the first vertex owned by $this that is
	neither $v0 and $v1. When $v0 and $v1 are on the same edge,
	this method returns a pointer to the vertex of $this that is not
	on the edge but still owned by $this
	@param v0 Pointer to first vertex to exclude
	@param v1 Pointer to first vertex to exclude
	@return Pointer to the first vertex of $this that is neither $v0 and $v1
	*/
	int vertex_not_on_edge(int v0,
		int v1) const ;

protected:

	virtual void compute_normal();
	virtual void compute_area();
	virtual void compute_center();

	unsigned int split_counter = 0;
	unsigned int hit_count = 0;

};
#endif


// Demonstrating the use of ranges built using istream_iterator with a custom stream operator
#include <string>
#include <iostream>
#include <sstream>
#include <iterator>
#include <cmath>

#include <boost/geometry.hpp>

#include <range/v3/all.hpp>

#include "nmea.h"

int main() {
    using namespace std;

    string test("$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,,*47");
    test +=   "\n$GPGGA,170834,4124.8963,N,08151.6838,W,1,05,1.5,280.2,M,-34.0,M,,,*59";

    using namespace ranges::v3;
    istringstream ss(test);
    ranges::for_each(istream_range<gga_t>(ss),
                     [](gga_t const& g) {
                         cout << g << "\n";
                     });

    // produce a filtered range of Boost Geometry world coordinates using Range views
    using namespace boost::geometry;
    typedef model::point<double, 2, cs::geographic<degree> > coord_t;
    ss = istringstream(test);   // reset stream
    auto construct_coord = [](gga_t const& g) {
        double lat_deg = floor(g.latitude / 100.0);
        if (g.lat_hemi == 'W') {
            lat_deg = -lat_deg;
        }
        double long_deg = floor(g.longitude / 100.0);
        if (g.lat_hemi == 'S') {
            long_deg = -long_deg;
        }
        return coord_t(lat_deg, long_deg);
    };
    auto coord_range =
        istream_range<gga_t>(ss) |
        ranges::view::remove_if([](gga_t const& g) {
                return g.dilution > 1.0;
            }) |
        ranges::view::transform(construct_coord);

    // print out resulting Geometry coordinates (only one passes dilution test)
    ranges::for_each(coord_range,
                     [](coord_t const& pt) {
                         std::cout << wkt(pt) << "\n";
                     });
}


//
// Copyright 2020 Debabrata Mandal <mandaldebabrata123@gmail.com>
//
// Use, modification and distribution are subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//

#include <boost/gil.hpp>
#include <boost/gil/extension/io/png.hpp>
#include <boost/gil/image_processing/histogram_matching.hpp>

#include <vector>

using namespace boost::gil;

std::vector<std::vector<bool>> get_mask(gray8_view_t const& mask)
{
    std::vector<std::vector<bool>> mask_vec(mask.height(), std::vector<bool>(mask.width(), 0));
    for (std::size_t i = 0; i < mask.height(); i++)
    {
        for (std::size_t j = 0; j < mask.width(); j++)
        {
            mask_vec[i][j] = mask(j, i);
        }
    }
    return mask_vec;
}

int main()
{
    gray8_image_t img;
    read_image("test_adaptive.png", img, png_tag{});

    gray8_image_t ref_img;
    read_image("test_adaptive.png", ref_img, png_tag{});

    gray8_image_t img_out(img.dimensions());

    boost::gil::histogram_matching(view(img), view(ref_img), view(img_out));

    write_view("histogram_gray_matching.png", view(img_out), png_tag{});

    return 0;
}


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

#include <boost/hana/sandbox/searchable_set.hpp>

#include <boost/hana/detail/assert.hpp>
#include <boost/hana/detail/constexpr.hpp>
#include <boost/hana/detail/minimal/comparable.hpp>

#include <tuple>
using namespace boost::hana;

BOOST_HANA_CONSTEXPR_LAMBDA auto f = [](auto x) {
    return std::make_tuple(x);
};

template <int i>
constexpr auto x = detail::minimal::comparable<>(i);

int main() {
    BOOST_HANA_CONSTEXPR_ASSERT(fmap(f, singleton(x<0>)) == singleton(f(x<0>)));
    BOOST_HANA_CONSTEXPR_ASSERT(fmap(f, doubleton(x<0>, x<1>)) == doubleton(f(x<0>), f(x<1>)));
    BOOST_HANA_CONSTEXPR_ASSERT(fmap(f, doubleton(x<0>, x<0>)) == singleton(f(x<0>)));
}