/*
*===============================================================================
*
* d/wilson_d501/dirac_test.c 
*
*-------------------------------------------------------------------------------
*
* Copyright (C) 2006 Thomas Streuer 
*
* This file is part of BQCD -- Berlin Quantum ChromoDynamics program
*
* BQCD is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* BQCD is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with BQCD.  If not, see <http:*www.gnu.org/licenses/>.
*
*-------------------------------------------------------------------------------
*/
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>

#ifndef NO_MPI
#include <mpi.h>
#endif
#ifdef BGL
#include <rts.h>
#include <bglpersonality.h>
#include "bglmemmap.h"
#endif


#include "defs.h"
#include "memory.h"
#include "io.h"
#include "neighb.h"
#include "shmem_comm.h"

#define DEBUG_PREF_

#ifdef DEBUG_PREF
int* debug_pref_buffer;
int* debug_pref_lbuffer;
#endif

int my_rank;
int num_procs;

volatile int wait_gdb=0;

int lattice[4]={LAT_X, LAT_Y, LAT_Z, LAT_T};
int grid[4]={MOD_NUM_X, MOD_NUM_Y, MOD_NUM_Z, MOD_NUM_T};

extern void test_t(int);

extern double global_sum_tree(double*);
extern double global_sum_torus(double*);
extern void global_sum_var(double*, double*, int*, int*);

/* extern void bgl_send_U_nb(int* mu1, int* mu2, int* n, int* off_list, */
/* 			  double* send_buf, double* recv_buf); */


