 /*****************************************************

  Copyright (C) 2012 Andrea Nobile

  This software is distributed under the terms of the GNU General Public
  License (GPL)
 

*******************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "mpi.h"
#include "su3.h"
#include "random.h"
#include "start.h"
#include "misc.h"
#include "update.h"
#include "linalg.h"
#include "block.h"
#include "sw_term.h"
#include "dirac.h"
#include "sap_gcr.h"
#include "global.h"
#include "init.h"
#include "dds_interface.h"

void set_gcr_parms_no_bs(int nmr,int ncy,int nkv);

extern spinor_dble *_dds_src;
extern spinor_dble *_dds_dst;
extern spinor_dble *_dds_tmp;

extern int _ndfl;
extern int _blkrel;

static dds_lat_grid dds_lg;


static inline int index_from_coord(int t, int x, int y, int z)
{
  int co[4];
  int ip, idx;

  co[0] = t;
  co[1] = x;
  co[2] = y;
  co[3] = z;

  ipt_global(co, &ip, &idx);

  return idx;

}


void dds_init_fc(int *np, int *gl, int *npr, int *cpr, int *id, int *bs)
{
  int i;

  for(i=0;i<4;i++) {
    dds_lg.np[i] = np[i];
    dds_lg.gl[i] = gl[i];
    dds_lg.cpr[i] = cpr[i];
  }

  for(i=0;i<8;i++) {
    dds_lg.npr[i] = npr[i];
  }
  
  dds_lg.id = *id;
  dds_init(&dds_lg);
  dds_tune(5, 5, 16, 1, 0, bs); //initialization of block grid and default tuning

}





void dds_spinor_src_put_fc(int *t, int *x, int *y, int *z, complex_dble *sp)
{
  int idx;

  idx = index_from_coord(*t, *x, *y, *z);
 
  spinor_dble lsp;

  //in   d1c1 d2c1 d3c1 d4c1  d1c2 d2c2 d3c2 d4c2  d1c3 d2c3 d3c3 d4c3
  //out  d1c1 d1c2 d1c3 d2c1  d2c2 d2c3 d3c1 d3c2  d3c3 d4c1 d4c2 d4c3

  lsp.c1.c1 = sp[0];
  lsp.c1.c2 = sp[4];
  lsp.c1.c3 = sp[8];
  
  lsp.c2.c1 = sp[1];
  lsp.c2.c2 = sp[5];
  lsp.c2.c3 = sp[9];

  lsp.c3.c1 = sp[2];
  lsp.c3.c2 = sp[6];
  lsp.c3.c3 = sp[10];

  lsp.c4.c1 = sp[3];
  lsp.c4.c2 = sp[7];
  lsp.c4.c3 = sp[11];

 
  /* 
  lsp.c1.c1 = sp[0];
  lsp.c1.c2 = sp[1];
  lsp.c1.c3 = sp[2];
  
  lsp.c2.c1 = sp[3];
  lsp.c2.c2 = sp[4];
  lsp.c2.c3 = sp[5];

  lsp.c3.c1 = sp[6];
  lsp.c3.c2 = sp[7];
  lsp.c3.c3 = sp[8];

  lsp.c4.c1 = sp[9];
  lsp.c4.c2 = sp[10];
  lsp.c4.c3 = sp[11];
  */
  _dds_src[idx] = lsp;
    
}

void dds_spinor_dst_get_fc(int *t, int *x, int *y, int *z, complex_dble *cd)
{
  int idx;

  idx = index_from_coord(*t, *x, *y, *z);

  spinor_dble lsp;
  
  lsp = _dds_dst[idx];
 
  cd[0] = lsp.c1.c1;
  cd[4] = lsp.c1.c2;
  cd[8] = lsp.c1.c3;
  
  cd[1] = lsp.c2.c1;
  cd[5] = lsp.c2.c2;
  cd[9] = lsp.c2.c3;

  cd[2] = lsp.c3.c1;
  cd[6] = lsp.c3.c2;
  cd[10] = lsp.c3.c3;

  cd[3] = lsp.c4.c1;
  cd[7] = lsp.c4.c2;
  cd[11] = lsp.c4.c3;
  

  /*
  cd[0] = lsp.c1.c1;
  cd[1] = lsp.c1.c2;
  cd[2] = lsp.c1.c3;
  
  cd[3] = lsp.c2.c1;
  cd[4] = lsp.c2.c2;
  cd[5] = lsp.c2.c3;

  cd[6] = lsp.c3.c1;
  cd[7] = lsp.c3.c2;
  cd[8] = lsp.c3.c3;

  cd[9] = lsp.c4.c1;
  cd[10] = lsp.c4.c2;
  cd[11] = lsp.c4.c3;
  */ 
}


