#include <boost/math/special_functions/next.hpp>
#include <boost/random.hpp>

#include <limits>

#include "caffe/common.hpp"
#include "caffe/util/math_functions.hpp"
#include "caffe/util/rng.hpp"
#include "os.h"
static GLuint tmpStorage;
static GLuint packStorage;
namespace caffe {

template<>
void caffe_cpu_gemm<float>(const CBLAS_TRANSPOSE TransA,
    const CBLAS_TRANSPOSE TransB, const int M, const int N, const int K,
    const float alpha, const float* A, const float* B, const float beta,
    float* C) {
#ifdef USE_EIGEN
  MAP_SMATRIX(eC, C, M, N);
  eC *= beta;
  if (TransA == CblasNoTrans && TransB == CblasNoTrans) {
    MAP_CONST_SMATRIX(eA, A, M, K);
    MAP_CONST_SMATRIX(eB, B, K, N);
    eC.noalias() += alpha * (eA * eB);
  } else if (TransA == CblasNoTrans && TransB == CblasTrans) {
    MAP_CONST_SMATRIX(eA, A, M, K);
    MAP_CONST_SMATRIX(eB, B, N, K);
    eC.noalias() += alpha * (eA * eB.transpose());
  } else if (TransA == CblasTrans && TransB == CblasNoTrans) {
    MAP_CONST_SMATRIX(eA, A, K, M);
    MAP_CONST_SMATRIX(eB, B, K, N);
    eC.noalias() += alpha * (eA.transpose() * eB);
  } else {
    MAP_CONST_SMATRIX(eA, A, K, M);
    MAP_CONST_SMATRIX(eB, B, N, K);
    eC.noalias() += alpha * (eA.transpose() * eB.transpose());
  }
#else
  int lda = (TransA == CblasNoTrans) ? K : M;
  int ldb = (TransB == CblasNoTrans) ? N : K;
  cblas_sgemm(CblasRowMajor, TransA, TransB, M, N, K, alpha, A, lda, B,
      ldb, beta, C, N);
#endif
}

template<>
void caffe_cpu_gemm<double>(const CBLAS_TRANSPOSE TransA,
    const CBLAS_TRANSPOSE TransB, const int M, const int N, const int K,
    const double alpha, const double* A, const double* B, const double beta,
    double* C) {
#ifdef USE_EIGEN
  MAP_DMATRIX(eC, C, M, N);
  eC *= beta;
  if (TransA == CblasNoTrans && TransB == CblasNoTrans) {
    MAP_CONST_DMATRIX(eA, A, M, K);
    MAP_CONST_DMATRIX(eB, B, K, N);
  eC.noalias() += alpha * (eA * eB);
  } else if (TransA == CblasNoTrans && TransB == CblasTrans) {
    MAP_CONST_DMATRIX(eA, A, M, K);
    MAP_CONST_DMATRIX(eB, B, N, K);
    eC.noalias() += alpha * (eA * eB.transpose());
  } else if (TransA == CblasTrans && TransB == CblasNoTrans) {
    MAP_CONST_DMATRIX(eA, A, K, M);
    MAP_CONST_DMATRIX(eB, B, K, N);
    eC.noalias() += alpha * (eA.transpose() * eB);
  } else {
    MAP_CONST_DMATRIX(eA, A, K, M);
    MAP_CONST_DMATRIX(eB, B, N, K);
    eC.noalias() += alpha * (eA.transpose() * eB.transpose());
  }
#else
  int lda = (TransA == CblasNoTrans) ? K : M;
  int ldb = (TransB == CblasNoTrans) ? N : K;
  cblas_dgemm(CblasRowMajor, TransA, TransB, M, N, K, alpha, A, lda, B,
      ldb, beta, C, N);
#endif
}

template<>
void caffe_shader_gemm<float>(const int num_output, const int num_conv_channel, const int output_width, const int output_height, const int kernel_width, const int kernel_height,
    const int padding, const int stride, const GLuint A, const GLuint B,
    const GLuint C) {

}

template<>
void caffe_shader_gemm<double>(const int num_output, const int num_conv_channel, const int output_width, const int output_height, const int kernel_width, const int kernel_height,
    const int padding, const int stride, const GLuint A, const GLuint B,
    const GLuint C) {

}

template <>
void caffe_cpu_gemv<float>(const CBLAS_TRANSPOSE TransA, const int M,
    const int N, const float alpha, const float* A, const float* x,
    const float beta, float* y) {
#ifdef USE_EIGEN
  MAP_CONST_SMATRIX(eA, A, M, N);
  if (TransA == CblasNoTrans) {
    MAP_SVECTOR(eY, y, M);
    eY *= beta;
    MAP_CONST_SVECTOR(eX, x, N);
    eY.noalias() += alpha * (eA * eX);
  } else {
    MAP_SVECTOR(eY, y, N);
    eY *= beta;
    MAP_CONST_SVECTOR(eX, x, M);
    eY.noalias() += alpha * (eA.transpose() * eX);
  }
#else
  cblas_sgemv(CblasRowMajor, TransA, M, N, alpha, A, N, x, 1, beta, y, 1);
#endif
}

template <>
void caffe_cpu_gemv<double>(const CBLAS_TRANSPOSE TransA, const int M,
    const int N, const double alpha, const double* A, const double* x,
    const double beta, double* y) {
#ifdef USE_EIGEN
  MAP_CONST_DMATRIX(eA, A, M, N);
  if (TransA == CblasNoTrans) {
    MAP_DVECTOR(eY, y, M);
    eY *= beta;
    MAP_CONST_DVECTOR(eX, x, N);
    eY.noalias() += alpha * (eA * eX);
  } else {
    MAP_DVECTOR(eY, y, N);
    eY *= beta;
    MAP_CONST_DVECTOR(eX, x, M);
    eY.noalias() += alpha * (eA.transpose() * eX);
  }
#else
  cblas_dgemv(CblasRowMajor, TransA, M, N, alpha, A, N, x, 1, beta, y, 1);
#endif
}

template <>
void caffe_axpy<float>(const int N, const float alpha, const float* X,
                       float* Y) {
#ifdef USE_EIGEN
  MAP_SVECTOR(eY, Y, N);
  MAP_CONST_SVECTOR(eX, X, N);
  eY = alpha * eX + eY;
#else
  cblas_saxpy(N, alpha, X, 1, Y, 1);
#endif
}

template <>
void caffe_axpy<double>(const int N, const double alpha, const double* X,
                        double* Y) {
#ifdef USE_EIGEN
  MAP_DVECTOR(eY, Y, N);
  MAP_CONST_DVECTOR(eX, X, N);
  eY = alpha * eX + eY;
#else
  cblas_daxpy(N, alpha, X, 1, Y, 1);
#endif
}

template <typename Dtype>
void caffe_set(const int N, const Dtype alpha, Dtype* Y) {
  if (alpha == 0) {
    memset(Y, 0, sizeof(Dtype) * N);  // NOLINT(caffe/alt_fn)
    return;
  }
  for (int i = 0; i < N; ++i) {
    Y[i] = alpha;
  }
}

template void caffe_set<int>(const int N, const int alpha, int* Y);
template void caffe_set<float>(const int N, const float alpha, float* Y);
template void caffe_set<double>(const int N, const double alpha, double* Y);

template <typename Dtype>
void caffe_shader_set(const int N, const int offset, const Dtype alpha, GLuint handle) {
  glBindBuffer(GL_SHADER_STORAGE_BUFFER,handle);
  GLint bufMask = GL_MAP_WRITE_BIT;
  Dtype* point = (Dtype*)glMapBufferRange(GL_SHADER_STORAGE_BUFFER,offset,N*sizeof(Dtype),bufMask);
  if (alpha == 0) {
    memset(point, 0, sizeof(Dtype) * N);  // NOLINT(caffe/alt_fn)
    return;
  }
  for (int i = 0; i < N; ++i) {
    point[i] = alpha;
  }
  glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
}

template void caffe_shader_set<int>(const int N, const int offset, const int alpha, GLuint handle);
template void caffe_shader_set<float>(const int N, const int offset, const float alpha, GLuint handle);
template void caffe_shader_set<double>(const int N, const int offset, const double alpha, GLuint handle);

template <>
void caffe_add_scalar(const int N, const float alpha, float* Y) {
  for (int i = 0; i < N; ++i) {
    Y[i] += alpha;
  }
}

template <>
void caffe_add_scalar(const int N, const double alpha, double* Y) {
  for (int i = 0; i < N; ++i) {
    Y[i] += alpha;
  }
}

template <typename Dtype>
void caffe_copy(const int N, const Dtype* X, Dtype* Y) {
  if (X != Y) {
    if (Caffe::mode() == Caffe::GPU) {
#ifndef CPU_ONLY
      // NOLINT_NEXT_LINE(caffe/alt_fn)
      CUDA_CHECK(cudaMemcpy(Y, X, sizeof(Dtype) * N, cudaMemcpyDefault));
#else
      NO_GPU;
#endif
    } else {
      memcpy(Y, X, sizeof(Dtype) * N);  // NOLINT(caffe/alt_fn)
    }
  }
}

template void caffe_copy<int>(const int N, const int* X, int* Y);
template void caffe_copy<unsigned int>(const int N, const unsigned int* X,
    unsigned int* Y);
template void caffe_copy<float>(const int N, const float* X, float* Y);
template void caffe_copy<double>(const int N, const double* X, double* Y);

template <>
void caffe_scal<float>(const int N, const float alpha, float *X) {
#ifdef USE_EIGEN
  MAP_SVECTOR(eX, X, N);
  eX *= alpha;
#else
  cblas_sscal(N, alpha, X, 1);
#endif
}

template <>
void caffe_scal<double>(const int N, const double alpha, double *X) {
#ifdef USE_EIGEN
  MAP_DVECTOR(eX, X, N);
  eX *= alpha;
#else
  cblas_dscal(N, alpha, X, 1);
#endif
}

template <>
void caffe_cpu_axpby<float>(const int N, const float alpha, const float* X,
                            const float beta, float* Y) {
#ifdef USE_EIGEN
  MAP_SVECTOR(eY, Y, N);
  MAP_CONST_SVECTOR(eX, X, N);
  eY = alpha * eX + beta * eY;
#else
  cblas_saxpby(N, alpha, X, 1, beta, Y, 1);
#endif
}

template <>
void caffe_cpu_axpby<double>(const int N, const double alpha, const double* X,
                             const double beta, double* Y) {
#ifdef USE_EIGEN
  MAP_DVECTOR(eY, Y, N);
  MAP_CONST_DVECTOR(eX, X, N);
  eY = alpha * eX + beta * eY;
#else
  cblas_daxpby(N, alpha, X, 1, beta, Y, 1);
#endif
}

template <>
void caffe_add<float>(const int n, const float* a, const float* b,
    float* y) {
  vsAdd(n, a, b, y);
}

template <>
void caffe_add<double>(const int n, const double* a, const double* b,
    double* y) {
  vdAdd(n, a, b, y);
}

template <>
void caffe_sub<float>(const int n, const float* a, const float* b,
    float* y) {
  vsSub(n, a, b, y);
}

template <>
void caffe_sub<double>(const int n, const double* a, const double* b,
    double* y) {
  vdSub(n, a, b, y);
}

template <>
void caffe_mul<float>(const int n, const float* a, const float* b,
    float* y) {
  vsMul(n, a, b, y);
}

template <>
void caffe_mul<double>(const int n, const double* a, const double* b,
    double* y) {
  vdMul(n, a, b, y);
}

template <>
void caffe_div<float>(const int n, const float* a, const float* b,
    float* y) {
  vsDiv(n, a, b, y);
}

template <>
void caffe_div<double>(const int n, const double* a, const double* b,
    double* y) {
  vdDiv(n, a, b, y);
}

template <>
void caffe_powx<float>(const int n, const float* a, const float b,
    float* y) {
  vsPowx(n, a, b, y);
}

template <>
void caffe_powx<double>(const int n, const double* a, const double b,
    double* y) {
  vdPowx(n, a, b, y);
}

template <>
void caffe_sqr<float>(const int n, const float* a, float* y) {
  vsSqr(n, a, y);
}

template <>
void caffe_sqr<double>(const int n, const double* a, double* y) {
  vdSqr(n, a, y);
}

template <>
void caffe_exp<float>(const int n, const float* a, float* y) {
  vsExp(n, a, y);
}

template <>
void caffe_exp<double>(const int n, const double* a, double* y) {
  vdExp(n, a, y);
}

template <>
void caffe_log<float>(const int n, const float* a, float* y) {
  vsLn(n, a, y);
}

template <>
void caffe_log<double>(const int n, const double* a, double* y) {
  vdLn(n, a, y);
}

template <>
void caffe_abs<float>(const int n, const float* a, float* y) {
    vsAbs(n, a, y);
}

template <>
void caffe_abs<double>(const int n, const double* a, double* y) {
    vdAbs(n, a, y);
}

unsigned int caffe_rng_rand() {
  return (*caffe_rng())();
}

template <typename Dtype>
Dtype caffe_nextafter(const Dtype b) {
  return boost::math::nextafter<Dtype>(
      b, std::numeric_limits<Dtype>::max());
}

template
float caffe_nextafter(const float b);

template
double caffe_nextafter(const double b);

template <typename Dtype>
void caffe_rng_uniform(const int n, const Dtype a, const Dtype b, Dtype* r) {
  CHECK_GE(n, 0);
  CHECK(r);
  CHECK_LE(a, b);
  boost::uniform_real<Dtype> random_distribution(a, caffe_nextafter<Dtype>(b));
  boost::variate_generator<caffe::rng_t*, boost::uniform_real<Dtype> >
      variate_generator(caffe_rng(), random_distribution);
  for (int i = 0; i < n; ++i) {
    r[i] = variate_generator();
  }
}

template
void caffe_rng_uniform<float>(const int n, const float a, const float b,
                              float* r);

template
void caffe_rng_uniform<double>(const int n, const double a, const double b,
                               double* r);

template <typename Dtype>
void caffe_rng_gaussian(const int n, const Dtype a,
                        const Dtype sigma, Dtype* r) {
  CHECK_GE(n, 0);
  CHECK(r);
  CHECK_GT(sigma, 0);
  boost::normal_distribution<Dtype> random_distribution(a, sigma);
  boost::variate_generator<caffe::rng_t*, boost::normal_distribution<Dtype> >
      variate_generator(caffe_rng(), random_distribution);
  for (int i = 0; i < n; ++i) {
    r[i] = variate_generator();
  }
}

template
void caffe_rng_gaussian<float>(const int n, const float mu,
                               const float sigma, float* r);

template
void caffe_rng_gaussian<double>(const int n, const double mu,
                                const double sigma, double* r);

template <typename Dtype>
void caffe_rng_bernoulli(const int n, const Dtype p, int* r) {
  CHECK_GE(n, 0);
  CHECK(r);
  CHECK_GE(p, 0);
  CHECK_LE(p, 1);
  boost::bernoulli_distribution<Dtype> random_distribution(p);
  boost::variate_generator<caffe::rng_t*, boost::bernoulli_distribution<Dtype> >
      variate_generator(caffe_rng(), random_distribution);
  for (int i = 0; i < n; ++i) {
    r[i] = variate_generator();
  }
}

template
void caffe_rng_bernoulli<double>(const int n, const double p, int* r);

template
void caffe_rng_bernoulli<float>(const int n, const float p, int* r);

template <typename Dtype>
void caffe_rng_bernoulli(const int n, const Dtype p, unsigned int* r) {
  CHECK_GE(n, 0);
  CHECK(r);
  CHECK_GE(p, 0);
  CHECK_LE(p, 1);
  boost::bernoulli_distribution<Dtype> random_distribution(p);
  boost::variate_generator<caffe::rng_t*, boost::bernoulli_distribution<Dtype> >
      variate_generator(caffe_rng(), random_distribution);
  for (int i = 0; i < n; ++i) {
    r[i] = static_cast<unsigned int>(variate_generator());
  }
}

template
void caffe_rng_bernoulli<double>(const int n, const double p, unsigned int* r);

template
void caffe_rng_bernoulli<float>(const int n, const float p, unsigned int* r);

template <>
float caffe_cpu_strided_dot<float>(const int n, const float* x, const int incx,
    const float* y, const int incy) {
#ifdef USE_EIGEN
  int lx = (n + incx - 1) / incx;
  int ly = (n + incy - 1) / incy;
  MAP_CONST_SVECTOR_STRIDE(eX, x, lx, incx);
  MAP_CONST_SVECTOR_STRIDE(eY, y, ly, incy);
  return eX.dot(eY);
#else
  return cblas_sdot(n, x, incx, y, incy);
#endif
}

template <>
double caffe_cpu_strided_dot<double>(const int n, const double* x,
    const int incx, const double* y, const int incy) {
#ifdef USE_EIGEN
  int lx = (n + incx - 1) / incx;
  int ly = (n + incy - 1) / incy;
  MAP_CONST_DVECTOR_STRIDE(eX, x, lx, incx);
  MAP_CONST_DVECTOR_STRIDE(eY, y, ly, incy);
  return eX.dot(eY);
#else
  return cblas_ddot(n, x, incx, y, incy);
#endif
}

template <typename Dtype>
Dtype caffe_cpu_dot(const int n, const Dtype* x, const Dtype* y) {
  return caffe_cpu_strided_dot(n, x, 1, y, 1);
}

template
float caffe_cpu_dot<float>(const int n, const float* x, const float* y);

template
double caffe_cpu_dot<double>(const int n, const double* x, const double* y);

template <>
float caffe_cpu_asum<float>(const int n, const float* x) {
#ifdef USE_EIGEN
  float *y = new float[n];
  vsAbs(n, x, y);
  MAP_SVECTOR(eY, y, n);
  return eY.sum();
#else
  return cblas_sasum(n, x, 1);
#endif
}

template <>
double caffe_cpu_asum<double>(const int n, const double* x) {
#ifdef USE_EIGEN
  double *y = new double[n];
  vdAbs(n, x, y);
  MAP_DVECTOR(eY, y, n);
  return eY.sum();
#else
  return cblas_dasum(n, x, 1);
#endif
}

template <>
void caffe_cpu_scale<float>(const int n, const float alpha, const float *x,
                            float* y) {
#ifdef USE_EIGEN
  memcpy(y, x, sizeof(float)*n);
  MAP_SVECTOR(eY, y, n);
  eY *= alpha;
#else
  cblas_scopy(n, x, 1, y, 1);
  cblas_sscal(n, alpha, y, 1);
#endif
}

template <>
void caffe_cpu_scale<double>(const int n, const double alpha, const double *x,
                             double* y) {
#ifdef USE_EIGEN
  memcpy(y, x, sizeof(double)*n);
  MAP_DVECTOR(eY, y, n);
  eY *= alpha;
#else
  cblas_dcopy(n, x, 1, y, 1);
  cblas_dscal(n, alpha, y, 1);
#endif
}

void caffe_init_storage(){
  glGenBuffers(1,&tmpStorage);
  glBindBuffer(GL_SHADER_STORAGE_BUFFER,tmpStorage);
  glBufferData(GL_SHADER_STORAGE_BUFFER,16777216,NULL,GL_DYNAMIC_DRAW);
}

GLuint caffe_getaxillary_storage(){
  return tmpStorage;
}

}  // namespace caffe


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

    @copyright 2016 NumScale SAS

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

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{

  /*!
    @ingroup group-reduction
    This function object computes the cumulate maximum of the argument elements

    @par Header <boost/simd/function/cummax.hpp>

    @par Example:

      @snippet cummax.cpp cummax

    @par Possible output:

      @snippet cummax.txt cummax

  **/
  Value cummax(Value const& x);
} }
#endif

