/****************************** su2.h ***********************************
 *									*
 *  Define here the su2 + operators                                     *
 *  MIMD version 3 							*
 *  Kari Rummukainen 1997        					*
 */


typedef struct {
  radix_link a,b,c,d;
} su2_matrix;

typedef struct {
  radix a,b,c;
} adjoint;

#ifdef T3E
void prefetch_su2(su2_matrix *);
#define prefetch_matrix(p) prefetch_su2((su2_matrix *)p)
#else
#define prefetch_matrix(par) 
#define prefetch_su2(par)
#define prefetch_adjoint(par)
#endif

#define nn_a(x,y) ( x.d*y.a + x.a*y.d - x.b*y.c + x.c*y.b)
#define nn_b(x,y) ( x.d*y.b + x.b*y.d - x.c*y.a + x.a*y.c)
#define nn_c(x,y) ( x.d*y.c + x.c*y.d - x.a*y.b + x.b*y.a)
#define nn_d(x,y) ( x.d*y.d - x.a*y.a - x.b*y.b - x.c*y.c)

#define na_a(x,y) (-x.d*y.a + x.a*y.d + x.b*y.c - x.c*y.b)
#define na_b(x,y) (-x.d*y.b + x.b*y.d + x.c*y.a - x.a*y.c)
#define na_c(x,y) (-x.d*y.c + x.c*y.d + x.a*y.b - x.b*y.a)
#define na_d(x,y) ( x.d*y.d + x.a*y.a + x.b*y.b + x.c*y.c)

#define an_a(x,y) ( x.d*y.a - x.a*y.d + x.b*y.c - x.c*y.b)
#define an_b(x,y) ( x.d*y.b - x.b*y.d + x.c*y.a - x.a*y.c)
#define an_c(x,y) ( x.d*y.c - x.c*y.d + x.a*y.b - x.b*y.a)
#define an_d(x,y) ( x.d*y.d + x.a*y.a + x.b*y.b + x.c*y.c)

#define aa_a(x,y) (-x.d*y.a - x.a*y.d - x.b*y.c + x.c*y.b)
#define aa_b(x,y) (-x.d*y.b - x.b*y.d - x.c*y.a + x.a*y.c)
#define aa_c(x,y) (-x.d*y.c - x.c*y.d - x.a*y.b + x.b*y.a)
#define aa_d(x,y) ( x.d*y.d - x.a*y.a - x.b*y.b - x.c*y.c)

#define mult_su2_nn(x,y,r) {\
r.a = nn_a(x,y); r.b = nn_b(x,y); \
r.c = nn_c(x,y); r.d = nn_d(x,y); }
#define mult_su2_na(x,y,r) {\
r.a = na_a(x,y); r.b = na_b(x,y); \
r.c = na_c(x,y); r.d = na_d(x,y); }
#define mult_su2_an(x,y,r) {\
r.a = an_a(x,y); r.b = an_b(x,y); \
r.c = an_c(x,y); r.d = an_d(x,y); }
#define mult_su2_aa(x,y,r) {\
r.a = aa_a(x,y); r.b = aa_b(x,y);\
r.c = aa_c(x,y); r.d = aa_d(x,y); }

#define mult_su2_nn_a(x,y,r) {\
r.a = nn_a(x,y); r.b = nn_b(x,y); r.c = nn_c(x,y); }
#define mult_su2_na_a(x,y,r) {\
r.a = na_a(x,y); r.b = na_b(x,y); r.c = na_c(x,y); }
#define mult_su2_an_a(x,y,r) {\
r.a = an_a(x,y); r.b = an_b(x,y); r.c = an_c(x,y); }
#define mult_su2_aa_a(x,y,r) {\
r.a = aa_a(x,y); r.b = aa_b(x,y); r.c = aa_c(x,y); }

#define add_su2_matrix(x,y,r) {\
r.a = x.a + y.a; r.b = x.b + y.b; \
r.c = x.c + y.c; r.d = x.d + y.d; }
#define sub_su2_matrix(x,y,r) {\
r.a = x.a - y.a; r.b = x.b - y.b; \
r.c = x.c - y.c; r.d = x.d - y.d; }
#define scalar_mult_sum_su2_matrix(x,s,r) {\
r.a += (s)*x.a; r.b += (s)*x.b; \
r.c += (s)*x.c; r.d += (s)*x.d; }
#define mult_su2_add(x,y,r) {\
r.a += nn_a(x,y); r.b += nn_b(x,y); \
r.c += nn_c(x,y); r.d += nn_d(x,y); }
#define su2_mul_inv_add(x,y,r) {\
r.a -= nn_a(x,y); r.b -= nn_b(x,y); \
r.c -= nn_c(x,y); r.d += nn_d(x,y); }

