//---------------------------------------------------------------------------//
// Copyright (c) 2014 Roshan <thisisroshansmail@gmail.com>
//
// 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
//
// See http://kylelutz.github.com/compute for more information.
//---------------------------------------------------------------------------//

#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_MERGE_PATH_HPP
#define BOOST_COMPUTE_ALGORITHM_DETAIL_MERGE_PATH_HPP

#include <iterator>

#include <boost/compute/algorithm/find_if.hpp>
#include <boost/compute/container/vector.hpp>
#include <boost/compute/detail/iterator_range_size.hpp>
#include <boost/compute/detail/meta_kernel.hpp>
#include <boost/compute/lambda.hpp>
#include <boost/compute/system.hpp>

namespace boost {
namespace compute {
namespace detail {

///
/// \brief Merge Path kernel class
///
/// Subclass of meta_kernel to break two sets into tiles according
/// to their merge path
///
class merge_path_kernel : public meta_kernel
{
public:
    unsigned int tile_size;

    merge_path_kernel() : meta_kernel("merge_path")
    {
        tile_size = 4;
    }

    template<class InputIterator1, class InputIterator2,
             class OutputIterator1, class OutputIterator2>
    void set_range(InputIterator1 first1,
                   InputIterator1 last1,
                   InputIterator2 first2,
                   InputIterator2 last2,
                   OutputIterator1 result_a,
                   OutputIterator2 result_b)
    {
        typedef typename std::iterator_traits<InputIterator1>::value_type value_type;

        m_a_count = iterator_range_size(first1, last1);
        m_a_count_arg = add_arg<uint_>("a_count");

        m_b_count = iterator_range_size(first2, last2);
        m_b_count_arg = add_arg<uint_>("b_count");

        *this <<
            "uint i = get_global_id(0);\n" <<
            "uint target = (i+1)*" << tile_size << ";\n" <<
            "uint start = max(convert_int(0),convert_int(target)-convert_int(b_count));\n" <<
            "uint end = min(target,a_count);\n" <<
            "uint a_index, b_index;\n" <<
            "while(start<end)\n" <<
            "{\n" <<
            "   a_index = (start + end)/2;\n" <<
            "   b_index = target - a_index - 1;\n" <<
            "   if(" << first1[expr<uint_>("a_index")] <<
            "       <=" << first2[expr<uint_>("b_index")] << ")\n" <<
            "       start = a_index + 1;\n" <<
            "   else end = a_index;\n" <<
            "}\n" <<
            result_a[expr<uint_>("i")] << " = start;\n" <<
            result_b[expr<uint_>("i")] << " = target - start;\n";

    }

    event exec(command_queue &queue)
    {
        if((m_a_count + m_b_count)/tile_size == 0) {
            return event();
        }

        set_arg(m_a_count_arg, uint_(m_a_count));
        set_arg(m_b_count_arg, uint_(m_b_count));

        return exec_1d(queue, 0, (m_a_count + m_b_count)/tile_size);
    }

private:
    size_t m_a_count;
    size_t m_a_count_arg;
    size_t m_b_count;
    size_t m_b_count_arg;
};

} //end detail namespace
} //end compute namespace
} //end boost namespace

#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_MERGE_PATH_HPP


//
// Created by kerin on 13/11/2018.
//

#ifndef BGEN_PROG_HYPS_HPP
#define BGEN_PROG_HYPS_HPP

#include "tools/eigen3.3/Dense"
#include "parameters.hpp"

#include <boost/iostreams/filtering_stream.hpp>

#include <iostream>
#include <iomanip>
#include <random>

namespace boost_io = boost::iostreams;

class Hyps {
	int sigma_ind   = 0;
	int sigma_b_ind = 1;
	int sigma_g_ind = 2;
	int lam_b_ind   = 3;
	int lam_g_ind   = 4;

public:
	double sigma;
	Eigen::ArrayXd slab_var;
	Eigen::ArrayXd spike_var;
	Eigen::ArrayXd slab_relative_var;
	Eigen::ArrayXd spike_relative_var;
	Eigen::ArrayXd lambda;

// Not hyperparameters, but things that depend on them
	Eigen::ArrayXd s_x;
	Eigen::ArrayXd pve;
	Eigen::ArrayXd pve_large;

	parameters p;
	int n_effects;

	Hyps(const parameters& my_params) : p(my_params) {
	};

	void resize(int my_n_effects){
		n_effects = my_n_effects;
		slab_var.resize(n_effects);
		spike_var.resize(n_effects);
		slab_relative_var.resize(n_effects);
		spike_relative_var.resize(n_effects);
		lambda.resize(n_effects);
		s_x.resize(n_effects);
	}
	void init_from_grid(const int& n_effects,
	                    const int& ii,
	                    const long& n_var,
	                    const Eigen::Ref<const Eigen::MatrixXd>& hyps_grid);
	void update_pve();
	double normL2() const;
	bool domain_is_valid() const;
	void random_init(int n_effects, long n_var);

	/*** IO ***/
	void to_file(const std::string &path) const;
	void from_file(const std::string &filename);
	friend std::ostream& operator<< (std::ostream &os, const Hyps& hyps);

	/*** Deprecated ***/
	Eigen::VectorXd get_sigmas(double h_b, double h_g, double lam_b, double lam_g,
	                           double f1_b, double f1_g, long n_var) const;
	Eigen::VectorXd get_sigmas(double h_b, double lam_b, double f1_b, long n_var) const;
};

Hyps operator+(const Hyps &h1, const Hyps &h2);
Hyps operator-(const Hyps &h1, const Hyps &h2);
Hyps operator*(const double &scalar, const Hyps &h1);

#endif //BGEN_PROG_HYPS_HPP


#include <frovedis.hpp>
#include <frovedis/matrix/blockcyclic_matrix.hpp>

#define BOOST_TEST_MODULE FrovedisTest
#include <boost/test/unit_test.hpp>

using namespace frovedis;
using namespace std;

bool check(blockcyclic_matrix_local<float>& m) {
  return m.is_valid();
}

BOOST_AUTO_TEST_CASE( frovedis_test )
{
    int argc = 1;
    char** argv = NULL;
    use_frovedis use(argc, argv);

    std::vector<float> vec = {1, 2, 3, 4};

    // constructing a blockcyclic matrix from a vector
    blockcyclic_matrix<float> bm1 (vec);

    // converting to vector
    // even after the successful conversion, the matrix will remain valid
    bm1.to_vector();
    BOOST_CHECK (bm1.data.map(check).get(0) == 1);

    // moving the contents while converting to vector
    // after the successful move operation, matrix will no longer valid
    bm1.moveto_vector();
    BOOST_CHECK (bm1.data.map(check).get(0) == 0);
}


#include "interface.h"
#include "classifersmodels_task.h"
#include "classifer_model_interface.h"
#include "running_mode.h"
#include "verifiers_task.h"
#include <boost/range/algorithm/copy.hpp>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <vector>

int main()
{
    act::oper::models_generators models_builders{
        {classifier::SVM_train{}},
        {classifier::EM_train{}},
        {classifier::random_tree_train{}},
        {classifier::boosting_train{}}
    };
    std::cout<<"model generation:\n";
    boost::copy(models_builders, std::ostream_iterator<act::oper::models_generators::value_type>(std::cout, "\n"));
    model_generator model{classifier::SVM_train{}};
    extract4training train_data{classifier::percent_split{20u, "my-name-is-mada"}};
    act::oper::experiment_call exper{AUC_ROC{}};
    std::cout<<"the model we've created is "<<model<<std::endl;
    std::cout<<"the extractor for the train is "<<train_data<<std::endl;
    std::cout<<"the experiment for the data is "<<exper<<std::endl;
    act::oper::expers_list experiments{
       logistic_loss{}, kalmogorov_smirnov_test{},
        AUC_ROC{}, kalmogorov_smirnov_test{}
    };
    boost::copy(experiments,
            std::ostream_iterator<act::oper::expers_list::value_type>(std::cout, "' "));
    std::cout<<std::endl;
    return 0;
}


#include "DataView.h"
#include "Generic/common/leak_detection.h"

#include <boost/foreach.hpp>
#include <boost/make_shared.hpp>
#pragma warning(push, 0)
#include <boost/thread.hpp>
#pragma warning(pop)
#include "Generic/common/ParamReader.h"

using Eigen::VectorXd;
using Eigen::SparseVector;
using std::set;

const double InferenceDataView::_epsilon = .000000001;

DataView::DataView(unsigned int n_instances, unsigned int n_features)
: _features(n_instances, SparseVector<double>(n_features)),
_features_to_instances(n_features, SparseVector<double>(n_instances)),
_nInstances(n_instances), _nFeatures(n_features),
_frequencies(VectorXd::Zero(n_features))
{

}

unsigned int DataView::nInstances() const {
	return _nInstances;
}

unsigned int DataView::nFeatures() const {
	return _nFeatures;
}

void DataView::observeFeaturesForInstance(unsigned int inst,
										  const set<unsigned int>& features) {
	BOOST_FOREACH(unsigned int feat, features) {
		_features[inst].insert(feat) = 1.0;
		++_frequencies[feat];
		_features_to_instances[feat].insert(inst) = 1.0;
	}
	_features[inst].finalize();
}

void DataView::finishedLoading() {
	BOOST_FOREACH(Eigen::SparseVector<double>& vec, _features_to_instances) {
		vec.finalize();
	}
}

const SparseVector<double>& DataView::features(int inst) const {
	return _features[inst];
}

const SparseVector<double>& DataView::instancesWithFeature(int feat) const {
	return _features_to_instances[feat];
}

double DataView::frequency(int feat) const {
	return _frequencies[feat];
}

bool DataView::hasFV(int inst) const {
	return features(inst).nonZeros() > 0;
}

InferenceDataView::InferenceDataView(int n_instances, int n_features)
: DataView(n_instances, n_features), _predictions(VectorXd::Zero(n_instances)),
	_threads(ParamReader::getOptionalIntParamWithDefaultValue("inference_threads", 1))
{}

void InferenceDataView::inferenceWork(const VectorXd& params,
		int num_threads, int thread_idx) const
{
	for (unsigned int i=thread_idx; i< nInstances(); i+=num_threads) {
		double score = features(i).dot(params);

		_predictions[i] = 1.0/(1.0 + exp(-score));
		if (_predictions[i] == 1.0) {
			_predictions[i] = 1.0 - _epsilon;
		} else if (_predictions[i] == 0) {
			_predictions[i] = _epsilon;
		}
	}
}

void InferenceDataView::inference(const VectorXd& params) const {
	if (_threads == 1) {
		inferenceWork(params, 1, 0);
	} else {
		typedef boost::shared_ptr<boost::thread> Thread_ptr;
		std::vector<Thread_ptr > threads;
		for (int i=0; i<_threads; ++i) {
			threads.push_back(Thread_ptr(new boost::thread(
				&InferenceDataView::inferenceWork, this, params, _threads, i)));
		}
		BOOST_FOREACH(Thread_ptr thread, threads) {
			thread->join();
		}
	}
}

double InferenceDataView::prediction(int inst) const {
	return _predictions[inst];
}

void InferenceDataView::inference(int inst, const VectorXd& params) const {
	double score = features(inst).dot(params);

	_predictions[inst] = 1.0/(1.0 + exp(-score));
}


#include <miopen/conv/invokers/impl_gemm_dynamic.hpp>
#include <miopen/conv/data_invoke_params.hpp>
#include <miopen/algorithm.hpp>
#include <miopen/handle.hpp>
#include <miopen/tensor_ops.hpp>
#include <miopen/solver/implicitgemm_util.hpp>
#include <miopen/util_sol.hpp>
#include <boost/any.hpp>