#include <boost/simd/function/scalar/cummax.hpp>
#include <boost/simd/function/simd/cummax.hpp>

#endif


/*******************************************************************************
* Copyright 2020 Jose Manuel Fajardo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/

#pragma once

#include <iostream>
#include <algorithm>
#include <Eigen/Dense>
#include <math.h>
#include <vector>

#include <ros/ros.h>

#include <std_msgs/String.h>
#include <std_msgs/Float32.h>
#include <std_msgs/Float64.h>

#include <geometry_msgs/TransformStamped.h>
#include <geometry_msgs/Twist.h>
#include <geometry_msgs/PointStamped.h>
#include <geometry_msgs/Pose.h>

namespace local_vel_publisher {

/*!
 * Class containing the EKF Filter
 */
class LocalVelPub {
public:
	/*!
	 * Constructor.
	 */
	LocalVelPub(ros::NodeHandle& nodeHandle);

	/*!
	 * Destructor.
	 */
	virtual ~LocalVelPub();

private:

	// Node handle
	ros::NodeHandle nodeHandle_;

	/*
	*	Subscribers
	*/
	ros::Subscriber subs_cmd_vel_;

	/*
	* Publisher
	*/
	ros::Publisher pub_command_wheel_0;
    ros::Publisher pub_command_wheel_1;
    ros::Publisher pub_command_wheel_2;

