!------------------------------------------------------------------------------------------------
! Copyright (c) 2021 - 2025, Oleg Shatrov
! All rights reserved.
! This file is part of dtFFT library.

! dtFFT is free software: you can redistribute it and/or modify
! it under the terms of the GNU General Public License as published by
! the Free Software Foundation, either version 3 of the License, or
! (at your option) any later version.

! dtFFT 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
! GNU General Public License for more details.

! You should have received a copy of the GNU General Public License
! along with this program.  If not, see <https://www.gnu.org/licenses/>.
!------------------------------------------------------------------------------------------------
#include "dtfft_config.h"
module dtfft_api
!! This module is a Fortran part of C interface
use iso_c_binding,    only: c_int8_t, c_int32_t, c_size_t,  &
                            c_float, c_bool, c_char,        &
                            c_null_ptr, c_ptr, c_loc,       &
                            c_f_pointer
use iso_fortran_env,  only: int8, int32
use dtfft_config,     only: dtfft_config_t, dtfft_set_config
use dtfft_errors
use dtfft_parameters
use dtfft_pencil,     only: dtfft_pencil_t, dtfft_pencil_c, pencil_c2f, pencil_f2c
use dtfft_plan,       only: dtfft_plan_t, dtfft_plan_r2r_t, dtfft_plan_c2c_t, dtfft_plan_r2c_t
use dtfft_utils,      only: is_null_ptr, string_f2c
#include "_dtfft_mpi.h"
implicit none
private

#define CHECK_PLAN_CREATED(c_plan, f_plan)      \
  if(is_null_ptr(c_plan)) then;                 \
    error_code = DTFFT_ERROR_PLAN_NOT_CREATED;  \
    return;                                     \
  endif;                                        \
  call c_f_pointer(c_plan, f_plan)

    type :: plan_c
    !! C pointer to Fortran plan
        class(dtfft_plan_t),  allocatable :: p
        !! Actual Fortran plan
    end type plan_c