int main(int argc, char** argv)
{
  int t0=time(0);
  int n_it=1;
  if (argc>1)
    if (argv[1][0]=='d')
      wait_gdb=1;
    else
      n_it=atoi(argv[1]);


  
#ifdef BGL
  char location[BGLPERSONALITY_MAX_LOCATION];
  BGLPersonality personality;
#endif

#ifndef NO_MPI
  MPI_Init(&argc, &argv);
  MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
  MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
#endif
  /*
   * Initialize MPI
   */


  /*
   * BlueGene runtime: get and print BG personality
   */

  srand48(t0*(my_rank+1));

#ifdef BGL
  rts_get_personality(&personality, sizeof(personality));
  BGLPersonality_getLocationString(&personality, location);



#endif



   wilson_init_test(lattice, grid);
/*    shmem_comm_init(); */

   while (wait_gdb);


   double loc[240]={2., 3., 4., 5., 1., -1., 0., 8., 1.1, 2.2, 3., 9.,
   -2., 13., 24., 5., 1., -1., 0., 8., 1.1, 2.2, 3., 9.};
   double glob[240]={1,1,1,1,1,1,1,1,1,1,1,1,
   1,1,1,1,1,1,1,1,1,1,1,1};
   long long loc_i8[24]={2, 3, 4, 5, 1, -1, 0, 8, 1, 2, 3, 9,
   -2, 13, 24, 5, 1, -1, 0, 8, 1, 2, 3, 9};
   long long glob_i8[24]={1,1,1,1,1,1,1,1,1,1,1,1,
   1,1,1,1,1,1,1,1,1,1,1,1};
   loc_i8[11]=-(my_rank+4294967296ll);
   loc_i8[12]=-(my_rank+4294967276ll);
//   double gs=global_sum_torus(&loc);
   int n_s=173;
   int op=0;
   for (int i=0; i<n_s; i++)
     loc[i]=drand48();
   global_sum_var(loc, glob, &n_s, &op);
/*    global_sum_var((double*) loc_i8, (double*) glob_i8, &n_s, &op); */
/*    printf("%4i %5i\t% 12.6e\t% 12.6e\t% 12.6e\t% 12.6e\t% 12.6e\n", my_rank, rts_get_processor_id(), */
/* 	  glob[0], glob[1], glob[7], glob[11], glob[12]); */
   for (int i=0; i<n_s; i++)
   {
     double l=loc[i];
     double g=global_sum_torus(&l);
//     if (g!=glob[i] && my_rank==0)
     if (my_rank==0)
       printf("D: %3i %3i   % 24.18e <-> % 24.18e\n", my_rank, i, g, glob[i]);
     if (g!=glob[i])
       printf("DIFF: %3i %3i   % 24.18e <-> % 24.18e\n", my_rank, i, g, glob[i]);
   }


/*    printf("%4i %5i\t%8lli\t%8lli\t%8lli\t%8lli\t%8lli\n", my_rank, rts_get_processor_id(), */
/* 	  glob_i8[0], glob_i8[1], glob_i8[7], glob_i8[11], glob_i8[12]); */
/*    sleep(2); */
/*    exit(1); */




   double* U_send=new_gc();
   double* U_recv=new_gc();
   for (int i=0; i<72*VOL; i++)
     U_send[i]=(i+1)*(my_rank+1);
   for (int i=0; i<72*VOL; i++)
     U_recv[i]=0.;


   sleep(1);

   int mu1=0;
   int mu2=5;
   int n=2;
   int* off_list=(int*) malloc(n*sizeof(int));
   off_list[0]=3;

   int d0=wilson_data.phys_dir[1];

/*    printf("[%i] %x %x %i\n", my_rank, U_send, U_recv, d0); */
/*    sleep(1); */

/*    bgl_send_U_nb(&mu1, &mu2, &n, off_list, U_send, U_recv); */

/*    sleep(1); */
/*    printf("[%i] s % 12.6e\tr % 12.6e\tr % 12.6e\n", my_rank, */
/* 	  U_send[0], U_recv[0], U_recv[18]); */
/*    sleep(1); */



/*    for (int f=0; f<2; f++) */
/*      for (int mu=0; mu<3; mu++) */
/*        printf("[%i] f %i  mu %i   s %i   r %i\n", */
/* 	      my_rank, f, mu, */
/* 	      send_fifo_stat(f, mu), */
/* 	      recv_fifo_stat(f, mu)); */


   exit(0);






   double test_data[200]={2,-4,-5,6,0,3,99,1,2,3, 17, 19, 23, 11, 23, 15, 12, 12, 43, 12};
   for (int i=0; i<20; i++)
     test_data[i]*=(my_rank+2.);
   double r_data[200];
   for (int i=0; i<10; i++)
     r_data[i]=-7;
#if 0

   if (my_rank==1)
   {
     for (int i=0; i<5; i++)
       fifo_send(0, 0, 6, test_data);
     sleep(1);
     for (int i=0; i<5; i++)
       fifo_send(0, 0, 6, test_data);
     sleep(1);
     for (int i=0; i<5; i++)
       fifo_send(0, 0, 6, test_data);
   }

   if (my_rank==0)
   {
     sleep(4);
     for (int i=0; i<5; i++)
       fifo_send(0, 0, 6, test_data);
/*      sleep(1); */
/*      for (int i=0; i<5; i++) */
/*        fifo_send(0, 0, 6, test_data); */
   }
   if (my_rank==1)
   {
/*      printf("BEF %i s %i\n", my_rank, send_fifo_stat(0, 0)); */
/*      for (int i=0; i<5; i++) */
/*        fifo_send(0, 0, 6, test_data); */
/*      printf("BEEF %i s %i\n", my_rank, send_fifo_stat(0, 0)); */
/*      for (int i=0; i<4; i++) */
/*        fifo_send(0, 0, 6, test_data); */
/*      sleep(1); */
/*      printf("BEEF %i s %i\n", my_rank, send_fifo_stat(0, 0)); */
/*      fifo_send(0, 0, 6, test_data); */
/*      printf("BEEF %i s %i\n", my_rank, send_fifo_stat(0, 0)); */
   }


   sleep(7);
   for (int f=0; f<2; f++)
     for (int mu=0; mu<3; mu++)
       printf("[%i] f %i  mu %i   s %i   r %i\n",
	      my_rank, f, mu,
	      send_fifo_stat(f, mu),
	      recv_fifo_stat(f, mu));
   sleep(3);

   exit(1);
#endif


  double* in=new_vec();
  double* out=new_vec();
  double* out1=new_vec();
  double* out2=new_vec();
  double* out3=new_vec();
  double* out4=new_vec();
  double* out5=new_vec();
  double* U=new_gc();
  double kappa=.12;
  read_gc("conf", U);
/*   for (int i=0; i<4*VOL; i++) */
/*     U[18*i]=1.; */
/*   write_gc("xconf", U); */
#ifndef SINGLE
  exchange_U(U);
#endif
#ifdef MU_SLOW
  transf_U(U);
#endif
/*   for (int i=0; i<40; i++) */
/*     printf("%3i % 12.6e\n", i, U[i+4608]); */



  int volume=LAT_X*LAT_Y*LAT_T*LAT_Z;

  read_vec("input", in);
/*   write_vec("xnput", in); */
  printf("[%3i] i 0x%x\to 0x%x\tU 0x%x\n", my_rank, in, out, U);
/*   for (int i=0; i<24*VOL; i++) */
/*     in[i]=0.; */
/*   if (my_rank==2) */
/*     in[0]=1.; */
/*   if (my_rank==0) */
/*   { */
/* /\*     for (int i=0; i<16; i++) *\/ */
/*       in[24]=1.; */

/* /\*     in[0]=1.; *\/ */
/* /\*     in[24]=10.; *\/ */
/* /\*     in[48]=100.; *\/ */
/* /\*     in[72]=1000.; *\/ */
/* /\*     in[96]=10000.; *\/ */
/* /\*     in[120]=100000.; *\/ */
/* /\*     in[144]=1000000.; *\/ */
/* /\*     in[168]=10000000.; *\/ */
/*   } */
/*   if (my_rank==0) */
/*     for (int i=0; i<24; i++) */
/*       in[i]=1000.+i; */
/*   if (my_rank==2) */
/*     in[0]=1.; */
/*   if (my_rank==1) */
/*     in[0*1296+24*20+0]=1.; */

/*  in[72]=1.; */
  for (int x=0; x<24*VOL; x++)
    out[x]=0.;


#ifndef NO_MPI
   MPI_Barrier(MPI_COMM_WORLD);
#endif


/*  if (my_rank==2) */
/*  { */
/*    int ctr=0; */
/*    int* np=neighb_offs; */
/*    int* nnp=neighb; */
/*    for (int i=0; i<VOL; i++) */
/*    { */
/*      for (int j=0; j<4; j++) */
/*      { */
/*        ctr+=(2*(*np))+192; */
/*        if (i==8|| i==9) */
/*        printf("[%i] (%i) %x\t%i<%x, %i>\n", my_rank, i, *np, ctr, *nnp, ((*np)*4)); */
/*        nnp++; */
/*        np++; */
/*      } */
/*      for (int j=0; j<4; j++) */
/*      { */
/*        ctr+=(2*(*np))+192; */
/*        if (i==8|| i==9) */
/*        printf("[%i] (%i) %x\t%i\t<%x, %i>\n", my_rank, i, *np, ctr, *nnp, ((*np)*4)); */
/*        np++; */
/*        np++; */
/*        nnp++; */
/*        nnp++; */
/*      } */
/*    } */
/*  } */
/*  MPI_Barrier(MPI_COMM_WORLD); */
/*  if (my_rank==0) */
/*  { */
/*    int ctr=0; */
/*    int* np=neighb_offs; */
/*    int* nnp=neighb; */
/*    for (int i=0; i<VOL; i++) */
/*    { */
/*      for (int j=0; j<4; j++) */
/*      { */
/*        ctr+=(2*(*np))+192; */
/*        if (i==8 || i==8) */
/* 	 printf("[%i] (%i) %x\t%i<%x, %i>\n", my_rank, i, *np, ctr, *nnp, ((*nnp)& 0x7fffffff)); */
/*        nnp++; */
/*        np++; */
/*      } */
/*      for (int j=0; j<4; j++) */
/*      { */
/*        ctr+=(2*(*np))+192; */
/*        if (i==8|| i==8) */
/*        printf("[%i] (%i) %x\t%i\t<%x, %i>\n", my_rank, i, *np, ctr, *nnp, ((*nnp)& 0x7fffffff)); */
/*        np++; */
/*        np++; */
/*        nnp++; */
/*        nnp++; */
/*      } */
/*    } */
/*  } */




#ifdef DEBUG_PREF
   debug_pref_buffer=(int*) malloc(50000*sizeof(int));
   debug_pref_lbuffer=(int*) malloc(50000*sizeof(int));
   for (int i=0; i<50000; i++)
   {
     debug_pref_buffer[i]=-1;
     debug_pref_lbuffer[i]=-1;
   }
#endif

   int oe0=0;
   int oe1=1;
   int oe2=2;
   t0=time(0);
  for (int i=0; i<(n_it+1)-1; i++)
  {
/*     dirac(U, in, out, &kappa, &oe2); */

/*     dirac(U, in, out, &kappa, &oe0); */
/*     dirac(U, in, out, &kappa, &oe1); */
/*     dirac_dag(U, in, out, &kappa, &oe1); */
/*     dirac_dag(U, in, out, &kappa, &oe1); */

    dirac(U, in+12*volume, out, &kappa, &oe0);
    dirac(U, in, out+12*volume, &kappa, &oe1);
/*     dirac_dag(U, out+12*volume, out1, &kappa, &oe0); */
/*     dirac_dag(U, out, out1+12*volume, &kappa, &oe1); */
/*     dirac(U, out1+12*volume, out2, &kappa, &oe0); */
/*     dirac(U, out1, out2+12*volume, &kappa, &oe1); */
/*     dirac_dag(U, out2+12*volume, out3, &kappa, &oe0); */
/*     dirac_dag(U, out2, out3+12*volume, &kappa, &oe1); */
  }
  t0=time(0)-t0;
   for (int i=0; i<24*volume; i++)
    if (out[i]!=0.)
      out[i]=in[i]-kappa*out[i];
  printf("O % 12.6e\n", *out);
  printf("[%i] T %i\n", my_rank, t0);


#ifdef DEBUG_PREF
  if (my_rank==0)
  {
    FILE* f=fopen("DEBUG_PREF_L", "w");
    for (int i=0; i<10000; i++)
      fprintf(f, "%8i\t%8x\n", i, debug_pref_lbuffer[i]);
    fclose(f);
    f=fopen("DEBUG_PREF_P", "w");
    for (int i=0; i<10000; i++)
      fprintf(f, "%8i\t%8x\n", i, debug_pref_buffer[i]);
    fclose(f);
  }
#endif

/*    if (my_rank==1) */
/*    { */
/*      sleep(1); */
/*      int s; */
/*      while (s=recv_fifo_stat(0, 3)) */
/*      { */
/*        printf("RSTAT >> %i\n", s); */
/*        fifo_recv(0, 3, 6, r_data); */
/*        usleep(10000); */
/*      } */
/*    } */



   double d[14];


/*    sleep(2); */
/*    for (int f=0; f<2; f++) */
/*      for (int mu=0; mu<4; mu++) */
/*        printf("[%i] RSTAT_%i_%i %i\n", */
/* 	      my_rank, f, mu, recv_fifo_stat(f, mu)); */

/*    sleep(2); */

   char fn[200];
   for (int fw=0; fw<2; fw++)
     for (int mu=0; mu<4; mu++)
     if (recv_fifo_stat(fw, mu))
     {
       sprintf(fn, "RECVD_r%i_%i_%i", my_rank, fw, mu);
       FILE* f=fopen(fn, "w");
       while (recv_fifo_stat(fw, mu))
       {
	 fifo_recv(fw, mu, 6, d);
	 fprintf(f, "---------------\n");
	 for (int j=0; j<6; j++)
	   fprintf(f, "% 12.6e\t% 12.6e\n", d[2*j], d[2*j+1]);
       }
       fclose(f);
     }
   
/*    sleep(2); */
/*    if (my_rank==0) */
/*    { */
/*      while (recv_fifo_stat(0, 0)) */
/* /\*      for (int i=0; i<2; i++) *\/ */
/*      { */
/*        printf("STAT %i\n", recv_fifo_stat(0, 0)); */
/*        fifo_recv(0, 0, 6, d); */
/*        for (int j=0; j<6; j++) */
/* 	 fprintf(f, "% 12.6e\t% 12.6e\n", d[2*j], d[2*j+1]); */
/*        usleep(100000); */
/*      } */
/*    } */

/*    fclose(f); */

/*  clear all fifos */
/*    sleep(2); */
/*    for (int f=0; f<2; f++) */
/*      for (int mu=0; mu<3; mu++) */
/*        while(recv_fifo_stat(f, mu)) */
/* 	 fifo_recv(f, mu, 6, d); */



#ifndef NO_MPI
   MPI_Barrier(MPI_COMM_WORLD);
#endif

   write_vec("out", out);

#ifndef NO_MPI
  MPI_Finalize();
#endif
  exit(0);

}