	/*!
	* Variables
	*/

	ros::Time time_update_odom;

    std::string robot_name;
    std::string wheel_0_name;
    std::string wheel_1_name;
    std::string wheel_2_name;
    std::string wheel_0_command_topic;
    std::string wheel_1_command_topic;
    std::string wheel_2_command_topic;

    double wheel_radius;
    double wheel_sep;

    // Eigen variables
    Eigen::MatrixXd J1;
    Eigen::MatrixXd J2;
    Eigen::MatrixXd Jacob_inv;
    Eigen::VectorXd q_dot;
    Eigen::VectorXd x_dot;

	/*
	Functions
	*/

	/*!
	* Function to wrap angle to [-pi,pi]
	* @param x the angle in radians
	*/
	float constrainAngle(float x);

	/*!
	*  ROS topic callback
	*  @param msg the cmd_vel received
	*/
	void Cmd_vel_callback(const geometry_msgs::Twist msg);

    /*!
	* Function to read parameters from server
	* @return if the parameters where correctly loaded
	*/
    bool readParameters();
};

} /* namespace */


//  (C) Copyright John Maddock 2006, 2015
//  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)

#ifndef BOOST_MATH_RELATIVE_ERROR
#define BOOST_MATH_RELATIVE_ERROR

#include <boost/math/special_functions/fpclassify.hpp>
#include <boost/math/tools/promotion.hpp>
#include <boost/math/tools/precision.hpp>

namespace boost{
   namespace math{

      template <class T, class U>
      typename boost::math::tools::promote_args<T,U>::type relative_difference(const T& arg_a, const U& arg_b)
      {
         typedef typename boost::math::tools::promote_args<T, U>::type result_type;
         result_type a = arg_a;
         result_type b = arg_b;
         BOOST_MATH_STD_USING
#ifdef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
         //
         // If math.h has no long double support we can't rely
         // on the math functions generating exponents outside
         // the range of a double:
         //
         result_type min_val = (std::max)(
         tools::min_value<result_type>(),
         static_cast<result_type>((std::numeric_limits<double>::min)()));
         result_type max_val = (std::min)(
            tools::max_value<result_type>(),
            static_cast<result_type>((std::numeric_limits<double>::max)()));
#else
         result_type min_val = tools::min_value<result_type>();
         result_type max_val = tools::max_value<result_type>();
#endif
         // Screen out NaN's first, if either value is a NaN then the distance is "infinite":
         if((boost::math::isnan)(a) || (boost::math::isnan)(b))
            return max_val;
         // Screen out infinites:
         if(fabs(b) > max_val)
         {
            if(fabs(a) > max_val)
               return (a < 0) == (b < 0) ? 0 : max_val;  // one infinity is as good as another!
            else
               return max_val;  // one infinity and one finite value implies infinite difference
         }
         else if(fabs(a) > max_val)
            return max_val;    // one infinity and one finite value implies infinite difference

         //
         // If the values have different signs, treat as infinite difference:
         //
         if(((a < 0) != (b < 0)) && (a != 0) && (b != 0))
            return max_val;
         a = fabs(a);
         b = fabs(b);
         //
         // Now deal with zero's, if one value is zero (or denorm) then treat it the same as
         // min_val for the purposes of the calculation that follows:
         //
         if(a < min_val)
            a = min_val;
         if(b < min_val)
            b = min_val;

         return (std::max)(fabs((a - b) / a), fabs((a - b) / b));
      }

#if defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)
      template <>
      inline boost::math::tools::promote_args<double, double>::type relative_difference(const double& arg_a, const double& arg_b)
      {
         BOOST_MATH_STD_USING
         double a = arg_a;
         double b = arg_b;
         //
         // On Mac OS X we evaluate "double" functions at "long double" precision,
         // but "long double" actually has a very slightly narrower range than "double"!
         // Therefore use the range of "long double" as our limits since results outside
         // that range may have been truncated to 0 or INF:
         //
         double min_val = (std::max)((double)tools::min_value<long double>(), tools::min_value<double>());
         double max_val = (std::min)((double)tools::max_value<long double>(), tools::max_value<double>());

         // Screen out NaN's first, if either value is a NaN then the distance is "infinite":
         if((boost::math::isnan)(a) || (boost::math::isnan)(b))
            return max_val;
         // Screen out infinites:
         if(fabs(b) > max_val)
         {
            if(fabs(a) > max_val)
               return 0;  // one infinity is as good as another!
            else
               return max_val;  // one infinity and one finite value implies infinite difference
         }
         else if(fabs(a) > max_val)
            return max_val;    // one infinity and one finite value implies infinite difference

         //
         // If the values have different signs, treat as infinite difference:
         //
         if(((a < 0) != (b < 0)) && (a != 0) && (b != 0))
            return max_val;
         a = fabs(a);
         b = fabs(b);
         //
         // Now deal with zero's, if one value is zero (or denorm) then treat it the same as
         // min_val for the purposes of the calculation that follows:
         //
         if(a < min_val)
            a = min_val;
         if(b < min_val)
            b = min_val;

         return (std::max)(fabs((a - b) / a), fabs((a - b) / b));
      }
#endif

      template <class T, class U>
      inline typename boost::math::tools::promote_args<T, U>::type epsilon_difference(const T& arg_a, const U& arg_b)
      {
         typedef typename boost::math::tools::promote_args<T, U>::type result_type;
         result_type r = relative_difference(arg_a, arg_b);
         if(tools::max_value<result_type>() * boost::math::tools::epsilon<result_type>() < r)
            return tools::max_value<result_type>();
         return r / boost::math::tools::epsilon<result_type>();
      }
} // namespace math
} // namespace boost

#endif


#include "Graph.h"

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE GraphTestSuite

#include <boost/test/unit_test.hpp>

#include <boost/assign/std/vector.hpp>
#include <boost/assign/list_of.hpp>

#include <ctime>

BOOST_AUTO_TEST_SUITE(GraphTestSuite)

void checkForValidSequence()
{
    AlfaTest::Graph graph;
    std::vector<std::string> dictionary;
    dictionary = boost::assign::list_of("asdfghj")("asddghj")("asddghd")("asds")("asddhhd")("bsddhhd")("bsddhho");

    std::vector<std::string> firstLastWords;
    firstLastWords = boost::assign::list_of("asdfghj")("bsddhho");

    for(size_t i(0); i < dictionary.size(); i++)
    {
        graph.addVertex(dictionary[i]);
    }

    graph.buildEdges();

    std::vector<std::string> path(graph.findShortestPath(firstLastWords[0], firstLastWords[1]));

	BOOST_CHECK_EQUAL(path.size(), 6);
	BOOST_CHECK_EQUAL(path[0], "asdfghj");
	BOOST_CHECK_EQUAL(path[1], "asddghj");
	BOOST_CHECK_EQUAL(path[2], "asddghd");
	BOOST_CHECK_EQUAL(path[3], "asddhhd");
	BOOST_CHECK_EQUAL(path[4], "bsddhhd");
	BOOST_CHECK_EQUAL(path[5], "bsddhho");
}