contains

    pure TYPE_MPI_COMM function get_comm(c_comm)
    !! Converts C communicator to Fortran communicator
        integer(c_int32_t),  intent(in) :: c_comm !! C communicator

        GET_MPI_VALUE(get_comm) = c_comm
    end function get_comm

    function dtfft_create_plan_r2r_c(ndims, dims, kinds, comm, precision, effort, executor, plan_ptr)                       &
    result(error_code)                                                                                                      &
    bind(C)
    !! Creates R2R dtFFT Plan, allocates all structures and prepares FFT, C interface
        integer(c_int8_t),              intent(in)    :: ndims                !! Rank of transform. Can be 2 or 3.
        type(c_ptr),            value,  intent(in)    :: dims                 !! Global sizes of transform
        type(c_ptr),            value,  intent(in)    :: kinds                !! FFT R2R kinds
        integer(c_int32_t),     value,  intent(in)    :: comm                 !! Communicator
        type(dtfft_precision_t),        intent(in)    :: precision            !! Precision of transform
        type(dtfft_effort_t),           intent(in)    :: effort               !! ``dtFFT`` planner effort type
        type(dtfft_executor_t),         intent(in)    :: executor             !! Type of External FFT Executor
        type(c_ptr),                    intent(out)   :: plan_ptr             !! C pointer to Fortran plan
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        integer(int32),                     pointer   :: fdims(:)             !! Fortran dims
        type(dtfft_r2r_kind_t),             pointer   :: fkinds(:)            !! Fortran R2R kinds
        type(plan_c),                       pointer   :: plan                 !! Pointer to Fortran object

        allocate(plan)
        allocate( dtfft_plan_r2r_t :: plan%p )

        call c_f_pointer(dims, fdims, [ndims])

        select type( p => plan%p )
        type is ( dtfft_plan_r2r_t )
            if ( is_null_ptr(kinds) ) then
                call p%create(fdims, comm=get_comm(comm), precision=precision, effort=effort, executor=executor, error_code=error_code)
            else
                call c_f_pointer(kinds, fkinds, [ndims])
                call p%create(fdims, fkinds, get_comm(comm), precision, effort, executor, error_code)
            endif
        endselect
        plan_ptr = c_loc(plan)
    end function dtfft_create_plan_r2r_c

    function dtfft_create_plan_r2r_pencil_c(pencil, kinds, comm, precision, effort, executor, plan_ptr)                     &
    result(error_code)                                                                                                      &
    bind(C)
    !! Creates R2R dtFFT Plan from Pencil, allocates all structures and prepares FFT, C interface
        type(dtfft_pencil_c),           intent(in)    :: pencil               !! C pointer to Fortran pencil
        type(c_ptr),            value,  intent(in)    :: kinds                !! FFT R2R kinds
        integer(c_int32_t),     value,  intent(in)    :: comm                 !! Communicator
        type(dtfft_precision_t),        intent(in)    :: precision            !! Precision of transform
        type(dtfft_effort_t),           intent(in)    :: effort               !! ``dtFFT`` planner effort type
        type(dtfft_executor_t),         intent(in)    :: executor             !! Type of External FFT Executor
        type(c_ptr),                    intent(out)   :: plan_ptr             !! C pointer to Fortran plan
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(dtfft_r2r_kind_t),             pointer   :: fkinds(:)            !! Fortran R2R kinds
        type(plan_c),                       pointer   :: plan                 !! Pointer to Fortran object
        type(dtfft_pencil_t)                          :: pencil_              !! Fortran pencil

        call pencil_c2f(pencil, pencil_, error_code)
        if ( error_code /= DTFFT_SUCCESS ) return

        allocate(plan)
        allocate( dtfft_plan_r2r_t :: plan%p )

        select type( p => plan%p )
        type is ( dtfft_plan_r2r_t )
            if ( is_null_ptr(kinds) ) then
                call p%create(pencil_, comm=get_comm(comm), precision=precision, effort=effort, executor=executor, error_code=error_code)
            else
                call c_f_pointer(kinds, fkinds, [pencil_%ndims])
                call p%create(pencil_, fkinds, get_comm(comm), precision, effort, executor, error_code)
            endif
        endselect
        plan_ptr = c_loc(plan)
    end function dtfft_create_plan_r2r_pencil_c

    function dtfft_create_plan_c2c_c(ndims, dims, comm, precision, effort, executor, plan_ptr)                        &
    result(error_code)                                                                                              &
    bind(C)
    !! Creates C2C dtFFT Plan, allocates all structures and prepares FFT, C interface
        integer(c_int8_t),              intent(in)    :: ndims                !! Rank of transform. Can be 2 or 3.
        type(c_ptr),            value,  intent(in)    :: dims                 !! Global sizes of transform
        integer(c_int32_t),     value,  intent(in)    :: comm                 !! Communicator
        type(dtfft_precision_t),        intent(in)    :: precision            !! Precision of transform
        type(dtfft_effort_t),           intent(in)    :: effort               !! ``dtFFT`` planner effort type
        type(dtfft_executor_t),         intent(in)    :: executor             !! Type of External FFT Executor
        type(c_ptr),                    intent(out)   :: plan_ptr             !! C pointer to Fortran plan
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        integer(int32),                     pointer   :: fdims(:)             !! Fortran dims
        type(plan_c),                       pointer   :: plan                 !! Pointer to Fortran object

        allocate(plan)
        allocate( dtfft_plan_c2c_t :: plan%p )

        call c_f_pointer(dims, fdims, [ndims])

        select type(p => plan%p)
        class is (dtfft_plan_c2c_t)
            call p%create(fdims, get_comm(comm), precision, effort, executor, error_code)
        endselect
        plan_ptr = c_loc(plan)
    end function dtfft_create_plan_c2c_c

    function dtfft_create_plan_c2c_pencil_c(pencil, comm, precision, effort, executor, plan_ptr)                          &
        result(error_code)                                                                                                  &
        bind(C)
    !! Creates C2C dtFFT plan from Pencil, allocates all structures and prepares FFT, C interface
        type(dtfft_pencil_c),           intent(in)    :: pencil               !! C pointer to Fortran pencil
        integer(c_int32_t),     value,  intent(in)    :: comm                 !! Communicator
        type(dtfft_precision_t),        intent(in)    :: precision            !! Precision of transform
        type(dtfft_effort_t),           intent(in)    :: effort               !! ``dtFFT`` planner effort type
        type(dtfft_executor_t),         intent(in)    :: executor             !! Type of External FFT Executor
        type(c_ptr),                    intent(out)   :: plan_ptr             !! C pointer to Fortran plan
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(dtfft_pencil_t)                          :: pencil_              !! Fortran pencil
        type(plan_c),                       pointer   :: plan                 !! Pointer to Fortran object

        call pencil_c2f(pencil, pencil_, error_code)
        if ( error_code /= DTFFT_SUCCESS ) return

        allocate(plan)
        allocate( dtfft_plan_c2c_t :: plan%p )
        select type(p => plan%p)
        class is (dtfft_plan_c2c_t)
            call p%create(pencil_, get_comm(comm), precision, effort, executor, error_code)
        endselect
        plan_ptr = c_loc(plan)
    end function dtfft_create_plan_c2c_pencil_c

    function dtfft_create_plan_r2c_c(ndims, dims, comm, precision, effort, executor, plan_ptr)                        &
        result(error_code)                                                                                              &
        bind(C)
    !! Creates R2C dtFFT Plan, allocates all structures and prepares FFT, C interface
        integer(c_int8_t),              intent(in)    :: ndims                !! Rank of transform. Can be 2 or 3.
        type(c_ptr),            value,  intent(in)    :: dims                 !! Global sizes of transform
        integer(c_int32_t),     value,  intent(in)    :: comm                 !! Communicator
        type(dtfft_precision_t),        intent(in)    :: precision            !! Precision of transform
        type(dtfft_effort_t),           intent(in)    :: effort               !! ``dtFFT`` planner effort type
        type(dtfft_executor_t),         intent(in)    :: executor             !! Type of External FFT Executor
        type(c_ptr),                    intent(out)   :: plan_ptr             !! C pointer to Fortran plan
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        integer(int32),                     pointer   :: fdims(:)             !! Fortran dims
        type(plan_c),                       pointer   :: plan                 !! Pointer to Fortran object

        allocate(plan)
        allocate( dtfft_plan_r2c_t :: plan%p )

        call c_f_pointer(dims, fdims, [ndims])

        select type(p => plan%p)
        class is (dtfft_plan_r2c_t)
            call p%create(fdims, get_comm(comm), precision, effort, executor, error_code)
        endselect
        plan_ptr = c_loc(plan)
    end function dtfft_create_plan_r2c_c

    function dtfft_create_plan_r2c_pencil_c(pencil, comm, precision, effort, executor, plan_ptr)                      &
        result(error_code)                                                                                              &
        bind(C)
    !! Creates R2C dtFFT Plan from Pencil, allocates all structures and prepares FFT, C interface
        type(dtfft_pencil_c),           intent(in)    :: pencil               !! C pointer to Fortran pencil
        integer(c_int32_t),    value,  intent(in)    :: comm                 !! Communicator
        type(dtfft_precision_t),        intent(in)    :: precision            !! Precision of transform
        type(dtfft_effort_t),           intent(in)    :: effort               !! ``dtFFT`` planner effort type
        type(dtfft_executor_t),         intent(in)    :: executor             !! Type of External FFT Executor
        type(c_ptr),                    intent(out)   :: plan_ptr             !! C pointer to Fortran plan
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(dtfft_pencil_t)                          :: pencil_              !! Fortran pencil
        type(plan_c),                       pointer   :: plan                 !! Pointer to Fortran object

        call pencil_c2f(pencil, pencil_, error_code)
        if ( error_code /= DTFFT_SUCCESS ) return

        allocate(plan)
        allocate( dtfft_plan_r2c_t :: plan%p )
        select type(p => plan%p)
        class is (dtfft_plan_r2c_t)
            call p%create(pencil_, get_comm(comm), precision, effort, executor, error_code)
        endselect
        plan_ptr = c_loc(plan)
    end function dtfft_create_plan_r2c_pencil_c

    function dtfft_get_z_slab_enabled_c(plan_ptr, is_z_slab_enabled)                                                  &
        result(error_code)                                                                                              &
        bind(C)
    !! Checks if dtFFT Plan is using Z-slab optimization
        type(c_ptr),        intent(in),     value     :: plan_ptr             !! C pointer to Fortran plan
        logical(c_bool),    intent(out)               :: is_z_slab_enabled    !! Is plan internally using Z-slab optimization
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                       pointer   :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        is_z_slab_enabled = plan%p%get_z_slab_enabled(error_code)
    end function dtfft_get_z_slab_enabled_c

    function dtfft_get_y_slab_enabled_c(plan_ptr, is_y_slab_enabled)                                                  &
        result(error_code)                                                                                              &
        bind(C)
    !! Checks if dtFFT Plan is using Y-slab optimization
        type(c_ptr),        intent(in),     value     :: plan_ptr             !! C pointer to Fortran plan
        logical(c_bool),    intent(out)               :: is_y_slab_enabled    !! Is plan internally using Y-slab optimization
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                       pointer   :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        is_y_slab_enabled = plan%p%get_y_slab_enabled(error_code)
    end function dtfft_get_y_slab_enabled_c

    function dtfft_execute_c(plan_ptr, in, out, execute_type, aux)                                                    &
        result(error_code)                                                                                              &
        bind(C)
    !! Executes dtFFT Plan, C interface. `aux` can be NULL.
        type(c_ptr),        value,    intent(in)      :: plan_ptr             !! C pointer to Fortran plan
        type(c_ptr),        value,    intent(in)      :: in                   !! Incomming pointer, not NULL
        type(c_ptr),        value,    intent(in)      :: out                  !! Outgoing buffer, not NULL
        type(dtfft_execute_t),        intent(in)      :: execute_type         !! Type of execution
        type(c_ptr),        value,    intent(in)      :: aux                  !! Aux buffer, can be NULL
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                       pointer   :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        call plan%p%execute_ptr(in, out, execute_type, aux, error_code)
    end function dtfft_execute_c

    function dtfft_transpose_c(plan_ptr, in, out, transpose_type, aux)                                                &
        result(error_code)                                                                                              &
        bind(C)
    !! Executes single transposition, C interface.
        type(c_ptr),        value,      intent(in)    :: plan_ptr             !! C pointer to Fortran plan
        type(c_ptr),        value,      intent(in)    :: in                   !! Incomming pointer, not NULL
        type(c_ptr),        value,      intent(in)    :: out                  !! Outgoing buffer, not NULL
        type(dtfft_transpose_t),        intent(in)    :: transpose_type       !! Type of transposition.
        type(c_ptr),        value,      intent(in)    :: aux                  !! Aux buffer, can be NULL
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                       pointer   :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        call plan%p%transpose_ptr(in, out, transpose_type, aux, error_code)
    end function dtfft_transpose_c

    function dtfft_transpose_start_c(plan_ptr, in, out, transpose_type, aux, request)                                 &
        result(error_code)                                                                                              &
        bind(C)
    !! Starts asynchronous transposition, returns transpose handle, C interface.
        type(c_ptr),        value,      intent(in)    :: plan_ptr             !! C pointer to Fortran plan
        type(c_ptr),        value,      intent(in)    :: in                   !! Incomming pointer, not NULL
        type(c_ptr),        value,      intent(in)    :: out                  !! Outgoing buffer, not NULL
        type(dtfft_transpose_t),        intent(in)    :: transpose_type       !! Type of transposition.
        type(c_ptr),        value,      intent(in)    :: aux                  !! Aux buffer, can be NULL
        type(dtfft_request_t),          intent(out)   :: request              !! Async transpose handle
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                       pointer   :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        request = plan%p%transpose_start_ptr(in, out, transpose_type, aux, error_code)
    end function dtfft_transpose_start_c

    function dtfft_transpose_end_c(plan_ptr, request)                                                                 &
        result(error_code)                                                                                              &
        bind(C)
    !! Finishes asynchronous transposition, C interface.
        type(c_ptr),          value,    intent(in)    :: plan_ptr             !! C pointer to Fortran plan
        type(dtfft_request_t),          intent(inout) :: request              !! Async transpose handle
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                       pointer   :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        call plan%p%transpose_end(request, error_code)
    end function dtfft_transpose_end_c

    function dtfft_reshape_c(plan_ptr, in, out, reshape_type, aux)                                                    &
        result(error_code)                                                                                              &
        bind(C)
    !! Executes dtFFT Reshape, C interface. `aux` can be NULL.
        type(c_ptr),        value,    intent(in)      :: plan_ptr             !! C pointer to Fortran plan
        type(c_ptr),        value,    intent(in)      :: in                   !! Incomming pointer, not NULL
        type(c_ptr),        value,    intent(in)      :: out                  !! Outgoing buffer, not NULL
        type(dtfft_reshape_t),        intent(in)      :: reshape_type         !! Type of reshape
        type(c_ptr),        value,    intent(in)      :: aux                  !! Aux buffer, can be NULL
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                       pointer   :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        call plan%p%reshape_ptr(in, out, reshape_type, aux, error_code)
    end function dtfft_reshape_c

    function dtfft_reshape_start_c(plan_ptr, in, out, reshape_type, aux, request)                                      &
        result(error_code)                                                                                              &
        bind(C)
    !! Starts asynchronous reshape, returns reshape handle, C interface.
        type(c_ptr),        value,      intent(in)    :: plan_ptr             !! C pointer to Fortran plan
        type(c_ptr),        value,      intent(in)    :: in                   !! Incomming pointer, not NULL
        type(c_ptr),        value,      intent(in)    :: out                  !! Outgoing buffer, not NULL
        type(dtfft_reshape_t),          intent(in)    :: reshape_type         !! Type of reshape
        type(c_ptr),        value,      intent(in)    :: aux                  !! Aux buffer, can be NULL
        type(dtfft_request_t),          intent(out)   :: request              !! Async reshape handle
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                       pointer   :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        request = plan%p%reshape_start_ptr(in, out, reshape_type, aux, error_code)
    end function dtfft_reshape_start_c

    function dtfft_reshape_end_c(plan_ptr, request)                                                                 &
        result(error_code)                                                                                              &
        bind(C)
    !! Finishes asynchronous reshape, C interface.
        type(c_ptr),          value,    intent(in)    :: plan_ptr             !! C pointer to Fortran plan
        type(dtfft_request_t),          intent(inout) :: request              !! Async reshape handle
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                       pointer   :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        call plan%p%reshape_end(request, error_code)
    end function dtfft_reshape_end_c

    function dtfft_destroy_c(plan_ptr)                                                                                &
        result(error_code)                                                                                              &
        bind(C)
    !! Destroys dtFFT Plan, C interface
        type(c_ptr)                                   :: plan_ptr             !! C pointer to Fortran plan
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                       pointer   :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        call plan%p%destroy(error_code)
        deallocate( plan%p )
        deallocate( plan )
        plan_ptr = c_null_ptr
    end function dtfft_destroy_c

    function dtfft_get_local_sizes_c(plan_ptr, in_starts, in_counts, out_starts, out_counts, alloc_size)              &
        result(error_code)                                                                                              &
        bind(C)
    !! Returns local sizes, counts in real and Fourier spaces and number of elements to be allocated for `in` and `out` buffers,
    !! C interface.
        type(c_ptr),                         value    :: plan_ptr             !! C pointer to Fortran plan
        integer(c_int32_t),  intent(out),    optional :: in_starts(3)         !! Starts of local portion of data in 'real' space
        integer(c_int32_t),  intent(out),    optional :: in_counts(3)         !! Counts of local portion of data in 'real' space
        integer(c_int32_t),  intent(out),    optional :: out_starts(3)        !! Starts of local portion of data in 'fourier' space
        integer(c_int32_t),  intent(out),    optional :: out_counts(3)        !! Counts of local portion of data in 'fourier' space
        integer(c_size_t),   intent(out),    optional :: alloc_size           !! Minimum data needs to be allocated
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                       pointer   :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        call plan%p%get_local_sizes(in_starts, in_counts, out_starts, out_counts, alloc_size, error_code)
    end function dtfft_get_local_sizes_c

    function dtfft_get_alloc_size_c(plan_ptr, alloc_size)                                                             &
        result(error_code)                                                                                              &
        bind(C)
    !! Returns minimum number of bytes to be allocated for `in` and `out` buffers, C interface
        type(c_ptr),                         value    :: plan_ptr             !! C pointer to Fortran plan
        integer(c_size_t),   intent(out)              :: alloc_size           !! Minimum data needs to be allocated
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                       pointer   :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        alloc_size = plan%p%get_alloc_size(error_code)
    end function dtfft_get_alloc_size_c

    function dtfft_get_aux_size_c(plan_ptr, aux_size)                                                                 &
        result(error_code)                                                                                              &
        bind(C)
    !! Returns size of auxiliary buffer in elements, C interface
        type(c_ptr),                         value    :: plan_ptr             !! C pointer to Fortran plan
        integer(c_size_t),    intent(out)             :: aux_size             !! Size of auxiliary buffer in elements
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                 pointer         :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        aux_size = plan%p%get_aux_size(error_code)
    end function dtfft_get_aux_size_c

    function dtfft_get_aux_bytes_c(plan_ptr, aux_bytes)                                                               &
        result(error_code)                                                                                              &
        bind(C)
    !! Returns minimum number of bytes required for auxiliary buffer, C interface
        type(c_ptr),                         value    :: plan_ptr             !! C pointer to Fortran plan
        integer(c_size_t),    intent(out)             :: aux_bytes            !! Number of bytes required for auxiliary buffer
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                 pointer         :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        aux_bytes = plan%p%get_aux_bytes(error_code)
    end function dtfft_get_aux_bytes_c

    function dtfft_get_aux_size_reshape_c(plan_ptr, aux_size)                                                         &
        result(error_code)                                                                                              &
        bind(C)
    !! Returns size of auxiliary buffer for reshape in elements, C interface
        type(c_ptr),                         value    :: plan_ptr             !! C pointer to Fortran plan
        integer(c_size_t),    intent(out)             :: aux_size             !! Size of auxiliary buffer for reshape in elements
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                 pointer         :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        aux_size = plan%p%get_aux_size_reshape(error_code)
    end function dtfft_get_aux_size_reshape_c

    function dtfft_get_aux_bytes_reshape_c(plan_ptr, aux_bytes)                                                         &
        result(error_code)                                                                                              &
        bind(C)
    !! Returns minimum number of bytes required for auxiliary buffer for reshape, C interface
        type(c_ptr),                         value    :: plan_ptr             !! C pointer to Fortran plan
        integer(c_size_t),    intent(out)             :: aux_bytes            !! Number of bytes required for auxiliary buffer for reshape
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                 pointer         :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        aux_bytes = plan%p%get_aux_bytes_reshape(error_code)
    end function dtfft_get_aux_bytes_reshape_c

    function dtfft_get_aux_size_transpose_c(plan_ptr, aux_size)                                                       &
        result(error_code)                                                                                              &
        bind(C)
    !! Returns size of auxiliary buffer for transpose in elements, C interface
        type(c_ptr),                         value    :: plan_ptr             !! C pointer to Fortran plan
        integer(c_size_t),    intent(out)             :: aux_size             !! Size of auxiliary buffer for transpose in elements
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                 pointer         :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        aux_size = plan%p%get_aux_size_transpose(error_code)
    end function dtfft_get_aux_size_transpose_c

    function dtfft_get_aux_bytes_transpose_c(plan_ptr, aux_bytes)                                                     &
        result(error_code)                                                                                              &
        bind(C)
    !! Returns minimum number of bytes required for auxiliary buffer for transpose, C interface
        type(c_ptr),                         value    :: plan_ptr             !! C pointer to Fortran plan
        integer(c_size_t),    intent(out)             :: aux_bytes            !! Number of bytes required for auxiliary buffer for transpose
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                 pointer         :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        aux_bytes = plan%p%get_aux_bytes_transpose(error_code)
    end function dtfft_get_aux_bytes_transpose_c

    function dtfft_get_pencil_c(plan_ptr, layout, pencil)                                                             &
        result(error_code)                                                                                              &
        bind(C)
    !! Returns pencil decomposition info, C interface
        type(c_ptr),                         value    :: plan_ptr             !! C pointer to Fortran plan
        type(dtfft_layout_t),  intent(in)             :: layout               !! Layout requested
        type(dtfft_pencil_c)                          :: pencil               !! Pencil pointer
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                       pointer   :: plan                 !! Pointer to Fortran object
        type(dtfft_pencil_t) :: pencil_         !! Fortran pencil

        CHECK_PLAN_CREATED(plan_ptr, plan)
        pencil_ = plan%p%get_pencil(layout, error_code)
        call pencil_f2c(pencil_, pencil)
    end function dtfft_get_pencil_c

    function dtfft_get_element_size_c(plan_ptr, element_size)                                                         &
        result(error_code)                                                                                              &
        bind(C)
    !! Returns size of element in bytes, C interface
        type(c_ptr),                         value    :: plan_ptr             !! C pointer to Fortran plan
        integer(c_size_t),    intent(out)             :: element_size         !! Size of element in bytes
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                 pointer         :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        element_size = plan%p%get_element_size(error_code)
    end function dtfft_get_element_size_c

    function dtfft_get_alloc_bytes_c(plan_ptr, alloc_bytes)                                                           &
        result(error_code)                                                                                              &
        bind(C)
    !! Returns minimum number of bytes required to execute plan, C interface
        type(c_ptr),                         value    :: plan_ptr             !! C pointer to Fortran plan
        integer(c_size_t),    intent(out)             :: alloc_bytes          !! Number of bytes required
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                 pointer         :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        alloc_bytes = plan%p%get_alloc_bytes(error_code)
    end function dtfft_get_alloc_bytes_c

    function dtfft_get_executor_c(plan_ptr, executor)                                                                 &
        result(error_code)                                                                                              &
        bind(C)
    !! Returns executor type used in plan, C interface
        type(c_ptr),                         value    :: plan_ptr             !! C pointer to Fortran plan
        type(dtfft_executor_t),     intent(out)       :: executor             !! The enumerated type dtfft_executor_t
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                 pointer         :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        executor = plan%p%get_executor(error_code)
    end function dtfft_get_executor_c

    function dtfft_get_precision_c(plan_ptr, precision)                                                               &
        result(error_code)                                                                                              &
        bind(C)
    !! Returns precision used in plan, C interface
        type(c_ptr),                         value    :: plan_ptr             !! C pointer to Fortran plan
        type(dtfft_precision_t),      intent(out)     :: precision            !! The enumerated type dtfft_precision_t
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                 pointer         :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        precision = plan%p%get_precision(error_code)
    end function dtfft_get_precision_c

    function dtfft_get_dims_c(plan_ptr, ndims, dims)                                                                  &
        result(error_code)                                                                                              &
        bind(C)
    !! Returns dimensions of plan, C interface
        type(c_ptr),                         value    :: plan_ptr             !! C pointer to Fortran plan
        integer(c_int8_t),           intent(out)      :: ndims                !! Number of dimensions
        type(c_ptr),                 intent(out)      :: dims                 !! Array of dimensions
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                 pointer         :: plan                 !! Pointer to Fortran object
        integer(c_int32_t), pointer :: dims_(:) => null()

        CHECK_PLAN_CREATED(plan_ptr, plan)
        call plan%p%get_dims(dims_, error_code=error_code)
        if ( error_code == DTFFT_SUCCESS ) then
            ndims = size(dims_, kind=c_int8_t)
            dims = c_loc(dims_)
        endif
    end function dtfft_get_dims_c

    function dtfft_get_grid_dims_c(plan_ptr, ndims, grid_dims)                                                        &
        result(error_code)                                                                                              &
        bind(C)
    !! Returns grid decomposition dimensions of plan, C interface
        type(c_ptr),                         value    :: plan_ptr             !! C pointer to Fortran plan
        integer(c_int8_t),           intent(out)      :: ndims                !! Number of dimensions
        type(c_ptr),                 intent(out)      :: grid_dims            !! Array of dimensions
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                 pointer         :: plan                 !! Pointer to Fortran object
        integer(c_int32_t), pointer :: grid_dims_(:) => null()

        CHECK_PLAN_CREATED(plan_ptr, plan)
        call plan%p%get_grid_dims(grid_dims_, error_code=error_code)
        if ( error_code == DTFFT_SUCCESS ) then
            ndims = size(grid_dims_, kind=c_int8_t)
            grid_dims = c_loc(grid_dims_)
        endif
    end function dtfft_get_grid_dims_c

    function dtfft_set_config_c(config)                                                                               &
        result(error_code)                                                                                              &
        bind(C)
    !! Sets dtFFT configuration, C interface
        type(dtfft_config_t),             intent(in)  :: config               !! Configuration to set
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        call dtfft_set_config(config, error_code)
    end function dtfft_set_config_c

    function dtfft_report_c(plan_ptr)                                                                                 &
        result(error_code)                                                                                              &
        bind(C)
    !! Reports dtFFT Plan, C interface
        type(c_ptr),                         value    :: plan_ptr             !! C pointer to Fortran plan
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                 pointer         :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        call plan%p%report(error_code)
    end function dtfft_report_c

