#ifndef _OTH_PROTOTYPE_H_
#define _OTH_PROTOTYPE_H_

#ifdef __CUDACC__
#include <cuComplex.h>
#endif

#ifdef __cplusplus
extern "C" {
#endif

// C
extern void        alloc_farfield(void);
extern int64_t     arrayIndex(int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int);
extern void        calcFar1d(const double [], d_complex_t ***, d_complex_t ***);
extern void        calcFar2d(double *, d_complex_t ***, d_complex_t ***);
extern void        calcPloss(void);
extern void        calcSpara(void);
extern void        calcZin(void);
extern void        cnode(int, int, int, int, d_complex_t []);
extern void        copyE(int);
extern void        copyEtoE(int);
extern d_complex_t coupling(int, int, int);
extern double      cputime(void);
extern d_complex_t cxyee(int, int, int, int);
extern d_complex_t cyyee(int, int, int, int);
extern d_complex_t czyee(int, int, int, int);
extern void        enode(int, int, int, int, int, d_complex_t []);
extern d_complex_t expjkr(d_complex_t, double);
extern d_complex_t exyee(int, int, int, int, int);
extern d_complex_t eyyee(int, int, int, int, int);
extern d_complex_t ezyee(int, int, int, int, int);
extern void        farComponent(d_complex_t, d_complex_t, double []);
extern void        farfactor(double *);
extern void        farfield(int, double, double, double, d_complex_t *, d_complex_t *);
extern void        fitgeometry(void);
extern int         geomlines(int, int, int *, int *, double (*)[8], double (*)[2][3], int *, double);
extern void        getspan(double *, int, double, double, int *, int *, double);
extern void        hnode(int, int, int, int, int, d_complex_t []);
extern d_complex_t hxyee(int, int, int, int, int);
extern d_complex_t hyyee(int, int, int, int, int);
extern d_complex_t hzyee(int, int, int, int, int);
extern int         ingeometry(double, double, double, int, double *, double);
extern int         input_data(FILE *);
extern void        interpolation(double, int, int, double **, double []);
extern void        memalloc(void);
extern void        memalloc_cg(void);
extern void        memfree_cg(void);
extern void        monitor1(FILE *, const char []);
extern void        monitor2(FILE *, int, int);
extern void        monitor3(FILE *);
extern void        monitor4(FILE *, const double []);
extern void        monitor5(FILE *, int);
extern void        monitor6(FILE *, int, double, double);
extern int         nearest(double, int, int, const double *);
extern void        outputChars(FILE *);
extern void        outputCoupling(FILE *);
extern void        outputCross(FILE *);
extern void        outputFar0d(void);
extern void        outputFar1d(void);
extern void        outputFar2d(void);
extern void        outputNear1d(void);
extern void        outputNear2d(void);
extern void        outputPloss(FILE *);
extern void        outputSpara(FILE *);
extern void        outputZin(FILE *);
extern d_complex_t planewave(int, double, double, double, int);
extern void        planewaveEx(d_complex_t *, int);
extern void        planewaveEy(d_complex_t *, int);
extern void        planewaveEz(d_complex_t *, int);
extern void        plot2dCoupling(int, int, int, double ***, scale_t, int, const double [], const char [], double, double, double);
extern void        plot2dFar0d0(int, double *[7], int, double, double, int, int, double, double, const char [], const char [], const char [], double, double, double);
extern void        plot2dFar1d0(int, double (*)[7], int [3], char, double, int, int, int, double, double, int, const char [], const char [], double, double, double, double);
extern void        plot2dFchar(int, const double [], scale_t, int, const double [], const char [], const char [], const char [], double, double, double);
extern void        plot2dFreq(void);
extern void        plot2dIter(void);
extern void        plot2dNear1d0(const char [], int, double *[3], double *[3], const double [], int, int, double, double, int, const char [], double, double [2][3], double, double, double);
extern void        plot2dNear2d0(int, int, double **, const double [], const double [], int, int, int, double, double, int, const char [], double, char, double, const char [], int, double (*)[2][3], double, double, double);
extern void        plot2dRef(int, int, const d_complex_t [], const double [], scale_t, int, const double [], const char [], double, double, double);
extern void        plot2dSmith(int, int, const d_complex_t [], const double [], const double [], const char [], double, double, double);
extern void        plot2dSpara(int, int, const d_complex_t [], scale_t, int, const double [], const char [], double, double, double);
extern void        plot2dYin(int, int, const d_complex_t [], scale_t, int, const double [], const char [], double, double, double);
extern void        plot2dZin(int, int, const d_complex_t [], scale_t, int, const double [], const char [], double, double, double);
extern void        plot3dFar2d(d_complex_t ***, d_complex_t ***);
extern void        plot3dFar2d0(int, int, double **, int, int, double, double, int, double (*)[2][3], double, int, char **);
extern void        plot3dGeom(void);
extern void        plot3dNear2d0(int, int, double **, const double [], const double [], int, int, int, double, double, int, const char [], double, char, double, const char [], int, double (*)[2][3]);
extern void        post(void);
extern int         post_data(FILE *);
extern void        readout(FILE *);
extern void        rhs(d_complex_t *, int);
extern void        setup(void);
extern void        setup2(int);
extern void        setupBg(void);
extern void        setupIndex(void);
extern void        setupMaterial(int);
extern void        setup_cells(int, int, int, int *, int *, int *);
extern void        setup_center(void);
extern void        setup_farfield(void);
extern void        setup_feed(const double *, const double *, const double *);
extern void        setup_geomline3d(void);
extern void        setup_load(material_data_t *, int);
extern void        setup_material(material_data_t *);
extern void        setup_node(int, int, int, double *, double *, double *, int *, int *, int *);
extern void        setup_planewave();
extern void        setup_point(const double *, const double *, const double *, const char []);
extern void        smithChart(int, double *, double *, double, double, double, double);
extern int         tokenize(char *, const char [], char *[], size_t);
extern void        writeout(FILE *);
extern void        zfeed(void);

// MPI
extern void        comm_broadcast(void);
extern void        comm_check(int, int, int);
extern double      comm_cputime(void);
extern void        comm_gather(const d_complex_t *, f_complex_t ***, f_complex_t ***, f_complex_t ***);
extern void        comm_string(const char *, char *);
extern d_complex_t comm_zsum(d_complex_t);
extern double      comm_dsum(double);
extern int         comm_isum(int);
extern void        mpi_close(void);
extern void        mpi_init(int, char **);
extern void        setupIndex_mpi(int, int);

#ifdef __cplusplus
}
#endif