void checkForInvalidSequence()
{
    AlfaTest::Graph graph;
    std::vector<std::string> dictionary;
    dictionary = boost::assign::list_of("asdfghj")("asddghj")("asddghd")("asds")("asddhhd")("bsddhhd")("bsdZZho");

    std::vector<std::string> firstLastWords;
    firstLastWords = boost::assign::list_of("asdfghj")("bsdZZho");

    for(size_t i(0); i < dictionary.size(); i++)
    {
        graph.addVertex(dictionary[i]);
    }

    graph.buildEdges();

    std::vector<std::string> path(graph.findShortestPath(firstLastWords[0], firstLastWords[1]));

	BOOST_CHECK_EQUAL(path.size(), 0);
}

void checkForValidRandomSequence()
{
    std::srand(std::time(0));
    std::string originalWord = "AFDSLFHBBZXBCBJSDEBFEWUGFUYSAGEFDSADF";
    AlfaTest::Graph graph;
    std::vector<std::string> dictionary;
    dictionary.push_back(originalWord);
    for(size_t i(0); i < 1000; i++)
    {
        size_t n (std::rand() % originalWord.length());
        originalWord[n]++;
        dictionary.push_back(originalWord);
    }

    for(size_t i(0); i < dictionary.size(); i++)
    {
        graph.addVertex(dictionary[i]);
    }

    graph.buildEdges();

    std::vector<std::string> path(graph.findShortestPath(dictionary[0], dictionary[dictionary.size()-1]));

    // have solution
	BOOST_CHECK(path.size()>0 && path.size() <= dictionary.size());
}

BOOST_AUTO_TEST_CASE(GraphTest1)
{
    checkForValidSequence();
    checkForInvalidSequence();

    for (size_t i(0); i < 10; i++)
    {
        checkForValidRandomSequence();
    }
}

BOOST_AUTO_TEST_SUITE_END()


/* ----------------------------------------------------------------------------
 * Copyright 2017, Massachusetts Institute of Technology,
 * Cambridge, MA 02139
 * All Rights Reserved
 * Authors: Luca Carlone, et al. (see THANKS for the full author list)
 * See LICENSE for the license information
 * -------------------------------------------------------------------------- */

/**
 * @file   Camera.h
 * @brief  Class describing a monocular camera
 * @author Antoni Rosinol
 */

#include "kimera-vio/frontend/Camera.h"

#include <Eigen/Core>

#include <opencv2/core.hpp>

#include <gtsam/geometry/Point2.h>

#include "kimera-vio/frontend/CameraParams.h"
#include "kimera-vio/utils/Macros.h"

namespace VIO {

Camera::Camera(const CameraParams& cam_params)
    : cam_params_(cam_params),
      calibration_(cam_params.intrinsics_.at(0),
                   cam_params.intrinsics_.at(1),
                   0.0,  // No skew
                   cam_params.intrinsics_.at(2),
                   cam_params.intrinsics_.at(3)),
      undistorter_(nullptr),
      camera_impl_(nullptr) {
  // NOTE: no rectification, use camera matrix as P for cv::undistortPoints
  // see https://stackoverflow.com/questions/22027419/bad-results-when-undistorting-points-using-opencv-in-python
  cv::Mat P = cam_params.K_;
  cv::Mat R = cv::Mat::eye(3,3,CV_32FC1);
  undistorter_ = VIO::make_unique<UndistorterRectifier>(P, cam_params_, R);
  CHECK(undistorter_);

  camera_impl_ =
      VIO::make_unique<CameraImpl>(cam_params.body_Pose_cam_, calibration_);
  CHECK(camera_impl_);
}

void Camera::project(const LandmarksCV& lmks, KeypointsCV* kpts) const {
  CHECK_NOTNULL(kpts)->clear();
  const auto& n_lmks = lmks.size();
  kpts->resize(n_lmks);
  // Can be greatly optimized with matrix mult or vectorization
  for (size_t i = 0u; i < n_lmks; i++) {
    project(lmks[i], &(*kpts)[i]);
  }
}

void Camera::project(const LandmarkCV& lmk, KeypointCV* kpt) const {
  CHECK_NOTNULL(kpt);
  // I believe this project call in gtsam is quite inefficient as it goes
  // through a cascade of calls... Only useful if you want gradients I guess...
  CHECK(camera_impl_);
  const gtsam::Point2& kp =
      camera_impl_->project2(gtsam::Point3(lmk.x, lmk.y, lmk.z));
  *kpt = KeypointCV(kp.x(), kp.y());
}

void Camera::backProject(const KeypointsCV& kps,
                         const Depths& depths,
                         LandmarksCV* lmks) const {
  CHECK_NOTNULL(lmks)->clear();
  lmks->reserve(kps.size());
  CHECK_EQ(kps.size(), depths.size());
  CHECK(camera_impl_);
  for (size_t i = 0u; i < kps.size(); i++) {
    LandmarkCV lmk;
    backProject(kps[i], depths[i], &lmk);
    lmks->push_back(lmk);
  }
}

void Camera::backProject(const KeypointCV& kp,
                         const Depth& depth,
                         LandmarkCV* lmk) const {
  CHECK_NOTNULL(lmk);
  CHECK(camera_impl_);
  CHECK_GT(depth, 0.0);
  CHECK_GE(kp.x, 0.0);
  CHECK_GE(kp.y, 0.0);
  CHECK_LT(kp.x, cam_params_.image_size_.width);
  CHECK_LT(kp.y, cam_params_.image_size_.height);
  gtsam::Point2 uv(kp.x, kp.y);
  gtsam::Point3 gtsam_lmk = camera_impl_->backproject(uv, depth);
  lmk->x = gtsam_lmk.x();
  lmk->y = gtsam_lmk.y();
  lmk->z = gtsam_lmk.z();
}

// TODO(marcus): copy-pasted from StereoCamera.
//  Maybe there should be some inheritance..
void Camera::undistortKeypoints(const KeypointsCV& keypoints,
                                StatusKeypointsCV* status_keypoints) const {
  KeypointsCV undistorted_keypoints;
  CHECK(undistorter_);
  undistorter_->undistortRectifyKeypoints(
      keypoints, &undistorted_keypoints);
  undistorter_->checkUndistortedRectifiedLeftKeypoints(
      keypoints, undistorted_keypoints, status_keypoints);
}

}  // namespace VIO


//
//  complex_conjugate.hpp
//  BGV-Adder
//
//  Created by Andris on 28/10/2020.
//  Copyright © 2020 RUG. All rights reserved.
//

#ifndef complex_conjugate_hpp
#define complex_conjugate_hpp

#include <stdio.h>
#include <stdio.h>
#include <helib/FHE.h>
#include <array>
#include <stdint.h>
#include <NTL/ZZ.h>
#include <helib/helib.h>

const static size_t ARRAY_SIZE = 2;

std::array<long, ARRAY_SIZE> complex_conjugate(int16_t a, int16_t b);

#endif /* complex_conjugate_hpp */


#include <boost/random/additive_combine.hpp>


#include <hb/crypto/random.h>
#include <iostream>
#include <boost/random/random_device.hpp>
#include "boost/random.hpp"
#include "boost/generator_iterator.hpp"

namespace hb { namespace crypto {
    using namespace std;

    std::string random_str(unsigned len)
    {
        std::string chars(
            "abcdefghijklmnopqrstuvwxyz"
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
            "1234567890");
            // "!@#$%^&*()"
            // "`~-_=+[{]}\\|;:'\",<.>/? ");
        boost::random::random_device rng;
        boost::random::uniform_int_distribution<> index_dist(0, chars.size() - 1);
        string digest;
        for(int i = 0; i < len; ++i) {
           digest.push_back(chars[index_dist(rng)]);
        }
        return digest;
    }
    unsigned random_num(unsigned limit_max){
        if (limit_max<=1) return 0;
        boost::random::random_device rng;
        boost::random::uniform_int_distribution<> index_dist(0, limit_max-1);
        return index_dist(rng);
    }

} } // namespace hb


#define BOOST_TEST_MODULE
#include <boost/test/unit_test.hpp>
#include <util/test_macros.hpp>
#include <stdlib.h>
#include <vector>
#include <string>
#include <functional>
#include <random>

#include <unity/lib/gl_sframe.hpp>
#include <unity/lib/unity_sframe.hpp>
#include <unity/lib/variant_deep_serialize.hpp>
#include <unity/toolkits/pattern_mining/fp_growth.hpp>

#include <cfenv>

using namespace turi;
using namespace turi::pattern_mining;

/**
 *  Run tests.
*/
struct fp_growth_test  {

  public:
    // tests closet_algorithm
    void testCLOSETAlgorithm1(void){
      gl_sarray database = gl_sarray({flex_list{0,1,4,6},
                                      flex_list{1,2,4},
                                      flex_list{2,4,6},
                                      flex_list{0,2,6},
                                      flex_list{1,3,4},
                                      flex_list{2,4,5},
                                      flex_list{1,2,3,4}});
      size_t min_support = 3;

      fp_results_tree closed_itemset_tree = closet_algorithm(database, min_support);
      // Tree should be
      // (root (6:3, 4:6 (1:4, 2:4), 2:5))
      // std::cout << closed_itemset_tree;

      gl_sframe closed_itemset = closed_itemset_tree.get_closed_itemsets();

      // Closed Itemsets should be
      // {4}:6, {2}:5, {1,4}:4, {2,4}:4, {6}:3,
      // std::cout << closed_itemset;

      TS_ASSERT_EQUALS(closed_itemset.size(), 5);
      TS_ASSERT_EQUALS(closed_itemset_tree.root_node->children_nodes.size(), 3);
      TS_ASSERT_EQUALS(closed_itemset_tree.root_node->children_nodes[0]->item_id, 6);
      TS_ASSERT_EQUALS(closed_itemset_tree.root_node->children_nodes[0]->item_count, 3);
    }