namespace miopen {
namespace conv {

static float CallImplGemmDynamicForward1x1(const miopen::Handle& handle,
                                           const ProblemDescription& conv_problem,
                                           ConstData_t src,
                                           Data_t dst,
                                           ConstData_t wei,
                                           const std::vector<KernelInvoke>& kernels)
{
    float elapsed = 0.0f;

    auto kernel = kernels[0];
    MIOPEN_LOG_I(kernel.GetName());

    // clang-format off
    int hi          = conv_problem.GetInHeight();
    int wi          = conv_problem.GetInWidth();
    int n           = conv_problem.GetInBatchSize();
    int k           = conv_problem.GetOutChannels();
    int c           = conv_problem.GetInChannels();
    int ho          = conv_problem.GetOutHeight();
    int wo          = conv_problem.GetOutWidth();
    int stride_h    = conv_problem.GetKernelStrideH();
    int stride_w    = conv_problem.GetKernelStrideW();
    int dilation_h  = conv_problem.GetDilationH();
    int dilation_w  = conv_problem.GetDilationW();
    int pad_h       = conv_problem.GetPadH();
    int pad_w       = conv_problem.GetPadW();
    int gap_0     = 0;
    // clang-format on

    std::vector<OpKernelArg> opArgs;
    opArgs.emplace_back(src);
    opArgs.emplace_back(wei);
    opArgs.emplace_back(dst);
    opArgs.emplace_back(hi);
    opArgs.emplace_back(wi);
    opArgs.emplace_back(n);
    opArgs.emplace_back(k);
    opArgs.emplace_back(c);
    opArgs.emplace_back(ho);
    opArgs.emplace_back(wo);
    opArgs.emplace_back(stride_h);
    opArgs.emplace_back(stride_w);
    opArgs.emplace_back(dilation_h);
    opArgs.emplace_back(dilation_w);
    opArgs.emplace_back(pad_h);
    opArgs.emplace_back(pad_w);
    opArgs.emplace_back(gap_0);

    kernel(opArgs);

    if(handle.IsProfilingEnabled())
        elapsed += handle.GetKernelTime();
    return elapsed;
}

InvokerFactory MakeImplGemmDynamicForward1x1InvokerFactory(const ConvolutionContext& ctx)
{
    const auto& conv_problem = ctx.conv_problem;
    return [conv_problem](const std::vector<Kernel>& kernels) {
        return [=](const Handle& handle, const AnyInvokeParams& primitive_parameters) {
            decltype(auto) data_ctx = primitive_parameters.CastTo<conv::DataInvokeParams>();
            const auto& tensors     = data_ctx.tensors;
            auto kernel             = handle.Run(kernels[0]);

            std::vector<KernelInvoke> ks;
            std::transform(kernels.begin(),
                           kernels.end(),
                           std::back_inserter(ks),
                           [&](const Kernel& k) { return handle.Run(k); });
            float elapsed = 0;
            elapsed       = CallImplGemmDynamicForward1x1(
                handle, conv_problem, tensors.in, tensors.out, tensors.w, ks);
            if(handle.IsProfilingEnabled())
            {
                handle.ResetKernelTime();
                handle.AccumKernelTime(elapsed);
            }
        };
    };
}

template <>
InvokerFactory MakeImplGemmDynamicBackwardDataInvokerFactory<int>(const ConvolutionContext& ctx,
                                                                  const int& cfg)
{
    const auto& conv_problem = ctx.conv_problem;
    int hi                   = conv_problem.GetOutHeight();
    int wi                   = conv_problem.GetOutWidth();
    int n                    = conv_problem.GetInBatchSize();
    int k                    = conv_problem.GetInChannels();
    int c                    = conv_problem.GetOutChannels();
    int ho                   = conv_problem.GetInHeight();
    int wo                   = conv_problem.GetInWidth();
    int stride_h             = conv_problem.GetInHeight() > 1 ? conv_problem.GetKernelStrideH() : 1;
    int stride_w             = conv_problem.GetInWidth() > 1 ? conv_problem.GetKernelStrideW() : 1;
    int dilation_h = conv_problem.GetWeightsHeight() > 1 ? conv_problem.GetDilationH() : 1;
    int dilation_w = conv_problem.GetWeightsWidth() > 1 ? conv_problem.GetDilationW() : 1;
    int pad_h      = conv_problem.GetPadH();
    int pad_w      = conv_problem.GetPadW();
    int y          = conv_problem.GetWeightsHeight();
    int x          = conv_problem.GetWeightsWidth();

    int gcd_stride_dilation_h = solver::gcd(stride_h, dilation_h);
    int gcd_stride_dilation_w = solver::gcd(stride_w, dilation_w);
    int y_tilda               = stride_h / gcd_stride_dilation_h;
    int x_tilda               = stride_w / gcd_stride_dilation_w;

    int y_dot = (y + y_tilda - 1) / y_tilda;
    int x_dot = (x + x_tilda - 1) / x_tilda;

    int h_tilda = ho + (dilation_h * (y - 1) + stride_h - 1) / stride_h;
    int w_tilda = wo + (dilation_w * (x - 1) + stride_w - 1) / stride_w;

    int h_tilda_left = std::max(0, pad_h - dilation_h * (y_tilda - 1)) / stride_h;
    int w_tilda_left = std::max(0, pad_w - dilation_w * (x_tilda - 1)) / stride_w;

    int h_tilda_right = std::min(h_tilda, (pad_h + hi - 1 + stride_h - 1) / stride_h + 1);
    int w_tilda_right = std::min(w_tilda, (pad_w + wi - 1 + stride_w - 1) / stride_w + 1);

    int h_tilda_slice = h_tilda_right - h_tilda_left;
    int w_tilda_slice = w_tilda_right - w_tilda_left;

    int num_of_gemms = x_tilda * y_tilda;

    int dtile_dy      = dilation_h / gcd_stride_dilation_h;
    int dtile_dx      = dilation_w / gcd_stride_dilation_w;
    int dtile_y       = y_tilda;
    int dtile_x       = x_tilda;
    int dtile_h       = h_tilda;
    int dtile_w       = w_tilda;
    int dslice_h      = h_tilda_slice;
    int dslice_w      = w_tilda_slice;
    int dslice_h_left = h_tilda_left;
    int dslice_w_left = w_tilda_left;
    int pack_align    = cfg;
    std::vector<int> dtile_iy_gid;
    std::vector<int> dtile_ix_gid;
    std::vector<int> y_dot_slice_gid;
    std::vector<int> x_dot_slice_gid;
    std::vector<bool> is_gemm_not_empty;
    for(int gemm_id = 0; gemm_id < num_of_gemms; gemm_id++)
    {
        dtile_iy_gid.emplace_back(gemm_id / x_tilda);
        dtile_ix_gid.emplace_back(gemm_id % x_tilda);
        y_dot_slice_gid.emplace_back((dtile_iy_gid[gemm_id] + 1) * y_dot <= y ? y_dot : y % y_dot);
        x_dot_slice_gid.emplace_back((dtile_ix_gid[gemm_id] + 1) * x_dot <= x ? x_dot : x % x_dot);
        const int gemm_k_gid = k * y_dot_slice_gid[gemm_id] * x_dot_slice_gid[gemm_id];
        is_gemm_not_empty.emplace_back(gemm_k_gid > 0);
    }
    bool need_set_zero = false;
    if(y < stride_h || x < stride_w || dilation_h != 1 || dilation_w != 1)
        need_set_zero = true;

    return [=](const std::vector<Kernel>& kernels) {
        const auto kernel = kernels[0];
        return [=](const Handle& handle, const AnyInvokeParams& primitive_parameters) {
            decltype(auto) data_ctx = primitive_parameters.CastTo<conv::DataInvokeParams>();
            const auto& tensors     = data_ctx.tensors;
            float elapsed           = 0;
            if(need_set_zero)
            {
                float zero = 0.f;
                SetTensor(handle, tensors.outDesc, tensors.out, &zero);

                if(handle.IsProfilingEnabled())
                    elapsed += handle.GetKernelTime();
            }
            for(int gemm_id = 0; gemm_id < num_of_gemms; gemm_id++)
            {
                if(is_gemm_not_empty[gemm_id])
                {
                    handle.Run(kernel)(tensors.out,
                                       tensors.w,
                                       tensors.in,
                                       hi,
                                       wi,
                                       n,
                                       k,
                                       c,
                                       ho,
                                       wo,
                                       stride_h,
                                       stride_w,
                                       dilation_h,
                                       dilation_w,
                                       pad_h,
                                       pad_w,
                                       y,
                                       x,
                                       dtile_iy_gid[gemm_id],
                                       dtile_ix_gid[gemm_id],
                                       dtile_dy,
                                       dtile_dx,
                                       dtile_y,
                                       dtile_x,
                                       dtile_h,
                                       dtile_w,
                                       y_dot_slice_gid[gemm_id],
                                       x_dot_slice_gid[gemm_id],
                                       dslice_h,
                                       dslice_w,
                                       dslice_h_left,
                                       dslice_w_left,
                                       pack_align);
                    if(handle.IsProfilingEnabled())
                        elapsed += handle.GetKernelTime();
                }
            }

            if(handle.IsProfilingEnabled())
            {
                handle.ResetKernelTime();
                handle.AccumKernelTime(elapsed);
            }
        };
    };
}

template <>
InvokerFactory
MakeImplGemmDynamicBackwardDataInvokerFactory<solver::TunableImplicitGemmGTCDynamic_t>(
    const ConvolutionContext& ctx, const solver::TunableImplicitGemmGTCDynamic_t& cfg)
{
    const auto& conv_problem = ctx.conv_problem;
    int hi                   = conv_problem.GetOutHeight();
    int wi                   = conv_problem.GetOutWidth();
    int n                    = conv_problem.GetInBatchSize();
    int k                    = conv_problem.GetInChannels();
    int c                    = conv_problem.GetOutChannels();
    int ho                   = conv_problem.GetInHeight();
    int wo                   = conv_problem.GetInWidth();
    int stride_h             = conv_problem.GetInHeight() > 1 ? conv_problem.GetKernelStrideH() : 1;
    int stride_w             = conv_problem.GetInWidth() > 1 ? conv_problem.GetKernelStrideW() : 1;
    int dilation_h = conv_problem.GetWeightsHeight() > 1 ? conv_problem.GetDilationH() : 1;
    int dilation_w = conv_problem.GetWeightsWidth() > 1 ? conv_problem.GetDilationW() : 1;
    int pad_h      = conv_problem.GetPadH();
    int pad_w      = conv_problem.GetPadW();
    int y          = conv_problem.GetWeightsHeight();
    int x          = conv_problem.GetWeightsWidth();
    int group      = conv_problem.GetGroupCount();

    int gcd_stride_dilation_h = solver::gcd(stride_h, dilation_h);
    int gcd_stride_dilation_w = solver::gcd(stride_w, dilation_w);
    int y_tilda               = stride_h / gcd_stride_dilation_h;
    int x_tilda               = stride_w / gcd_stride_dilation_w;

    int y_dot = (y + y_tilda - 1) / y_tilda;
    int x_dot = (x + x_tilda - 1) / x_tilda;

    int h_tilda = ho + (dilation_h * (y - 1) + stride_h - 1) / stride_h;
    int w_tilda = wo + (dilation_w * (x - 1) + stride_w - 1) / stride_w;

    int h_tilda_left = std::max(0, pad_h - dilation_h * (y_tilda - 1)) / stride_h;
    int w_tilda_left = std::max(0, pad_w - dilation_w * (x_tilda - 1)) / stride_w;

    int h_tilda_right = std::min(h_tilda, (pad_h + hi - 1 + stride_h - 1) / stride_h + 1);
    int w_tilda_right = std::min(w_tilda, (pad_w + wi - 1 + stride_w - 1) / stride_w + 1);

    int h_tilda_slice = h_tilda_right - h_tilda_left;
    int w_tilda_slice = w_tilda_right - w_tilda_left;

    int num_of_gemms = x_tilda * y_tilda;

    int dtile_dy      = dilation_h / gcd_stride_dilation_h;
    int dtile_dx      = dilation_w / gcd_stride_dilation_w;
    int dtile_y       = y_tilda;
    int dtile_x       = x_tilda;
    int dtile_h       = h_tilda;
    int dtile_w       = w_tilda;
    int dslice_h      = h_tilda_slice;
    int dslice_w      = w_tilda_slice;
    int dslice_h_left = h_tilda_left;
    int dslice_w_left = w_tilda_left;
    int pack_align    = 0;
    std::vector<int> dtile_iy_gid;
    std::vector<int> dtile_ix_gid;
    std::vector<int> y_dot_slice_gid;
    std::vector<int> x_dot_slice_gid;
    std::vector<bool> is_gemm_not_empty;
    for(int gemm_id = 0; gemm_id < num_of_gemms; gemm_id++)
    {
        dtile_iy_gid.emplace_back(gemm_id / x_tilda);
        dtile_ix_gid.emplace_back(gemm_id % x_tilda);
        y_dot_slice_gid.emplace_back((dtile_iy_gid[gemm_id] + 1) * y_dot <= y ? y_dot : y % y_dot);
        x_dot_slice_gid.emplace_back((dtile_ix_gid[gemm_id] + 1) * x_dot <= x ? x_dot : x % x_dot);
        const int gemm_k_gid = k * y_dot_slice_gid[gemm_id] * x_dot_slice_gid[gemm_id];
        is_gemm_not_empty.emplace_back(gemm_k_gid > 0);
    }
    bool need_set_zero = false;
    if(y < stride_h || x < stride_w || dilation_h != 1 || dilation_w != 1)
        need_set_zero = true;

    int nxb = cfg.nxb;
    int b   = h_tilda_slice * w_tilda_slice;
    b       = (cfg.nxe == 0) ? (b) : ((b + nxb - 1) / nxb) * nxb; // pad to nxb modulo when nxe != 0

    uint32_t nb_n0          = cfg.tensor_b_cluster_lengths[2] * cfg.tensor_b_thread_lengths[2];
    uint32_t nb_n1b         = cfg.tensor_b_cluster_lengths[3] * cfg.tensor_b_thread_lengths[3];
    uint32_t unmerge_sub_n  = cfg.gemm_n_per_block / cfg.nxb;
    uint32_t unmerge_sub_n1 = unmerge_sub_n / nb_n0;

    magic_div_u32_t mdiv_2 =
        magic_div_u32_gen(((c / group) * n * b) / (cfg.gemm_m_per_block * cfg.gemm_n_per_block));
    magic_div_u32_t mdiv_3 = magic_div_u32_gen((n * b) / cfg.gemm_n_per_block);
    magic_div_u32_t mdiv_4 = magic_div_u32_gen(b * unmerge_sub_n1 / nb_n1b);
    magic_div_u32_t mdiv_5 = magic_div_u32_gen(b);
    magic_div_u32_t mdiv_6 = magic_div_u32_gen(w_tilda_slice);

    std::vector<magic_div_u32_t> mdiv_0_vec;
    std::vector<magic_div_u32_t> mdiv_1_vec;
    std::vector<uint32_t> shift_pack_0_vec;
    uint32_t shift_pack_1;

    for(int gemm_id = 0; gemm_id < num_of_gemms; gemm_id++)
    {
        if(is_gemm_not_empty[gemm_id])
        {
            mdiv_0_vec.push_back(
                magic_div_u32_gen(y_dot_slice_gid[gemm_id] * x_dot_slice_gid[gemm_id]));
            mdiv_1_vec.push_back(magic_div_u32_gen(x_dot_slice_gid[gemm_id]));
        }
        else
        {
            mdiv_0_vec.push_back(magic_div_u32_t({0, 0}));
            mdiv_1_vec.push_back(magic_div_u32_t({0, 0}));
        };

        shift_pack_0_vec.push_back(magic_div_u32_pack_shift(
            mdiv_0_vec[gemm_id].shift, mdiv_1_vec[gemm_id].shift, mdiv_2.shift, mdiv_3.shift));
    };

    shift_pack_1 = magic_div_u32_pack_shift(mdiv_4.shift, mdiv_5.shift, mdiv_6.shift, 0);

    return [=](const std::vector<Kernel>& kernels) {
        const auto kernel = kernels[0];
        return [=](const Handle& handle, const AnyInvokeParams& primitive_parameters) {
            decltype(auto) data_ctx = primitive_parameters.CastTo<conv::DataInvokeParams>();
            const auto& tensors     = data_ctx.tensors;
            float elapsed           = 0;
            if(need_set_zero)
            {
                float zero = 0.f;
                SetTensor(handle, tensors.outDesc, tensors.out, &zero);

                if(handle.IsProfilingEnabled())
                    elapsed += handle.GetKernelTime();
            }
            for(int gemm_id = 0; gemm_id < num_of_gemms; gemm_id++)
            {
                if(is_gemm_not_empty[gemm_id])
                {
                    handle.Run(kernel)(tensors.out,
                                       tensors.w,
                                       tensors.in,
                                       hi,
                                       wi,
                                       n,
                                       k / group,
                                       c / group,
                                       ho,
                                       wo,
                                       stride_h,
                                       stride_w,
                                       dilation_h,
                                       dilation_w,
                                       pad_h,
                                       pad_w,
                                       y,
                                       x,
                                       dtile_iy_gid[gemm_id],
                                       dtile_ix_gid[gemm_id],
                                       dtile_dy,
                                       dtile_dx,
                                       dtile_y,
                                       dtile_x,
                                       dtile_h,
                                       dtile_w,
                                       y_dot_slice_gid[gemm_id],
                                       x_dot_slice_gid[gemm_id],
                                       dslice_h,
                                       dslice_w,
                                       dslice_h_left,
                                       dslice_w_left,
                                       group,
                                       mdiv_0_vec[gemm_id].magic,
                                       mdiv_1_vec[gemm_id].magic,
                                       mdiv_2.magic,
                                       mdiv_3.magic,
                                       mdiv_4.magic,
                                       mdiv_5.magic,
                                       mdiv_6.magic,
                                       shift_pack_0_vec[gemm_id],
                                       shift_pack_1,
                                       pack_align);
                    if(handle.IsProfilingEnabled())
                        elapsed += handle.GetKernelTime();
                }
            }
            if(handle.IsProfilingEnabled())
            {
                handle.ResetKernelTime();
                handle.AccumKernelTime(elapsed);
            }
        };
    };
}

InvokerFactory MakeImplGemmDynamicForwardXdlopsNHWCInvokerFactory(
    const ConvolutionContext& ctx,
    const solver::PerformanceConfigAsmImplicitGemmGTCFwdXdlopsNHWC& config)
{
    const auto& conv_problem = ctx.conv_problem;
    int hi                   = conv_problem.GetInHeight();
    int wi                   = conv_problem.GetInWidth();
    int n                    = conv_problem.GetInBatchSize();
    int k                    = conv_problem.GetOutChannels();
    int c                    = conv_problem.GetInChannels();
    int ho                   = conv_problem.GetOutHeight();
    int wo                   = conv_problem.GetOutWidth();
    int stride_h             = conv_problem.GetKernelStrideH();
    int stride_w             = conv_problem.GetKernelStrideW();
    int dilation_h           = conv_problem.GetDilationH();
    int dilation_w           = conv_problem.GetDilationW();
    int pad_h                = conv_problem.GetPadH();
    int pad_w                = conv_problem.GetPadW();
    int y                    = conv_problem.GetWeightsHeight();
    int x                    = conv_problem.GetWeightsWidth();
    int group                = conv_problem.GetGroupCount();
    int c_karg               = c / group;
    int y_karg               = y;
    int x_karg               = x;

    int splits_4G = solver::igemm_split_batch_size(
        hi, wi, ho, wo, n, k, c, miopen::GetTypeSize(ctx.in_data_type));
    splits_4G = splits_4G == 0 ? n : splits_4G;

    uint32_t gemm_m = (n / splits_4G) * ho * wo;
    uint32_t gemm_n = k / group;
    magic_div_u32_t mdiv_0, mdiv_1, mdiv_2, mdiv_3, mdiv_4, mdiv_5;
    uint32_t shift_pack_0, shift_pack_1;
    uint32_t pack0 = 0;

    mdiv_0 = magic_div_u32_gen((gemm_n + config.gemm_n_per_block - 1) / config.gemm_n_per_block);
    mdiv_1 = magic_div_u32_gen(ho * wo);
    mdiv_2 = magic_div_u32_gen(wo);
    mdiv_3 = magic_div_u32_gen(((gemm_m + config.gemm_m_per_block - 1) / config.gemm_m_per_block) *
                               ((gemm_n + config.gemm_n_per_block - 1) / config.gemm_n_per_block));

    shift_pack_0 = magic_div_u32_pack_shift(mdiv_0.shift, mdiv_1.shift, mdiv_2.shift, mdiv_3.shift);
    if(config.merge_e != 0)
    {
        mdiv_4       = magic_div_u32_gen(x * (c / group));
        mdiv_5       = magic_div_u32_gen(c / group);
        shift_pack_1 = magic_div_u32_pack_shift(mdiv_4.shift, mdiv_5.shift, 0, 0);

        uint32_t s_move_slice_k_y = (config.gemm_k_per_block / (x * (c / group))) % y;
        uint32_t s_move_slice_k_x = (config.gemm_k_per_block / (c / group)) % x;
        uint32_t s_move_slice_k_c = config.gemm_k_per_block % (c / group);
        y_karg                    = static_cast<int>((s_move_slice_k_y << 24) | y);
        x_karg                    = static_cast<int>((s_move_slice_k_x << 24) | x);
        c_karg                    = static_cast<int>((s_move_slice_k_c << 24) | (c / group));
    }
    else
    {
        mdiv_4       = magic_div_u32_gen(1);
        mdiv_5       = magic_div_u32_gen(1);
        shift_pack_1 = 0;
    }

    bool need_set_zero                 = config.gemm_k_global_split > 0;
    bool use_fp32_global_split_on_fp16 = config.vector_store == 1 && config.gemm_k_global_split > 0;

    std::vector<OpKernelArg> opArgs;
    opArgs.emplace_back(0); // placeholder
    opArgs.emplace_back(0); // placeholder
    opArgs.emplace_back(0); // placeholder
    opArgs.emplace_back(hi);
    opArgs.emplace_back(wi);
    opArgs.emplace_back(n / splits_4G);
    opArgs.emplace_back(k / group);
    opArgs.emplace_back(c_karg);
    opArgs.emplace_back(ho);
    opArgs.emplace_back(wo);
    opArgs.emplace_back(stride_h);
    opArgs.emplace_back(stride_w);
    opArgs.emplace_back(dilation_h);
    opArgs.emplace_back(dilation_w);
    opArgs.emplace_back(pad_h);
    opArgs.emplace_back(pad_w);
    opArgs.emplace_back(y_karg);
    opArgs.emplace_back(x_karg);
    opArgs.emplace_back(group);
    opArgs.emplace_back(mdiv_0.magic);
    opArgs.emplace_back(mdiv_1.magic);
    opArgs.emplace_back(mdiv_2.magic);
    opArgs.emplace_back(mdiv_3.magic);
    opArgs.emplace_back(mdiv_4.magic);
    opArgs.emplace_back(mdiv_5.magic);
    opArgs.emplace_back(shift_pack_0);
    opArgs.emplace_back(shift_pack_1);
    opArgs.emplace_back(config.gemm_k_global_split);
    opArgs.emplace_back(pack0);

    std::vector<std::vector<OpKernelArg>> opArgsTrans;

    const auto lowp_quant = ctx.conv_problem.GetConv().lowp_quant;
    const auto isGfx90aFp16altSupport =
        (ctx.GetStream().GetDeviceName() == "gfx90a") && conv_problem.IsFp16();

    const bool need_cast = [&]() {
        if(ctx.conv_problem.GetOut().GetType() == miopenHalf)
            return use_fp32_global_split_on_fp16;
        if(ctx.conv_problem.GetOut().GetType() == miopenBFloat16)
            return need_set_zero;
        return false;
    }();
    const auto is_nchw = ctx.IsLayoutDefault();

    size_t trans_input_offset = 0;
    size_t trans_input_size   = 0;

    size_t trans_weight_offset = 0;
    size_t trans_weight_size   = 0;

    size_t trans_output_offset = 0;
    size_t trans_output_size   = 0;

    bool trans_input_skippable  = false;
    bool trans_weight_skippable = false;
    bool trans_output_skippable = false;

    int trans_input_idx  = -1;
    int trans_weight_idx = -1;
    int trans_output_idx = -1;

    constexpr size_t buf_alignment = 256;

    if(is_nchw)
    {
        TransposeSolutionDefault2Nhwc trans_input(ctx, ctx.in_data_type, n, c, hi, wi);
        TransposeSolutionDefault2Nhwc trans_weight(ctx,
                                                   ctx.weights_data_type,
                                                   k,
                                                   c / group,
                                                   y,
                                                   x); // group * k_per_group as batch for weight
        TransposeSolutionNhwc2Default trans_output(ctx, ctx.out_data_type, n, k, ho, wo);

        trans_input_skippable  = trans_input.IsSkippable();
        trans_weight_skippable = trans_weight.IsSkippable();
        trans_output_skippable = trans_output.IsSkippable();

        if(!trans_input_skippable)
            opArgsTrans.emplace_back(trans_input.GetKernelArg());
        if(!trans_weight_skippable)
            opArgsTrans.emplace_back(trans_weight.GetKernelArg());
        if(!trans_output_skippable)
            opArgsTrans.emplace_back(trans_output.GetKernelArg());

        trans_input_size  = trans_input_skippable ? 0 : trans_input.GetSize();
        trans_weight_size = trans_weight_skippable ? 0 : trans_weight.GetSize();
        trans_output_size = trans_output_skippable ? 0 : trans_output.GetSize();

        int idx = 0;
        if(!trans_input_skippable)
            trans_input_idx = idx++;
        if(!trans_weight_skippable)
            trans_weight_idx = idx++;
        if(!trans_output_skippable)
            trans_output_idx = idx++;
    }

    const size_t cast_size = need_cast ? miopen::GetTypeSize(miopenFloat) * n * k * ho * wo : 0;

    MultiBufferWorkspaceTraits wt(
        {trans_input_size, trans_weight_size, trans_output_size, cast_size}, buf_alignment);

    trans_input_offset  = wt.GetOffset(0);
    trans_weight_offset = wt.GetOffset(1);
    trans_output_offset = wt.GetOffset(2);

    const size_t cast_offset = wt.GetOffset(3);

    const int kID_trans_start = isGfx90aFp16altSupport ? 2 : 1;

    const TensorDescriptor cast_desc(miopenFloat,
                                     ctx.conv_problem.GetOut().GetLengths(),
                                     ctx.conv_problem.GetOut().GetStrides());
    auto null_buf = shared<Data_t>{};

    return [=](const std::vector<Kernel>& kernels) mutable {
        return [=](const Handle& handle, const AnyInvokeParams& primitive_parameters) mutable {
            decltype(auto) data_ctx = primitive_parameters.CastTo<conv::DataInvokeParams>();
            const auto& tensors     = data_ctx.tensors;
            const auto& workSpace   = data_ctx.workSpace;
            const auto ker =
                handle.Run(kernels[(isGfx90aFp16altSupport && data_ctx.gfx90aFp16alt) ? 1 : 0]);
            float elapsed = 0;

            auto trans_input_buf =
                trans_input_size == 0
                    ? null_buf
                    : handle.CreateSubBuffer(workSpace, trans_input_offset, trans_input_size);
            auto trans_weight_buf =
                trans_weight_size == 0
                    ? null_buf
                    : handle.CreateSubBuffer(workSpace, trans_weight_offset, trans_weight_size);
            auto trans_output_buf =
                trans_output_size == 0
                    ? null_buf
                    : handle.CreateSubBuffer(workSpace, trans_output_offset, trans_output_size);
            auto cast_buf = cast_size == 0
                                ? null_buf
                                : handle.CreateSubBuffer(workSpace, cast_offset, cast_size);

            if(need_set_zero)
            {
                auto zero_buf = need_cast
                                    ? cast_buf.get()
                                    : ((is_nchw && !trans_output_skippable) ? trans_output_buf.get()
                                                                            : tensors.out);
                auto& zero_desc =
                    need_cast
                        ? cast_desc
                        : tensors.outDesc; // use the same desc for NCHW/NHWC for this dense tensor
                float zero = 0.f;

                SetTensor(handle, zero_desc, zero_buf, &zero);
                if(handle.IsProfilingEnabled())
                    elapsed += handle.GetKernelTime();
            }

            if(is_nchw)
            {
                if(!trans_input_skippable)
                {
                    auto& karg_input = opArgsTrans[trans_input_idx];
                    karg_input[0]    = OpKernelArg(trans_input_buf.get());
                    karg_input[1]    = OpKernelArg(tensors.in);
                    handle.Run(kernels[kID_trans_start + trans_input_idx])(karg_input);
                    if(handle.IsProfilingEnabled())
                        elapsed += handle.GetKernelTime();
                }
                if(!trans_weight_skippable)
                {
                    auto& karg_weight = opArgsTrans[trans_weight_idx];
                    karg_weight[0]    = OpKernelArg(trans_weight_buf.get());
                    karg_weight[1]    = OpKernelArg(tensors.w);
                    handle.Run(kernels[kID_trans_start + trans_weight_idx])(karg_weight);
                    if(handle.IsProfilingEnabled())
                        elapsed += handle.GetKernelTime();
                }
            }

            opArgs[0] = (is_nchw && !trans_input_skippable) ? OpKernelArg(trans_input_buf.get())
                                                            : OpKernelArg(tensors.in);
            opArgs[1] = (is_nchw && !trans_weight_skippable) ? OpKernelArg(trans_weight_buf.get())
                                                             : OpKernelArg(tensors.w);

            opArgs[2] = need_cast ? OpKernelArg(cast_buf.get())
                                  : ((is_nchw && !trans_output_skippable)
                                         ? OpKernelArg(trans_output_buf.get())
                                         : OpKernelArg(tensors.out));
            ker(opArgs);
            if(handle.IsProfilingEnabled())
                elapsed += handle.GetKernelTime();

            if(need_cast)
            {
                CastTensor(handle,
                           &lowp_quant,
                           cast_desc,
                           cast_buf.get(),
                           tensors.outDesc,
                           (is_nchw && !trans_output_skippable) ? trans_output_buf.get()
                                                                : tensors.out,
                           0,
                           0);
                if(handle.IsProfilingEnabled())
                    elapsed += handle.GetKernelTime();
            }

            if(is_nchw && !trans_output_skippable)
            {
                auto& karg_output = opArgsTrans[trans_output_idx];
                karg_output[0]    = OpKernelArg(tensors.out);
                karg_output[1]    = OpKernelArg(trans_output_buf.get());
                handle.Run(kernels[kID_trans_start + trans_output_idx])(karg_output);
                if(handle.IsProfilingEnabled())
                    elapsed += handle.GetKernelTime();
            }

            if(handle.IsProfilingEnabled())
            {
                handle.ResetKernelTime();
                handle.AccumKernelTime(elapsed);
            }
        };
    };
}

InvokerFactory MakeImplGemmDynamicBackwardDataXdlopsNHWCInvokerFactory(
    const ConvolutionContext& ctx,
    const solver::PerformanceConfigAsmImplicitGemmGTCBwdXdlopsNHWC& config)
{
    const auto& conv_problem = ctx.conv_problem;
    int hi                   = conv_problem.GetOutHeight();
    int wi                   = conv_problem.GetOutWidth();
    int n                    = conv_problem.GetInBatchSize();
    int k                    = conv_problem.GetInChannels();
    int c                    = conv_problem.GetOutChannels();
    int ho                   = conv_problem.GetInHeight();
    int wo                   = conv_problem.GetInWidth();
    int stride_h             = conv_problem.GetInHeight() > 1 ? conv_problem.GetKernelStrideH() : 1;
    int stride_w             = conv_problem.GetInWidth() > 1 ? conv_problem.GetKernelStrideW() : 1;
    int dilation_h = conv_problem.GetWeightsHeight() > 1 ? conv_problem.GetDilationH() : 1;
    int dilation_w = conv_problem.GetWeightsWidth() > 1 ? conv_problem.GetDilationW() : 1;
    int pad_h      = conv_problem.GetPadH();
    int pad_w      = conv_problem.GetPadW();
    int y          = conv_problem.GetWeightsHeight();
    int x          = conv_problem.GetWeightsWidth();
    int group      = conv_problem.GetGroupCount();

    int gcd_stride_dilation_h = solver::gcd(stride_h, dilation_h);
    int gcd_stride_dilation_w = solver::gcd(stride_w, dilation_w);
    int y_tilda               = stride_h / gcd_stride_dilation_h;
    int x_tilda               = stride_w / gcd_stride_dilation_w;

    int h_tilda = ho + (dilation_h * (y - 1) + stride_h - 1) / stride_h;
    int w_tilda = wo + (dilation_w * (x - 1) + stride_w - 1) / stride_w;

    int h_tilda_left = std::max(0, pad_h - dilation_h * (y_tilda - 1)) / stride_h;
    int w_tilda_left = std::max(0, pad_w - dilation_w * (x_tilda - 1)) / stride_w;

    int h_tilda_right = std::min(h_tilda, (pad_h + hi - 1 + stride_h - 1) / stride_h + 1);
    int w_tilda_right = std::min(w_tilda, (pad_w + wi - 1 + stride_w - 1) / stride_w + 1);

    int h_tilda_slice = h_tilda_right - h_tilda_left;
    int w_tilda_slice = w_tilda_right - w_tilda_left;

    int num_of_gemms = x_tilda * y_tilda;

    int splits_4G = solver::igemm_split_batch_size(
        hi, wi, ho, wo, n, k, c, miopen::GetTypeSize(ctx.in_data_type));
    int n_in_1_block = splits_4G == 0 ? 1 : (n / splits_4G);

    uint32_t gemm_m = n_in_1_block * h_tilda_slice * w_tilda_slice;
    uint32_t gemm_n = c / group;

    magic_div_u32_t mdiv_x_tilda  = magic_div_u32_gen(x_tilda);
    magic_div_u32_t mdiv_y_tilda  = magic_div_u32_gen(y_tilda);
    magic_div_u32_t mdiv_group_mn = magic_div_u32_gen(
        group * ((gemm_n + config.gemm_n_per_block - 1) / config.gemm_n_per_block) *
        ((gemm_m + config.gemm_m_per_block - 1) / config.gemm_m_per_block));

    magic_div_u32_t mdiv_0 =
        magic_div_u32_gen((gemm_n + config.gemm_n_per_block - 1) / config.gemm_n_per_block);
    magic_div_u32_t mdiv_1 =
        magic_div_u32_gen(((gemm_n + config.gemm_n_per_block - 1) / config.gemm_n_per_block) *
                          ((gemm_m + config.gemm_m_per_block - 1) / config.gemm_m_per_block));
    magic_div_u32_t mdiv_2 = magic_div_u32_gen(config.nxe != 0 ? w_tilda_slice : wi);
    magic_div_u32_t mdiv_3 = magic_div_u32_gen(h_tilda_slice * w_tilda_slice);
    uint32_t shift_pack_0 =
        magic_div_u32_pack_shift(mdiv_0.shift, mdiv_1.shift, mdiv_2.shift, mdiv_3.shift);

    int dtile_iy = num_of_gemms > 1 ? static_cast<int>(mdiv_x_tilda.magic) : 0;
    int dtile_ix = num_of_gemms > 1 ? static_cast<int>(mdiv_x_tilda.shift) : 0;
    int dslice_y = num_of_gemms > 1 ? static_cast<int>(mdiv_y_tilda.magic) : y;
    int dslice_x = num_of_gemms > 1 ? static_cast<int>(mdiv_y_tilda.shift) : x;
    int dtile_h  = num_of_gemms > 1 ? static_cast<int>(mdiv_group_mn.magic) : h_tilda;
    int dtile_w  = num_of_gemms > 1 ? static_cast<int>(mdiv_group_mn.shift) : w_tilda;

    bool need_set_zero                 = false;
    bool use_fp32_global_split_on_fp16 = config.vector_store == 1 && config.gemm_k_global_split > 0;
    if(y < stride_h || x < stride_w || dilation_h != 1 || dilation_w != 1)
        need_set_zero = true;
    need_set_zero |= config.gemm_k_global_split > 0;

    std::vector<OpKernelArg> opArgs;
    opArgs.emplace_back(0); // placeholder
    opArgs.emplace_back(0); // placeholder
    opArgs.emplace_back(0); // placeholder
    opArgs.emplace_back(hi);
    opArgs.emplace_back(wi);
    opArgs.emplace_back(n_in_1_block);
    opArgs.emplace_back(k / group);
    opArgs.emplace_back(c / group);
    opArgs.emplace_back(ho);
    opArgs.emplace_back(wo);
    opArgs.emplace_back(stride_h);
    opArgs.emplace_back(stride_w);
    opArgs.emplace_back(dilation_h);
    opArgs.emplace_back(dilation_w);
    opArgs.emplace_back(pad_h);
    opArgs.emplace_back(pad_w);
    opArgs.emplace_back(y);
    opArgs.emplace_back(x);

    opArgs.emplace_back(dtile_iy);
    opArgs.emplace_back(dtile_ix);
    opArgs.emplace_back(dilation_h / gcd_stride_dilation_h);
    opArgs.emplace_back(dilation_w / gcd_stride_dilation_w);
    opArgs.emplace_back(y_tilda);
    opArgs.emplace_back(x_tilda);
    opArgs.emplace_back(dtile_h);
    opArgs.emplace_back(dtile_w);
    opArgs.emplace_back(dslice_y);
    opArgs.emplace_back(dslice_x);

    opArgs.emplace_back(h_tilda_slice);
    opArgs.emplace_back(w_tilda_slice);
    opArgs.emplace_back(h_tilda_left);
    opArgs.emplace_back(w_tilda_left);
    opArgs.emplace_back(group);

    opArgs.emplace_back(mdiv_0.magic);
    opArgs.emplace_back(mdiv_1.magic);
    opArgs.emplace_back(mdiv_2.magic);
    opArgs.emplace_back(mdiv_3.magic);
    opArgs.emplace_back(shift_pack_0);
    opArgs.emplace_back(config.gemm_k_global_split);

    std::vector<std::vector<OpKernelArg>> opArgsTrans;

    const auto lowp_quant = ctx.conv_problem.GetConv().lowp_quant;
    const auto isGfx90aFp16altSupport =
        (ctx.GetStream().GetDeviceName() == "gfx90a") && conv_problem.IsFp16();
    const bool need_cast = [&]() {
        if(ctx.conv_problem.GetOut().GetType() == miopenHalf)
            return use_fp32_global_split_on_fp16;
        if(ctx.conv_problem.GetOut().GetType() == miopenBFloat16)
            return need_set_zero;
        return false;
    }();
    const auto is_nchw = ctx.IsLayoutDefault();

    size_t trans_input_offset = 0;
    size_t trans_input_size   = 0;

    size_t trans_weight_offset = 0;
    size_t trans_weight_size   = 0;

    size_t trans_output_offset = 0;
    size_t trans_output_size   = 0;

    bool trans_input_skippable  = false;
    bool trans_weight_skippable = false;
    bool trans_output_skippable = false;

    int trans_input_idx  = -1;
    int trans_weight_idx = -1;
    int trans_output_idx = -1;

    constexpr size_t buf_alignment = 256;

    if(is_nchw)
    {
        TransposeSolutionNhwc2Default trans_input(ctx, ctx.out_data_type, n, c, hi, wi);
        TransposeSolutionDefault2Nhwc trans_weight(ctx,
                                                   ctx.weights_data_type,
                                                   k,
                                                   c / group,
                                                   y,
                                                   x); // group * k_per_group as batch for weight
        TransposeSolutionDefault2Nhwc trans_output(ctx, ctx.in_data_type, n, k, ho, wo);

        trans_input_skippable  = trans_input.IsSkippable();
        trans_weight_skippable = trans_weight.IsSkippable();
        trans_output_skippable = trans_output.IsSkippable();

        if(!trans_input_skippable)
            opArgsTrans.emplace_back(trans_input.GetKernelArg());
        if(!trans_weight_skippable)
            opArgsTrans.emplace_back(trans_weight.GetKernelArg());
        if(!trans_output_skippable)
            opArgsTrans.emplace_back(trans_output.GetKernelArg());

        trans_input_size  = trans_input_skippable ? 0 : trans_input.GetSize();
        trans_weight_size = trans_weight_skippable ? 0 : trans_weight.GetSize();
        trans_output_size = trans_output_skippable ? 0 : trans_output.GetSize();

        int idx = 0;
        if(!trans_input_skippable)
            trans_input_idx = idx++;
        if(!trans_weight_skippable)
            trans_weight_idx = idx++;
        if(!trans_output_skippable)
            trans_output_idx = idx++;
    }

    const size_t cast_size = need_cast ? miopen::GetTypeSize(miopenFloat) * n * c * hi * wi : 0;

    MultiBufferWorkspaceTraits wt(
        {trans_input_size, trans_weight_size, trans_output_size, cast_size}, buf_alignment);

    trans_input_offset  = wt.GetOffset(0);
    trans_weight_offset = wt.GetOffset(1);
    trans_output_offset = wt.GetOffset(2);

    const size_t cast_offset = wt.GetOffset(3);

    const int kID_trans_start = isGfx90aFp16altSupport ? 2 : 1;

    const TensorDescriptor cast_desc(miopenFloat,
                                     ctx.conv_problem.GetOut().GetLengths(),
                                     ctx.conv_problem.GetOut().GetStrides());
    auto null_buf = shared<Data_t>{};

    return [=](const std::vector<Kernel>& kernels) mutable {
        return [=](const Handle& handle, const AnyInvokeParams& primitive_parameters) mutable {
            decltype(auto) data_ctx = primitive_parameters.CastTo<conv::DataInvokeParams>();
            const auto& tensors     = data_ctx.tensors;
            const auto& workSpace   = data_ctx.workSpace;
            const auto ker =
                handle.Run(kernels[(isGfx90aFp16altSupport && data_ctx.gfx90aFp16alt) ? 1 : 0]);
            float elapsed = 0;

            auto trans_input_buf =
                trans_input_size == 0
                    ? null_buf
                    : handle.CreateSubBuffer(workSpace, trans_input_offset, trans_input_size);
            auto trans_weight_buf =
                trans_weight_size == 0
                    ? null_buf
                    : handle.CreateSubBuffer(workSpace, trans_weight_offset, trans_weight_size);
            auto trans_output_buf =
                trans_output_size == 0
                    ? null_buf
                    : handle.CreateSubBuffer(workSpace, trans_output_offset, trans_output_size);
            auto cast_buf = cast_size == 0
                                ? null_buf
                                : handle.CreateSubBuffer(workSpace, cast_offset, cast_size);

            if(need_set_zero)
            {
                auto zero_buf = need_cast
                                    ? cast_buf.get()
                                    : ((is_nchw && !trans_input_skippable) ? trans_input_buf.get()
                                                                           : tensors.out);
                auto& zero_desc =
                    need_cast
                        ? cast_desc
                        : tensors.outDesc; // use the same desc for NCHW/NHWC for this dense tensor
                float zero = 0.f;

                SetTensor(handle, zero_desc, zero_buf, &zero);
                if(handle.IsProfilingEnabled())
                    elapsed += handle.GetKernelTime();
            }

            if(is_nchw)
            {
                if(!trans_output_skippable)
                {
                    auto& karg_output = opArgsTrans[trans_output_idx];
                    karg_output[0]    = OpKernelArg(trans_output_buf.get());
                    karg_output[1]    = OpKernelArg(tensors.in);
                    handle.Run(kernels[kID_trans_start + trans_output_idx])(karg_output);
                    if(handle.IsProfilingEnabled())
                        elapsed += handle.GetKernelTime();
                }
                if(!trans_weight_skippable)
                {
                    auto& karg_weight = opArgsTrans[trans_weight_idx];
                    karg_weight[0]    = OpKernelArg(trans_weight_buf.get());
                    karg_weight[1]    = OpKernelArg(tensors.w);
                    handle.Run(kernels[kID_trans_start + trans_weight_idx])(karg_weight);
                    if(handle.IsProfilingEnabled())
                        elapsed += handle.GetKernelTime();
                }
            }

            opArgs[0] = need_cast ? OpKernelArg(cast_buf.get())
                                  : ((is_nchw && !trans_input_skippable)
                                         ? OpKernelArg(trans_input_buf.get())
                                         : OpKernelArg(tensors.out));
            opArgs[1] = (is_nchw && !trans_weight_skippable) ? OpKernelArg(trans_weight_buf.get())
                                                             : OpKernelArg(tensors.w);
            opArgs[2] = (is_nchw && !trans_output_skippable) ? OpKernelArg(trans_output_buf.get())
                                                             : OpKernelArg(tensors.in);

            ker(opArgs);
            if(handle.IsProfilingEnabled())
                elapsed += handle.GetKernelTime();

            if(need_cast)
            {
                CastTensor(handle,
                           &lowp_quant,
                           cast_desc,
                           cast_buf.get(),
                           tensors.outDesc,
                           (is_nchw && !trans_input_skippable) ? trans_input_buf.get()
                                                               : tensors.out,
                           0,
                           0);
                if(handle.IsProfilingEnabled())
                    elapsed += handle.GetKernelTime();
            }
            if((is_nchw && !trans_input_skippable))
            {
                auto& karg_input = opArgsTrans[trans_input_idx];
                karg_input[0]    = OpKernelArg(tensors.out);
                karg_input[1]    = OpKernelArg(trans_input_buf.get());
                handle.Run(kernels[kID_trans_start + trans_input_idx])(karg_input);
                if(handle.IsProfilingEnabled())
                    elapsed += handle.GetKernelTime();
            }

            if(handle.IsProfilingEnabled())
            {
                handle.ResetKernelTime();
                handle.AccumKernelTime(elapsed);
            }
        };
    };
}

} // namespace conv
} // namespace miopen