#ifdef DTFFT_WITH_COMPRESSION
    function dtfft_report_compression_c(plan_ptr)                                                                     &
        result(error_code)                                                                                              &
        bind(C)
    !! Reports compression information for dtFFT Plan, C interface
        type(c_ptr),                         value    :: plan_ptr             !! C pointer to Fortran plan
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                 pointer         :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        call plan%p%report_compression(error_code)
    end function dtfft_report_compression_c
#endif

    function dtfft_mem_alloc_c(plan_ptr, alloc_bytes, ptr)                                                            &
        result(error_code)                                                                                              &
        bind(C)
    !! Allocates memory for dtFFT Plan, C interface
        type(c_ptr),                         value    :: plan_ptr             !! C pointer to Fortran plan
        integer(c_size_t),                   value    :: alloc_bytes          !! Number of bytes to allocate
        type(c_ptr)                                   :: ptr                  !! Allocated pointer
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                 pointer         :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        ptr = plan%p%mem_alloc_ptr(alloc_bytes, error_code)
    end function dtfft_mem_alloc_c

    function dtfft_mem_free_c(plan_ptr, ptr)                                                                          &
        result(error_code)                                                                                              &
        bind(C)
    !! Frees memory for dtFFT Plan, C interface
        type(c_ptr),                         value    :: plan_ptr             !! C pointer to Fortran plan
        type(c_ptr),                         value    :: ptr                  !! Pointer to deallocate
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                 pointer         :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        call plan%p%mem_free_ptr(ptr, error_code)
    end function dtfft_mem_free_c

    subroutine dtfft_get_error_string_c(error_code, error_string, error_string_size) bind(C)
    !! Returns an explaination of ``error_code`` that could have been previously returned by one of dtFFT API calls,
    !! C interface
        integer(c_int32_t),  intent(in)               :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        character(c_char),   intent(out)              :: error_string(*)      !! Explanation of error
        integer(c_size_t),   intent(out)              :: error_string_size    !! Size of ``error_string``

        call string_f2c(dtfft_get_error_string(error_code), error_string, error_string_size)
    end subroutine dtfft_get_error_string_c

    subroutine dtfft_get_precision_string_c(precision, precision_string, precision_string_size) bind(C)
    !! Returns string representation of ``dtfft_precision_t``, C interface
        type(dtfft_precision_t),  intent(in)          :: precision                !! The enumerated type dtfft_precision_t
        character(c_char),        intent(out)         :: precision_string(*)      !! Resulting string
        integer(c_size_t),        intent(out)         :: precision_string_size    !! Size of string

        call string_f2c(dtfft_get_precision_string(precision), precision_string, precision_string_size)
    end subroutine dtfft_get_precision_string_c

    subroutine dtfft_get_executor_string_c(executor, executor_string, executor_string_size) bind(C)
    !! Returns string representation of ``dtfft_executor_t``, C interface
        type(dtfft_executor_t),   intent(in)          :: executor                 !! The enumerated type dtfft_executor_t
        character(c_char),        intent(out)         :: executor_string(*)       !! Resulting string
        integer(c_size_t),        intent(out)         :: executor_string_size     !! Size of string

        call string_f2c(dtfft_get_executor_string(executor), executor_string, executor_string_size)
    end subroutine dtfft_get_executor_string_c

    function dtfft_get_backend_c(plan_ptr, backend)                                                                   &
        result(error_code)                                                                                              &
        bind(C)
    !! Returns selected [[dtfft_backend_t]] during autotuning
        type(c_ptr),                         value    :: plan_ptr             !! C pointer to Fortran plan
        type(dtfft_backend_t),      intent(out)       :: backend              !! The enumerated type dtfft_backend_t
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                 pointer         :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        backend = plan%p%get_backend(error_code)
    end function dtfft_get_backend_c

    function dtfft_get_reshape_backend_c(plan_ptr, backend)                                                           &
        result(error_code)                                                                                              &
        bind(C)
    !! Returns selected [[dtfft_backend_t]] for reshapes during autotuning
        type(c_ptr),                         value    :: plan_ptr             !! C pointer to Fortran plan
        type(dtfft_backend_t),      intent(out)       :: backend              !! The enumerated type dtfft_backend_t
        integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                            !! defines API call result codes.
        type(plan_c),                 pointer         :: plan                 !! Pointer to Fortran object

        CHECK_PLAN_CREATED(plan_ptr, plan)
        backend = plan%p%get_reshape_backend(error_code)
    end function dtfft_get_reshape_backend_c

    subroutine dtfft_get_backend_string_c(backend, backend_string, backend_string_size) bind(C)
    !! Returns string representation of ``dtfft_backend_t``
        type(dtfft_backend_t),  intent(in)  :: backend             !! The enumerated type dtfft_backend_t
        character(c_char),      intent(out) :: backend_string(*)   !! Resulting string
        integer(c_size_t),      intent(out) :: backend_string_size !! Size of string

        call string_f2c(dtfft_get_backend_string(backend), backend_string, backend_string_size)
    end subroutine dtfft_get_backend_string_c

    logical(c_bool) function dtfft_get_backend_pipelined_c(backend) bind(C)
    !! Returns true if passed backend is pipelined and false otherwise.
        integer(c_int32_t),     intent(in)  :: backend             !! The enumerated type dtfft_backend_t

        dtfft_get_backend_pipelined_c = dtfft_get_backend_pipelined(dtfft_backend_t(backend))
    end function dtfft_get_backend_pipelined_c

