/*
*===============================================================================
*
* d/wilson_d520/lattice_bqcd.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 "lattice.h"

#include <stdio.h>

unsigned* bqcd_nn;
unsigned volh_tot;


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


unsigned neighbor(unsigned x, unsigned oe, unsigned mu, unsigned fw)
{
  return bqcd_nn[x + volh_tot * (oe + 2 * (mu + 4 * fw))] - 1;
}


void init_nb_list(uint16_t* neighb, int oe)
{
  unsigned b_ctr = 0;
  for (unsigned i=0; i<half_volume[oe]; ++i, neighb+=8)
  {
    for (unsigned fw=0; fw<2; ++fw)
      for (unsigned mu=0; mu<4; ++mu)
      {
	unsigned j = neighbor(i, oe, mu, fw);
	if ((j | NB_ARRAY_IDX_MASK) != NB_ARRAY_IDX_MASK)
	  die("local lattice too large for nb array");
	unsigned b = 0;
	if (j < half_volume[1-oe]) // local
	  j *= 3;
	else              // remote
	{
	  b = 1;
	  j = (3 * (b_ctr++)) & NB_ARRAY_IDX_MASK;
	}
	neighb[mu + 4 * fw] = (b << NB_ARRAY_RECV_BIT) | j;
      }
  }
}

void init_wilson_u(double* U)
{
  double* U_fw[4];
  for (unsigned oe=0; oe<2; ++oe)
  {
    double* U_ptr = wilson_data.w_data[0][oe][0].U;
    if (T0_LAST == 0)
    {
      for (unsigned mu=0; mu<4; ++mu)
	U_fw[mu] = U + 18 * (0 + volh_tot * (oe + 2*mu));
      for (unsigned i=0; i<half_volume[oe]; ++i)
      {
	U_ptr += 8 * sizeof(uint16_t) / sizeof(double);
	for (unsigned mu=0; mu<4; ++mu)
	{
	  double f = mu==3? 2. : 1.;
	  for (unsigned a=0; a<18; ++a)
	    *U_ptr++ = f * (*U_fw[mu]++);
	}
	for (unsigned mu=0; mu<4; ++mu)
	{
	  double f = mu==3? 2. : 1.;
	  unsigned nb = neighbor(i, oe, mu, 1);
	  double* U_bw = U + 18 * (nb + volh_tot * ((1-oe) + 2*mu));
	  for (unsigned a=0; a<9; ++a)
	  {
	    *U_ptr++ = f * ( U_bw[2 * transpose[a]    ]);
	    *U_ptr++ = f * (-U_bw[2 * transpose[a] + 1]);
	  }
	}
      }
    }
    else // T0_LAST=1
    {
      for (unsigned pass=0; pass<2; ++pass)
      {
	unsigned i0, i1;
	if (pass == 0)
	{
	  i0 = half_slice[oe];
	  i1 = half_volume[oe];
	  for (unsigned mu=0; mu<4; ++mu)
	    U_fw[mu] = U + 18 * (half_slice[oe] + volh_tot * (oe + 2*mu));
	}
	else
	{
	  i0 = 0;
	  i1 = half_slice[oe];
	  for (unsigned mu=0; mu<4; ++mu)
	    U_fw[mu] = U + 18 * (0 + volh_tot * (oe + 2*mu));
	}
	for (unsigned i=i0; i<i1; ++i)
	{
	  U_ptr += 8 * sizeof(uint16_t) / sizeof(double);
	  for (unsigned mu=0; mu<4; ++mu)
	  {
	    double f = mu==3? 2. : 1.;
	    for (unsigned a=0; a<18; ++a)
	      *U_ptr++ = f * (*U_fw[mu]++);
	  }
	  for (unsigned mu=0; mu<4; ++mu)
	  {
	    double f = mu==3? 2. : 1.;
	    unsigned nb = neighbor(i, oe, mu, 1);
	    double* U_bw = U + 18 * (nb + volh_tot * ((1-oe) + 2*mu));
	    for (unsigned a=0; a<9; ++a)
	    {
	      *U_ptr++ = f * ( U_bw[2 * transpose[a]    ]);
	      *U_ptr++ = f * (-U_bw[2 * transpose[a] + 1]);
	    }
	  }
	}
      }
    }
  }
}

void wilson_init(unsigned* l, unsigned* g, unsigned* vht, unsigned* nn)
{
  mesh_correct = 1;
  volh_tot = *vht;
  bqcd_nn = nn;
  init_lat(l, g);
}