//----------------------------------------------------------------------------
// Copyright (C) 2018-2020 Mikhail Komarov <nemo@nil.foundation>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Server Side Public License, version 1,
// as published by the author.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// Server Side Public License for more details.
//
// You should have received a copy of the Server Side Public License
// along with this program. If not, see
// <https://github.com/NilFoundation/plugin/blob/master/LICENSE_1_0.txt>.
//----------------------------------------------------------------------------

#define BOOST_TEST_MODULE post_fallback_vanilla_test

#include <boost/test/data/monomorphic.hpp>
#include <boost/test/data/test_case.hpp>
#include <boost/test/unit_test.hpp>

#include <nil/filecoin/storage/proofs/core/sector.hpp>

#include <nil/filecoin/storage/proofs/post/fallback/vanilla.hpp>

#include "../../core/merkle/generate_tree.hpp"

using namespace nil::filecoin;

BOOST_AUTO_TEST_SUITE(post_fallback_vanilla_test_suite)

template<typename MerkleTreeType>
void test_fallback_post() {
    const auto rng = XorShiftRng::from_seed(crate::TEST_SEED);

    std::size_t leaves = 64 * get_base_tree_count<MerkleTreeType>();
    std::size_t sector_size = leaves * NODE_SIZE;

    PublicParams pub_params = {sector_size, 40, 1};

    const auto randomness = typename MerkleTreeType::hash_type::digest_type::random(rng);
    const auto prover_id = typename MerkleTreeType::hash_type::digest_type::random(rng);

    std::vector<sector_id_type> sectors;
    auto trees = BTreeMap();

    // Construct and store an MT using a named store.
    const auto temp_dir = tempfile::tempdir();
    const auto temp_path = temp_dir.path();

    for (int i = 0; i < 5; i++) {
        sectors.push_back(i.into());
        auto data, tree;
        const std::tie(data, tree) = merkletree::generate_tree<MerkleTreeType>(rng, leaves, Some(temp_path.to_path_buf()));
        trees.insert(i.into(), tree);
    }

    const auto candidates = generate_candidates<MerkleTreeType>(pub_params, sectors, trees, prover_id, randomness);

    const auto candidate = &candidates[0];
    const auto tree = trees.remove(&candidate.sector_id);
    const auto comm_r_last = tree.root();
    const auto comm_c = typename MerkleTreeType::hash_type::digest_type::random(rng);
    const auto comm_r = <typename MerkleTreeType::hash_type>::Function::hash2(&comm_c, &comm_r_last);

    PublicInputs pub_inputs = {randomness, candidate.sector_id, prover_id, comm_r, candidate.partial_ticket, 0};

    PrivateInputs<MerkleTreeType> priv_inputs = {tree, comm_c, comm_r_last};

    const auto proof = ElectionPoSt<MerkleTreeType>::prove(&pub_params, &pub_inputs, &priv_inputs).expect("proving failed");

    const auto is_valid = ElectionPoSt<MerkleTreeType>::verify(&pub_params, &pub_inputs, &proof)
                       .expect(
                           "verification "
                           "failed");

    BOOST_CHECK(is_valid);
}