// C + CUDA
extern void        solve(FILE *, int);

// C or CUDA
#ifndef __CUDACC__
extern void        bicgstab(int, int64_t, d_complex_t *, d_complex_t *, d_complex_t *, d_complex_t *, d_complex_t *, d_complex_t *, d_complex_t *, d_complex_t *, d_complex_t *, int *, double *, double *, FILE *, int);
extern void        copyV(d_complex_t *);
extern void        copyXpbc(d_complex_t *);
extern void        copyYpbc(d_complex_t *);
extern void        copyZpbc(d_complex_t *);
extern void        factorXpbc(d_complex_t *);
extern void        factorYpbc(d_complex_t *);
extern void        factorZpbc(d_complex_t *);
extern void        matrixEx(int, int, d_complex_t [], const d_complex_t [], d_complex_t [], d_complex_t []);
extern void        matrixEy(int, int, d_complex_t [], const d_complex_t [], d_complex_t [], d_complex_t []);
extern void        matrixEz(int, int, d_complex_t [], const d_complex_t [], d_complex_t [], d_complex_t []);
extern void        prodmv(int, int, d_complex_t [], d_complex_t [], d_complex_t [], d_complex_t []);
extern void        prodmvEx(d_complex_t [], d_complex_t [], d_complex_t []);
extern void        prodmvEy(d_complex_t [], d_complex_t [], d_complex_t []);
extern void        prodmvEz(d_complex_t [], d_complex_t [], d_complex_t []);
#else
extern void        bicgstab(int, int64_t, cuDoubleComplex *, cuDoubleComplex *, cuDoubleComplex *, cuDoubleComplex *, cuDoubleComplex *, cuDoubleComplex *, cuDoubleComplex *, cuDoubleComplex *, cuDoubleComplex *, int *, double *, double *, FILE *, int);
extern void        copyV(cuDoubleComplex *);
extern void        copyXpbc(cuDoubleComplex *);
extern void        copyYpbc(cuDoubleComplex *);
extern void        copyZpbc(cuDoubleComplex *);
extern void        factorXpbc(cuDoubleComplex *);
extern void        factorYpbc(cuDoubleComplex *);
extern void        factorZpbc(cuDoubleComplex *);
extern void        matrixEx(int, int, cuDoubleComplex *, const cuDoubleComplex *, cuDoubleComplex *, cuDoubleComplex *);
extern void        matrixEy(int, int, cuDoubleComplex *, const cuDoubleComplex *, cuDoubleComplex *, cuDoubleComplex *);
extern void        matrixEz(int, int, cuDoubleComplex *, const cuDoubleComplex *, cuDoubleComplex *, cuDoubleComplex *);
extern void        prodmv(int, int, cuDoubleComplex *, const cuDoubleComplex *, cuDoubleComplex *, cuDoubleComplex *);
extern void        prodmvEx(const cuDoubleComplex *, const cuDoubleComplex *, cuDoubleComplex *);
extern void        prodmvEy(const cuDoubleComplex *, const cuDoubleComplex *, cuDoubleComplex *);
extern void        prodmvEz(const cuDoubleComplex *, const cuDoubleComplex *, cuDoubleComplex *);
#endif