    void testCLOSETAlgorithm2(void){
      gl_sarray database = gl_sarray({flex_list{1},
                                      flex_list{1,2},
                                      flex_list{1,2,3},
                                      flex_list{1,2,3,4},
                                      flex_list{1,2,3,4,5}});
      size_t min_support = 1;

      fp_results_tree closed_itemset_tree = closet_algorithm(database, min_support);
      // Tree should be
      // (root (1:5 (2:4 (3:3 (4:2 (5:1))))))
      // std::cout << closed_itemset_tree;

      gl_sframe closed_itemset = closed_itemset_tree.get_closed_itemsets();

      // Closed Itemsets should be
      // {1}:5, {1,2}:4, {1,2,3}:3, {1,2,3,4}:2, {1,2,3,4,5}:1
      // std::cout << closed_itemset;

      TS_ASSERT_EQUALS(closed_itemset.size(), 5);
      auto root_node = closed_itemset_tree.root_node;
      TS_ASSERT_EQUALS(root_node->children_nodes.size(), 1);
      TS_ASSERT_EQUALS(root_node->children_nodes[0]->item_id, 1);
      TS_ASSERT_EQUALS(root_node->children_nodes[0]->item_count, 5);
      TS_ASSERT_EQUALS(root_node->children_nodes[0]->children_nodes[0]->item_id, 2);
      TS_ASSERT_EQUALS(root_node->children_nodes[0]->children_nodes[0]->item_count, 4);

    }

    void testCLOSETAlgorithm3(void){
      gl_sarray database = gl_sarray({flex_list{1},
                                      flex_list{1,2},
                                      flex_list{1,2,3},
                                      flex_list{1,2,4}});
      size_t min_support = 1;

      fp_results_tree closed_itemset_tree = closet_algorithm(database, min_support);
      // Tree should be
      // (root (1:4 (2:3 (3:1, 4:1))))
      // std::cout << closed_itemset_tree;

      gl_sframe closed_itemset = closed_itemset_tree.get_closed_itemsets();

      // Closed Itemsets should be
      // {1}:4, {1,2}:3, {1,2,3}:1, {1,2,4}:1
      // std::cout << closed_itemset;

      TS_ASSERT_EQUALS(closed_itemset.size(), 4);
      auto root_node = closed_itemset_tree.root_node;
      TS_ASSERT_EQUALS(root_node->children_nodes.size(), 1);
      TS_ASSERT_EQUALS(root_node->children_nodes[0]->item_id, 1);
      TS_ASSERT_EQUALS(root_node->children_nodes[0]->item_count, 4);
      TS_ASSERT_EQUALS(root_node->children_nodes[0]->children_nodes[0]->item_id, 2);
      TS_ASSERT_EQUALS(root_node->children_nodes[0]->children_nodes[0]->item_count, 3);

    }
    void testCLOSETAlgorithm4(void){
      gl_sarray database = gl_sarray({flex_list{1},
                                      flex_list{1,2},
                                      flex_list{1,2,3},
                                      flex_list{1,2,4},
                                      flex_list{5},
                                      flex_list{5}});
      size_t min_support = 1;

      fp_results_tree closed_itemset_tree = closet_algorithm(database, min_support);
      // Tree should be
      // (root (1:4 (2:3 (3:1, 4:1)), 5:2))
      // std::cout << closed_itemset_tree;

      gl_sframe closed_itemset = closed_itemset_tree.get_closed_itemsets();

      // Closed Itemsets should be
      // {1}:4, {1,2}:3, {1,2,3}:1, {1,2,4}:1, {5}:2
      // std::cout << closed_itemset;

      TS_ASSERT_EQUALS(closed_itemset.size(), 5);
      auto root_node = closed_itemset_tree.root_node;
      TS_ASSERT_EQUALS(root_node->children_nodes.size(), 2);
      TS_ASSERT_EQUALS(root_node->children_nodes[0]->item_id, 1);
      TS_ASSERT_EQUALS(root_node->children_nodes[0]->item_count, 4);
      TS_ASSERT_EQUALS(root_node->children_nodes[0]->children_nodes[0]->item_id, 2);
      TS_ASSERT_EQUALS(root_node->children_nodes[0]->children_nodes[0]->item_count, 3);

    }
    void testCLOSETAlgorithm5(void){
      gl_sarray database = gl_sarray({flex_list{0,1,4,6},
                                      flex_list{1,2,4},
                                      flex_list{2,4,6},
                                      flex_list{0,2,6},
                                      flex_list{1,3,4},
                                      flex_list{2,4,5},
                                      flex_list{1,2,3,4}});
      size_t min_support = 1;

      fp_results_tree closed_itemset_tree = closet_algorithm(database, min_support);
      // Tree should be
      // header: {4:6, 2:5, 1:4, 6:3, 0:2, 3:2, 5:1}
      // (root (4:6 (2:4(5:1, 1:2 (3:1), 6:1), 1:4 (3:2, 6:1 (0:1)), 6:2),
      //        2:5 (6:2 (0:1)), 6:3 (0:2)))
      // std::cout << closed_itemset_tree;

      gl_sframe closed_itemset = closed_itemset_tree.get_closed_itemsets();

      // Closed Itemsets should have (15 itemsest)
      //std::cout << closed_itemset;

      TS_ASSERT_EQUALS(closed_itemset.size(), 15);
    }

    // Test top_k_algorithm
    void testTOPKAlgorithm1(void){
      // Filter on top_k
      gl_sarray database = gl_sarray({flex_list{0,1,4,6},
                                      flex_list{1,2,4},
                                      flex_list{2,4,6},
                                      flex_list{0,2,6},
                                      flex_list{1,3,4},
                                      flex_list{2,4,5},
                                      flex_list{1,2,3,4}});
      size_t min_support = 1;
      size_t top_k = 5;
      size_t min_length = 1;

      fp_top_k_results_tree closed_itemset_tree;
      closed_itemset_tree = top_k_algorithm(database, min_support, top_k, min_length);

      TS_ASSERT_EQUALS(closed_itemset_tree.top_k, top_k);
      TS_ASSERT_EQUALS(closed_itemset_tree.min_length, min_length);
      TS_ASSERT_EQUALS(min_support, 3);

      // Tree should be
      // (root (6:3, 4:6 (1:4, 2:4), 2:5))
      // std::cout << closed_itemset_tree;

      gl_sframe closed_itemset = closed_itemset_tree.get_closed_itemsets();

      // Closed Itemsets should be
      // {4}:6, {2}:5, {1,4}:4, {2,4}:4, {6}:3,
      // std::cout << closed_itemset;

      TS_ASSERT_EQUALS(closed_itemset.size(), 5);
      TS_ASSERT_EQUALS(closed_itemset_tree.root_node->children_nodes.size(), 3);
      TS_ASSERT_EQUALS(closed_itemset_tree.root_node->children_nodes[0]->item_id, 4);
      TS_ASSERT_EQUALS(closed_itemset_tree.root_node->children_nodes[0]->item_count, 6);
    }
    void testTOPKAlgorithm2(void){
      // Filter on min_length
      gl_sarray database = gl_sarray({flex_list{0,1,4},
                                      flex_list{1,2,4,6},
                                      flex_list{2,4,6},
                                      flex_list{0,2,6},
                                      flex_list{1,3,4},
                                      flex_list{2,4,5},
                                      flex_list{1,2,3,4}});
      size_t min_support = 1;
      size_t top_k = 3;
      size_t min_length = 2;

      fp_top_k_results_tree closed_itemset_tree;
      closed_itemset_tree = top_k_algorithm(database, min_support, top_k, min_length);

      TS_ASSERT_EQUALS(closed_itemset_tree.top_k, top_k);
      TS_ASSERT_EQUALS(closed_itemset_tree.min_length, min_length);
      TS_ASSERT_EQUALS(min_support, 3);

      // Tree should be
      // (root (4:6 (1:4, 2:4), 2:5 (6:3)))
      // std::cout << closed_itemset_tree;

      gl_sframe closed_itemset = closed_itemset_tree.get_closed_itemsets();

      // Closed Itemsets should be
      // {2, 6}:3, {1,4}:4, {2,4}:4,
      // std::cout << closed_itemset;
      TS_ASSERT_EQUALS(closed_itemset.size(), 3);
    }
    void testTOPKAlgorithm3(void){
      // Check it matches CLOSET
      gl_sarray database = gl_sarray({flex_list{0,1,4,6},
                                      flex_list{1,2,4},
                                      flex_list{2,4,6},
                                      flex_list{0,2,6},
                                      flex_list{1,3,4},
                                      flex_list{2,4,5},
                                      flex_list{1,2,3,4}});
      size_t min_support = 3;
      size_t top_k = TOP_K_MAX;
      size_t min_length = 1;

      fp_top_k_results_tree closed_itemset_tree;
      closed_itemset_tree = top_k_algorithm(database, min_support, top_k, min_length);

      TS_ASSERT_EQUALS(closed_itemset_tree.top_k, top_k);
      TS_ASSERT_EQUALS(closed_itemset_tree.min_length, min_length);
      TS_ASSERT_EQUALS(min_support, 3);

      // Tree should be
      // (root (6:3, 4:6 (1:4, 2:4), 2:5))
      //std::cout << closed_itemset_tree;

      gl_sframe closed_itemset = closed_itemset_tree.get_closed_itemsets();

      // Closed Itemsets should be
      // {4}:6, {2}:5, {1,4}:4, {2,4}:4, {6}:3,
      //std::cout << closed_itemset;

      TS_ASSERT_EQUALS(closed_itemset.size(), 5);

    }
    void testTOPKAlgorithm4(void){
      // Impossible Conditions
      gl_sarray database = gl_sarray({flex_list{0,1,4,6},
                                      flex_list{1,2,4},
                                      flex_list{2,4,6},
                                      flex_list{0,2,6},
                                      flex_list{1,3,4},
                                      flex_list{2,4,5},
                                      flex_list{1,2,3,4}});
      size_t min_support = 4;
      size_t top_k = 5;
      size_t min_length = 3;

      fp_top_k_results_tree closed_itemset_tree;
      closed_itemset_tree = top_k_algorithm(database, min_support, top_k, min_length);

      TS_ASSERT_EQUALS(closed_itemset_tree.top_k, top_k);
      TS_ASSERT_EQUALS(closed_itemset_tree.min_length, min_length);
      TS_ASSERT_EQUALS(min_support, 4);

      // Tree should be empty
      // std::cout << closed_itemset_tree;

      gl_sframe closed_itemset = closed_itemset_tree.get_closed_itemsets();

      // Closed Itemsets should be empty
      // std::cout << closed_itemset;

      TS_ASSERT_EQUALS(closed_itemset.size(), 0);

      }