BOOST_AUTO_TEST_CASE(fallback_post_pedersen) {
    test_fallback_post<LCTree<PedersenHasher, U8, U0, U0>>();
}

BOOST_AUTO_TEST_CASE(fallback_post_poseidon) {
    test_fallback_post<LCTree<PoseidonHasher, U8, U0, U0>>();
}

BOOST_AUTO_TEST_CASE(fallback_post_poseidon_8_8) {
    test_fallback_post<LCTree<PoseidonHasher, U8, U8, U0>>();
}

BOOST_AUTO_TEST_CASE(fallback_post_poseidon_8_8_2) {
    test_fallback_post<LCTree<PoseidonHasher, U8, U8, U2>>();
}

BOOST_AUTO_TEST_SUITE_END()


#include <iostream>
#include <sstream>

#include <armadillo>

#include <cpp-argparse/OptionParser.h>
#include <besiq/io/covariates.hpp>
#include <glm/irls.hpp>
#include <glm/models/normal.hpp>

#include <plink/plink_file.hpp>

#include "gene_environment.hpp"

using namespace arma;
using namespace optparse;

const std::string USAGE = "besiq-gxe [OPTIONS] plink_file variable_file";
const std::string DESCRIPTION = "Calculates the full regression model (only linear).";
const std::string VERSION = "besiq 0.0.1";
const std::string EPILOG = "";

