/*
*===============================================================================
*
* d/wilson_d520/lattice_testing.c
*
*-------------------------------------------------------------------------------
*
* Copyright (C) 2010 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 "globals.h"

#include <mpi.h>
#include <stdlib.h>

static const unsigned transpose[9] = { 0, 3, 6, 1, 4, 7, 2, 5, 8 };

unsigned* debug_buffer_asm;
unsigned* debug_buffer;


void coords0(int i, int* x)
{
  x[0]=i % lattice[0];
  x[1]=(i/lattice[0]) % lattice[1];
  x[2]=(i/(lattice[0]*lattice[1])) % lattice[2];
  x[3]=(i/(lattice[0]*lattice[1]*lattice[2])) % lattice[3];
}

// convert local coordinates to index i (0..local_volume-1)
int idx(int* x)
{
  int oe = (x[0] + x[1] + x[2] + x[3] + global_oe) % 2;
  return (x[0] + lattice[0] * (x[1] + lattice[1] * (x[2] + lattice[2] * (x[3])))) / 2 +
    oe * half_volume[0];
}

// convert local coordinates to index i (0..local_volume-1) in "idx0" format
int idx0(int* x)
{
  return x[0] + lattice[0] * (x[1] + lattice[1] *
			      (x[2] + lattice[2] * (x[3])));
}

/* convert index i (0 to local_volume-1) to local coordinates */
/* nb=-1: local node */
/* nb=0..3: neighbor node in direction nb*/

void coords(int i, int* x, int nb)
{
  int oe = i >= half_volume[0];
  i      = 2 * (i - oe * half_volume[0]);
  x[0]   = i % lattice[0];
  x[1]   = (i/lattice[0]) % lattice[1];
  x[2]   = (i/(lattice[0]*lattice[1])) % lattice[2];
  x[3]   = (i/(lattice[0]*lattice[1]*lattice[2])) % lattice[3];
  int oe1;
  if (nb == -1)
    oe1 = (x[0]+x[1]+x[2]+x[3]+global_oe)%2;
  else
    oe1 = (x[0]+x[1]+x[2]+x[3]+global_oe_nb[nb])%2;
  if (oe != oe1)
  {
    x[0]=(x[0]+1)%lattice[0];
    if (x[0]==0)
    {
      x[1]=(x[1]+1)%lattice[1];
      if (x[1]==0)
      {
	x[2]=(x[2]+1)%lattice[2];
	if (x[2]==0)
	  x[3]=(x[3]+1)%lattice[3];
      }
    }
  }
}




// convert lattice site index
// idx0 : x running fastest, t slowest, no even/odd
// idx  : even/odd-distributed, maybe some other permutation
//
// fw=1 : idx0 to idx
// fw=0 : idx  to idx0

int reorder(int i, int fw)
{
  int x[4];
  if (fw == 1)
  {
    coords0(i, x);
    return idx(x);
  }
  else
  {
    coords(i, x, -1);
    return idx0(x);
  }
}


// must be done while conf is in "idx0" format

void double_conf(double* U_single, double* U_double)
{
  double* Us = U_single;
  double* Ud = U_double;
  for (unsigned i=0; i<volume; ++i)
  {
    Ud += (8 * sizeof(uint16_t)) / sizeof(double);
//forward
    for (unsigned a=0; a<3*18; ++a)
      *Ud++ = *Us++;
    for (unsigned a=0; a<18; ++a)
      *Ud++ = 2. * (*Us++);
//backward
    int x[4], y[4];
    coords0(i, x);
    for (unsigned mu=0; mu<4; ++mu)
    {
      double f = mu == 3 ? 2. : 1.;
      for (unsigned nu=0; nu<4; ++nu)
	y[nu] = x[nu];
      y[mu] = (y[mu] + lattice[mu] - 1) % lattice[mu];
      if (y[mu] == (lattice[mu] - 1) && boundary[mu])
      {
	double U_tmp[18];
	int j = idx0(y);
	MPI_Status stat;
	MPI_Sendrecv(
	  U_single+18*(mu+4*j), 18*sizeof(double), MPI_BYTE,
	  grid_nb[0][mu], 0,
	  U_tmp, 18*sizeof(double), MPI_BYTE, grid_nb[1][mu], 0,
	  MPI_COMM_WORLD, &stat);
	for (unsigned a=0; a<9; ++a)
	{
	  Ud[2*transpose[a]+0] = f *   U_tmp[2*a  ];
	  Ud[2*transpose[a]+1] = f * (-U_tmp[2*a+1]);
	}
	Ud += 18;
      }
      else
      {
	int j = idx0(y);
	for (unsigned a=0; a<9; ++a)
	{
	  Ud[2*transpose[a]+0] = f *   U_single[2*a+18*(mu+4*j)  ];
	  Ud[2*transpose[a]+1] = f * (-U_single[2*a+18*(mu+4*j)+1]);
	}
	Ud += 18;
      }
    }
  }
}

