#ifndef _SU3_H
#define _SU3_H
#include "../include/machine.h"
#include "../include/complex.h"
#include "../include/random.h"
#include "../include/macros.h"

#include "targetDP.h"

/* SU(3) */
typedef struct
{
    complex e[3][3];
} su3_matrix;
typedef struct
{
    float a[8];
} su3_matrix_comp;
typedef struct
{
    complex e[3][3][3][3];
} su3_hypermatrix;
typedef struct
{
    complex c[3];
} su3_vector;
typedef struct
{
    complex m01, m02, m12;
    double m00im, m11im, m22im;
    double space;
} anti_hermitmat;

#ifdef SPINORFIRST
typedef struct
{
    su3_vector d[4];
} wilson_vector;
#else
typedef struct
{
    complex d[4];
} spinor_vector;
typedef struct
{
    spinor_vector c[3];
} wilson_vector;
#endif

typedef struct
{
    su3_vector h[2];
} half_wilson_vector;
typedef struct
{
    wilson_vector d[4];
} spin_wilson_vector;
typedef struct
{
    spin_wilson_vector c[3];
} wilson_propagator;
typedef struct
{
    wilson_vector c[3];
} color_wilson_vector;
typedef struct
{
    color_wilson_vector d[4];
} wilson_matrix;

#ifdef CLOVER
/* Clover vectors */
typedef struct
{
    complex tr[2][15];
} triangular;
typedef struct
{
    double di[2][6];
} diagonal;
#endif
/* SU2 for gauge fixing */
typedef struct
{
    complex esu2[2][2];
} su2_matrix;
typedef struct
{
    double a[4];
} su2_matr_comp;


#define GAMMAFIVE -1		/* some integer which is not a direction */
#define PLUS 1			/* flags for selecting M or M_adjoint */
#define MINUS -1
/* Macros to multiply complex numbers by +-1 and +-i */
#define TIMESPLUSONE(a,b) { (b).real =  (a).real; (b).imag = (a).imag; }
#define TIMESMINUSONE(a,b) { (b).real =  -(a).real; (b).imag = -(a).imag; }
#define TIMESPLUSI(a,b) { (b).real = -(a).imag; (b).imag =  (a).real; }
#define TIMESMINUSI(a,b) { (b).real =  (a).imag; (b).imag = -(a).real; }

#define FORMAT(a,b) for(a=0;a<3;a++) for (b=0;b<3;b++)

/* for lattice i/o */
__targetHost__ void su3_to_comp( su3_matrix * U, su3_matrix_comp * alpha );
__targetHost__ void comp_to_su3( su3_matrix_comp * alpha, su3_matrix * result );

double magsq_hwvec( half_wilson_vector * vec );
complex hwvec_dot( half_wilson_vector * a, half_wilson_vector * b );


__targetHost__ double realtrace_su3_KE( su3_matrix * a, su3_matrix * b );
__targetHost__ complex trace_su3_KE( su3_matrix * a );
__targetHost__ complex complextrace_su3_KE( su3_matrix * a, su3_matrix * b );
__targetHost__ complex det_su3_KE( su3_matrix * a );
__targetHost__ void add_su3_matrix_KE( su3_matrix * a, su3_matrix * b, su3_matrix * c );
__targetHost__ void sub_su3_matrix_KE( su3_matrix * a, su3_matrix * b, su3_matrix * c );
__targetHost__ void scalar_mult_su3_matrix_KE( su3_matrix * src, double scalar, su3_matrix * dest );
__targetHost__ void scalar_mult_sub_su3_matrix_KE( su3_matrix * src1, su3_matrix * src2, double scalar, su3_matrix * dest );
__targetHost__ void c_scalar_mult_su3mat_KE( su3_matrix * src, complex * scalar, su3_matrix * dest );
__targetHost__ void c_scalar_mult_add_su3mat_KE( su3_matrix * src1, su3_matrix * src2, complex * scalar, su3_matrix * dest );
__targetHost__ void c_scalar_mult_sub_su3mat_KE( su3_matrix * src1, su3_matrix * src2, complex * scalar, su3_matrix * dest );
__targetHost__ void su3_adjoint_KE( su3_matrix * a, su3_matrix * b );
__targetHost__ void su3_transpose( su3_matrix * a, su3_matrix * b );
__targetHost__ void make_anti_hermitian_KE( su3_matrix * m3, anti_hermitmat * ah3 );
__targetHost__ void make_traceless( su3_matrix * m3, su3_matrix * m4 );