std::vector<std::string>
parse_variables(std::istream &stream)
{
    std::string line;
    std::vector<std::string> variable_list;
    while( std::getline( stream, line ) )
    {
        std::istringstream line_stream( line );
        std::string variable_name;

        line_stream >> variable_name;

        variable_list.push_back( variable_name );
    }

    return variable_list;
}

arma::uvec get_included_indices(const std::vector<std::string> variable_order, const std::vector<std::string> &labels)
{
    std::vector<size_t> indices;
    std::map<std::string, int> variable_to_index;
    for(int i = 0; i < variable_order.size( ); i++)
    {
        variable_to_index[ variable_order[ i ] ] = i;
    }

    for(int i = 0; i < labels.size( ); i++)
    {
        if( variable_to_index.count( labels[ i ] ) > 0 )
        {
            indices.push_back( variable_to_index[ labels[ i ] ] );
        }
    }

    arma::uvec result = arma::zeros<arma::uvec>( indices.size( ) );
    for(int i = 0; i < indices.size( ); i++)
    {
        result[ i ] = indices[ i ];
    }

    return result;
}

int
main(int argc, char *argv[])
{
    OptionParser parser = OptionParser( ).usage( USAGE )
                                         .version( VERSION )
                                         .description( DESCRIPTION )
                                         .epilog( EPILOG );

    parser.add_option( "-p", "--pheno" ).help( "Read phenotypes from this file instead of a plink file." );
    parser.add_option( "-e", "--env" ).action( "store" ).type( "string" ).metavar( "filename" ).help( "Environemental variables." );
    parser.add_option( "-o", "--out" ).help( "The output file that will contain the results (binary)." );
    parser.add_option( "--standardize" ).help( "Use standardized genotypes and covariates." ).action( "store_true" );

    Values options = parser.parse_args( argc, argv );
    if( parser.args( ).size( ) != 2 )
    {
        parser.print_help( );
        exit( 1 );
    }

    std::ios_base::sync_with_stdio( false );

    /* Read all genotypes */
    plink_file_ptr genotype_file = open_plink_file( parser.args( )[ 0 ] );
    genotype_matrix_ptr genotypes = create_genotype_matrix( genotype_file );
    std::vector<std::string> order = genotype_file->get_sample_iids( );
    std::vector< std::pair<std::string, std::string> > fid_iid = genotype_file->get_sample_fid_iid( );

    std::ifstream variable_file( parser.args( )[ 1 ].c_str( ) );
    std::vector<std::string> variable_list = parse_variables( variable_file );

    /* Make error streams separate from stdout */
    arma::set_stream_err1( std::cerr );
    arma::set_stream_err2( std::cerr );

    /* Parse phenotypes */
    arma::uvec missing_not_used = arma::zeros<arma::uvec>( genotype_file->get_samples( ).size( ) );
    arma::vec phenotype;
    if( options.is_set( "pheno" ) )
    {
        std::ifstream phenotype_file( options[ "pheno" ].c_str( ) );
        phenotype = parse_phenotypes( phenotype_file, missing_not_used, order, options[ "mpheno" ] );
    }
    else
    {
        phenotype = create_phenotype_vector( genotype_file->get_samples( ), missing_not_used );
    }

    /* Parse env */
    arma::uvec env_missing = arma::zeros<arma::uvec>( genotype_file->get_samples( ).size( ) );
    arma::mat env;
    std::vector<std::string> env_names;
    if( options.is_set( "env" ) )
    {
        std::ifstream env_file( options[ "env" ].c_str( ) );
        env = parse_covariate_matrix( env_file, env_missing, order, &env_names );
    }

    /* Open output stream */
    std::ofstream output_file;
    if( options.is_set( "out" ) )
    {
        output_file.open( options[ "out" ].c_str( ) );
    }
    std::ostream &out = options.is_set( "out" ) ? output_file : std::cout;

    gene_environment ge( genotypes, env, phenotype, env_names, false );
    ge.impute_missing( );

    arma::uvec included = get_included_indices( ge.get_names( ), variable_list );
    if( included.n_elem != variable_list.size( ) )
    {
        std::cout << "besiq-gxe: Error could not find all genotype or covariate variables.\n";
        return 1;
    }

    bool standardize = options.is_set( "only_pvalues" );

    arma::mat X;
    if( standardize )
    {
        X = ge.get_active( included );
    }
    else
    {
        X = ge.get_active_raw( included );
    }

    glm_info result;
    normal model( "identity" );
    arma::uvec missing = arma::zeros<arma::uvec>( genotype_file->get_samples( ).size( ) );
    arma::vec beta = irls( X, ge.get_centered_phenotype(), missing, model, result);

    out << "snp\tbeta\tse_beta\tpvalue\n";
    if( result.success && result.converged )
    {
        for(int i = 0; i < included.n_elem; i++)
        {
            out << ge.get_name( included[ i ] ) << "\t" <<
                beta[ i ] << "\t" <<
                result.se_beta[ i ] << "\t" <<
                result.p_value[ i ] << "\n";
        }
    }

    return 0;
}