void print_debug_buffer()
{
  unsigned* d = debug_buffer;
  unsigned c = 0;
  while (*d == 1)
  {
    printf("[%i]\tDEBUG %04i\n", my_rank, c++);
    ++d;
    for (unsigned i=0; i<34; ++i, ++d)
    {
      if (i < 32)
	printf("[%i]\tgpr %2i\t", my_rank, i);
      if (i == 32)
	printf("[%i]\tctr\t", my_rank);
      if (i == 33)
	printf("[%i]\tcr\t", my_rank);
      printf("%08x\t%12u\t%12i\n", *d, *d, *d);
    }
    double tmp[2];
    for (unsigned i=0; i<32; ++i, d+=4)
    {
      memcpy(tmp, d, 16);
      printf("[%i]\tfpr %2i\t% 20.16e % 20.16e\n", my_rank, i, tmp[0], tmp[1]);
    }
  }
}


void init_nb_list(uint16_t* neighb, int oe)
{
  uint16_t* nb_ptr = neighb;
  unsigned b_ctr = 0;
  unsigned i0, i1;
  if (oe == 0)
  {
    i0 = 0;
    i1 = half_volume[0];
  }
  else
  {
    i0 = half_volume[0];
    i1 = volume;
  }
  for (unsigned i=i0; i<i1; ++i)
  {
    int x[4];
    coords(i, x, -1);
//forward
    for (unsigned mu=0; mu<4; ++mu)
    {
      int y[4];
      for (int a=0; a<4; ++a)
	y[a] = x[a];
      y[mu] = (x[mu] + 1) % lattice[mu];
      unsigned j = 3 * (idx(y) - (1 - oe) * half_volume[0]);
      unsigned b = (y[mu] == 0 && boundary[mu]);
      if (b)
      {
	if (use_shmem && mu == 3) // use shmem in 3-direction
	{
	  b = 0;
	  int nb3 = (position[3] + 1) % grid[3];
	  int delta = (nb3 - position[3]) * volume;
	  j += 3 * delta;
	}
	else // use torus
	  j = (3 * (b_ctr++)) & NB_ARRAY_IDX_MASK;
      }
      else
	if ((j | NB_ARRAY_IDX_MASK) != NB_ARRAY_IDX_MASK)
	  die("local lattice too large for nb array");
      nb_ptr[mu] = (b << NB_ARRAY_RECV_BIT) | j;
    }
//backward
    for (unsigned mu=0; mu<4; ++mu)
    {
      int y[4];
      for (int a=0; a<4; ++a)
	y[a] = x[a];
      y[mu] = (x[mu] + lattice[mu] - 1) % lattice[mu];
      unsigned j = 3 * (idx(y) - (1 - oe) * half_volume[0]);
      unsigned b = (y[mu] == (lattice[mu] - 1) && boundary[mu]);
      if (b)
      {
	if (use_shmem && mu == 3) // use shmem in 3-direction
	{
	  b = 0;
	  int nb3 = (position[3] + grid[3] - 1) % grid[3];
	  int delta = (nb3 - position[3]) * volume;
	  j += 3 * delta;
	}
	else // use torus
	  j = (3 * (b_ctr++)) & NB_ARRAY_IDX_MASK;
      }
      else
	if ((j | NB_ARRAY_IDX_MASK) != NB_ARRAY_IDX_MASK)
	  die("local lattice too large for nb array");
      nb_ptr[mu+4] = (b << NB_ARRAY_RECV_BIT) | j;
    }
    nb_ptr += 8;
  }
}

void init(int* lat, int* grid)
{
  debug_buffer = malloc(16384 * sizeof(*debug_buffer));
  debug_buffer_asm = debug_buffer;
  init_lat(lat, grid);
}