/*
double *dds_uptr(int t, int x, int y, int z, int mu)
{
  double *ptr;
  int co[4];
  int ip, idx;
  
  co[0] = t;
  co[1] = x;
  co[2] = y;
  co[3] = z;
  
  ipt_global(co, &ip, &idx);
  ptr = (double*) pud[idx][mu];
  
  return ptr;
}
*/

void dds_gauge_updated_fc() 
{
  dds_gauge_updated();
}

void dds_clover_fc(double *kappa, double *csw)
{
  dds_clover(*kappa, *csw);
}


void dds_tune_fc(int *nmr, int *ncy, int *nkv, int *ndfl)
{
   _ndfl = *ndfl;
   _blkrel = 0;
   
   set_gcr_parms_no_bs(*nmr, *ncy, *nkv);
   
   if(*ndfl == 0) {
     _ndfl = 1;
     //     free_s();
     //alloc_s(2*(*nkv)+3);
   }  
}

int dds_solve_fc(double *tol_, int *nmx_, double *res)
{
  
  int id0,id1;
  double res1;
  int fail, status;
  
  double tol = *tol_;
  int nmx = *nmx_;
  
  //  int mr;

  //MPI_Comm_rank(MPI_COMM_WORLD,&mr);
  
  

  id0=(_dds_src-psd[0][0])/NSPIN;
  id1=(_dds_dst-psd[0][0])/NSPIN;
  
  
  MPI_Barrier(MPI_COMM_WORLD);

  if(_ndfl == 0) {
    res1=sap_gcr(nmx, tol,id0,id1,&status,&fail);
  } else {
    if(_blkrel == 0) {
      res1=sap_fgmres_dr(nmx, _ndfl, tol,id0,id1,&status,&fail);
    }else if(_blkrel==1) {
      res1=ssap_fgmres_dr(nmx, _ndfl, tol,id0,id1,&status,&fail);
    }
    else res1=1.0;
  }
  
  
  MPI_Barrier(MPI_COMM_WORLD);
  
  *res = res1;
  
  return status;

}

void ddg(su3_dble *uu, int *ix, int *iy, int *iz, int *it, int *mu, int *up, int *gid);

void dd_put_gauge(int *gid)
{

  int co[4];
  int idx, ip;
  int mu;
  int up;
  su3_dble u;
  su3_dble *uptr;
 
  int i;
  
  /*
  int mr;

  MPI_Comm_rank(MPI_COMM_WORLD,&mr);
  
  if(!mr) {
    printf("putting gauge field\n");
    fflush(stdout);
    
  }
  */

  int mus[4] = {4, 1, 2, 3};
  
  for(co[0]=0;co[0]<(L0);co[0]++) {
    for(co[1]=0;co[1]<(L1);co[1]++) {
      for(co[2]=0;co[2]<(L2);co[2]++) {
        for(co[3]=0;co[3]<(L3);co[3]++) {

          if((co[0]+co[1]+co[2]+co[3])&0x1) {

            ipt_global(co, &ip, &idx);
	    uptr = pud[idx][0];

	    for(i=0;i<4;i++) {
	      mu = mus[i];
	      for(up=1; up>=0; up--) {
		ddg(&u, &co[1], &co[2], &co[3], &co[0], &mu, &up, gid);
		*uptr = u;
		uptr++;
	      }
	    }

	  } //if 

	} //for
      } //for
    } //for
  } //for
}


