 int nfft_of_plan;
 int size_of_plan;
 
+void cufft_check_error(const char* file_name, const int line_number, cufftResult result)
+{
+    if (result == CUFFT_SUCCESS) return;
+    
+    printf("CUFFT error at line %i of file %s: ", line_number, file_name);
+    switch (result)
+    {
+        case CUFFT_INVALID_PLAN:
+        {
+            printf("CUFFT_INVALID_PLAN\n");
+            break;
+        }
+        case CUFFT_ALLOC_FAILED:
+        {
+            printf("CUFFT_ALLOC_FAILED\n");
+            break;
+        }
+        case CUFFT_INVALID_VALUE:
+        {
+            printf("CUFFT_INVALID_VALUE\n");
+            break;
+        }
+        case CUFFT_INTERNAL_ERROR:
+        {
+            printf("CUFFT_INTERNAL_ERROR\n");
+            break;
+        }
+        case CUFFT_SETUP_FAILED:
+        {
+            printf("CUFFT_SETUP_FAILED\n");
+            break;
+        }
+        case CUFFT_INVALID_SIZE:
+        {
+            printf("CUFFT_INVALID_SIZE\n");
+            break;
+        }
+        default:
+        {
+            printf("unknown error code %i\n", result);
+            break;
+        }
+    }
+    exit(-100);
+}
+
 extern "C" void cufft_create_plan_handle(void)
 {
-    if (cufftCreate(&plan) != CUFFT_SUCCESS)
-    {
-        printf("failed to execute cufftCreate()\n");
-        exit(0);
-    }
+    cufftResult result = cufftCreate(&plan);
+    cufft_check_error(__FILE__, __LINE__, result);
 }
 
 extern "C" size_t cufft_get_size(int nx, int ny, int nz, int nfft)


extern "C" void cuda_stream_synchronize(int stream_id)
{
    if (cudaStreamSynchronize(streams[stream_id]) != cudaSuccess)
    {
        printf("failed to execute cudaStreamSynchronize()\n");
        exit(0);
    }
}

extern "C" void cuda_async_copy_to_device(void* target, void* source, size_t size, int stream_id)
{
    cudaStream_t stream = (stream_id == -1) ? NULL : streams[stream_id];

    if (cudaMemcpyAsync(target, source, size, cudaMemcpyHostToDevice, stream) != cudaSuccess)
    {
        printf("failed to execute cudaMemcpy(cudaMemcpyHostToDevice)\n");
        exit(0);
    }
}

extern "C" void cuda_async_copy_to_host(void* target, void* source, size_t size, int stream_id)
{
    cudaStream_t stream = (stream_id == -1) ? NULL : streams[stream_id];

    if (cudaMemcpyAsync(target, source, size, cudaMemcpyDeviceToHost, stream) != cudaSuccess)
    {
        printf("failed to execute cudaMemcpy(cudaMemcpyDeviceToHost)\n");
        exit(0);
    }
}

extern "C" void cuda_memset(void* ptr, int value, size_t size)
{
    if (cudaMemset(ptr, value, size) != cudaSuccess)
    {
        printf("failed to execute cudaMemset()\n");
        exit(0);
    }
}

extern "C" void cuda_host_register(void* ptr, size_t size)
{
    assert(ptr);
    
    cudaError_t err = cudaHostRegister(ptr, size, cudaHostRegisterMapped);
    if (err != cudaSuccess)
    {
        printf("failed to execute cudaHostRegister\n");
        switch (err)
        {
            case cudaErrorInvalidValue:
                printf("cudaErrorInvalidValue\n");
                break;
            case cudaErrorMemoryAllocation:
                printf("cudaErrorMemoryAllocation\n");
                break;
            default:
                printf("unrecognized error\n");
        }
        exit(-1);
    }
}

extern "C" void cuda_host_unregister(void* ptr)
{
    if (cudaHostUnregister(ptr) != cudaSuccess)
    {
        printf("failed to execute cudaHostUnregister\n");
        exit(-1);
    }
}

//* cudaDeviceProp& cuda_devprop()

    int fft_size = nx * ny * nz;
    int n[] = {nz, ny, nx};

    cufftResult result = cufftSetAutoAllocation(plan, false);
    cufft_check_error(__FILE__, __LINE__, result);
    
    size_t work_size;
    result = cufftMakePlanMany(plan, 3, n, n, 1, fft_size, n, 1, fft_size, CUFFT_Z2Z, nfft, &work_size);
    cufft_check_error(__FILE__, __LINE__, result);

    nfft_of_plan = nfft;
    size_of_plan = fft_size;
