#ifndef _SU3_H
#define _SU3_H
#include "../include/machine.h"
#include "../include/complex.h"
#include "../include/random.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 */
void su3_to_comp( su3_matrix * U, su3_matrix_comp * alpha );
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 );


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

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

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

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

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

void scalar_mult_su3_vector_KE( su3_vector * src, double scalar, su3_vector * dest );
void scalar_mult_sum_su3_vector_KE( su3_vector * src1, su3_vector * src2, double scalar );
void scalar_mult_sub_su3_vector_KE( su3_vector * src1, su3_vector * src2, double scalar, su3_vector * dest );
void scalar_mult_wvec( wilson_vector * src, double s, wilson_vector * dest );
void scalar_mult_hwvec( half_wilson_vector * src, double s, half_wilson_vector * dest );
void scalar_mult_add_wvec( wilson_vector * src1, wilson_vector * src2, double scalar, wilson_vector * dest );
void scalar2_mult_add_wvec( wilson_vector * src1, double t, wilson_vector * src2, double s, wilson_vector * dest );
void scalar3_mult_add_wvec( wilson_vector * src1, double t, 
        wilson_vector * src2, double s, 
        wilson_vector * src3, double u, 
        wilson_vector * dest );

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

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

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

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

void add_wilson_vector( wilson_vector * src1, wilson_vector * src2, wilson_vector * dest );
void sub_wilson_vector( wilson_vector * src1, wilson_vector * src2, wilson_vector * dest );
double magsq_wvec( wilson_vector * src );
complex wvec_dot( wilson_vector * src1, wilson_vector * src2 );
complex wvec2_dot( wilson_vector * src1, wilson_vector * src2 );
double wvec_rdot( wilson_vector * a, wilson_vector * b );

void wp_shrink( wilson_vector * src, half_wilson_vector * dest, int dir, int sign );
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 );
void wp_grow_hch( half_wilson_vector * src, wilson_vector * dest, int dir, int sign );
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 );
void wp_shrink_hch( wilson_vector * src, half_wilson_vector * dest, int dir, int sign );
void wp_grow_add_hch( half_wilson_vector * src, wilson_vector * dest, int dir, int sign );
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 );
void wp_grow( half_wilson_vector * src, wilson_vector * dest, int dir, int sign );
void wp_grow_add( half_wilson_vector * src, wilson_vector * dest, int dir, int sign );
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 );
void mult_by_gamma( wilson_vector * src, wilson_vector * dest, int dir );
void su3_projector_w( wilson_vector * a, wilson_vector * b, su3_matrix * c );
void clear_wvec( wilson_vector * dest );
void clear_half_wvec( half_wilson_vector * dest );
void copy_wvec( wilson_vector * src, wilson_vector * dest );
void copy_half_wvec( half_wilson_vector * src, half_wilson_vector * dest );
void dump_wvec( wilson_vector * src );
void dump_wvec_32( float * v );
void dump_half_wvec( half_wilson_vector * src );
void dump_half_wvec_32( float * v );

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

void mult_su3_nn_KE( su3_matrix * a, su3_matrix * b, su3_matrix * c );
void mult_su3_na_KE( su3_matrix * a, su3_matrix * b, su3_matrix * c );
void mult_su3_an_KE( su3_matrix * a, su3_matrix * b, su3_matrix * c );
void mult_su3_aa_KE( su3_matrix * a, su3_matrix * b, su3_matrix * c );
void mult_su3_mat_vec_KE( su3_matrix * a, su3_vector * b, su3_vector * c );
void mult_adj_su3_mat_vec_KE( su3_matrix * a, su3_vector * b, su3_vector * c );
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 );
void mult_adj_su3_mat_vec_4dir_KE( su3_matrix * a, su3_vector * b, su3_vector * c );
void mult_adj_su3_mat_4vec( su3_matrix * mat, su3_vector * src,
			    su3_vector * dest0, su3_vector * dest1, su3_vector * dest2, su3_vector * dest3 );
void su3_projector_KE( su3_vector * a, su3_vector * b, su3_matrix * c );
void mult_su3_mat_hwvec( su3_matrix * mat, half_wilson_vector * src, half_wilson_vector * dest );
void mult_adj_su3_mat_hwvec( su3_matrix * mat, half_wilson_vector * src, half_wilson_vector * dest );
void sub_four_su3_vecs_KE( su3_vector * a, su3_vector * b1, su3_vector * b2, su3_vector * b3, su3_vector * b4 );
void add_su3_vector_KE( su3_vector * a, su3_vector * b, su3_vector * c );
void scalar_mult_add_su3_vector_KE( su3_vector * src1, su3_vector * src2, double scalar, su3_vector * dest );
void scalar_mult_add_su3_matrix_KE( su3_matrix * src1, su3_matrix * src2, double scalar, su3_matrix * dest );

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


#endif