__targetHost__ void funny_anti_hermitian( int ix, int iy, int iz, int it, int idir, anti_hermitmat * mat_antihermit );
__targetHost__ void random_anti_hermitian_KE( anti_hermitmat * mat_antihermit );
__targetHost__ void uncompress_anti_hermitian_KE( anti_hermitmat * mat_anti, su3_matrix * mat );
__targetHost__ void compress_anti_hermitian_KE( su3_matrix * mat, anti_hermitmat * mat_anti );
__targetHost__ void clear_su3mat_KE( su3_matrix * dest );
__targetHost__ void unit_su3mat( su3_matrix *dest );
__targetHost__ void su3mat_copy_KE( su3_matrix * a, su3_matrix * b );
__targetHost__ void dump_mat( su3_matrix * m );
__targetHost__ void scalar_mult_ahm( anti_hermitmat * a, double s, anti_hermitmat * b );
__targetHost__ void clear_ahm( anti_hermitmat * a );

__targetHost__ complex su3_dot_KE( su3_vector * a, su3_vector * b );
__targetHost__ double su3_rdot_KE( su3_vector * a, su3_vector * b );
__targetHost__ double magsq_su3vec_KE( su3_vector * a );
__targetHost__ void su3vec_copy_KE( su3_vector * a, su3_vector * b );
__targetHost__ void dumpvec_KE( su3_vector * v );
__targetHost__ void clearvec_KE( su3_vector * v );

__targetHost__ void mult_su3_mat_vec_sum_KE( su3_matrix * a, su3_vector * b, su3_vector * c );
__targetHost__ void mult_su3_mat_vec_nsum_KE( su3_matrix * a, su3_vector * b, su3_vector * c );
__targetHost__ void mult_adj_su3_mat_vec_sum_KE( su3_matrix * a, su3_vector * b, su3_vector * c );
__targetHost__ void mult_adj_su3_mat_vec_nsum_KE( su3_matrix * a, su3_vector * b, su3_vector * c );

__targetHost__ void sub_su3_vector_KE( su3_vector * a, su3_vector * b, su3_vector * c );

__targetHost__ void scalar_mult_su3_vector_KE( su3_vector * src, double scalar, su3_vector * dest );
__targetHost__ void scalar_mult_sum_su3_vector_KE( su3_vector * src1, su3_vector * src2, double scalar );
__targetHost__ void scalar_mult_sub_su3_vector_KE( su3_vector * src1, su3_vector * src2, double scalar, su3_vector * dest );
__targetHost__ void scalar_mult_wvec( wilson_vector * src, double s, wilson_vector * dest );
__targetHost__ void scalar_mult_hwvec( half_wilson_vector * src, double s, half_wilson_vector * dest );
__targetEntry__  void scalar_mult_add_wvec_lattice( wilson_vector * src1, wilson_vector * src2, const double sval, wilson_vector * dest );
__targetHost__ __target__ void scalar_mult_add_wvec( wilson_vector * src1, wilson_vector * src2, double scalar, wilson_vector * dest );
__target__ void scalar_mult_add_wvec_tdp( double * src1, double * src2, double s, double * dest, int isite );
__targetHost__ void scalar2_mult_add_wvec( wilson_vector * src1, double t, wilson_vector * src2, double s, wilson_vector * dest );
__targetHost__ void scalar3_mult_add_wvec( wilson_vector * src1, double t, 
        wilson_vector * src2, double s, 
        wilson_vector * src3, double u, 
        wilson_vector * dest );

__targetHost__ void scalar_mult_add_g5_wvec( wilson_vector * src1, wilson_vector * src2, double scalar, wilson_vector * dest );
__targetHost__ void scalar_g5_mult_add_wvec( wilson_vector * src1, wilson_vector * src2, double scalar, wilson_vector * dest );
__targetHost__ void g5_mult_wvec( wilson_vector * src, wilson_vector * dest );