#define su2_sqr(x)      (x.a*x.a + x.b*x.b + x.c*x.c + x.d*x.d)
#define su2_det(x)      su2_sqr(x)
#define su2_dot(x,y)    (x.d*y.d - x.a*y.a - x.b*y.b - x.c*y.c)
#define su2_rdot(x,y)   (x.d*y.d + x.a*y.a + x.b*y.b + x.c*y.c)
#define su2_tr(x)       (2.0*x.d)
#define su2_tr2(x)      x.d
#define su2_inv(x,r) { r.a=-x.a; r.b=-x.b; r.c=-x.c; r.d= x.d; }
#define su2_inv1(x)  { x.a=-x.a; x.b=-x.b; x.c=-x.c; }
#define su2_cpy(x,r) r = x
#define su2_scalar_mul(x,s,r) {\
r.a = (s)*x.a; r.b = (s)*x.b; r.c = (s)*x.c; r.d = (s)*x.d; }
#define su2_scalar_mul_add(x,s,r) {\
r.a += (s)*x.a; r.b += (s)*x.b; r.c += (s)*x.c; r.d += (s)*x.d; }
#define su2_scalar_mul_inv_add(x,s,r) {\
r.a -= (s)*x.a; r.b -= (s)*x.b; r.c -= (s)*x.c; r.d += (s)*x.d; }
#define su2_scalar_mul_sub(x,s,r) {\
r.a -= (s)*x.a; r.b -= (s)*x.b; r.c -= (s)*x.c; r.d -= (s)*x.d; }
#define su2_add(x,r)     { r.a += x.a; r.b += x.b; r.c += x.c; r.d += x.d; }
#define su2_add_inv(x,r) { r.a -= x.a; r.b -= x.b; r.c -= x.c; r.d += x.d; }
#define su2_zero(x) x.a = x.b = x.c = x.d = 0.0
#define su2_one(x) { x.a = x.b = x.c = 0.0; x.d = 1.0; }
#define su2_scalar(s,x) { x.a = x.b = x.c = 0.0; x.d = s; }
#define su2_scalar_add(s,r) { r.d += s; }

#define mult_su2_vec(m,v,t)         mult_su2_nn(m,v,t)
#define mult_su2_vec_sum(m,v,t)     mult_su2_add(m,v,t)
#define mult_adj_su2_vec(m,v,t)     mult_su2_an(m,v,t)
#define add_su2_vector(x,y,t)       add_su2_matrix(x,y,t)
#define scalar_mult_vec(a,s,t)      su2_scalar_mul(a,s,t)
#define scalar_mult_add_vec(a,s,t)  su2_scalar_mul_add(a,s,t)

#define mult_su2_nadj(u,t,r) {\
r.a =   u.d*t.a - u.b*t.c + u.c*t.b; \
r.b =   u.d*t.b - u.c*t.a + u.a*t.c; \
r.c =   u.d*t.c - u.a*t.b + u.b*t.a; \
r.d = - u.c*t.c - u.b*t.b - u.a*t.a; }
#define mult_su2_aadj(u,t,r) {\
r.a =   u.d*t.a + u.b*t.c - u.c*t.b; \
r.b =   u.d*t.b + u.c*t.a - u.a*t.c; \
r.c =   u.d*t.c + u.a*t.b - u.b*t.a; \
r.d =   u.c*t.c + u.b*t.b + u.a*t.a; }
#define mult_su2_adjn(t,u,r) {\
r.a =   u.d*t.a + u.b*t.c - u.c*t.b; \
r.b =   u.d*t.b + u.c*t.a - u.a*t.c; \
r.c =   u.d*t.c + u.a*t.b - u.b*t.a; \
r.d = - u.c*t.c - u.b*t.b - u.a*t.a; }
#define mult_su2_adja(t,u,r) {\
r.a =   u.d*t.a - u.b*t.c + u.c*t.b; \
r.b =   u.d*t.b - u.c*t.a + u.a*t.c; \
r.c =   u.d*t.c - u.a*t.b + u.b*t.a; \
r.d =   u.c*t.c + u.b*t.b + u.a*t.a; }

#define project_to_adjoint(u,s) { s.a = u.a; s.b = u.b; s.c = u.c; }
#define adjoint_to_matrix(s,u) { \
  u.d = 0; u.a = s.a; u.b = s.b; u.c = s.c; }