    void testTOPKAlgorithm5(void){
      // Repeated Input
      gl_sarray database = gl_sarray({flex_list{0},
                                      flex_list{0,1,1},
                                      flex_list{0,1,2,2},
                                      flex_list{0,1,2,2,2,3,3},
                                      flex_list{0,0,0,1,2,3,4},
                                      flex_list{0,1,2,3,4,5}});
      size_t min_support = 1;
      size_t top_k = 3;
      size_t min_length = 2;

      fp_top_k_results_tree closed_itemset_tree;
      closed_itemset_tree = top_k_algorithm(database, min_support, top_k, min_length);

      TS_ASSERT_EQUALS(closed_itemset_tree.top_k, top_k);
      TS_ASSERT_EQUALS(closed_itemset_tree.min_length, min_length);
      TS_ASSERT_EQUALS(min_support, 3);

      // Tree should be
      // (root (0:6 (1:5 (2:4 (3:3 (4:2 (5:1)))))))
      // std::cout << closed_itemset_tree;

      gl_sframe closed_itemset = closed_itemset_tree.get_closed_itemsets();

      // Closed Itemsets should be
      // {0,1}: 5, {0,1,2}:4, {0,1,2,3}:3
      // std::cout << closed_itemset;

      TS_ASSERT_EQUALS(closed_itemset.size(), 3);

      }

    void testTOPKAlgorithm6(void){
      // Undefined Input
      gl_sarray database = gl_sarray({flex_list{0},
                                      flex_list{0,1},
                                      flex_list{0,1,2},
                                      flex_list{0,1,2,3, FLEX_UNDEFINED},
                                      flex_list{0,1,2,3,4},
                                      flex_list{0,1,2,3,4,5},
                                      flex_list{FLEX_UNDEFINED}});
      size_t min_support = 1;
      size_t top_k = 3;
      size_t min_length = 2;

      fp_top_k_results_tree closed_itemset_tree;
      closed_itemset_tree = top_k_algorithm(database, min_support, top_k, min_length);

      TS_ASSERT_EQUALS(closed_itemset_tree.top_k, top_k);
      TS_ASSERT_EQUALS(closed_itemset_tree.min_length, min_length);
      TS_ASSERT_EQUALS(min_support, 3);

      // Tree should be
      // (root (0:6 (1:5 (2:4 (3:3 (4:2 (5:1)))))))
      // std::cout << closed_itemset_tree;

      gl_sframe closed_itemset = closed_itemset_tree.get_closed_itemsets();

      // Closed Itemsets should be
      // {0,1}: 5, {0,1,2}:4, {0,1,2,3}:3
      // std::cout << closed_itemset;

      TS_ASSERT_EQUALS(closed_itemset.size(), 3);
      TS_ASSERT_EQUALS(closed_itemset_tree.root_node->item_count, 7);

      }

};

BOOST_FIXTURE_TEST_SUITE(_fp_growth_test, fp_growth_test)
BOOST_AUTO_TEST_CASE(testCLOSETAlgorithm1) {
  fp_growth_test::testCLOSETAlgorithm1();
}
BOOST_AUTO_TEST_CASE(testCLOSETAlgorithm2) {
  fp_growth_test::testCLOSETAlgorithm2();
}
BOOST_AUTO_TEST_CASE(testCLOSETAlgorithm3) {
  fp_growth_test::testCLOSETAlgorithm3();
}
BOOST_AUTO_TEST_CASE(testCLOSETAlgorithm4) {
  fp_growth_test::testCLOSETAlgorithm4();
}
BOOST_AUTO_TEST_CASE(testCLOSETAlgorithm5) {
  fp_growth_test::testCLOSETAlgorithm5();
}
BOOST_AUTO_TEST_CASE(testTOPKAlgorithm1) {
  fp_growth_test::testTOPKAlgorithm1();
}
BOOST_AUTO_TEST_CASE(testTOPKAlgorithm2) {
  fp_growth_test::testTOPKAlgorithm2();
}
BOOST_AUTO_TEST_CASE(testTOPKAlgorithm3) {
  fp_growth_test::testTOPKAlgorithm3();
}
BOOST_AUTO_TEST_CASE(testTOPKAlgorithm4) {
  fp_growth_test::testTOPKAlgorithm4();
}
BOOST_AUTO_TEST_CASE(testTOPKAlgorithm5) {
  fp_growth_test::testTOPKAlgorithm5();
}
BOOST_AUTO_TEST_CASE(testTOPKAlgorithm6) {
  fp_growth_test::testTOPKAlgorithm6();
}
BOOST_AUTO_TEST_SUITE_END()


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

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

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

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

#include <iostream>

#ifndef BOOST_TEST_MODULE
#define BOOST_TEST_MODULE test_distance_spherical_equatorial_point_box
#endif

#include <boost/range.hpp>
#include <boost/type_traits/is_same.hpp>

#include <boost/test/included/unit_test.hpp>
#include <boost/geometry/util/condition.hpp>
#include <boost/geometry/strategies/strategies.hpp>

#include "test_distance_se_common.hpp"

typedef bg::cs::spherical_equatorial<bg::degree> cs_type;
typedef bg::model::point<double, 2, cs_type> point_type;
typedef bg::model::segment<point_type> segment_type;
typedef bg::model::box<point_type> box_type;

namespace distance = bg::strategy::distance;
namespace services = distance::services;
typedef bg::default_distance_result<point_type, point_type>::type return_type;

typedef distance::cross_track_point_box<> point_box_strategy;
typedef distance::cross_track_point_box
    <
        void, distance::comparable::cross_track<>
    > comparable_point_box_strategy;

//===========================================================================

inline bg::distance_result
    <
        point_type, point_type, distance::haversine<double>
    >::type
distance_pp(std::string const& wkt1,
            std::string const& wkt2,
            double radius)
{
    point_type p1, p2;
    bg::read_wkt(wkt1, p1);
    bg::read_wkt(wkt2, p2);

    distance::haversine<double> strategy(radius);
    return bg::distance(p1, p2, strategy);
}

inline bg::default_comparable_distance_result<point_type>::type
comparable_distance_pp(std::string const& wkt1,
                       std::string const& wkt2)
{
    point_type p1, p2;
    bg::read_wkt(wkt1, p1);
    bg::read_wkt(wkt2, p2);
    return bg::comparable_distance(p1, p2);
}

inline bg::distance_result
    <
        point_type, point_type, distance::cross_track<>
    >::type
distance_ps(std::string const& wkt_point,
            std::string const& wkt_segment,
            double radius)
{
    point_type point;
    segment_type segment;
    bg::read_wkt(wkt_point, point);
    bg::read_wkt(wkt_segment, segment);

    distance::cross_track<> strategy(radius);
    return bg::distance(point, segment, strategy);
}

inline bg::default_comparable_distance_result<point_type>::type
comparable_distance_ps(std::string const& wkt_point,
                       std::string const& wkt_segment)
{
    point_type point;
    segment_type segment;
    bg::read_wkt(wkt_point, point);
    bg::read_wkt(wkt_segment, segment);
    return bg::comparable_distance(point, segment);
}

enum features_type { pp, ps };

