//          Copyright Carl Philipp Reh 2006 - 2019.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#ifndef SGE_CG_PARAMETER_VECTOR_DETAIL_SET_HPP_INCLUDED
#define SGE_CG_PARAMETER_VECTOR_DETAIL_SET_HPP_INCLUDED

#include <sge/cg/parameter/object_fwd.hpp>
#include <sge/cg/parameter/detail/pp_types.hpp>
#include <sge/cg/parameter/vector/detail/set_double.hpp>
#include <sge/cg/parameter/vector/detail/set_float.hpp>
#include <sge/cg/parameter/vector/detail/set_int.hpp>
#include <fcppt/math/size_type.hpp>
#include <fcppt/math/vector/object_impl.hpp>
#include <fcppt/config/external_begin.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <fcppt/config/external_end.hpp>

namespace sge::cg::parameter::vector::detail
{

#define SGE_CG_PARAMETER_VECTOR_DETAIL_SET(seq, _, type) \
  template <fcppt::math::size_type N, typename S> \
  void set( \
      sge::cg::parameter::object const &_parameter, \
      fcppt::math::vector::object<type, N, S> const &_vector) \
  { \
    sge::cg::parameter::vector::detail::BOOST_PP_CAT(set_, type)( \
        _parameter, _vector.storage().data(), N); \
  }

BOOST_PP_SEQ_FOR_EACH(SGE_CG_PARAMETER_VECTOR_DETAIL_SET, _, SGE_CG_PARAMETER_DETAIL_PP_TYPES)

}

#endif


/**TODO:  Add copyright*/

#define BOOST_TEST_MODULE OpToTensorOp test suite
#include <boost/test/included/unit_test.hpp>
#include <SmartPeak/ml/OpToTensorOp.h>

using namespace SmartPeak;
using namespace std;

BOOST_AUTO_TEST_SUITE(OpToTensorOp1)

BOOST_AUTO_TEST_CASE(constructorActivationOpToActivationTensorOp)
{
	ActivationOpToActivationTensorOp<float, Eigen::DefaultDevice>* ptr = nullptr;
	ActivationOpToActivationTensorOp<float, Eigen::DefaultDevice>* nullPointer = nullptr;
	ptr = new ActivationOpToActivationTensorOp<float, Eigen::DefaultDevice>();
  BOOST_CHECK_NE(ptr, nullPointer);
}

BOOST_AUTO_TEST_CASE(destructorActivationOpToActivationTensorOp)
{
	ActivationOpToActivationTensorOp<float, Eigen::DefaultDevice>* ptr = nullptr;
	ptr = new ActivationOpToActivationTensorOp<float, Eigen::DefaultDevice>();
  delete ptr;
}

BOOST_AUTO_TEST_CASE(convertOpToTensorOpActivationOpToActivationTensorOp)
{
	ActivationOpToActivationTensorOp<float, Eigen::DefaultDevice> op_to_tensor_op;
	std::shared_ptr<ActivationOp<float>> op_class;
	std::shared_ptr<ActivationTensorOp<float, Eigen::DefaultDevice>> op_tensor_class;

	op_class = std::make_shared<ReLUOp<float>>(ReLUOp<float>(1, 2, 3));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "ReLUTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);

	op_class = std::make_shared<ReLUGradOp<float>>(ReLUGradOp<float>(1.0f, 2.0f, 3.0f));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "ReLUGradTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);

	op_class = std::make_shared<ELUOp<float>>(ELUOp<float>(1.0f, 2.0f, 3.0f, 4.0f));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "ELUTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);

	op_class = std::make_shared<ELUGradOp<float>>(ELUGradOp<float>(1.0f, 2.0f, 3.0f, 4.0f));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "ELUGradTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);

	op_class = std::make_shared<SigmoidOp<float>>(SigmoidOp<float>(1.0f, 2.0f, 3.0f));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "SigmoidTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);

	op_class = std::make_shared<SigmoidGradOp<float>>(SigmoidGradOp<float>(1.0f, 2.0f, 3.0f));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "SigmoidGradTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);

	op_class = std::make_shared<TanHOp<float>>(TanHOp<float>(1.0f, 2.0f, 3.0f));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "TanHTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);

	op_class = std::make_shared<TanHGradOp<float>>(TanHGradOp<float>(1.0f, 2.0f, 3.0f));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "TanHGradTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);

	op_class = std::make_shared<ReTanHOp<float>>(ReTanHOp<float>(1.0f, 2.0f, 3.0f));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "ReTanHTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);

	op_class = std::make_shared<ReTanHGradOp<float>>(ReTanHGradOp<float>(1.0f, 2.0f, 3.0f));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "ReTanHGradTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);

	op_class = std::make_shared<LinearOp<float>>(LinearOp<float>(1.0f, 2.0f, 3.0f));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "LinearTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);

	op_class = std::make_shared<LinearGradOp<float>>(LinearGradOp<float>(1.0f, 2.0f, 3.0f));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "LinearGradTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);

	op_class = std::make_shared<InverseOp<float>>(InverseOp<float>(1.0f, 2.0f, 3.0f));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "InverseTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);

	op_class = std::make_shared<InverseGradOp<float>>(InverseGradOp<float>(1.0f, 2.0f, 3.0f));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "InverseGradTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);

	op_class = std::make_shared<ExponentialOp<float>>(ExponentialOp<float>(1.0f, 2.0f, 3.0f));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "ExponentialTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);

	op_class = std::make_shared<ExponentialGradOp<float>>(ExponentialGradOp<float>(1.0f, 2.0f, 3.0f));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "ExponentialGradTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);

	op_class = std::make_shared<LogOp<float>>(LogOp<float>(1.0f, 2.0f, 3.0f));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "LogTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);

	op_class = std::make_shared<LogGradOp<float>>(LogGradOp<float>(1.0f, 2.0f, 3.0f));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "LogGradTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);

	op_class = std::make_shared<PowOp<float>>(PowOp<float>(1.0f,2.0f,3.0f,2.0f));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "PowTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);

	op_class = std::make_shared<PowGradOp<float>>(PowGradOp<float>(1.0f, 2.0f, 3.0f, 2.0f));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "PowGradTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);

	op_class = std::make_shared<LeakyReLUOp<float>>(LeakyReLUOp<float>(1.0f, 2.0f, 3.0f, 4.0f));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "LeakyReLUTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);

	op_class = std::make_shared<LeakyReLUGradOp<float>>(LeakyReLUGradOp<float>(1.0f, 2.0f, 3.0f, 4.0f));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "LeakyReLUGradTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);

	op_class = std::make_shared<SinOp<float>>(SinOp<float>(1.0f, 2.0f, 3.0f));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "SinTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);

	op_class = std::make_shared<SinGradOp<float>>(SinGradOp<float>(1.0f, 2.0f, 3.0f));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "SinGradTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);

	op_class = std::make_shared<CosOp<float>>(CosOp<float>(1.0f, 2.0f, 3.0f));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "CosTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);

	op_class = std::make_shared<CosGradOp<float>>(CosGradOp<float>(1.0f, 2.0f, 3.0f));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "CosGradTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);

  op_class = std::make_shared<BatchNormOp<float>>(BatchNormOp<float>(1.0f, 2.0f, 3.0f));
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "BatchNormTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);

  op_class = std::make_shared<BatchNormGradOp<float>>(BatchNormGradOp<float>(1.0f, 2.0f, 3.0f));
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "BatchNormGradTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getEps(), 1);
  BOOST_CHECK_EQUAL(op_tensor_class->getMin(), 2);
  BOOST_CHECK_EQUAL(op_tensor_class->getMax(), 3);
}