// C or CUDA (BLAS)
#ifndef __CUDACC__
extern double          Dznrm2(int64_t, const d_complex_t *);
extern void            Zaxpy(int64_t, d_complex_t, const d_complex_t *, d_complex_t *);
extern void            Zcopy(int64_t, const d_complex_t *, d_complex_t *);
//extern d_complex_t     Zdotu(int64_t, const d_complex_t *, const d_complex_t *);
extern d_complex_t     Zdotc(int64_t, const d_complex_t *, const d_complex_t *);
extern void            Zaxbycz(int64_t, d_complex_t, d_complex_t, d_complex_t, d_complex_t *, const d_complex_t *, const d_complex_t *);
#else
extern double          Dznrm2(int64_t, const cuDoubleComplex *);
extern void            Zaxpy(int64_t, cuDoubleComplex, const cuDoubleComplex *, cuDoubleComplex *);
extern void            Zcopy(int64_t, const cuDoubleComplex *, cuDoubleComplex *);
//extern cuDoubleComplex Zdotu(int64_t, const cuDoubleComplex *, const cuDoubleComplex *);
extern cuDoubleComplex Zdotc(int64_t, const cuDoubleComplex *, const cuDoubleComplex *);
extern void            Zaxbycz(int64_t, cuDoubleComplex, cuDoubleComplex, cuDoubleComplex, cuDoubleComplex *, const cuDoubleComplex *, const cuDoubleComplex *);
#endif

// C + CUDA (MPI)
#ifndef __CUDACC__
extern void        comm_boundary(d_complex_t *);
extern void        comm_Xpbc(d_complex_t *);
#else
extern void        comm_boundary(cuDoubleComplex *);
extern void        comm_Xpbc(cuDoubleComplex *);
#endif

// CUDA
#ifdef __CUDACC__
extern int         check_gpu(int, char []);
extern void        cuda_free(int, void *);
extern void        cuda_malloc(int, int, void **, size_t);
extern void        cuda_memcpy(int, void *, const void *, size_t, cudaMemcpyKind);
extern void        cuda_memset(int, void *, int, size_t);
extern void        info_gpu(FILE *, int, int, int);
extern void        info_gpu_mpi(FILE *, int, const int [], int, int, int, int, int);
extern void        memalloc_cg_gpu(void);
extern void        memalloc_gpu(void);
extern void        memcopy_gpu(void);
extern void        memcopy2_gpu(void);
extern void        memfree_cg_gpu(void);
extern int         rank2device(int, int, const int []);
extern void        setup_parameter_gpu(void);
#endif

#endif