template <typename Geometry1, typename Geometry2>
struct test_distances
{
    template <typename Strategy>
    static inline void apply(std::string const& case_id,
                             std::string const& wkt1,
                             std::string const& wkt2,
                             double expected_distance,
                             double expected_comparable_distance,
                             Strategy const& strategy)
    {
        typedef test_distance_of_geometries<Geometry1, Geometry2> tester;

        bool const is_comparable = boost::is_same
            <
                Strategy,
                typename services::comparable_type<Strategy>::type
            >::value;

        if (BOOST_GEOMETRY_CONDITION(is_comparable))
        {
            tester::apply(case_id, wkt1, wkt2,
                          expected_comparable_distance,
                          expected_comparable_distance,
                          strategy);
        }
        else
        {
            tester::apply(case_id, wkt1, wkt2,
                          expected_distance,
                          expected_comparable_distance,
                          strategy);
        }
    }

    template <typename Strategy>
    static inline void apply(std::string const& case_id,
                             std::string const& wkt1,
                             std::string const& wkt2,
                             std::string const& feature1,
                             std::string const& feature2,
                             features_type ftype,
                             Strategy const& strategy)
    {
        double const radius = strategy.radius();
        double expected_distance, expected_comparable_distance;

        if (ftype == pp)
        {
            expected_distance = distance_pp(feature1, feature2, radius);
            expected_comparable_distance
                = comparable_distance_pp(feature1, feature2);
        }
        else
        {
            expected_distance = distance_ps(feature1, feature2, radius);
            expected_comparable_distance
                = comparable_distance_ps(feature1, feature2);
        }

        apply(case_id, wkt1, wkt2,
              expected_distance, expected_comparable_distance,
              strategy);
    }
};

template <typename T, typename U>
T to_comparable(T const& value, U const& radius)
{
    T x = sin(value / (radius * 2.0));
    return x * x;
}

//===========================================================================

template <typename Strategy>
void test_distance_point_box(Strategy const& strategy)
{
#ifdef BOOST_GEOMETRY_TEST_DEBUG
    std::cout << std::endl;
    std::cout << "point/box distance tests" << std::endl;
#endif
    typedef test_distances<point_type, box_type> tester;

    double const radius = strategy.radius();
    double const d2r = bg::math::d2r<double>();

    // Cases for relative location of a point wrt to a box
    //
    //           |         |
    //           |    3    |
    //           |         |
    //           +---------+
    //           |         |
    //       1   |    5    |   2
    //           |         |
    //           +---------+
    //           |         |
    //           |    4    |
    //           |         |
    //
    // and also the following cases
    //
    //           |         |
    //           A         B
    //           |         |
    //           +----C----+
    //           |         |
    //           D         E
    //           |         |
    //           +----F----+
    //           |         |
    //           G         H
    //           |         |
    //
    // and finally we have the corners
    //
    //           |         |
    //           |         |
    //           |         |
    //           a---------b
    //           |         |
    //           |         |
    //           |         |
    //           c---------d
    //           |         |
    //           |         |
    //           |         |
    //
    // for each relative position we also have to test the shifted point
    // (this is due to the fact that boxes have longitudes in the
    // range [-180, 540)

    std::string const box1 = "BOX(10 10,20 20)";

    // case 1
    tester::apply("pb1-1a", "POINT(5 25)", box1,
                  "POINT(5 25)", "POINT(10 20)", pp,
                  strategy);

    // case 1
    tester::apply("pb1-1b", "POINT(3 12)", box1,
                  "POINT(3 12)", "SEGMENT(10 10,10 20)", ps,
                  strategy);

    // case 1
    tester::apply("pb1-1c", "POINT(3 17)", box1,
                  "POINT(3 17)", "SEGMENT(10 10,10 20)", ps,
                  strategy);

    // case 1
    tester::apply("pb1-1d", "POINT(5 4)", box1,
                  "POINT(5 4)", "POINT(10 10)", pp,
                  strategy);

    // case 1
    tester::apply("pb1-1e", "POINT(-100 20)", box1,
                  "POINT(-100 20)", "POINT(10 20)", pp,
                  strategy);

    // case 1
    tester::apply("pb1-1g", "POINT(-100 10)", box1,
                  "POINT(-100 10)", "SEGMENT(10 10,10 20)", ps,
                  strategy);

    // case 2
    tester::apply("pb1-2a", "POINT(31 25)", box1,
                  "POINT(31 25)", "POINT(20 20)", pp,
                  strategy);

    // case 2
    tester::apply("pb1-2b", "POINT(23 17)", box1,
                  "POINT(23 17)", "SEGMENT(20 10,20 20)", ps,
                  strategy);

    // case 2
    tester::apply("pb1-2c", "POINT(29 3)", box1,
                  "POINT(29 3)", "POINT(20 10)", pp,
                  strategy);

    // case 2
    tester::apply("pb1-2d", "POINT(131 65)", box1,
                  "POINT(131 65)", "POINT(20 20)", pp,
                  strategy);

    // case 2
    tester::apply("pb1-2e", "POINT(110 10)", box1,
                  "POINT(110 10)", "SEGMENT(20 10,20 20)", ps,
                  strategy);

    // case 2
    tester::apply("pb1-2f", "POINT(150 20)", box1,
                  "POINT(150 20)", "POINT(20 20)", pp,
                  strategy);

    // case 3
    tester::apply("pb1-3a", "POINT(11 25)", box1,
                  5.0 * d2r * radius,
                  to_comparable(5.0 * d2r * radius, radius),
                  strategy);

    // case 3
    tester::apply("pb1-3b", "POINT(15 25)", box1,
                  5.0 * d2r * radius,
                  to_comparable(5.0 * d2r * radius, radius),
                  strategy);

    // case 3
    tester::apply("pb1-3c", "POINT(18 25)", box1,
                  5.0 * d2r * radius,
                  to_comparable(5.0 * d2r * radius, radius),
                  strategy);

    // case 4
    tester::apply("pb1-4a", "POINT(13 4)", box1,
                  6.0 * radius * d2r,
                  to_comparable(6.0 * radius * d2r, radius),
                  strategy);

    // case 4
    tester::apply("pb1-4b", "POINT(19 4)", box1,
                  6.0 * radius * d2r,
                  to_comparable(6.0 * radius * d2r, radius),
                  strategy);

    // case 5
    tester::apply("pb1-5", "POINT(15 14)", box1, 0, 0, strategy);

    // case A
    tester::apply("pb1-A", "POINT(10 28)", box1,
                  8.0 * d2r * radius,
                  to_comparable(8.0 * d2r * radius, radius),
                  strategy);

    // case B
    tester::apply("pb1-B", "POINT(20 28)", box1,
                  8.0 * d2r * radius,
                  to_comparable(8.0 * d2r * radius, radius),
                  strategy);

    // case C
    tester::apply("pb1-C", "POINT(14 20)", box1, 0, 0, strategy);

    // case D
    tester::apply("pb1-D", "POINT(10 17)", box1, 0, 0, strategy);

    // case E
    tester::apply("pb1-E", "POINT(20 11)", box1, 0, 0, strategy);

    // case F
    tester::apply("pb1-F", "POINT(19 10)", box1, 0, 0, strategy);

    // case G
    tester::apply("pb1-G", "POINT(10 -40)", box1,
                  50.0 * d2r * radius,
                  to_comparable(50.0 * d2r * radius, radius),
                  strategy);

    // case H
    tester::apply("pb1-H",
                  "POINT(20 -50)", box1,
                  60.0 * d2r * radius,
                  to_comparable(60.0 * d2r * radius, radius),
                  strategy);

    // case a
    tester::apply("pb1-a", "POINT(10 20)", box1, 0, 0, strategy);
    // case b
    tester::apply("pb1-b", "POINT(20 20)", box1, 0, 0, strategy);
    // case c
    tester::apply("pb1-c", "POINT(10 10)", box1, 0, 0, strategy);
    // case d
    tester::apply("pb1-d", "POINT(20 10)", box1, 0, 0, strategy);

    std::string const box2 = "BOX(170 -60,400 80)";

    // case 1 - point is closer to western meridian
    tester::apply("pb2-1a", "POINT(160 0)", box2,
                  "POINT(160 0)", "SEGMENT(170 -60,170 80)", ps,
                  strategy);

    // case 1 - point is closer to eastern meridian
    tester::apply("pb2-1b", "POINT(50 0)", box2,
                  "POINT(50 0)", "SEGMENT(40 -60,40 80)", ps,
                  strategy);

    // case 3 - equivalent point POINT(390 85) is above the box
    tester::apply("pb2-3", "POINT(30 85)", box2,
                  5.0 * d2r * radius,
                  to_comparable(5.0 * d2r * radius, radius),
                  strategy);

    // case 4 - equivalent point POINT(390 -75) is below the box
    tester::apply("pb2-4", "POINT(30 -75)", box2,
                  15.0 * d2r * radius,
                  to_comparable(15.0 * d2r * radius, radius),
                  strategy);

    // case 5 - equivalent point POINT(390 0) is inside box
    tester::apply("pb2-5", "POINT(30 0)", box2, 0, 0, strategy);

    std::string const box3 = "BOX(-150 -50,-40 70)";

    // case 1 - point is closer to western meridian
    tester::apply("pb3-1a", "POINT(-170 10)", box3,
                  "POINT(-170 10)", "SEGMENT(-150 -50,-150 70)", ps,
                  strategy);

    // case 2 - point is closer to eastern meridian
    tester::apply("pb3-2a", "POINT(5 10)", box3,
                  "POINT(5 10)", "SEGMENT(-40 -50,-40 70)", ps,
                  strategy);

    // case 2 - point is closer to western meridian
    tester::apply("pb3-2a", "POINT(160 10)", box3,
                  "POINT(160 10)", "SEGMENT(-150 -50,-150 70)", ps,
                  strategy);

    // case 2 - point is at equal distance from eastern and western meridian
    tester::apply("pb3-2c1", "POINT(85 20)", box3,
                  "POINT(85 20)", "SEGMENT(-150 -50,-150 70)", ps,
                  strategy);

    // case 2 - point is at equal distance from eastern and western meridian
    tester::apply("pb3-2c2", "POINT(85 20)", box3,
                  "POINT(85 20)", "SEGMENT(-40 -50,-40 70)", ps,
                  strategy);

    // box that is symmetric wrt the prime meridian
    std::string const box4 = "BOX(-75 -45,75 65)";

    // case 1 - point is closer to western meridian
    tester::apply("pb4-1a", "POINT(-100 10)", box4,
                  "POINT(-100 10)", "SEGMENT(-75 -45,-75 65)", ps,
                  strategy);

    // case 2 - point is closer to eastern meridian
    tester::apply("pb4-2a", "POINT(90 15)", box4,
                  "POINT(90 15)", "SEGMENT(75 -45,75 65)", ps,
                  strategy);

    // case 2 - point is at equal distance from eastern and western meridian
    tester::apply("pb4-2c1", "POINT(-180 20)", box4,
                  "POINT(-180 20)", "SEGMENT(-75 -45,-75 65)", ps,
                  strategy);

    // case 2 - point is at equal distance from eastern and western meridian
    tester::apply("pb4-2c2", "POINT(-180 20)", box4,
                  "POINT(-180 20)", "SEGMENT(75 -45,75 65)", ps,
                  strategy);
}