//Copyright (c) 2013 Singapore-MIT Alliance for Research and Technology
//Licensed under the terms of the MIT License, as described in the file:
//   license.txt   (http://opensource.org/licenses/MIT)

#pragma once

#include <boost/random.hpp>
#include <map>
#include <set>
#include <string>

#include "config/params/ParameterManager.hpp"
#include "entities/models/Constants.hpp"
#include "entities/roles/driver/Driver.hpp"
#include "entities/roles/driver/DriverPathMover.hpp"
#include "entities/vehicle/VehicleBase.hpp"

using namespace std;

namespace sim_mob
{
class DriverUpdateParams;
class NearestVehicle;
class Driver;

/**Abstract class which describes car following*/
class CarFollowingModel
{
public:
    /**The model name*/
    string modelName;

    /**The acceleration grade factor*/
    double accGradeFactor;

    /**The next perception size*/
    double nextPerceptionSize;

    /**Minimum speed*/
    double minSpeed;

    /**The update step sizes*/
    std::vector<double> updateStepSize;

    /**The perception sizes*/
    std::vector<double> perceptionSize;

    /**Split delimiter in the driver parameters XML file*/
    string splitDelimiter;

protected:
    /**The pointer to the driver path mover object*/
    DriverPathMover *fwdDriverMovement;

    /**
     * Calculates the step size of update state variables
     *
     * @param params
     *
     * @return
     */
    double calcNextStepSize(DriverUpdateParams &params);

public:
    CarFollowingModel(DriverPathMover *pathMover) : fwdDriverMovement(pathMover)
    {
    }
    virtual ~CarFollowingModel()
    {
        updateStepSize.clear();
        perceptionSize.clear();
    }

    /**
     * Calculates the acceleration rate based on interaction with other vehicles
     *
     * @param params the update parameters
     *
     * @return the acceleration
     */
    virtual double makeAcceleratingDecision(DriverUpdateParams &params) = 0;
};

/**
 * MITSIM version of car following model
 */
class MITSIM_CF_Model : public CarFollowingModel
{
private:
    /**Represents the container to store the normal distribution*/
    struct UpdateStepSizeParam
    {
        double mean;
        double stdev;
        double lower;
        double upper;
        double perception; //percentage of total reaction time
    };

    /**Represents the container to store the car following model parameters*/
    struct CarFollowingParams
    {
        double alpha;
        double beta;
        double gama;
        double lambda;
        double rho;
        double stddev;
    };

    /**The upper bound on the maximum acceleration*/
    int maxAccUpperBound;

    /**The upper bound on the normal deceleration*/
    int normalDecelerationUpperBound;

    /**The upper bound on the maximum deceleration*/
    int maxDecelerationUpperBound;

    /**This parameter is used to compute the desired speed based on the posted speed limit*/
    double speedFactor;

    /**The visibility distance of the traffic signal*/
    double signalVisibilityDist;

    /**The maximum value of headway a driver can have to pass a yellow light.*/
    double maxYellowLightHeadway;

    /**The minimum speed for approaching a yellow light*/
    double minYellowLightSpeed;

    /**The road slope*/
    double allGrades;

    /**
     * This is the minimum space headway between the lead and following vehicles for which the following
     * vehicle must apply some acceleration (or deceleration)
     */
    double minResponseDistance;

    /**Upper bound for the headway buffer*/
    double hBufferUpper;

    /**Lower bound for the headway buffer*/
    double hBufferLower;

    /**Defines how close the lead vehicle needs to be in order for the following vehicle to brake (in meter)*/
    double visibilityDistance;

    /**Random number generator for calculating update step sizes*/
    boost::mt19937 updateSizeRNG;

    /**The car following parameters*/
    CarFollowingParams CF_parameters[2];

    /**Merging parameters*/
    vector<double> mergingParams;

    /**Update step size while deceleration*/
    UpdateStepSizeParam decUpdateStepSize;

    /**Update step size while acceleration*/
    UpdateStepSizeParam accUpdateStepSize;

    /**Update step size when the speed is constant*/
    UpdateStepSizeParam uniformSpeedUpdateStepSize;

    /**Update step size when the vehicle has stopped*/
    UpdateStepSizeParam stoppedUpdateStepSize;

