#include<stdio.h>
#include<cuda_runtime.h>
#include"cudaConfig.h"
#include<cuComplex.h>

__global__ void calc_dyn_pol_kernel(
	double* dyn_pol,int dyn_pol_width,const int dyn_pol_height,
	const double* product,const double* delta_eng,
	const int delta_eng_product_size,const double* omegas,
	const int omegas_size,const double cdelta,const int iq)
{
	int indexThread = blockIdx.x * blockDim.x + threadIdx.x;
	if (indexThread < omegas_size) {
		double temp = omegas[indexThread];
		double real = 0., imag = 0.;
		for (size_t i = 0; i < delta_eng_product_size; i++)
		{
			cuDoubleComplex r3;
			cuDoubleComplex x = make_cuDoubleComplex(product[2 * i + 0], product[2 * i + 1]);
			cuDoubleComplex y = make_cuDoubleComplex(delta_eng[i] + temp, cdelta);
			r3 = cuCdiv(x, y);
			real += r3.x;
			imag += r3.y;
		}
		int rowIndex = indexThread;
		dyn_pol[2 * (iq * dyn_pol_height + rowIndex) + 0] += real;
		dyn_pol[2 * (iq * dyn_pol_height + rowIndex) + 1] += imag;
	}
	return;
};

__host__ void CudaConfig::calc_dyn_pol_GPU(
	complex_t* dyn_pol_H,const int dyn_pol_width,const int dyn_pol_height,
	complex_t* product_H,double* delta_eng_H,
	const int delta_eng_product_size,const int omegas_size,
	const double delta, const int iq,double* omegas_H)
{
	double* dyn_pol_D = nullptr, * product_D = nullptr, * delta_eng_D = nullptr, * omegas_D = nullptr;
	CHECKERROR(cudaMalloc(&dyn_pol_D, dyn_pol_width * dyn_pol_height * sizeof(complex_t)));
	CHECKERROR(cudaMemcpy(dyn_pol_D, dyn_pol_H, dyn_pol_width * dyn_pol_height * sizeof(complex_t), cudaMemcpyHostToDevice));
	CHECKERROR(cudaMalloc(&product_D, delta_eng_product_size * sizeof(complex_t)));
	CHECKERROR(cudaMemcpy(product_D, product_H, delta_eng_product_size * sizeof(complex_t), cudaMemcpyHostToDevice));
	CHECKERROR(cudaMalloc(&delta_eng_D, delta_eng_product_size * sizeof(double)));
	CHECKERROR(cudaMemcpy(delta_eng_D, delta_eng_H, delta_eng_product_size * sizeof(double), cudaMemcpyHostToDevice));
	CHECKERROR(cudaMalloc(&omegas_D, omegas_size * sizeof(double)));
	CHECKERROR(cudaMemcpy(omegas_D, omegas_H, omegas_size * sizeof(double), cudaMemcpyHostToDevice));
	dim3 block(1024);
	dim3 grid((omegas_size + block.x - 1) / block.x);
	calc_dyn_pol_kernel <<<grid, block >> > (
		dyn_pol_D,
		dyn_pol_width,
		dyn_pol_height,
		product_D,
		delta_eng_D,
		delta_eng_product_size,
		omegas_D,
		omegas_size,
		delta,iq);
	CHECKERROR(cudaDeviceSynchronize())
	CHECKERROR(cudaMemcpy(dyn_pol_H, dyn_pol_D, dyn_pol_width * dyn_pol_height * sizeof(complex_t), cudaMemcpyDeviceToHost));
	CHECKERROR(cudaMemcpy(product_H, product_D, delta_eng_product_size * sizeof(complex_t), cudaMemcpyDeviceToHost));
	CHECKERROR(cudaMemcpy(delta_eng_H, delta_eng_D, delta_eng_product_size * sizeof(double), cudaMemcpyDeviceToHost));
	CHECKERROR(cudaMemcpy(omegas_H, omegas_D, omegas_size * sizeof(double), cudaMemcpyDeviceToHost));
	CHECKERROR(cudaFree(dyn_pol_D));
	dyn_pol_D = nullptr;
	CHECKERROR(cudaFree(product_D));
	product_D = nullptr;
	CHECKERROR(cudaFree(delta_eng_D));
	delta_eng_D = nullptr;
	CHECKERROR(cudaFree(omegas_D));
	omegas_D = nullptr;
	return;
};