#ifdef DTFFT_WITH_CUDA
  function dtfft_get_stream_c(plan_ptr, stream)                                                                     &
    result(error_code)                                                                                              &
    bind(C)
  !! Returns Stream associated with plan
    type(c_ptr),                         value    :: plan_ptr             !! C pointer to Fortran plan
    type(dtfft_stream_t),       intent(out)       :: stream               !! CUDA stream
    integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                          !! defines API call result codes.
    type(plan_c),                 pointer         :: plan                 !! Pointer to Fortran object

    CHECK_PLAN_CREATED(plan_ptr, plan)
    call plan%p%get_stream(stream, error_code)
  end function dtfft_get_stream_c

  function dtfft_get_platform_c(plan_ptr, platform)                                                                 &
    result(error_code)                                                                                              &
    bind(C)
  !! Returns selected [[dtfft_platform_t]] during autotuning
    type(c_ptr),                         value    :: plan_ptr             !! C pointer to Fortran plan
    type(dtfft_platform_t),  intent(out)          :: platform             !! The enumerated type dtfft_platform_t
    integer(c_int32_t)                            :: error_code           !! The enumerated type dtfft_error_t
                                                                          !! defines API call result codes.
    type(plan_c),                 pointer         :: plan                 !! Pointer to Fortran object

    CHECK_PLAN_CREATED(plan_ptr, plan)
    platform = plan%p%get_platform(error_code)
  end function dtfft_get_platform_c
#endif
end module dtfft_api