    /**Parameters to calculate the target gap acceleration*/
    vector<double> targetGapAccParm;

    /**The maximum acceleration scale*/
    vector<double> maxAccelerationScale;

    /**The normal deceleration scale*/
    vector<double> normalDecelerationScale;

    /**The maximum deceleration scale*/
    vector<double> maxDecelerationScale;

    /**The speed limit add-on distribution*/
    vector<double> speedLimitAddon;

    /**The car following acceleration add-on distribution*/
    vector<double> accelerationAddon;

    /**The car following deceleration add-on distribution*/
    vector<double> decelerationAddon;

    /**The distribution for the headway upperbound*/
    vector<double> hBufferUpperScale;

    /**The maximum acceleration indices. Key: Vehicle type, Value: Map with Key: Speed, Value: Max. Acceleration*/
    map< VehicleBase::VehicleType, map<int, double> > maxAccelerationIndex;

    /**The normal deceleration indices. Key: Vehicle type, Value: Map with Key: Speed, Value: Normal deceleration*/
    map< VehicleBase::VehicleType, map<int, double> > normalDecelerationIndex;

    /**The maximum deceleration indices. Key: Vehicle type, Value: Map with Key: Speed, Value: Normal deceleration*/
    map< VehicleBase::VehicleType, map<int, double> > maxDecelerationIndex;

    /**
     * Reads the car following model parameters from the driver parameters XML file
     *
     * @param params the drivers parameters
     */
    void readDriverParameters(DriverUpdateParams &params);

    /**
     * Creates the car following parameters from the given parameter values in string format
     *
     * @param strParams the parameter string
     * @param cfParams the car following parameters
     */
    void createCF_Params(string &strParams, CarFollowingParams &cfParams);

    /**
     * Creates the speed indices based on the speed scalars
     *
     * @param vehicleType vehicle type
     * @param speedScalerStr speed scalar
     * @param cstr the index value
     * @param idx the container holding the indices
     * @param upperBound the upper bound of the index
     */
    void createSpeedIndices(VehicleBase::VehicleType vehicleType, string &speedScalerStr, string &cstr, map< VehicleBase::VehicleType, map<int, double> > &idx,
                            int &upperBound);

    /**
     * Creates the scale indices based on the string data
     *
     * @param data the data in the format "0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5"
     * @param container the container that stores the created indices
     */
    void createScaleIndices(string &data, vector<double> &container);

    /**
     * Creates the update step size parameters from the given parameter values in string format
     * @param strParams the parameter string
     * @param stepSizeParams the container for the update step size parameters
     */
    void createUpdateSizeParams(string &strParams, UpdateStepSizeParam &stepSizeParams);

    /**
     * Builds and gets a sample from the normal distribution created from the given parameters
     *
     * @param stepSizeParams the step size parameters from which the distribution is to be created and sampled
     *
     * @return sampled value from the distribution
     */
    double sampleFromNormalDistribution(UpdateStepSizeParam &stepSizeParams);

    /**
     * Returns the maximum acceleration for the given vehicle type
     *
     * @param params the driver parameters
     * @param vhType the vehicle type
     *
     * @return max acceleration of the given vehicle type
     */
    double getMaxAcceleration(DriverUpdateParams &params, VehicleBase::VehicleType vhType = VehicleBase::CAR);

    /**
     * Returns the normal deceleration for the given vehicle type
     *
     * @param params the driver parameters
     * @param vhType the vehicle type
     *
     * @return normal deceleration of the given vehicle type
     */
    double getNormalDeceleration(DriverUpdateParams &params, VehicleBase::VehicleType vhType = VehicleBase::CAR);

    /**
     * Returns the maximum deceleration for the given vehicle type
     * @param params the driver parameters
     * @param vhType the vehicle type
     *
     * @return the maximum deceleration for the given vehicle type
     */
    double getMaxDeceleration(DriverUpdateParams &params, VehicleBase::VehicleType vhType = VehicleBase::CAR);

    /**
     * Calculates the maximum acceleration scalar
     *
     * @return maximum acceleration scalar
     */
    double getMaxAccScalar();

    /**
     * Calculates the normal deceleration scalar
     *
     * @return normal deceleration scalar
     */
    double getNormalDecScalar();

    /**
     * Calculates the maximum deceleration scalar
     *
     * @return maximum deceleration scalar
     */
    double getMaxDecScalar();

    /**
     * Calculates the acceleration based on the car-following constraints.
     *
     * CAUTION: The two vehicles concerned in this function may not necessarily be in the same lane or even the
     * same segment.
     * A modified GM model is used in this implementation.
     *
     * @param params the update parameters
     * @param nearestVehicle the nearest vehicle
     *
     * @return acceleration
     */
    double calcCarFollowingAcc(DriverUpdateParams &params, NearestVehicle &nearestVehicle);

    /**
     * Calculates the car following accelerations for 2 scenarios:
     * 1. Vehicle in front is on the same link
     * 2. Vehicle in front is on the next link (beyond the intersection)
     * and returns the lower of the two values
     *
     * @param params the update parameters
     *
     * @return acceleration
     */
    double calcCarFollowingAcc(DriverUpdateParams &params);

    /**
     * Calculate the acceleration based on the merging constraints
     *
     * @param params the update parameters
     *
     * @return acceleration
     */
    double calcMergingAcc(DriverUpdateParams &params);

    /**
     * Checks if this driver considers the gap from an incoming vehicle to be an acceptable gap for merging or crossing.
     * It depends on the speed of the coming vehicle and this driver's behaviour parameters
     *
     * @param params update parameters
     * @param nearestVehicle the incoming vehicle
     *
     * @return returns true if the gap is acceptable, or false otherwise.
     */
    bool isGapAcceptable(DriverUpdateParams &params, NearestVehicle &nearestVehicle);

    /**
     * Calculates the acceleration when the car needs to stop for a red light or needs to start moving for a
     * green light
     *
     * @param params the driver parameters
     *
     * @return acceleration
     */
    double calcTrafficSignalAcc(DriverUpdateParams &params);

    /**
     * Calculates the acceleration when performing a courtesy yielding
     * @param params the driver parameters
     * @return acceleration
     */
    double calcYieldingAcc(DriverUpdateParams &params);

    /**
     * Calculate the maximum acceleration for creating a gap to the nearest vehicle
     *
     * @param params the driver parameters
     * @param nearestVeh the vehicle to which we are trying to open a gap
     * @param gap the gap distance required
     *
     * @return acceleration
     */
    double calcAccToCreateGap(DriverUpdateParams &params, NearestVehicle &nearestVeh, float gap);

    /**
     * Calculates the acceleration while exiting a specific lane. For e.g: If a vehicle is not in the correct lane
     * close to the end of a link, it may decelerate to a stop and wait for lane changing.
     *
     * @param params
     *
     * @return acceleration
     */
    double calcWaitForLaneExitAcc(DriverUpdateParams &params);

    /**
     * Calculates the acceleration rate when current lane is incorrect and the distance is close to the
     * an incurrent lane of the segment
     *
     * @param params driver parameters
     *
     * @return acceleration
     */
    double calcWaitForAllowedLaneAcc(DriverUpdateParams& p);

    /**
     * Calculates the acceleration required to reach the forward gap
     *
     * @param params driver parameters
     * @return acceleration
     */
    double calcForwardGapAcc(DriverUpdateParams &params);

    /**
     * Calculates the desired speed
     * @param params driver parameters
     * @return desired speed (m/s)
     */
    double calcDesiredSpeed(DriverUpdateParams &params);

    /**
     * Calculates the acceleration to reach the backward gap
     *
     * @param params driver parameters
     *
     * @return acceleration
     */
    double calcBackwardGapAcc(DriverUpdateParams &params);

    /**
     * Calculates the acceleration to reach the adjacent gap
     * @param params driver parameters
     * @return acceleration
     */
    double calcAdjacentGapRate(DriverUpdateParams &params);

    /**
     * Calculates the acceleration required to stop at a stopping point
     *
     * @param params
     *
     * @return acceleration
     */
    double calcAccForStoppingPoint(DriverUpdateParams &params);

    /**
     * Calculates acceleration required to reach the desired speed
     *
     * @param params driver parameters
     *
     * @return acceleration
     */
    double calcDesiredSpeedAcc(DriverUpdateParams &params);

    /**
     * Calculates the acceleration / deceleration required to speed up / slow down to the target speed
     * with in the given distance
     *
     * @param params driver parameters
     * @param distance distance within which the target speed is to be achieved
     * @param velocity the target speed
     *
     * @return acceleration
     */
    double calcTargetSpeedAcc(DriverUpdateParams &params, double distance, double velocity);

    /**
     * Calculates the acceleration rate required to accelerate / decelerate from current speed to a full
     * stop within a given distance
     *
     * @param params driver parameters
     * @param distance distance within which we need to stop
     *
     * @return acceleration
     */
    double calcBrakeToStopAcc(DriverUpdateParams &params, double distance);

    /**
     * Calculates the acceleration required to slow down in an emergency
     * NOTE: when headway \< lower threshold, use this function
     *
     * @param params driver parameters
     *
     * @return acceleration
     */
    double calcEmergencyDeceleration(DriverUpdateParams &params);

    /**
     * Calculates the acceleration required to follow another car
     * NOTE: when lower threshold \< headway \< upper threshold, use this function
     *
     * @param params driver parameters
     *
     * @return acceleration
     */
    double calcAccOfCarFollowing(DriverUpdateParams &params);

    /**
     * Calculates the acceleration when the traffic is freely flowing
     *
     * @param params driver parameters
     * @param targetSpeed the target speed
     * @param maxLaneSpeed the speed limit of the current lane
     *
     * @return acceleration
     */
    double calcFreeFlowingAcc(DriverUpdateParams &params, double targetSpeed);

    /**
     * Calculates either the car following acceleration or the free flow acceleration depending on the gap between
     * the vehicles
     *
     * @param params driver parameters
     * @param targetSpeed the desired speed
     *
     * @return acceleration
     */
    double accOfMixOfCFandFF(DriverUpdateParams &params, double targetSpeed);

    /**
     * Calculates the distance required for the vehicle to arrive to a stop under normal circumstances
     *
     * @param params driver parameters
     */
    void calcDistanceForNormalStop(DriverUpdateParams &params);

    /**
     * Updates the variables that depend on the vehicle type and speed (i.e. the current state)
     *
     * @param params driver parameters driver parameters
     */
    void calcStateBasedVariables(DriverUpdateParams& p);

    /**
     * Calculates the step sizes for making car-following decisions
     */
    void calcUpdateStepSizes();

    /**
     * Calculates the upper bound of the headway buffer
     *
     * @return upper bound of the headway buffer
     */
    double getH_BufferUpperBound();

    /**
     * Calculates a headway buffer for a vehicle, which is a behavioural parameter that describes the aggressiveness
     * of a driver for accepting a headway gap in lane changing, merging, and car-following.
     *
     * @return headway (seconds)
     */
    double getHeadwayBuffer();

    /**
     * Calculates the add on for calculating the desired speed
     *
     * @return add on value (m/s)
     */
    double getSpeedLimitAddon();

    /**
     * Calculates the add on for the acceleration
     *
     * @return add on value (m/s^2)
     */
    double getAccelerationAddon();

    /**
     * Calculates the add on for the deceleration
     *
     * @return add on value (m/s^2)
     */
    double getDecelerationAddon();

    double upMergingArea()
    {
        return mergingParams[0];
    }

    double dnMergingArea()
    {
        return mergingParams[1];
    }

    int nVehiclesAllowedInMergingArea()
    {
        return (int) mergingParams[2];
    }

    float aggresiveRampMergeProb()
    {
        return mergingParams[3];
    }

public:
    MITSIM_CF_Model(DriverUpdateParams &params, DriverPathMover *pathMover);
    ~MITSIM_CF_Model();