BOOST_AUTO_TEST_CASE( test_point_box )
{
    test_distance_point_box(point_box_strategy());
    test_distance_point_box(point_box_strategy(earth_radius_km));
    test_distance_point_box(point_box_strategy(earth_radius_miles));

    test_distance_point_box(comparable_point_box_strategy());
    test_distance_point_box(comparable_point_box_strategy(earth_radius_km));
    test_distance_point_box(comparable_point_box_strategy(earth_radius_miles));
}


#include <iostream>
#include <cassert>
#include <limits>

#include <boost/safe_numerics/utility.hpp>
#include <boost/safe_numerics/safe_integer_range.hpp>

template<typename T>
void display_log(T Max){
    std::cout
        << "ilog2(" << Max << ") = "
        << boost::safe_numerics::utility::ilog2(Max) << std::endl;
}

bool test_significant_bits(){
    using namespace boost::safe_numerics;
    assert(utility::significant_bits(127u) == 7); // 7 bits
    assert(utility::significant_bits(127u) == 7); // 7 bits
    assert(utility::significant_bits(128u) == 8); // 8 bits
    assert(utility::significant_bits(129u) == 8); // 8 bits
    assert(utility::significant_bits(255u) == 8); // 8 bits
    assert(utility::significant_bits(256u) == 9); // 9 bits
    return true;
}

bool test1(){
    using namespace boost::safe_numerics;

    safe_signed_range<-128, 127> s1(1);
    safe_signed_range<-256, 254> s2(2);

    using t2 = safe_unsigned_range<0u, 1000u>;
    static_assert(
        std::numeric_limits<t2>::is_signed == false,
        "this range should be unsigned"
    );

    return true;
}

#include <boost/safe_numerics/automatic.hpp>

template <
    std::intmax_t Min,
    std::intmax_t Max
>
using safe_t = boost::safe_numerics::safe_signed_range<
    Min,
    Max,
    boost::safe_numerics::automatic,
    boost::safe_numerics::default_exception_policy
>;

bool test2(){
    std::cout << "test1" << std::endl;
    try{
        const safe_t<-64, 63> x(1);
        safe_t<-64, 63> y;
        y = 2;
        std::cout << "x = " << x << std::endl;
        std::cout << "y = " << y << std::endl;
        auto z = x + y;
        std::cout << "x + y = ["
            << std::numeric_limits<decltype(z)>::min() << ","
            << std::numeric_limits<decltype(z)>::max() << "] = "
            << z << std::endl;

        auto z2 = x - y;
        std::cout << "x - y = ["
            << std::numeric_limits<decltype(z2)>::min() << ","
            << std::numeric_limits<decltype(z2)>::max() << "] = "
            << z2 << std::endl;

            short int yi, zi;
            yi = y;
            zi = x + yi;
    }
    catch(std::exception e){
        // none of the above should trap. Mark failure if they do
        std::cout << e.what() << std::endl;
        return false;
    }
    return true;
}

int main(){
    //using namespace boost::safe_numerics;
    //safe_signed_literal2<100> one_hundred;
    //one_hundred = 200;

    bool rval =
        test_significant_bits() &&
        test1() &&
        test2() /* &&
        test3() &&
        test4()
        */
    ;
    std::cout << (rval ? "success!" : "failure") << std::endl;
    return rval ? EXIT_SUCCESS : EXIT_FAILURE;
}


//  (C) Copyright Jeremy Siek 2004
//  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)

/*
  Sample output:

  After initializing properties for G1:
          Global and local properties for vertex G0[C]...
                  G0[C]= A1
                  G1[A1]= A1
          Global and local properties for vertex G0[E]...
                  G0[E]= B1
                  G1[B1]= B1
          Global and local properties for vertex G0[F]...
                  G0[F]= C1
                  G1[C1]= C1

  After initializing properties for G2:
          Global and local properties for vertex G0[A]
                  G0[A]= A2
                  G2[A2]= A2
          Global and local properties for vertex G0[C]...
                  G0[C]= B2
                  G1[A1]= B2
                  G2[B2]= B2

 */

#include <boost/config.hpp>
#include <iostream>
#include <boost/graph/subgraph.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_utility.hpp>

int main(int,char*[])
{
  using namespace boost;
  //typedef adjacency_list_traits<vecS, vecS, directedS> Traits;// Does nothing?
  typedef property<   vertex_color_t, int,
    property< vertex_name_t, std::string > > VertexProperty;

  typedef subgraph< adjacency_list<  vecS, vecS, directedS,
    VertexProperty, property<edge_index_t, int> > > Graph;

  const int N = 6;
  Graph G0(N);
  enum { A, B, C, D, E, F};     // for conveniently refering to vertices in G0

  property_map<Graph, vertex_name_t>::type name = get(vertex_name_t(), G0);
  name[A] = "A";
  name[B] = "B";
  name[C] = "C";
  name[D] = "D";
  name[E] = "E";
  name[F] = "F";

  Graph& G1 = G0.create_subgraph();
  enum { A1, B1, C1 };          // for conveniently refering to vertices in G1

  add_vertex(C, G1); // global vertex C becomes local A1 for G1
  add_vertex(E, G1); // global vertex E becomes local B1 for G1
  add_vertex(F, G1); // global vertex F becomes local C1 for G1

  property_map<Graph, vertex_name_t>::type name1 = get(vertex_name_t(), G1);
  name1[A1] = "A1";

  std::cout << std::endl << "After initializing properties for G1:" << std::endl;
  std::cout << "  Global and local properties for vertex G0[C]..." << std::endl;
  std::cout << "    G0[C]= " << boost::get(vertex_name, G0, C) << std::endl;// prints: "G0[C]= C"
  std::cout << "    G1[A1]= " << boost::get(vertex_name, G1, A1) << std::endl;// prints: "G1[A1]= A1"

  name1[B1] = "B1";

  std::cout << "  Global and local properties for vertex G0[E]..." << std::endl;
  std::cout << "    G0[E]= " << boost::get(vertex_name, G0, E) << std::endl;// prints: "G0[E]= E"
  std::cout << "    G1[B1]= " << boost::get(vertex_name, G1, B1) << std::endl;// prints: "G1[B1]= B1"

  name1[C1] = "C1";

  std::cout << "  Global and local properties for vertex G0[F]..." << std::endl;
  std::cout << "    G0[F]= " << boost::get(vertex_name, G0, F) << std::endl;// prints: "G0[F]= F"
  std::cout << "    G1[C1]= " << boost::get(vertex_name, G1, C1) << std::endl;// prints: "G1[C1]= C1"

  Graph& G2 = G0.create_subgraph();
  enum { A2, B2 };              // for conveniently refering to vertices in G2

  add_vertex(A, G2); // global vertex A becomes local A2 for G2
  add_vertex(C, G2); // global vertex C becomes local B2 for G2

  property_map<Graph, vertex_name_t>::type name2 = get(vertex_name_t(), G2);
  name2[A2] = "A2";

  std::cout << std::endl << std::endl << "After initializing properties for G2:" << std::endl;
  std::cout << "  Global and local properties for vertex G0[A]" << std::endl;
  std::cout << "    G0[A]= " << boost::get(vertex_name, G0, A) << std::endl;// prints: "G0[A]= A"
  std::cout << "    G2[A2]= " << boost::get(vertex_name, G2, A2) << std::endl;// prints: "G2[A2]= A2"

  name2[B2] = "B2";

  std::cout << "  Global and local properties for vertex G0[C]..." << std::endl;
  std::cout << "    G0[C]= " << boost::get(vertex_name, G0, C) << std::endl;// prints: "G0[C]= C"
  std::cout << "    G1[A1]= " << boost::get(vertex_name, G1, A1) << std::endl;// prints: "G1[A1]= A1"
  std::cout << "    G2[B2]= " << boost::get(vertex_name, G2, B2) << std::endl;// prints: "G2[B2]= B2"

  return 0;
}