#define adj_scalar(x,s) x.a = x.b = x.c = (s) 
#define adj_sqr(x) (x.a*x.a + x.b*x.b + x.c*x.c)
#define adj_scalar_mul(x,s,r) {r.a = (s)*x.a; r.b = (s)*x.b; r.c = (s)*x.c;}
#define adj_scalar_mul_add(x,s,r) {\
r.a += (s)*x.a; r.b += (s)*x.b; r.c += (s)*x.c;}
#define adj_scalar_mul_sub(x,s,r) {\
r.a -= (s)*x.a; r.b -= (s)*x.b; r.c -= (s)*x.c;}
#define add_adjoint(u,t,r) {\
r.a = u.a + t.a; r.b = u.b + t.b; r.c = u.c + t.c; }
#define sub_adjoint(u,t,r) {\
r.a = u.a - t.a; r.b = u.b - t.b; r.c = u.c - t.c; }
#define adj_add(t,r) {r.a += t.a; r.b += t.b; r.c += t.c; }
#define adj_sub(t,r) {r.a -= t.a; r.b -= t.b; r.c -= t.c; }
#define adj_zero(x) x.a = x.b = x.c = 0.0
#define adj_dot(x,y) (x.a*y.a + x.b*y.b + x.c*y.c)
#define adj_cpy(y,x) { x.a = y.a; x.b = y.b; x.c = y.c; }
#define adj_2scalar_mul(x,s,y,t,r) \
{r.a = (s)*x.a + (t)*y.a; r.b = (s)*x.b + (t)*y.b; r.c = (s)*x.c + (t)*y.c;}

#define trans_adj_up(u,e,r) { register radix t1,t2,t3;	\
  t1  = 2.0*u.d;					\
  t3  = t1*u.d - 1.0;					\
  t2  = 2.0*(e.a*u.a + e.b*u.b + e.c*u.c);		\
  r.a = e.a*t3 + u.a*t2 - t1*(e.b*u.c - e.c*u.b);	\
  r.b = e.b*t3 + u.b*t2 - t1*(e.c*u.a - e.a*u.c);	\
  r.c = e.c*t3 + u.c*t2 - t1*(e.a*u.b - e.b*u.a);}    

#define trans_adj_down(u,e,r) { register radix t1,t2,t3;	\
  t1  = 2.0*u.d;						\
  t3  = t1*u.d - 1.0;						\
  t2  = 2.0*(e.a*u.a + e.b*u.b + e.c*u.c);			\
  r.a = e.a*t3 + u.a*t2 + t1*(e.b*u.c - e.c*u.b);		\
  r.b = e.b*t3 + u.b*t2 + t1*(e.c*u.a - e.a*u.c);		\
  r.c = e.c*t3 + u.c*t2 + t1*(e.a*u.b - e.b*u.a);}    

/* exp of a matrix: exp(i E) = cos(|E|) + i E/|E| sin(|E|)
 */
#define su2_exp(x,u) { register radix r_t,s_t;			\
  r_t = sqrt((double)adj_sqr(x));				\
  if (r_t>0)							\
    { s_t = sin((double)r_t)/r_t; adj_scalar_mul(x,s_t,u);	\
      u.d = cos((double)r_t); }					\
  else su2_one(u); }

/* log of a matrix: exp(i E) = U  -> E = -i log[ U ]
 * exp(i E) = cos(|E|) + i s_a E_a/|E| sin(|E|) = U_0 + i s_a U_a 
 * limit |E| to the interval [0,pi)
 */
#define su2_log(u,x) { register double s_q; register radix r_t;		\
  s_q = adj_sqr(u);							\
  if (s_q > 0 && u.d < 1.0) {						\
    r_t = acos((double)u.d) / sqrt(s_q); adj_scalar_mul(u,r_t,x); }	\
  else adj_zero(x); }

#define su2_normalize(u,v) { register radix r_t;			\
  r_t = 1.0/sqrt( (double)su2_sqr(u) );  su2_scalar_mul( u, r_t, v ); }

/* gaussian_adjoint returns adjoint r with <r.a^2> = w^2/2 
 *  so that if want p~exp( -d r^2 ), use w = 1/sqrt(d)
 */
#define gaussian_adjoint(r,w) {			\
    r.a = (w) * gaussian_ran();			\
    r.b = (w) * gaussian_ran();     		\
    r.c = (w) * gaussian_ran(); }

#define metro_su2h( h, scale, r ) {		\
r.a = h.a + scale * (dran() - 0.5);		\
r.b = h.b + scale * (dran() - 0.5);		\
r.c = h.c + scale * (dran() - 0.5);		\
r.d = h.d + scale * (dran() - 0.5); }

#define su2_R_I( h1, h2, R, I ) { R = an_d(h1,h2); I = -an_c(h1,h2); }
#define su2_isigma3( x, r ) { r.a = -x.b; r.b = x.a; r.c = x.d; r.d = -x.c; }
#define tr2_isigma3(x) (-x.c)

/* this gives a gaussian vector of width <g^2> = w^2/2,
 * so that if we want distribution exp( -a g^2 ) 
 * we have to use w = 1/sqrt(a) 
 */
#define gaussian_su2_vector( g, t ) { 		\
  /* double t = 1/sqrt((double)w); */           \
  g.a = t*gaussian_ran();			\
  g.b = t*gaussian_ran();			\
  g.c = t*gaussian_ran();			\
  g.d = t*gaussian_ran();			\
}

#define random_su2(u) { register radix t;	\
  u.a = dran()-0.5;				\
  u.b = dran()-0.5;				\
  u.c = dran()-0.5;				\
  u.d = dran()-0.5;				\
  su2_normalize( u, u );			\
}