BOOST_AUTO_TEST_CASE(getTensorParamsActivationOpToActivationTensorOp)
{
	ActivationOpToActivationTensorOp<float, Eigen::DefaultDevice> op_to_tensor_op;
	std::shared_ptr<ActivationOp<float>> op_class = std::make_shared<ReLUOp<float>>(ReLUOp<float>());
	std::vector<float> params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<ReLUGradOp<float>>(ReLUGradOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<ELUOp<float>>(ELUOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<ELUGradOp<float>>(ELUGradOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<SigmoidOp<float>>(SigmoidOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<SigmoidGradOp<float>>(SigmoidGradOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<TanHOp<float>>(TanHOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<TanHGradOp<float>>(TanHGradOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<ReTanHOp<float>>(ReTanHOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<ReTanHGradOp<float>>(ReTanHGradOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<LinearOp<float>>(LinearOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<LinearGradOp<float>>(LinearGradOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<InverseOp<float>>(InverseOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<InverseGradOp<float>>(InverseGradOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<ExponentialOp<float>>(ExponentialOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<ExponentialGradOp<float>>(ExponentialGradOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<LogOp<float>>(LogOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<LogGradOp<float>>(LogGradOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<PowOp<float>>(PowOp<float>(2));
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<PowGradOp<float>>(PowGradOp<float>(2));
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<LeakyReLUOp<float>>(LeakyReLUOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<LeakyReLUGradOp<float>>(LeakyReLUGradOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<SinOp<float>>(SinOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<SinGradOp<float>>(SinGradOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<CosOp<float>>(CosOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<CosGradOp<float>>(CosGradOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<BatchNormOp<float>>(BatchNormOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<BatchNormGradOp<float>>(BatchNormGradOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);
}

BOOST_AUTO_TEST_CASE(constructorSolverOpToSolverTensorOp)
{
	SolverOpToSolverTensorOp<float, Eigen::DefaultDevice>* ptr = nullptr;
	SolverOpToSolverTensorOp<float, Eigen::DefaultDevice>* nullPointer = nullptr;
	ptr = new SolverOpToSolverTensorOp<float, Eigen::DefaultDevice>();
	BOOST_CHECK_NE(ptr, nullPointer);
}

BOOST_AUTO_TEST_CASE(destructorSolverOpToSolverTensorOp)
{
	SolverOpToSolverTensorOp<float, Eigen::DefaultDevice>* ptr = nullptr;
	ptr = new SolverOpToSolverTensorOp<float, Eigen::DefaultDevice>();
	delete ptr;
}

BOOST_AUTO_TEST_CASE(convertOpToTensorOpSolverOpToSolverTensorOp)
{
	SolverOpToSolverTensorOp<float, Eigen::DefaultDevice> op_to_tensor_op;
	std::shared_ptr<SolverOp<float>> op_class;
	std::shared_ptr<SolverTensorOp<float, Eigen::DefaultDevice>> op_tensor_class;

	op_class = std::make_shared<SGDOp<float>>(SGDOp<float>(0.1, 0.9, 10.0, 1.0, 0.55));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "SGDTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getGradientThreshold(), 10);
  BOOST_CHECK_EQUAL(op_tensor_class->getGradientNoiseSigma(), 1);
  BOOST_CHECK_CLOSE(op_tensor_class->getGradientNoiseGamma(), 0.55, 1e-4);

  op_class = std::make_shared<SSDOp<float>>(SSDOp<float>(0.1, 0.9, 10.0, 1.0, 0.55));
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "SSDTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getGradientThreshold(), 10);
  BOOST_CHECK_EQUAL(op_tensor_class->getGradientNoiseSigma(), 1);
  BOOST_CHECK_CLOSE(op_tensor_class->getGradientNoiseGamma(), 0.55, 1e-4);

	op_class = std::make_shared<AdamOp<float>>(AdamOp<float>(0.001, 0.9, 0.999, 1e-8, 10.0, 1.0, 0.55));
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "AdamTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getGradientThreshold(), 10);
  BOOST_CHECK_EQUAL(op_tensor_class->getGradientNoiseSigma(), 1);
  BOOST_CHECK_CLOSE(op_tensor_class->getGradientNoiseGamma(), 0.55, 1e-4);

  op_class = std::make_shared<SVAGOp<float>>(SVAGOp<float>(0.001, 0.9,10.0, 1.0, 0.55));
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "SVAGTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getGradientThreshold(), 10);
  BOOST_CHECK_EQUAL(op_tensor_class->getGradientNoiseSigma(), 1);
  BOOST_CHECK_CLOSE(op_tensor_class->getGradientNoiseGamma(), 0.55, 1e-4);

	op_class = std::make_shared<DummySolverOp<float>>(DummySolverOp<float>());
  op_class->setGradientThreshold(10);
  op_class->setGradientNoiseSigma(1);
  op_class->setGradientNoiseGamma(0.55);
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "DummySolverTensorOp");
  BOOST_CHECK_EQUAL(op_tensor_class->getGradientThreshold(), 10);
  BOOST_CHECK_EQUAL(op_tensor_class->getGradientNoiseSigma(), 1);
  BOOST_CHECK_CLOSE(op_tensor_class->getGradientNoiseGamma(), 0.55, 1e-4);
}

BOOST_AUTO_TEST_CASE(getTensorParamsSolverOpToSolverTensorOp)
{
	SolverOpToSolverTensorOp<float, Eigen::DefaultDevice> op_to_tensor_op;

	std::shared_ptr<SolverOp<float>> op_class = std::make_shared<SGDOp<float>>(SGDOp<float>(1, 2));
	std::vector<float> params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 3);
	BOOST_CHECK_EQUAL(params[0], 1); BOOST_CHECK_EQUAL(params[1], 2); BOOST_CHECK_EQUAL(params[2], 0);

  op_class = std::make_shared<SSDOp<float>>(SSDOp<float>(1, 2));
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 3);
  BOOST_CHECK_EQUAL(params[0], 1); BOOST_CHECK_EQUAL(params[1], 2); BOOST_CHECK_EQUAL(params[2], 0);

	op_class = std::make_shared<AdamOp<float>>(AdamOp<float>(1, 2, 3, 4));
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 6);
	BOOST_CHECK_EQUAL(params[0], 1); BOOST_CHECK_EQUAL(params[1], 2); BOOST_CHECK_EQUAL(params[2], 3);
	BOOST_CHECK_EQUAL(params[3], 4); BOOST_CHECK_EQUAL(params[4], 0); BOOST_CHECK_EQUAL(params[5], 0);

  op_class = std::make_shared<SVAGOp<float>>(SVAGOp<float>(1, 2));
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 4);
  BOOST_CHECK_EQUAL(params[0], 1); BOOST_CHECK_EQUAL(params[1], 2); BOOST_CHECK_EQUAL(params[2], 0);
  BOOST_CHECK_EQUAL(params[3], 0);

	op_class = std::make_shared<DummySolverOp<float>>(DummySolverOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);
}

BOOST_AUTO_TEST_CASE(constructorIntegrationOpToIntegrationTensorOp)
{
	IntegrationOpToIntegrationTensorOp<float, Eigen::DefaultDevice>* ptr = nullptr;
	IntegrationOpToIntegrationTensorOp<float, Eigen::DefaultDevice>* nullPointer = nullptr;
	ptr = new IntegrationOpToIntegrationTensorOp<float, Eigen::DefaultDevice>();
	BOOST_CHECK_NE(ptr, nullPointer);
}

BOOST_AUTO_TEST_CASE(destructorIntegrationOpToIntegrationTensorOp)
{
	IntegrationOpToIntegrationTensorOp<float, Eigen::DefaultDevice>* ptr = nullptr;
	ptr = new IntegrationOpToIntegrationTensorOp<float, Eigen::DefaultDevice>();
	delete ptr;
}

BOOST_AUTO_TEST_CASE(convertOpToTensorOpIntegrationOpToIntegrationTensorOp)
{
	IntegrationOpToIntegrationTensorOp<float, Eigen::DefaultDevice> op_to_tensor_op;
	std::shared_ptr<IntegrationOp<float>> op_class;
	std::shared_ptr<IntegrationTensorOp<float, Eigen::DefaultDevice>> op_tensor_class;

	op_class = std::make_shared<SumOp<float>>(SumOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "SumTensorOp");

	op_class = std::make_shared<ProdOp<float>>(ProdOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "ProdTensorOp");

  op_class = std::make_shared<ProdSCOp<float>>(ProdSCOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "ProdSCTensorOp");

	op_class = std::make_shared<MaxOp<float>>(MaxOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "MaxTensorOp");

  op_class = std::make_shared<MinOp<float>>(MinOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "MinTensorOp");

	//op_class = std::make_shared<VarOp<float>>(VarOp<float>()); //TODO...
	//op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	//BOOST_CHECK_EQUAL(op_tensor_class->getName(), "VarTensorOp");

	op_class = std::make_shared<CountOp<float>>(CountOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "CountTensorOp");

	op_class = std::make_shared<VarModOp<float>>(VarModOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "VarModTensorOp");
}

BOOST_AUTO_TEST_CASE(getTensorParamsIntegrationOpToIntegrationTensorOp)
{
	IntegrationOpToIntegrationTensorOp<float, Eigen::DefaultDevice> op_to_tensor_op;

	std::shared_ptr<IntegrationOp<float>> op_class = std::make_shared<SumOp<float>>(SumOp<float>());
	std::vector<float> params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<ProdOp<float>>(ProdOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<ProdSCOp<float>>(ProdSCOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<MaxOp<float>>(MaxOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<MinOp<float>>(MinOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<MeanOp<float>>(MeanOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	//op_class = std::make_shared<VarOp<float>>(VarOp<float>());
	//params = op_to_tensor_op.getTensorParams(op_class);
	//BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<CountOp<float>>(CountOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<VarModOp<float>>(VarModOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);
}

BOOST_AUTO_TEST_CASE(constructorIntegrationErrorOpToIntegrationErrorTensorOp)
{
	IntegrationErrorOpToIntegrationErrorTensorOp<float, Eigen::DefaultDevice>* ptr = nullptr;
	IntegrationErrorOpToIntegrationErrorTensorOp<float, Eigen::DefaultDevice>* nullPointer = nullptr;
	ptr = new IntegrationErrorOpToIntegrationErrorTensorOp<float, Eigen::DefaultDevice>();
	BOOST_CHECK_NE(ptr, nullPointer);
}

BOOST_AUTO_TEST_CASE(destructorIntegrationErrorOpToIntegrationErrorTensorOp)
{
	IntegrationErrorOpToIntegrationErrorTensorOp<float, Eigen::DefaultDevice>* ptr = nullptr;
	ptr = new IntegrationErrorOpToIntegrationErrorTensorOp<float, Eigen::DefaultDevice>();
	delete ptr;
}

BOOST_AUTO_TEST_CASE(convertOpToTensorOpIntegrationErrorOpToIntegrationErrorTensorOp)
{
	IntegrationErrorOpToIntegrationErrorTensorOp<float, Eigen::DefaultDevice> op_to_tensor_op;
	std::shared_ptr<IntegrationErrorOp<float>> op_class;
	std::shared_ptr<IntegrationErrorTensorOp<float, Eigen::DefaultDevice>> op_tensor_class;

	op_class = std::make_shared<SumErrorOp<float>>(SumErrorOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "SumErrorTensorOp");

	op_class = std::make_shared<ProdErrorOp<float>>(ProdErrorOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "ProdErrorTensorOp");

	op_class = std::make_shared<MaxErrorOp<float>>(MaxErrorOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "MaxErrorTensorOp");

  op_class = std::make_shared<MinErrorOp<float>>(MinErrorOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "MinErrorTensorOp");

	op_class = std::make_shared<MeanErrorOp<float>>(MeanErrorOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "MeanErrorTensorOp");

	//op_class = std::make_shared<VarErrorOp<float>>(VarErrorOp<float>()); //TODO...
	//op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	//BOOST_CHECK_EQUAL(op_tensor_class->getName(), "VarErrorTensorOp");

	op_class = std::make_shared<CountErrorOp<float>>(CountErrorOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "CountErrorTensorOp");

	op_class = std::make_shared<VarModErrorOp<float>>(VarModErrorOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "VarModErrorTensorOp");
}

BOOST_AUTO_TEST_CASE(getTensorParamsIntegrationErrorOpToIntegrationErrorTensorOp)
{
	IntegrationErrorOpToIntegrationErrorTensorOp<float, Eigen::DefaultDevice> op_to_tensor_op;

	std::shared_ptr<IntegrationErrorOp<float>> op_class = std::make_shared<SumErrorOp<float>>(SumErrorOp<float>());
	std::vector<float> params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<ProdErrorOp<float>>(ProdErrorOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<MaxErrorOp<float>>(MaxErrorOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<MinErrorOp<float>>(MinErrorOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<MeanErrorOp<float>>(MeanErrorOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	//op_class = std::make_shared<VarErrorOp<float>>(VarErrorOp<float>());
	//params = op_to_tensor_op.getTensorParams(op_class);
	//BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<CountErrorOp<float>>(CountErrorOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<VarModErrorOp<float>>(VarModErrorOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);
}

BOOST_AUTO_TEST_CASE(constructorIntegrationWeightGradOpToIntegrationWeightGradTensorOp)
{
	IntegrationWeightGradOpToIntegrationWeightGradTensorOp<float, Eigen::DefaultDevice>* ptr = nullptr;
	IntegrationWeightGradOpToIntegrationWeightGradTensorOp<float, Eigen::DefaultDevice>* nullPointer = nullptr;
	ptr = new IntegrationWeightGradOpToIntegrationWeightGradTensorOp<float, Eigen::DefaultDevice>();
	BOOST_CHECK_NE(ptr, nullPointer);
}

BOOST_AUTO_TEST_CASE(destructorIntegrationWeightGradOpToIntegrationWeightGradTensorOp)
{
	IntegrationWeightGradOpToIntegrationWeightGradTensorOp<float, Eigen::DefaultDevice>* ptr = nullptr;
	ptr = new IntegrationWeightGradOpToIntegrationWeightGradTensorOp<float, Eigen::DefaultDevice>();
	delete ptr;
}

BOOST_AUTO_TEST_CASE(convertOpToTensorOpIntegrationWeightGradOpToIntegrationWeightGradTensorOp)
{
	IntegrationWeightGradOpToIntegrationWeightGradTensorOp<float, Eigen::DefaultDevice> op_to_tensor_op;
	std::shared_ptr<IntegrationWeightGradOp<float>> op_class;
	std::shared_ptr<IntegrationWeightGradTensorOp<float, Eigen::DefaultDevice>> op_tensor_class;

	op_class = std::make_shared<SumWeightGradOp<float>>(SumWeightGradOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "SumWeightGradTensorOp");

	op_class = std::make_shared<ProdWeightGradOp<float>>(ProdWeightGradOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "ProdWeightGradTensorOp");

	op_class = std::make_shared<MaxWeightGradOp<float>>(MaxWeightGradOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "MaxWeightGradTensorOp");

  op_class = std::make_shared<MinWeightGradOp<float>>(MinWeightGradOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "MinWeightGradTensorOp");

	op_class = std::make_shared<MeanWeightGradOp<float>>(MeanWeightGradOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "MeanWeightGradTensorOp");

	//op_class = std::make_shared<VarWeightGradOp<float>>(VarWeightGradOp<float>()); //TODO...
	//op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	//BOOST_CHECK_EQUAL(op_tensor_class->getName(), "VarWeightGradTensorOp");

	op_class = std::make_shared<CountWeightGradOp<float>>(CountWeightGradOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "CountWeightGradTensorOp");

	op_class = std::make_shared<VarModWeightGradOp<float>>(VarModWeightGradOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "VarModWeightGradTensorOp");
}

BOOST_AUTO_TEST_CASE(getTensorParamsIntegrationWeightGradOpToIntegrationWeightGradTensorOp)
{
	IntegrationWeightGradOpToIntegrationWeightGradTensorOp<float, Eigen::DefaultDevice> op_to_tensor_op;

	std::shared_ptr<IntegrationWeightGradOp<float>> op_class = std::make_shared<SumWeightGradOp<float>>(SumWeightGradOp<float>());
	std::vector<float> params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<ProdWeightGradOp<float>>(ProdWeightGradOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<MaxWeightGradOp<float>>(MaxWeightGradOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<MinWeightGradOp<float>>(MinWeightGradOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<MeanWeightGradOp<float>>(MeanWeightGradOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	//op_class = std::make_shared<VarWeightGradOp<float>>(VarWeightGradOp<float>());
	//params = op_to_tensor_op.getTensorParams(op_class);
	//BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<CountWeightGradOp<float>>(CountWeightGradOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<VarModWeightGradOp<float>>(VarModWeightGradOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);
}

BOOST_AUTO_TEST_CASE(constructorLossFunctionGradOpToLossFunctionGradTensorOp)
{
	LossFunctionGradOpToLossFunctionGradTensorOp<float, Eigen::DefaultDevice>* ptr = nullptr;
	LossFunctionGradOpToLossFunctionGradTensorOp<float, Eigen::DefaultDevice>* nullPointer = nullptr;
	ptr = new LossFunctionGradOpToLossFunctionGradTensorOp<float, Eigen::DefaultDevice>();
	BOOST_CHECK_NE(ptr, nullPointer);
}

BOOST_AUTO_TEST_CASE(destructorLossFunctionGradOpToLossFunctionGradTensorOp)
{
	LossFunctionGradOpToLossFunctionGradTensorOp<float, Eigen::DefaultDevice>* ptr = nullptr;
	ptr = new LossFunctionGradOpToLossFunctionGradTensorOp<float, Eigen::DefaultDevice>();
	delete ptr;
}

BOOST_AUTO_TEST_CASE(convertOpToTensorOpLossFunctionGradOpToLossFunctionGradTensorOp)
{
	LossFunctionGradOpToLossFunctionGradTensorOp<float, Eigen::DefaultDevice> op_to_tensor_op;
	std::shared_ptr<LossFunctionGradOp<float>> op_class;
	std::shared_ptr<LossFunctionGradTensorOp<float, Eigen::DefaultDevice>> op_tensor_class;

	op_class = std::make_shared<ManhattanDistanceLossGradOp<float>>(ManhattanDistanceLossGradOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "ManhattanDistanceLossGradTensorOp");

	op_class = std::make_shared<L2NormLossGradOp<float>>(L2NormLossGradOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "L2NormLossGradTensorOp");

	op_class = std::make_shared<NegativeLogLikelihoodLossGradOp<float>>(NegativeLogLikelihoodLossGradOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "NegativeLogLikelihoodLossGradTensorOp");

	op_class = std::make_shared<MSELossGradOp<float>>(MSELossGradOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "MSELossGradTensorOp");

  op_class = std::make_shared<MAELossGradOp<float>>(MAELossGradOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "MAELossGradTensorOp");

  op_class = std::make_shared<MRSELossGradOp<float>>(MRSELossGradOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "MRSELossGradTensorOp");

  op_class = std::make_shared<MLELossGradOp<float>>(MLELossGradOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "MLELossGradTensorOp");

	op_class = std::make_shared<KLDivergenceMuLossGradOp<float>>(KLDivergenceMuLossGradOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "KLDivergenceMuLossGradTensorOp");

	op_class = std::make_shared<KLDivergenceLogVarLossGradOp<float>>(KLDivergenceLogVarLossGradOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "KLDivergenceLogVarLossGradTensorOp");

	op_class = std::make_shared<BCEWithLogitsLossGradOp<float>>(BCEWithLogitsLossGradOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "BCEWithLogitsLossGradTensorOp");

	op_class = std::make_shared<CrossEntropyWithLogitsLossGradOp<float>>(CrossEntropyWithLogitsLossGradOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "CrossEntropyWithLogitsLossGradTensorOp");

  op_class = std::make_shared<MSERangeLBLossGradOp<float>>(MSERangeLBLossGradOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "MSERangeLBLossGradTensorOp");

  op_class = std::make_shared<MSERangeUBLossGradOp<float>>(MSERangeUBLossGradOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "MSERangeUBLossGradTensorOp");

  op_class = std::make_shared<KLDivergenceCatLossGradOp<float>>(KLDivergenceCatLossGradOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "KLDivergenceCatLossGradTensorOp");

  op_class = std::make_shared<MAPELossGradOp<float>>(MAPELossGradOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "MAPELossGradTensorOp");
}

BOOST_AUTO_TEST_CASE(getTensorParamsLossFunctionGradOpToLossFunctionGradTensorOp)
{
	LossFunctionGradOpToLossFunctionGradTensorOp<float, Eigen::DefaultDevice> op_to_tensor_op;
	std::shared_ptr<LossFunctionGradOp<float>> op_class;
	std::vector<float> params;

	op_class = std::make_shared<ManhattanDistanceLossGradOp<float>>(ManhattanDistanceLossGradOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<L2NormLossGradOp<float>>(L2NormLossGradOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<NegativeLogLikelihoodLossGradOp<float>>(NegativeLogLikelihoodLossGradOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<MSELossGradOp<float>>(MSELossGradOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<MAELossGradOp<float>>(MAELossGradOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<MRSELossGradOp<float>>(MRSELossGradOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<MLELossGradOp<float>>(MLELossGradOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<KLDivergenceMuLossGradOp<float>>(KLDivergenceMuLossGradOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<KLDivergenceLogVarLossGradOp<float>>(KLDivergenceLogVarLossGradOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<BCEWithLogitsLossGradOp<float>>(BCEWithLogitsLossGradOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<CrossEntropyWithLogitsLossGradOp<float>>(CrossEntropyWithLogitsLossGradOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<MSERangeLBLossGradOp<float>>(MSERangeLBLossGradOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<MSERangeUBLossGradOp<float>>(MSERangeUBLossGradOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<KLDivergenceCatLossGradOp<float>>(KLDivergenceCatLossGradOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<MAPELossGradOp<float>>(MAPELossGradOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);
}

BOOST_AUTO_TEST_CASE(constructorLossFunctionOpToLossFunctionTensorOp)
{
	LossFunctionOpToLossFunctionTensorOp<float, Eigen::DefaultDevice>* ptr = nullptr;
	LossFunctionOpToLossFunctionTensorOp<float, Eigen::DefaultDevice>* nullPointer = nullptr;
	ptr = new LossFunctionOpToLossFunctionTensorOp<float, Eigen::DefaultDevice>();
	BOOST_CHECK_NE(ptr, nullPointer);
}

BOOST_AUTO_TEST_CASE(destructorLossFunctionOpToLossFunctionTensorOp)
{
	LossFunctionOpToLossFunctionTensorOp<float, Eigen::DefaultDevice>* ptr = nullptr;
	ptr = new LossFunctionOpToLossFunctionTensorOp<float, Eigen::DefaultDevice>();
	delete ptr;
}

BOOST_AUTO_TEST_CASE(convertOpToTensorOpLossFunctionOpToLossFunctionTensorOp)
{
	LossFunctionOpToLossFunctionTensorOp<float, Eigen::DefaultDevice> op_to_tensor_op;
	std::shared_ptr<LossFunctionOp<float>> op_class;
	std::shared_ptr<LossFunctionTensorOp<float, Eigen::DefaultDevice>> op_tensor_class;

	op_class = std::make_shared<ManhattanDistanceLossOp<float>>(ManhattanDistanceLossOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "ManhattanDistanceLossTensorOp");

	op_class = std::make_shared<L2NormLossOp<float>>(L2NormLossOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "L2NormLossTensorOp");

	op_class = std::make_shared<NegativeLogLikelihoodLossOp<float>>(NegativeLogLikelihoodLossOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "NegativeLogLikelihoodLossTensorOp");

	op_class = std::make_shared<MSELossOp<float>>(MSELossOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "MSELossTensorOp");

  op_class = std::make_shared<MAELossOp<float>>(MAELossOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "MAELossTensorOp");

  op_class = std::make_shared<MRSELossOp<float>>(MRSELossOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "MRSELossTensorOp");

  op_class = std::make_shared<MLELossOp<float>>(MLELossOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "MLELossTensorOp");

	op_class = std::make_shared<KLDivergenceMuLossOp<float>>(KLDivergenceMuLossOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "KLDivergenceMuLossTensorOp");

	op_class = std::make_shared<KLDivergenceLogVarLossOp<float>>(KLDivergenceLogVarLossOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "KLDivergenceLogVarLossTensorOp");

	op_class = std::make_shared<BCEWithLogitsLossOp<float>>(BCEWithLogitsLossOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "BCEWithLogitsLossTensorOp");

	op_class = std::make_shared<CrossEntropyWithLogitsLossOp<float>>(CrossEntropyWithLogitsLossOp<float>());
	op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
	BOOST_CHECK_EQUAL(op_tensor_class->getName(), "CrossEntropyWithLogitsLossTensorOp");

  op_class = std::make_shared<MSERangeLBLossOp<float>>(MSERangeLBLossOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "MSERangeLBLossTensorOp");

  op_class = std::make_shared<MSERangeUBLossOp<float>>(MSERangeUBLossOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "MSERangeUBLossTensorOp");

  op_class = std::make_shared<KLDivergenceCatLossOp<float>>(KLDivergenceCatLossOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "KLDivergenceCatLossTensorOp");

  op_class = std::make_shared<MAPELossOp<float>>(MAPELossOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "MAPELossTensorOp");
}

BOOST_AUTO_TEST_CASE(getTensorParamsLossFunctionOpToLossFunctionTensorOp)
{
	LossFunctionOpToLossFunctionTensorOp<float, Eigen::DefaultDevice> op_to_tensor_op;
  std::shared_ptr<LossFunctionOp<float>> op_class;
	std::vector<float> params;

	op_class = std::make_shared<ManhattanDistanceLossOp<float>>(ManhattanDistanceLossOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<L2NormLossOp<float>>(L2NormLossOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<NegativeLogLikelihoodLossOp<float>>(NegativeLogLikelihoodLossOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<MSELossOp<float>>(MSELossOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<MAELossOp<float>>(MAELossOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<MRSELossOp<float>>(MRSELossOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<MLELossOp<float>>(MLELossOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<KLDivergenceMuLossOp<float>>(KLDivergenceMuLossOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<KLDivergenceLogVarLossOp<float>>(KLDivergenceLogVarLossOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<BCEWithLogitsLossOp<float>>(BCEWithLogitsLossOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

	op_class = std::make_shared<CrossEntropyWithLogitsLossOp<float>>(CrossEntropyWithLogitsLossOp<float>());
	params = op_to_tensor_op.getTensorParams(op_class);
	BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<MSERangeLBLossOp<float>>(MSERangeLBLossOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<MSERangeUBLossOp<float>>(MSERangeUBLossOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<KLDivergenceCatLossOp<float>>(KLDivergenceCatLossOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<MAPELossOp<float>>(MAPELossOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);
}

BOOST_AUTO_TEST_CASE(constructorMetricFunctionOpToMetricFunctionTensorOp)
{
  MetricFunctionOpToMetricFunctionTensorOp<float, Eigen::DefaultDevice>* ptr = nullptr;
  MetricFunctionOpToMetricFunctionTensorOp<float, Eigen::DefaultDevice>* nullPointer = nullptr;
  ptr = new MetricFunctionOpToMetricFunctionTensorOp<float, Eigen::DefaultDevice>();
  BOOST_CHECK_NE(ptr, nullPointer);
}

BOOST_AUTO_TEST_CASE(destructorMetricFunctionOpToMetricFunctionTensorOp)
{
  MetricFunctionOpToMetricFunctionTensorOp<float, Eigen::DefaultDevice>* ptr = nullptr;
  ptr = new MetricFunctionOpToMetricFunctionTensorOp<float, Eigen::DefaultDevice>();
  delete ptr;
}

BOOST_AUTO_TEST_CASE(convertOpToTensorOpMetricFunctionOpToMetricFunctionTensorOp)
{
  MetricFunctionOpToMetricFunctionTensorOp<float, Eigen::DefaultDevice> op_to_tensor_op;
  std::shared_ptr<MetricFunctionOp<float>> op_class;
  std::shared_ptr<MetricFunctionTensorOp<float, Eigen::DefaultDevice>> op_tensor_class;

  op_class = std::make_shared<AccuracyBCOp<float>>(AccuracyBCOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "AccuracyBCTensorOp");

  op_class = std::make_shared<AccuracyMCMicroOp<float>>(AccuracyMCMicroOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "AccuracyMCMicroTensorOp");

  op_class = std::make_shared<AccuracyMCMacroOp<float>>(AccuracyMCMacroOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "AccuracyMCMacroTensorOp");

  op_class = std::make_shared<PrecisionBCOp<float>>(PrecisionBCOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "PrecisionBCTensorOp");

  op_class = std::make_shared<PrecisionMCMicroOp<float>>(PrecisionMCMicroOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "PrecisionMCMicroTensorOp");

  op_class = std::make_shared<PrecisionMCMacroOp<float>>(PrecisionMCMacroOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "PrecisionMCMacroTensorOp");

  op_class = std::make_shared<RecallBCOp<float>>(RecallBCOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "RecallBCTensorOp");

  op_class = std::make_shared<RecallMCMicroOp<float>>(RecallMCMicroOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "RecallMCMicroTensorOp");

  op_class = std::make_shared<RecallMCMacroOp<float>>(RecallMCMacroOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "RecallMCMacroTensorOp");

  op_class = std::make_shared<F1ScoreBCOp<float>>(F1ScoreBCOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "F1ScoreBCTensorOp");

  op_class = std::make_shared<F1ScoreMCMicroOp<float>>(F1ScoreMCMicroOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "F1ScoreMCMicroTensorOp");

  op_class = std::make_shared<F1ScoreMCMacroOp<float>>(F1ScoreMCMacroOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "F1ScoreMCMacroTensorOp");

  op_class = std::make_shared<MAEOp<float>>(MAEOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "MAETensorOp");

  op_class = std::make_shared<CosineSimilarityOp<float>>(CosineSimilarityOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "CosineSimilarityTensorOp");

  op_class = std::make_shared<PearsonROp<float>>(PearsonROp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "PearsonRTensorOp");

  op_class = std::make_shared<EuclideanDistOp<float>>(EuclideanDistOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "EuclideanDistTensorOp");

  op_class = std::make_shared<ManhattanDistOp<float>>(ManhattanDistOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "ManhattanDistTensorOp");

  op_class = std::make_shared<LogarithmicDistOp<float>>(LogarithmicDistOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "LogarithmicDistTensorOp");

  op_class = std::make_shared<JeffreysAndMatusitaDistOp<float>>(JeffreysAndMatusitaDistOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "JeffreysAndMatusitaDistTensorOp");

  op_class = std::make_shared<PercentDifferenceOp<float>>(PercentDifferenceOp<float>());
  op_tensor_class = op_to_tensor_op.convertOpToTensorOp(op_class);
  BOOST_CHECK_EQUAL(op_tensor_class->getName(), "PercentDifferenceTensorOp");
}

BOOST_AUTO_TEST_CASE(getTensorParamsMetricFunctionOpToMetricFunctionTensorOp)
{
  MetricFunctionOpToMetricFunctionTensorOp<float, Eigen::DefaultDevice> op_to_tensor_op;
  std::shared_ptr<MetricFunctionOp<float>> op_class;
  std::vector<float> params;

  op_class = std::make_shared<AccuracyBCOp<float>>(AccuracyBCOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<AccuracyMCMicroOp<float>>(AccuracyMCMicroOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<AccuracyMCMacroOp<float>>(AccuracyMCMacroOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<PrecisionBCOp<float>>(PrecisionBCOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<PrecisionMCMicroOp<float>>(PrecisionMCMicroOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<PrecisionMCMacroOp<float>>(PrecisionMCMacroOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<RecallBCOp<float>>(RecallBCOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<RecallMCMicroOp<float>>(RecallMCMicroOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<RecallMCMacroOp<float>>(RecallMCMacroOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<F1ScoreBCOp<float>>(F1ScoreBCOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<F1ScoreMCMicroOp<float>>(F1ScoreMCMicroOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<F1ScoreMCMacroOp<float>>(F1ScoreMCMacroOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<MAEOp<float>>(MAEOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<CosineSimilarityOp<float>>(CosineSimilarityOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<PearsonROp<float>>(PearsonROp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<EuclideanDistOp<float>>(EuclideanDistOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<ManhattanDistOp<float>>(ManhattanDistOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<LogarithmicDistOp<float>>(LogarithmicDistOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<JeffreysAndMatusitaDistOp<float>>(JeffreysAndMatusitaDistOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);

  op_class = std::make_shared<PercentDifferenceOp<float>>(PercentDifferenceOp<float>());
  params = op_to_tensor_op.getTensorParams(op_class);
  BOOST_CHECK_EQUAL(params.size(), 0);
}

BOOST_AUTO_TEST_SUITE_END()


#pragma once

#include <Eigen/Core>

//#define CHECK_QUEUE_SIZE

//#define TI_LIMIT_QUEUE_SIZE
#ifdef TI_LIMIT_QUEUE_SIZE
#define MAX_QSIZE 1000
#endif

namespace ticcd {
    inline bool ticcd_using_double()
    {
#ifdef TIGHT_INCLUSION_DOUBLE
        return true;
#else
        return false;
#endif
    }

#ifdef TIGHT_INCLUSION_DOUBLE
    typedef double Scalar;
#else
    typedef float Scalar;
#endif
    typedef Eigen::Matrix<Scalar, 3, 1> Vector3;
    typedef Eigen::Array<Scalar, 3, 1> Array3;
} // namespace ticcd


/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * File:   distrubuted_degree_random_ingress.hpp
 * Author: vincent
 *
 * Created on July 19, 2017, 5:56 PM
 */

#ifndef DISTRUBUTED_DEGREE_RANDOM_INGRESS_HPP
#define DISTRUBUTED_DEGREE_RANDOM_INGRESS_HPP

#include <boost/functional/hash.hpp>

#include <graphlab/rpc/buffered_exchange.hpp>
#include <graphlab/graph/graph_basic_types.hpp>
#include <graphlab/graph/ingress/distributed_ingress_base.hpp>
#include <graphlab/graph/distributed_graph.hpp>

#include <graphlab/macros_def.hpp>
namespace graphlab {
  template<typename VertexData, typename EdgeData>
  class distributed_graph;

  /**
   * \brief Ingress object assigning edges using randoming hash function on
   * vertex with higher degree between source and target
   */
  template<typename VertexData, typename EdgeData>
  class distributed_random_ingress :
    public distributed_ingress_base<VertexData, EdgeData> {
  public:
    typedef distributed_graph<VertexData, EdgeData> graph_type;
    /// The type of the vertex data stored in the graph
    typedef VertexData vertex_data_type;
    /// The type of the edge data stored in the graph
    typedef EdgeData   edge_data_type;

    typedef distributed_ingress_base<VertexData, EdgeData> base_type;

    //Variables for partition alg
    std::vector<size_t> degree_vector;

  public:
    distributed_random_ingress(distributed_control& dc, graph_type& graph) :
    base_type(dc, graph) {
    } // end of constructor

    ~distributed_random_ingress() { }

    /** Add an edge to the ingress object using random assignment. */
    void add_edge(vertex_id_type source, vertex_id_type target,
                  const EdgeData& edata) {
      typedef typename base_type::edge_buffer_record edge_buffer_record;

      procid_t procid;
      if(degree_vector.at(source) > degree_vector.at(target)) procid = graph_hash::hash_vertex(source) % base_type::rpc.numprocs();
      else graph_hash::hash_vertex(target) % base_type::rpc.numprocs();
      const procid_t owning_proc = procid;
      const edge_buffer_record record(source, target, edata);
      base_type::edge_exchange.send(owning_proc, record);
    } // end of add edge
  }; // end of distributed_random_ingress
}; // end of namespace graphlab
#include <graphlab/macros_undef.hpp>

#endif /* DISTRUBUTED_DEGREE_RANDOM_INGRESS_HPP */


#ifndef GUIDANCE_CONTROLLER
#define GUIDANCE_CONTROLLER

#include "auv_control/auv_model.hpp"
#include "auv_core/constants.hpp"
#include "auv_core/eigen_ros.hpp"
#include "auv_guidance/basic_trajectory.hpp"
#include "auv_guidance/tgen_limits.hpp"
#include "auv_guidance/waypoint.hpp"
#include "auv_msgs/SixDoF.h"
#include "auv_msgs/Thrust.h"
#include "auv_msgs/Trajectory.h"
#include "auv_msgs/TrajectoryGeneratorAction.h"

#include <actionlib/server/simple_action_server.h>
#include <ros/ros.h>

#include <yaml-cpp/yaml.h>
#include <algorithm>
#include <boost/thread.hpp>
#include <vector>
#include "math.h"

namespace acc = auv_core::constants;

namespace auv_gnc
{
class GuidanceController
{
private:
  // AUV Model Parameters
  int numActiveThrusters_;
  std::vector<std::string> activeThrusterNames_, inactiveThrusterNames_;
  std::string auvConfigFile_;
  YAML::Node auvConfig_;
  auv_control::AUVModel *auvModel_;

  // LQR Parameters
  std::vector<double> Qdiag_, QdiagIntegral_, Rdiag_;
  bool enableLQRIntegral_;

  // Trajectory Generator Parameters
  auv_msgs::Trajectory desiredTrajectory_;
  auv_guidance::TGenLimits *tgenLimits_;
  auv_guidance::Waypoint *startWaypoint_, *endWaypoint_;
  auv_guidance::BasicTrajectory *basicTrajectory_;
  auv_guidance::Vector13d state_;
  auv_guidance::Vector13d ref_;
  auv_guidance::Vector6d accel_;
  Eigen::Vector3d linearAccel_;
  Eigen::Quaterniond quaternion_;
  int tgenType_;
  bool tgenInit_, newTrajectory_;
  ros::Time startTime_;
  auv_control::Vector8d thrust_;

  // ROS Parameters
  ros::NodeHandle nh_;
  ros::Subscriber sixDofSub_;
  ros::Publisher thrustPub_;
  std::string subTopic_, pubTopic_, actionName_;
  double trajectoryDuration_;
  bool resultMessageSent_;

  typedef actionlib::SimpleActionServer<auv_msgs::TrajectoryGeneratorAction> TGenActionServer;
  typedef std::shared_ptr<TGenActionServer> TGenActionServerPtr;
  TGenActionServerPtr tgenActionServer_;

  // Private Methods
  void initAUVModel();
  void sixDofCB(const auv_msgs::SixDoF::ConstPtr &state);
  void tgenActionGoalCB();
  void tgenActionPreemptCB();
  bool isActionServerActive();
  bool isTrajectoryTypeValid(int type);
  void initNewTrajectory();
  void publishThrustMessage();

public:
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW

  GuidanceController(ros::NodeHandle nh);
  void runController();
};
}  // namespace auv_gnc

#endif


#include <boost/polygon/polygon_45_set_data.hpp>


/*
 * signature.cpp
 *
 *  Created on: Jun 1, 2015
 *      Author: simonenkos
 */

#include <string>
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <future>
#include <queue>
#include <memory>

#include <boost/regex.hpp>
#include <boost/program_options.hpp>
#include <boost/crc.hpp>

#include <thool/thread_pool.hpp>

namespace bpo = boost::program_options;

namespace
{

constexpr size_t BLOCK_SIZE_KILOBYTE = 1024;
constexpr size_t BLOCK_SIZE_MEGABYTE = 1024 * BLOCK_SIZE_KILOBYTE;
constexpr size_t BLOCK_SIZE_GIGABYTE = 1024 * BLOCK_SIZE_MEGABYTE;
constexpr size_t BLOCK_CRC_MAP_PROCESSING_SIZE = 100;

}

/**
 * Supporting class to handle a size of a block which consists of two
 * parts: a numeric part and a suffix, represented by K (kilobytes),
 * M (megabytes), and G (gigabytes) symbols.
 */
class block_size
{
   uint64_t number_;
   uint64_t suffix_;

public:
   block_size() : number_(0), suffix_(1)
   { }
   block_size(uint64_t number) : number_(number), suffix_(1)
   { }

   // Sets a numeric part and a suffix of the block_size and checks them to be correct.
   bool set(uint64_t number, char suffix)
   {
      uint64_t tmp = 1;

      if (suffix != 0)
      {
         // If suffix was provided, check if it's in range (K, M, G).
         switch (suffix)
         {
            case 'K': tmp = BLOCK_SIZE_KILOBYTE; break;
            case 'M': tmp = BLOCK_SIZE_MEGABYTE; break;
            case 'G': tmp = BLOCK_SIZE_GIGABYTE; break;

            default: return false;
         }
      }

      // Check values of a numeric part of the block size and it's suffix
      // to be in range of uint64_t type storage size.
      if (std::numeric_limits<uint64_t>::max() / tmp < number)
         return false;
      if (std::numeric_limits<uint64_t>::max() / number < tmp)
         return false;

      uint64_t bs = number * tmp;

      if (bs < BLOCK_SIZE_KILOBYTE)
         return false;

      number_ = number;
      suffix_ = tmp;

      return true;
   }

   uint64_t get() const
   {
      return number_ * suffix_;
   }
};

/**
 * Overload function for validation of block_size class objects needed for boost::program_options.
 */
void validate(boost::any & value, const std::vector<std::string> & string_values, block_size * target_type, int)
{
   static const boost::regex r("(^\\d+)([K|M|G]?$)");

   bpo::validators::check_first_occurrence(value);
   boost::smatch match;

   // Match parts of block_size according to regex.
   if (regex_match(bpo::validators::get_single_string(string_values), match, r))
   {
      uint64_t number;
      std::string suffix;

      try
      {
         // Parse numeric part of the block size option.
         number = boost::lexical_cast<uint64_t>(match[1]);
         // Parse suffix to char if it was provided.
         suffix = boost::lexical_cast<std::string>(match[2]);
      }
      catch (boost::bad_lexical_cast & e)
      {
         throw bpo::validation_error
         (
               bpo::validation_error::invalid_option_value
         );
      }

      block_size bs;

      if (bs.set(number, (suffix.size() > 0) ? suffix[0] : 0))
      {
         value = boost::any(bs);
         return;
      }
   }
   throw bpo::validation_error
   (
         bpo::validation_error::invalid_option_value
   );
}

int main(int argc, char ** argv)
{
   // Set default block size.
   block_size block_size_value { BLOCK_SIZE_MEGABYTE };

   std::string input_file_name, output_file_name;
   bpo::options_description help_desc, main_desc, desc;
   bpo::variables_map vm;

   // Make a map of application options that provide user interface using boost::program_options.
   help_desc.add_options()
         ("help,h", "print help");
   main_desc.add_options()
         ("input,i",  bpo::value<std::string>(&input_file_name)->required(),   "input file")
         ("output,o", bpo::value<std::string>(&output_file_name)->required(),  "output file to store input file's signature")
         ("block,b",  bpo::value<block_size> (&block_size_value),              "size of a processing block in bytes (1K, 1M, 1G)");
   desc.add(help_desc).add(main_desc);

   try
   {
      // Process application arguments.
      bpo::store(bpo::parse_command_line(argc, argv, desc), vm);

      if (vm.count("help") || (argc == 1))
      {
         std::cout << desc << std::endl;
         return EXIT_SUCCESS;
      }

      bpo::notify(vm);
   }
   catch (std::exception & e)
   {
      std::cerr << e.what() << std::endl;
      std::cout << desc << std::endl;
      return EXIT_FAILURE;
   }

   // Check application arguments for special cases and limitations.
   if (input_file_name == output_file_name)
   {
      std::cerr << "input and output files are same" << std::endl;
      return EXIT_FAILURE;
   }

   // Print information about processing details.
   std::cout << "input  file = " << input_file_name        << std::endl;
   std::cout << "output file = " << output_file_name       << std::endl;
   std::cout << "block  size = " << block_size_value.get() << std::endl;

   std::ifstream input_file { input_file_name, std::ios::binary };

   if (!input_file.is_open())
   {
      std::cerr << "can't open input file" << std::endl;
      return EXIT_FAILURE;
   }

   std::ofstream output_file_stream { output_file_name, std::ios::binary | std::ios::trunc };

   if (!output_file_stream.is_open())
   {
      std::cerr << "can't open output file" << std::endl;
      return EXIT_FAILURE;
   }

   uint64_t block_counter = 0;
   uint64_t last_processed_block_id = 0;

   // Map of crc of blocks ordered by block number.
   std::map <uint64_t, uint32_t> block_crc_map;
   // Mutex for map that will be accessed through several threads.
   std::mutex block_crc_map_mutex;

   // Lambda for output file operations such as saving a crc of a block into a file according to block id.
   auto crc_saver = [&output_file_stream, &block_crc_map, &last_processed_block_id]()
   {
      while (true)
      {
         // Search for a checksum for a current block.
         auto block = block_crc_map.find(last_processed_block_id);

         if (block_crc_map.end() != block)
         {
            // If the checksum for the block has been calculated, save it
            // to the output stream and move to a next block.
            output_file_stream.write(reinterpret_cast<char *>(&block->second), sizeof(block->second));
            last_processed_block_id++;
         }
         else break;
      }
   };

   // Get an instance of the thread pool.
   auto & tp = thool::thread_pool::instance();

   // Reading a data from the input stream till the end.
   while (!input_file.eof())
   {
      // Create a temporary buffer to get block's data from input file.
      std::shared_ptr<std::vector<char>> buffer_ptr;
      std::streamsize readed_size;

      try
      {
         // Allocate vector of size block_size and put it to a shared pointer,
         // cause it should be available for a task out of scope of the cycle.
         buffer_ptr = std::make_shared<std::vector<char>>(block_size_value.get(), 0);
         // Read data from input stream to buffer, which size is equal to block_size.
         input_file.read(buffer_ptr->data(), buffer_ptr->size());
         // Get real amount of data that was read.
         readed_size = input_file.gcount();
      }
      catch (const std::bad_alloc & err)
      {
         // Not enough memory to create a new buffer. Wait till active tasks will be finished.
         std::this_thread::sleep_for(std::chrono::milliseconds(10));
         // And repeat current cycle.
         continue;
      }
      catch (const std::exception & err)
      {
         std::cerr << "unexpected exception: " << err.what() << std::endl;
         tp.stop();
         return EXIT_FAILURE;
      }

      auto task = [buffer_ptr, readed_size, &block_crc_map, &block_crc_map_mutex, block_counter]()
      {
         boost::crc_32_type crc_hash;
         // Calculate CRC32 hash for a given data in the buffer.
         crc_hash.process_bytes(buffer_ptr->data(), readed_size);
         // Get resulting checksum.
         boost::crc_32_type::value_type crc_value = crc_hash.checksum();
         // Free up memory hold by buffer.
         buffer_ptr->clear();
         buffer_ptr->shrink_to_fit();

         while (true)
         {
            try
            {
               std::lock_guard<std::mutex> lock(block_crc_map_mutex);
               // Inserting a checksum of the block into the map to keep order of blocks.
               block_crc_map.insert({ block_counter, crc_value });
               // Break out of the cycle.
               break;
            }
            catch (const std::bad_alloc & err)
            {
               // Put task to sleep, to wait for free memory.
               std::this_thread::sleep_for(std::chrono::milliseconds(10));
            }
         }
      };
      tp.add_task
      (
            std::make_shared<thool::task>(task, 0)
      );
      block_counter++;

      std::lock_guard<std::mutex> lock(block_crc_map_mutex);
      // Wait while map will have a number of checksums to be processed.
      if (block_crc_map.size() >= BLOCK_CRC_MAP_PROCESSING_SIZE) crc_saver();
   }

   while (last_processed_block_id != block_counter)
   {
      // Processing remaining checksums by calling crc_saver every 10 ms
      // to be sure if some tasks are finished.
      std::this_thread::sleep_for(std::chrono::milliseconds(10));
      std::lock_guard<std::mutex> lock(block_crc_map_mutex);
      crc_saver();
   }
   tp.stop();

   std::cout << "done" << std::endl;

   input_file.close();
   output_file_stream.close();

   return EXIT_SUCCESS;
}


/// HEADER
#include <cslibs_vectormaps/dxf/dxf_map.h>
#include <cslibs_boost_geometry/algorithms.h>
#include <boost/geometry/algorithms/difference.hpp>
#include <boost/geometry/strategies/strategies.hpp>
#include <boost/geometry/geometries/ring.hpp>

#include <iostream>

using namespace cslibs_vectormaps;
using namespace dxf;

DXFMap::DXFMap(const bool debug) :
    debug_(debug),
    dxf_source_(NULL),
    dxf_layer_(NULL)
{
    RegisterOGRDXF();
}

DXFMap::~DXFMap()
{
    if(dxf_source_ != NULL)
        OGRDataSource::DestroyDataSource(dxf_source_);
}

bool DXFMap::open(const std::string &path)
{
    if(dxf_source_ != NULL) {
        OGRDataSource::DestroyDataSource(dxf_source_);
        dxf_layer_ = NULL;
    }

    dxf_source_ = OGRSFDriverRegistrar::Open(path.c_str());
    if(dxf_source_ == NULL) {
        std::cerr << "[DXFVectorMap] : Failed to open file '" << path << "'!\n";
        return false;
    }

    dxf_layer_ = dxf_source_->GetLayerByName("entities");
    if(dxf_layer_ == NULL) {
        std::cerr << "[DXFVectorMap] : Failed to load 'entities' layer!\n";
        return false;
    }

    return retrieveBounding();
}

void DXFMap::getPolygon(Polygon &polygon,
                        const std::string &attrib_filter)
{
    /// first find the containing polygon
    Polygons polygons;
    getPolygons(polygons, attrib_filter);

    Polygons::iterator bounding_polygon_it = polygons.end();

    for(Polygons::iterator
        outer_it  = polygons.begin() ;
        outer_it != polygons.end() ;
        ++outer_it) {
        bool contains_all = true;
        for(Polygons::iterator
            inner_it  = polygons.begin() ;
            inner_it != polygons.end() ;
            ++inner_it) {
            cslibs_boost_geometry::algorithms::within<Point>(*inner_it, *outer_it);
        }

        if(contains_all) {
            bounding_polygon_it = outer_it;
            break;
        }
    }

    if(bounding_polygon_it == polygons.end()) {
        if(debug_)
            std::cerr << "Couldn't find a bounding polygon!\n";
        polygon = Polygon();
        return;
    }

    /// second step calculate all intersections
    polygon = *bounding_polygon_it;
    polygons.erase(bounding_polygon_it);

    if(polygons.size() > 0) {
        for(Polygons::iterator
            it  = polygons.begin() ;
            it != polygons.end() ;
            ++it) {
            Polygon::ring_type ring;
            ring.insert(ring.begin(), it->outer().begin(), it->outer().end());
            polygon.inners().push_back(ring);
        }
    }
}

void DXFMap::getPolygons(Polygons &polygons,
                         const std::string &attrib_filter)
{
    retrievePolygons(polygons, attrib_filter);
}

void DXFMap::getVectors(Vectors &vectors,
                        const std::string &attrib_filter) const
{
    retrieveVectors(vectors, attrib_filter);
}

void DXFMap::getVectors(const BoundingBox &bounding,
                        Vectors &vectors,
                        const std::string &attrib_filter)
{
    const Point &min = bounding.min_corner();
    const Point &max = bounding.max_corner();
    dxf_layer_->SetSpatialFilterRect(min.x(), min.y(),
                                     max.x(), max.y());
    retrieveVectors(vectors, attrib_filter);
    dxf_layer_->SetSpatialFilter(NULL);
}

void DXFMap::getVectors(const Point &min,
                        const Point &max,
                              Vectors &vectors,
                        const std::string &attrib_filter)
{
    dxf_layer_->SetSpatialFilterRect(min.x(), min.y(),
                                     max.x(), max.y());
    retrieveVectors(vectors, attrib_filter);
    dxf_layer_->SetSpatialFilter(NULL);
}

void DXFMap::getVectors(const double min_x, const double min_y,
                        const double max_x, const double max_y,
                              Vectors &vectors,
                        const std::string &attrib_filter)
{
    dxf_layer_->SetSpatialFilterRect(min_x, min_y,
                                     max_x, max_y);
    retrieveVectors(vectors, attrib_filter);
    dxf_layer_->SetSpatialFilter(NULL);

}

void DXFMap::getBounding(Point &min,
                         Point &max)
{
    min = bounding_.min_corner();
    max = bounding_.max_corner();
}

void DXFMap::getBounding(BoundingBox &bounding)
{
    bounding = bounding_;
}

void DXFMap::getDimension(Dimension &dim)
{
    dim.x(width_);
    dim.y(height_);
}

void DXFMap::getDimension(double &height, double &width)
{
    height = height_;
    width  = width_;
}

void DXFMap::printInfo()
{
    std::cout << "[DXFVectorMap] lower bound : "
              << "[" << bounding_.min_corner().x()
              << "," << bounding_.min_corner().y()
              << "]" << "\n";
    std::cout << "[DXFVectorMap] upper bound : "
              << "[" << bounding_.max_corner().x()
              << "," << bounding_.max_corner().y()
              << "]" << "\n";

}

void DXFMap::getLayerNames(std::vector<std::string> &names) const
{
    OGRLayer *layers = dxf_source_->ExecuteSQL("SELECT DISTINCT Layer FROM entities", NULL, NULL);
    for(int i = 0 ; i < layers->GetFeatureCount() ; ++i) {
        OGRFeature *feature = layers->GetFeature(i);
        names.push_back(feature->GetFieldAsString("Layer"));
        OGRFeature::DestroyFeature(feature);
    }
}

namespace {
/// DECLARATION
inline void geometryToVectors(OGRGeometry     *geometry,
                              DXFMap::Vectors &vectors,
                              const bool debug = false);
inline void geometryToPolygons(OGRGeometry *geometry,
                               DXFMap::Polygons &polygons,
                               const bool debug = false);

/// IMPLEMENTATION
inline DXFMap::Point asDXF(const OGRPoint &pt)
{
    return DXFMap::Point(pt.getX(), pt.getY());
}

inline DXFMap::Vector asDXF(const OGRPoint &pt1,
                            const OGRPoint &pt2)
{
    return DXFMap::Vector(asDXF(pt1), asDXF(pt2));
}

inline void lineStringToVectors(OGRLineString *line_string,
                                DXFMap::Vectors &vectors)
{
    int points_num = line_string->getNumPoints();
    if(points_num < 2)
        return;

    OGRPoint last;
    OGRPoint curr;
    line_string->getPoint(0, &last);
    for(int j = 1 ; j < points_num ; ++j) {
        line_string->getPoint(j, &curr);
        vectors.push_back(asDXF(last, curr));
        last = curr;
    }
}

inline void multiLineStringToVectors(OGRMultiLineString *line_strings,
                                     DXFMap::Vectors    &vectors)
{
    int geom_num = line_strings->getNumGeometries();
    for(int i = 0 ; i < geom_num ; ++i) {
        OGRLineString *line_string = (OGRLineString*) line_strings->getGeometryRef(i);
        lineStringToVectors(line_string, vectors);
    }
}

inline void geometryCollectionToVectors(OGRGeometryCollection *collection,
                                        DXFMap::Vectors &vectors)
{
    int geom_num = collection->getNumGeometries();
    for(int i = 0 ; i < geom_num ; ++i) {
        OGRGeometry *geom = collection->getGeometryRef(i);
        geometryToVectors(geom, vectors);
    }
}

inline void lineStringToPolygon(OGRLineString   *line_string,
                                DXFMap::Polygons &polygons)
{
    OGRPoint point;
    DXFMap::Polygon poly;
    int points_num = line_string->getNumPoints();
    for(int i = 0 ; i < points_num ; ++i) {
        line_string->getPoint(i, &point);
        boost::geometry::append(poly.outer(), asDXF(point));
    }
    polygons.push_back(poly);
}

inline void multiLineStringToPolygons(OGRMultiLineString *line_strings,
                                      DXFMap::Polygons     &polygons)
{
    int geom_num = line_strings->getNumGeometries();
    for(int i = 0 ; i < geom_num ; ++i) {
        OGRLineString *line_string = (OGRLineString*) line_strings->getGeometryRef(i);
        lineStringToPolygon(line_string, polygons);
    }
}

inline void geometryCollectionToPolygons(OGRGeometryCollection *collection,
                                         DXFMap::Polygons      &polygons)
{
    int geom_num = collection->getNumGeometries();
    for(int i = 0 ; i < geom_num ; ++i) {
        OGRGeometry *geom = collection->getGeometryRef(i);
        geometryToPolygons(geom, polygons);
    }
}

inline void geometryToVectors(OGRGeometry     *geometry,
                              DXFMap::Vectors &vectors,
                              const bool debug)
{
    OGRwkbGeometryType type = wkbFlatten(geometry->getGeometryType());
    switch(type) {
    case wkbPoint:
        if(debug)
            std::cerr << "Single points will not be inserted into vector map!\n";
        break;
    case wkbLineString:
        lineStringToVectors((OGRLineString*) geometry, vectors);
        break;
    case wkbMultiLineString:
        multiLineStringToVectors((OGRMultiLineString*) geometry, vectors);
        break;
    case wkbGeometryCollection:
        geometryCollectionToVectors((OGRGeometryCollection*) geometry, vectors);
        break;
    case wkbPolygon:
        if(debug)
            std::cerr << "[DXFVectorMap] : Polygon will not be inserted into vector map!\n";
        break;
    default:
        if(debug)
            std::cerr << "[DXFVectorMap] : Unknown geometry type!\n";
        break;
    }
}

inline void geometryToPolygons(OGRGeometry      *geometry,
                              DXFMap::Polygons  &polygons,
                              const bool debug)
{
    OGRwkbGeometryType type = wkbFlatten(geometry->getGeometryType());
    switch(type) {
    case wkbPoint:
        if(debug)
            std::cerr << "Single points will not be inserted into vector map!\n";
        break;
    case wkbLineString:
        lineStringToPolygon((OGRLineString*) geometry, polygons);
        break;
    case wkbMultiLineString:
        multiLineStringToPolygons((OGRMultiLineString*) geometry, polygons);
        break;
    case wkbGeometryCollection:
        geometryCollectionToPolygons((OGRGeometryCollection*) geometry, polygons);
        break;
    case wkbPolygon:
        if(debug)
            std::cerr << "[DXFVectorMap] : Polygon will not be inserted into vector map!\n";
        break;
    default:
        if(debug)
            std::cerr << "[DXFVectorMap] : Unknown geometry type!\n";
        break;
    }
}
}

bool DXFMap::retrieveBounding()
{

    dxf_layer_->ResetReading();
    OGREnvelope env;
    if(dxf_layer_->GetExtent(&env) != OGRERR_NONE) {
        std::cerr << "[DXFVectorMap] : Error retrieving dimensions of map!\n";
        return false;
    }

    Point &min = bounding_.min_corner();
    Point &max = bounding_.max_corner();
    min.x(env.MinX);
    min.y(env.MinY);
    max.x(env.MaxX);
    max.y(env.MaxY);
    height_ = max.y() - min.y();
    width_  = max.x() - min.x();

    return true;
}

void DXFMap::retrieveVectors(Vectors &vectors,
                             const std::string &attrib_filter) const
{
    if(dxf_layer_ == nullptr)
        return;

    dxf_layer_->ResetReading();
    OGRErr err = dxf_layer_->SetAttributeFilter(attrib_filter.c_str());
    if(err != 0) {
        std::cerr << "[DXFVectorMap] : Error attribute filter '"<< err << "'!\n";
    }

    OGRFeature *feature(NULL);
    while((feature = dxf_layer_->GetNextFeature()) != NULL) {
        /// MAYBE CHECK FOR LABEL
        OGRGeometry *geometry = feature->GetGeometryRef();

        if(geometry != NULL) {
            geometryToVectors(geometry, vectors);
        }
        OGRFeature::DestroyFeature(feature);
    }
}

void DXFMap::retrievePolygons(Polygons &polygons,
                              const std::string &attrib_filter)
{
    dxf_layer_->ResetReading();
    OGRErr err = dxf_layer_->SetAttributeFilter(attrib_filter.c_str());
    if(err != 0) {
        std::cerr << "[DXFVectorMap] : Error attribute filter '"<< err << "'!\n";
    }

    OGRFeature *feature(NULL);
    while((feature = dxf_layer_->GetNextFeature()) != NULL) {
        /// MAYBE CHECK FOR LABEL
        OGRGeometry *geometry = feature->GetGeometryRef();

        if(geometry != NULL) {
            geometryToPolygons(geometry, polygons);
        }
        OGRFeature::DestroyFeature(feature);
    }
}


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

  @copyright 2016 NumScale SAS
  @copyright 2016 J.T. Lapreste

  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_SPLAT_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_SPLAT_HPP_INCLUDED

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{
  /*!

  **/
  template<typename T, typename U> T splat(U const& x) {}
} }
#endif

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

#endif


/*
==============================================================================
KratosStructuralApplication
A library based on:
Kratos
A General Purpose Software for Multi-Physics Finite Element Analysis
Version 1.0 (Released on march 05, 2007).

Copyright 2007
Pooyan Dadvand, Riccardo Rossi, Janosch Stascheit, Felix Nagel
pooyan@cimne.upc.edu
rrossi@cimne.upc.edu
janosch.stascheit@rub.de
nagel@sd.rub.de
- CIMNE (International Center for Numerical Methods in Engineering),
Gran Capita' s/n, 08034 Barcelona, Spain
- Ruhr-University Bochum, Institute for Structural Mechanics, Germany

Permission is hereby granted, free  of charge, to any person obtaining
a  copy  of this  software  and  associated  documentation files  (the
"Software"), to  deal in  the Software without  restriction, including
without limitation  the rights to  use, copy, modify,  merge, publish,
distribute,  sublicense and/or  sell copies  of the  Software,  and to
permit persons to whom the Software  is furnished to do so, subject to
the following condition:

Distribution of this code for  any  commercial purpose  is permissible
ONLY BY DIRECT ARRANGEMENT WITH THE COPYRIGHT OWNERS.

The  above  copyright  notice  and  this permission  notice  shall  be
included in all copies or substantial portions of the Software.

THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT  SHALL THE AUTHORS OR COPYRIGHT HOLDERS  BE LIABLE FOR ANY
CLAIM, DAMAGES OR  OTHER LIABILITY, WHETHER IN AN  ACTION OF CONTRACT,
TORT  OR OTHERWISE, ARISING  FROM, OUT  OF OR  IN CONNECTION  WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

==============================================================================
*/

//
//   Project Name:        Kratos
//   Last Modified by:    $Author: hurga $
//   Date:                $Date: 2009-02-02 14:03:23 $
//   Revision:            $Revision: 1.5 $
//
//

#if !defined(KRATOS_ADD_CONSTITUTIVE_LAWS_TO_PYTHON_H_INCLUDED )
#define  KRATOS_ADD_CONSTITUTIVE_LAWS_TO_PYTHON_H_INCLUDED

// System includes
#include <boost/python.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>

// External includes
#include "boost/smart_ptr.hpp"

// Project includes
#include "custom_python/add_constitutive_laws_to_python.h"
#include "includes/define.h"
#include "includes/constitutive_law.h"
#include "constitutive_laws/viscoplastic_3d.h"
#include "constitutive_laws/clay_and_sand_model.h"
#include "constitutive_laws/casm.h"
#include "constitutive_laws/modified_barcelona_basic_model.h"
#include "constitutive_laws/freezing_soil_elastoplastic_model.h"
//#include "constitutive_laws/freezing_soil_elastoviscoplastic_model.h"
#include "includes/node.h"
#include "includes/variables.h"
#include "includes/mesh.h"
#include "includes/element.h"
#include "includes/condition.h"
#include "includes/properties.h"
#include "python/add_mesh_to_python.h"
#include "python/pointer_vector_set_python_interface.h"
#include "python/variable_indexing_python.h"

namespace Kratos
{

    namespace Python
    {

        using namespace boost::python;

        typedef ConstitutiveLaw ConstitutiveLawBaseType;
        typedef Mesh<Node<3>, Properties, Element, Condition> MeshType;
        typedef Properties::Pointer PropertiesPointer;

        typedef std::vector<ConstitutiveLaw::Pointer> MaterialsContainer;
        typedef ConstitutiveLaw::Pointer  ConstitutiveLawPointer;

        void  AddConstitutiveLawsToPython()
        {
            class_< Viscoplastic3D, bases< ConstitutiveLawBaseType >, boost::noncopyable >
            ( "Viscoplastic3D",
              init<>() )
            ;
            class_< ClayAndSandModel, bases< ConstitutiveLawBaseType >, boost::noncopyable >
            ( "ClayAndSandModel",
              init<>() )
            ;
            class_< CASM, bases< ConstitutiveLawBaseType >, boost::noncopyable >
            ( "CASM",
              init<>() )
            ;
            class_< ModifiedBarcelonaBasicModel, bases< ConstitutiveLawBaseType >, boost::noncopyable >
            ( "ModifiedBarcelonaBasicModel",
              init<>() )
            ;
            class_< FreezingSoilElastoplasticModel, bases< ConstitutiveLawBaseType >, boost::noncopyable >
            ( "FreezingSoilElastoplasticModel",
              init<>() )
            ;
 //           class_< FreezingSoilElastoviscoplasticModel, bases< ConstitutiveLawBaseType >, boost::noncopyable >
   //         ( "FreezingSoilElastoviscoplasticModel",
     //         init<>() )
       //     ;
        }
    }  // namespace Python.
}  // namespace Kratos.
#endif // KRATOS_ADD_CONSTITUTIVE_LAWS_TO_PYTHON_H_INCLUDED defined


/*
	Copyright (c) 2019,	Mobile Robots Laboratory:
	-Jan Wietrzykowski (jan.wietrzykowski@put.poznan.pl).
	Poznan University of Technology
	All rights reserved.

	Redistribution and use in source and binary forms, with or without modification,
	are permitted provided that the following conditions are met:

	1. Redistributions of source code must retain the above copyright notice,
	this list of conditions and the following disclaimer.

	2. Redistributions in binary form must reproduce the above copyright notice,
	this list of conditions and the following disclaimer in the documentation
	and/or other materials provided with the distribution.

	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
	AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
	THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
	ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
	FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
	DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
	AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
	OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
	OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <cmath>

#include <boost/serialization/vector.hpp>

#include "pgm/Pgm.h"
#include "Utils.hpp"
#include "WiFiSeqFeatures.hpp"

LocFeature::LocFeature(int iid,
                       int iparamNum,
                       const std::vector <std::shared_ptr<RandVar>> &irandVarsOrdered,
                       const std::vector<int> &iobsNums)
        : Feature(iid,
                  iparamNum,
                  irandVarsOrdered,
                  iobsNums)
{

}

double LocFeature::comp(const std::vector<double> &vals, const std::vector<double> &obsVec) {
    int loc = (int)round(vals[0]);
    // probabilities are the same for all orientations
    loc /= orientSectors;

    double ret = log(obsVec[loc]);
    ret = std::max(ret, -20.0);

    return ret;
}

double LocFeature::compParam(const std::vector<double> &vals,
                             const std::vector<double> &params,
                             const std::vector<double> &obsVec)
{
    return params[paramNum()]*comp(vals, obsVec);
}

MoveFeature::MoveFeature(int iid,
                         int iparamNum,
                         const std::vector<std::shared_ptr<RandVar>> &irandVarsOrdered,
                         const std::vector<int> &iobsNums,
                         int imapSize,
                         double isigmaDist,
                         std::shared_ptr<Graph> igraph)
        : Feature(iid,
                 iparamNum,
                 irandVarsOrdered,
                 iobsNums),
          mapSize(imapSize),
          sigmaDist(isigmaDist),
          graph(igraph)
{

}

double MoveFeature::comp(const std::vector<double> &vals, const std::vector<double> &obsVec) {
    int loc1 = (int)round(vals[0]);
    int loc2 = (int)round(vals[1]);

    double distStep = obsVec[0];

    double dist = graph->getDist(loc1, loc2);
    double error = dist - distStep;

    double ret = -error * error / (sigmaDist * sigmaDist);

    ret = std::max(ret, -20.0);

    return ret;
}

double MoveFeature::compParam(const std::vector<double> &vals,
                              const std::vector<double> &params,
                              const std::vector<double> &obsVec)
{
    return params[paramNum()]*comp(vals, obsVec);
}

OrientMoveFeature::OrientMoveFeature(int iid,
                                     int iparamNum,
                                     const std::vector<std::shared_ptr<RandVar>> &irandVarsOrdered,
                                     const std::vector<int> &iobsNums,
                                     int imapSize,
                                     double isigmaOrient)
        : Feature(iid,
                  iparamNum,
                  irandVarsOrdered,
                  iobsNums),
          mapSize(imapSize),
          sigmaOrient(isigmaOrient)
{

}

double OrientMoveFeature::comp(const std::vector<double> &vals, const std::vector<double> &obsVec) {
    static constexpr double thresh = M_PI / 2.0;
//    static constexpr double alpha =

    int loc1 = (int)round(vals[0]);
    int loc2 = (int)round(vals[1]);

    double orientMeas = obsVec[0];

    double x1 = obsVec[1 + loc1];
    double y1 = obsVec[1 + mapSize + loc1];
    double o1 = obsVec[1 + 2 * mapSize + loc1];
    double x2 = obsVec[1 + loc2];
    double y2 = obsVec[1 + mapSize + loc2];
    double o2 = obsVec[1 + 2 * mapSize + loc2];

    double dx = x2 - x1;
    double dy = y2 - y1;

    double error = 0.0;
    // if there is some movement
    if(sqrt(dx*dx + dy*dy) > 0.5) {
        double orient = atan2(dy, dx);
        error = Utils::angDiff(orient, orientMeas);
    }

    // berHu loss function
//    double ret = 0.0;
//    if(std::abs(error) < thresh){
//        ret = -std::abs(error);
////        ret = 0;
//    }
//    else{
//        ret = -(error * error + thresh * thresh) / (2 * thresh);
////        double errorThresh = std::abs(error) - thresh;
////        ret = -errorThresh * errorThresh;
//    }

//    double ret = -error * error;

    double ret = -exp(std::abs(error) - thresh);

    ret /= (sigmaOrient * sigmaOrient);

    ret = std::max(ret, -20.0);

    return ret;
}

double OrientMoveFeature::compParam(const std::vector<double> &vals,
                                    const std::vector<double> &params,
                                    const std::vector<double> &obsVec)
{
    return params[paramNum()]*comp(vals, obsVec);
}