__targetHost__ void scalar_mult_addtm_wvec( wilson_vector * src1, wilson_vector * src2, double scalar, wilson_vector * dest );
__targetHost__ void c_scalar_mult_wvec( wilson_vector * src1, complex * phase, wilson_vector * dest );
__targetHost__ void c_scalar_mult_add_wvec( wilson_vector * src1, wilson_vector * src2, complex * phase, wilson_vector * dest );
__targetHost__ void c_scalar_mult_add_wvec2( wilson_vector * src1, wilson_vector * src2, complex s, wilson_vector * dest );
__targetHost__ void c_scalar_mult_su3vec_KE( su3_vector * src, complex * phase, su3_vector * dest );
__targetHost__ void c_scalar_mult_add_su3vec_KE( su3_vector * v1, complex * phase, su3_vector * v2 );
__targetHost__ void c_scalar_mult_sub_su3vec_KE( su3_vector * v1, complex * phase, su3_vector * v2 );

__targetHost__ void mult_by_gamma_left( wilson_matrix * src, wilson_matrix * dest, int dir );
__targetHost__ void mult_by_gamma_right( wilson_matrix * src, wilson_matrix * dest, int dir );
__targetHost__ void mult_by_gamma_l( spin_wilson_vector * src, spin_wilson_vector * dest, int dir );
__targetHost__ void mult_by_gamma_r( spin_wilson_vector * src, spin_wilson_vector * dest, int dir );

__targetHost__ void mult_mat_wilson_vec( su3_matrix * mat, wilson_vector * src, wilson_vector * dest );
__targetHost__ void mult_adj_mat_wilson_vec( su3_matrix * mat, wilson_vector * src, wilson_vector * dest );

__targetHost__ void add_wilson_vector( wilson_vector * src1, wilson_vector * src2, wilson_vector * dest );
__targetHost__ void sub_wilson_vector( wilson_vector * src1, wilson_vector * src2, wilson_vector * dest );
__targetEntry__ void sub_wilson_vector_lattice( wilson_vector * src1, wilson_vector * src2, wilson_vector * dest);
__targetHost__ double magsq_wvec( wilson_vector * src );
__targetEntry__ void magsq_wvec_lattice( wilson_vector * vec, double* result );
__targetHost__ complex wvec_dot( wilson_vector * src1, wilson_vector * src2 );
__targetHost__ complex wvec2_dot( wilson_vector * src1, wilson_vector * src2 );
__targetHost__ double wvec_rdot( wilson_vector * a, wilson_vector * b );
__targetEntry__ void wvec_rdot_tdp_lattice(double* t_result, wilson_vector* t_cg_p,  wilson_vector* t_cg_mp);
__target__ double wvec_rdot_tdp( double* ss, double * a, int isitea, double * b, int isiteb );
__targetHost__ void wp_shrink( wilson_vector * src, half_wilson_vector * dest, int dir, int sign );
__targetHost__ __target__ void wp_shrink_4dir( wilson_vector * a, half_wilson_vector * b1,
		     half_wilson_vector * b2, half_wilson_vector * b3, half_wilson_vector * b4, int sign );
__target__ void wp_shrink_4dir_tdp(double * a, double* b1,
				   double* b2, double* b3, double* b4, int isite, int sign );
__targetHost__ void wp_grow_hch( half_wilson_vector * src, wilson_vector * dest, int dir, int sign );
__targetHost__ void wp_shrink_4dir_hch( wilson_vector * a, half_wilson_vector * b1,
			 half_wilson_vector * b2, half_wilson_vector * b3, half_wilson_vector * b4, int sign );
__targetHost__ void wp_shrink_hch( wilson_vector * src, half_wilson_vector * dest, int dir, int sign );
__targetHost__ void wp_grow_add_hch( half_wilson_vector * src, wilson_vector * dest, int dir, int sign );
__targetHost__ void grow_add_four_wvecs_hch( wilson_vector * a, half_wilson_vector * b1,
			      half_wilson_vector * b2, half_wilson_vector * b3, half_wilson_vector * b4, int sign, int sum );
__targetHost__ void wp_grow( half_wilson_vector * src, wilson_vector * dest, int dir, int sign );
__targetHost__ void wp_grow_add( half_wilson_vector * src, wilson_vector * dest, int dir, int sign );
__targetHost__ __target__ void grow_add_four_wvecs( wilson_vector * a, half_wilson_vector * b1,
			  half_wilson_vector * b2, half_wilson_vector * b3, half_wilson_vector * b4, int sign, int sum );