    /**
     * Calculates the acceleration rate based on interaction with other vehicles. The function returns a the
     * most restrictive acceleration (deceleration if negative) rate among the rates given by several constraints.
     *
     * Car following algorithm is evaluated every stepSize seconds, or whenever some special event has set
     * reactionTimeCounter of this vehicle to 0. After each evaluation, we set the countdown clock reactionTimeCounter
     * back to nextStepSize().
     *
     * @param params driver parameters
     *
     * @return most restrictive acceleration
     */
    virtual double makeAcceleratingDecision(DriverUpdateParams &params);

    const double getHBufferUpper() const
    {
        return hBufferUpper;
    }
};
}


#include "active_medium.hpp"
#include "active_medium_functiods.hpp"

#include <boost/program_options.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/property_tree/exceptions.hpp>

#include <vector>
#include <stdexcept>
#include <string>

using namespace std;
namespace pt = boost::property_tree;

//the number of required and optional sections and thus functiods in the active medium file
const size_t numberOfRequired = 6;
const size_t numberOfOptional = 1;

//construct active medium properties from an INI-file with boost::property_tree
active_medium::active_medium(std::string pMediumFile)
{
    string tempString;

    //vector detailing the sections of the INI-File, this allows to simply iterate the vector
    //instead of repeating code for each section, all sections here are required
    //when adding more section: increase vector size, add alements and also define appropriate methods
    vector<string> names(numberOfRequired);
    names[0] = "volume_recombination";
    names[1] = "positive_ion_mobility";
    names[2] = "negative_ion_mobility";
    names[3] = "electron_attachment_rate";
    names[4] = "electron_mobility";
    names[5] = "direct_recombination";
    //only iterating up to 6 currently, additional ones could be defined here

    //define additional, optional parameters (so far only simple constants)
    //their default values are defined in a the second vector (super inelegant, but everything else is so complicated)
    vector<string> optionalNames(numberOfOptional);
    optionalNames[0] = "relative_permittivity";

    vector<double> optionalValues(numberOfOptional);
    optionalValues[0] = 1.0;

    pt::ptree properties;
    pt::read_ini(pMediumFile,properties);

    string modelType;

    for (size_t i=0; i < names.size();i++){
        // read the type of model and construct the corresponding functiod
        modelType = properties.get<string>(names[i]+".function");
        if (modelType == "constant")
        {
            mFunctiods.push_back(new constant (properties.get<double>(names[i]+".param0")));
        }
        else if (modelType == "line")
        {
            mFunctiods.push_back(new linear (properties.get<double>(names[i]+".param0"),
                                             (properties.get<double>(names[i]+".param1")-properties.get<double>(names[i]+".param0"))/4000.));
        }
        else if (modelType == "exp")
        {
            mFunctiods.push_back(new exponential (properties.get<double>(names[i]+".param0"),
                                                  properties.get<double>(names[i]+".param1"),
                                                  properties.get<double>(names[i]+".param2")));
        }
        else if (modelType == "spline")
        {
            tempString = properties.get<string>(names[i]+".file_name");
            ifstream parameterFile(tempString.c_str() );
            try{
                mFunctiods.push_back(new spline_eval_only (parameterFile));
                }
            catch(ifstream::failure e){
                tempString = "failure parsing parameter file for "+names[i]+": "+e.what();
                throw ifstream::failure(tempString);
                }
        }
        else if (modelType == "poly4")
        {
            mFunctiods.push_back(new pol4 (properties.get<double>(names[i]+".param0"),
                                           properties.get<double>(names[i]+".param1"),
                                           properties.get<double>(names[i]+".param2"),
                                           properties.get<double>(names[i]+".param3"),
                                           properties.get<double>(names[i]+".param4")));

        }
        else if (modelType == "poly5")
        {
            mFunctiods.push_back(new pol5 (properties.get<double>(names[i]+".param0"),
                                           properties.get<double>(names[i]+".param1"),
                                           properties.get<double>(names[i]+".param2"),
                                           properties.get<double>(names[i]+".param3"),
                                           properties.get<double>(names[i]+".param4"),
                                           properties.get<double>(names[i]+".param5")));

        }
        else
        {
            tempString = "Model type: "+modelType +", for "+names[i];
            throw unknown_arg_error(tempString);
        }

        //check for the smalles range of the functions
        mMaxE = std::numeric_limits<double>::max();
        for (size_t i=0; i < mFunctiods.size();i++){
            if (mFunctiods[i]->mMaxE < mMaxE){
                mMaxE = mFunctiods[i]->mMaxE;
            }
        }

    }
    //iterate over the optional values
    for (size_t i=0; i < optionalNames.size();i++){
        mFunctiods.push_back(new constant (properties.get(optionalNames[i]+".param0",optionalValues[i])));
    }

}

//define functions for the different properties, by pointing to the created array pointers
double active_medium::volume_recombination(double pEField){
    return (mFunctiods[0]->evaluate(pEField));
}
double active_medium::mobility_pos(double pEField){
    return (mFunctiods[1]->evaluate(pEField));
}

double active_medium::mobility_neg(double pEField){
    return (mFunctiods[2]->evaluate(pEField));
}

double active_medium::attachment(double pEField){
    return (mFunctiods[3]->evaluate(pEField));
}

double active_medium::mobility_e(double pEField){
    return (mFunctiods[4]->evaluate(pEField));
}

double active_medium::direct_recombination(double pEField){
    return (mFunctiods[5]->evaluate(pEField));
}

//the optional ones
double active_medium::relative_permittivity(double pEField){
    return (mFunctiods[numberOfRequired+0]->evaluate(pEField));
}

void active_medium::write_log(ofstream &pLogFile){
    pLogFile << "E in V/cm\t"
             << "µ_I+ in m^2/Vs\t"
             << "µ_I- in m^2/Vs\t"
             << "µ_e in m^2/Vs\t"
             << "electron attachment in 1/ns\t"
             << "Ion-ion-recombination in m^3/s\t"
             << "Ion-electron-recombination in m^3/s\t"
             << endl;
    for(size_t i=0; i < 8001; i++)
    {
        pLogFile << i << "\t"
                 << this->mobility_pos(i) << "\t"
                 << this->mobility_neg(i) << "\t"
                 << this->mobility_e(i) << "\t"
                 << this->attachment(i) << "\t"
                 << this->volume_recombination(i) << "\t"
                 << this->direct_recombination(i) << "\t"
                 << endl;
     }
    pLogFile << endl;

}


/**
 * @file
 * @copyright This code is licensed under the 3-clause BSD license.\n
 *            Copyright ETH Zurich, Laboratory for Physical Chemistry, Reiher Group.\n
 *            See LICENSE.txt for details.
 */

#include <Sparrow/Implementations/Nddo/Utils/IntegralsEvaluationUtils/AtomPairOverlap.h>
#include <Sparrow/Implementations/Nddo/Utils/IntegralsEvaluationUtils/OverlapMatrix.h>
#include <Sparrow/Implementations/Nddo/Utils/ParameterUtils/AtomicParameters.h>
#include <Sparrow/Implementations/Nddo/Utils/ParameterUtils/ElementParameters.h>
#include <Utils/DataStructures/AtomicGtos.h>
#include <Utils/DataStructures/AtomsOrbitalsIndexes.h>
#include <Utils/DataStructures/GtoExpansion.h>
#include <Utils/DataStructures/MatrixWithDerivatives.h>
#include <Utils/DataStructures/SlaterToGaussian.h>
#include <Utils/Typenames.h>
#include <gmock/gmock.h>
#include <Eigen/Core>
#include <memory>

namespace Scine {
namespace Sparrow {

using namespace testing;
using namespace Utils;
using namespace nddo;

class AOverlapMatrix : public Test {
 public:
  AOverlapMatrix() : S(elementTypes_, positions_, aoIndexes_, elementParameters) {
  }

  Utils::AtomsOrbitalsIndexes aoIndexes_;
  ElementParameters elementParameters;
  OverlapMatrix S;
  std::unique_ptr<AtomicParameters> apH, apC, apV;
  AtomPairOverlap<Utils::derivOrder::zero> pairOverlap;
  Utils::GtoExpansion gs1, gs2, gp1, gp2, gd1, gd2;
  Utils::AtomicGtos aS1, aS2, aP1, aP2, aD1, aD2;
  Eigen::Vector3d arbitraryVector;
  Eigen::RowVector3d pos1, pos2, pos3, pos4;
  Utils::ElementTypeCollection elementTypes_;
  Utils::PositionCollection positions_;

  void SetUp() override {
    arbitraryVector = Eigen::Vector3d(1.1, 2.0, 0.23);
    pos1 = Utils::Position(0.0, 0.0, 0.0);
    pos2 = Utils::Position(0.1, -0.6, 0.5);
    pos3 = Utils::Position(-1.0, 0.2, -0.3);
    pos4 = Utils::Position(1e6, 1e6, 1e6);
    apH = std::make_unique<AtomicParameters>(Utils::ElementType::H);
    apC = std::make_unique<AtomicParameters>(Utils::ElementType::C);
    apV = std::make_unique<AtomicParameters>(Utils::ElementType::V);

    gs1 = Utils::SlaterToGaussian::getGTOExpansion(6, 1, 0);
    gs2 = Utils::SlaterToGaussian::getGTOExpansion(6, 4, 0, 1.42);
    gp1 = Utils::SlaterToGaussian::getGTOExpansion(6, 2, 1, 2.2);
    gp2 = Utils::SlaterToGaussian::getGTOExpansion(6, 3, 1);
    gd1 = Utils::SlaterToGaussian::getGTOExpansion(6, 5, 2, 0.92);
    gd2 = Utils::SlaterToGaussian::getGTOExpansion(6, 4, 2);

    aS1.setS(gs1);
    aS2.setS(gs2);
    aP1 = aS1;
    aP1.setP(gp1);
    aP2 = aS2;
    aP2.setP(gp2);
    aD1 = aP1;
    aD1.setD(gd1);
    aD2 = aP2;
    aD2.setD(gd2);

    elementParameters.set(Utils::ElementType::H, std::move(apH));
    elementParameters.set(Utils::ElementType::C, std::move(apC));
    elementParameters.set(Utils::ElementType::V, std::move(apV));
  }
};

TEST_F(AOverlapMatrix, ReturnsIdentityForSingleAtom) {
  elementTypes_.push_back(Utils::ElementType::H);
  positions_.resize(1, 3);
  positions_.row(0) = pos1;
  aoIndexes_.setSize(1);
  aoIndexes_.addAtom(1);
  // S.newStructure... -> set matrix size, compute diagonal, etc.

  S.reset();
  S.calculateOverlap(derivOrder::zero);
  const auto& m = S.getOverlap();
  ASSERT_TRUE(m.get0().isApprox(Eigen::Matrix<double, 1, 1>::Identity(), 1e-7));
  elementTypes_.clear();
  elementTypes_.push_back(Utils::ElementType::C);
  positions_.row(0) = pos1;
  aoIndexes_.clear();
  aoIndexes_.setSize(1);
  aoIndexes_.addAtom(4);
  S.reset();
  S.calculateOverlap(derivOrder::zero);
  const auto& m2 = S.getOverlap();
  ASSERT_TRUE(m2.get0().isApprox(Eigen::Matrix<double, 4, 4>::Identity(), 1e-7));
}

TEST_F(AOverlapMatrix, IsNearToIdentityIfAtomsAreFarAway) {
  positions_.resize(2, 3);
  positions_.row(0) = pos1;
  positions_.row(1) = pos4;
  elementTypes_.push_back(Utils::ElementType::H);
  elementTypes_.push_back(Utils::ElementType::C);
  aoIndexes_.setSize(2);
  aoIndexes_.addAtom(1);
  aoIndexes_.addAtom(4);

  Utils::MatrixWithDerivatives m;
  S.reset();
  S.calculateOverlap(derivOrder::zero);
  m = S.getOverlap();
  ASSERT_TRUE(m.get0().isApprox(Eigen::Matrix<double, 5, 5>::Identity(), 1e-7));
}
} // namespace Sparrow
} // namespace Scine