__target__ void grow_add_four_wvecs_tdp( double * a,  double* b1,
					 double* b2, double* b3, double* b4, int isite,int sign, int sum );
__targetHost__ void mult_by_gamma( wilson_vector * src, wilson_vector * dest, int dir );
__targetHost__ void su3_projector_w( wilson_vector * a, wilson_vector * b, su3_matrix * c );
__targetHost__ void clear_wvec( wilson_vector * dest );
__targetHost__ void clear_half_wvec( half_wilson_vector * dest );
__targetHost__ void copy_wvec( wilson_vector * src, wilson_vector * dest );
__targetHost__ void copy_half_wvec( half_wilson_vector * src, half_wilson_vector * dest );
__targetHost__ void dump_wvec( wilson_vector * src );
__targetHost__ void dump_wvec_32( float * v );
__targetHost__ void dump_half_wvec( half_wilson_vector * src );
__targetHost__ void dump_half_wvec_32( float * v );

__targetHost__ double gaussian_rand_no_KE(  );
#include "../include/int32type.h"
__targetHost__ void byterevn( int32type w[], int n );

__targetHost__ void mult_su3_nn_KE( su3_matrix * a, su3_matrix * b, su3_matrix * c );
__targetHost__ void mult_su3_na_KE( su3_matrix * a, su3_matrix * b, su3_matrix * c );
__targetHost__ void mult_su3_an_KE( su3_matrix * a, su3_matrix * b, su3_matrix * c );
__targetHost__  void mult_su3_aa_KE( su3_matrix * a, su3_matrix * b, su3_matrix * c );
__targetHost__ void mult_su3_mat_vec_KE( su3_matrix * a, su3_vector * b, su3_vector * c );
__targetHost__ void mult_adj_su3_mat_vec_KE( su3_matrix * a, su3_vector * b, su3_vector * c );
__targetHost__ void mult_su3_mat_vec_sum_4dir_KE( su3_matrix * a, su3_vector * b0, su3_vector * b1, su3_vector * b2, su3_vector * b3, su3_vector * c );
__targetHost__ void mult_adj_su3_mat_vec_4dir_KE( su3_matrix * a, su3_vector * b, su3_vector * c );
__targetHost__ void mult_adj_su3_mat_4vec( su3_matrix * mat, su3_vector * src,
			    su3_vector * dest0, su3_vector * dest1, su3_vector * dest2, su3_vector * dest3 );
__targetHost__ void su3_projector_KE( su3_vector * a, su3_vector * b, su3_matrix * c );
__targetHost__ __target__ void mult_su3_mat_hwvec( su3_matrix * mat, half_wilson_vector * src, half_wilson_vector * dest );
__target__ void mult_su3_mat_hwvec_tdp(  const double* __restrict__ mat, int idirmat,   const double* __restrict__ src, double* dest, int isite );
__targetHost__ __target__ void mult_adj_su3_mat_hwvec( su3_matrix * mat, half_wilson_vector * src, half_wilson_vector * dest );
__target__ void mult_adj_su3_mat_hwvec_tdp(  double* mat, int idirmat,   double* src,  double* dest, int isite );
__targetHost__ void sub_four_su3_vecs_KE( su3_vector * a, su3_vector * b1, su3_vector * b2, su3_vector * b3, su3_vector * b4 );
__targetHost__ void add_su3_vector_KE( su3_vector * a, su3_vector * b, su3_vector * c );
__targetHost__ void scalar_mult_add_su3_vector_KE( su3_vector * src1, su3_vector * src2, double scalar, su3_vector * dest );
__targetHost__ void scalar_mult_add_su3_matrix_KE( su3_matrix * src1, su3_matrix * src2, double scalar, su3_matrix * dest );

/* su2 */
__targetHost__ void left_su2_hit_n_KE( su2_matrix * u, int p, int q, su3_matrix * link );
__targetHost__ void right_su2_hit_a( su2_matrix * u, int p, int q, su3_matrix * link );
__targetHost__ void mult_su2_mat_vec_elem_n_KE( su2_matrix * u, complex * x0, complex * x1 );
__targetHost__ void mult_su2_mat_vec_elem_a( su2_matrix * u, complex * x0, complex * x1 );


#endif
