/*
*===============================================================================
*
* d/wilson_d500/neighb.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 <stdio.h>

#ifndef NO_MPI
#include "mpi.h"
#endif

#ifdef BGL
#include <rts.h>
#include <bglpersonality.h>
#include <bglmemmap.h>
#endif

#include "neighb.h"

extern int test_stat(const void* stat_addr, int byte);


int* neighb;

w_data wilson_data __attribute__((aligned(16)));;

int lattice[4];
int grid[4];          /* logial grid                          */
int  boundary[4];     /* boundaries in logical grid	      */
int  position[4];     /* position in logical grid	      */
int  grid_nb[2][4];   /* rank of neighbour in logical grid    */
int  t_position[4];   /* position in torus grid               */
int  my_rank=0;
int  num_procs=1;
int* bnd_list[2][4]; 
int  bnd_size[4];
int  bnd_offs[4];
int  tot_bnd_size;
int  which_cpu;  /*  0/1   */
int  torus_dir[4]={PHYS_DIR_X, PHYS_DIR_Y, PHYS_DIR_Z, PHYS_DIR_T};   // maps logical direction to torus direction
int  logical_dir[4];                                                  // maps torus direction to logical direction
int  global_oe;
int  global_oe_nb[4];
int* bqcd_nn;

const int fifo4_size=1024;
const int fifo4_mask=0x3ff;


char* send_fifo_addr[2][3];
char* recv_fifo_addr[2][3];
char* send_stat_addr[2][3];
char* recv_stat_addr[2][3];

char* send_fifo4_addr;
char* recv_fifo4_addr;

int* rm_list[2][4];
int* rm_list_idx[2][4];
int* rm_list_num[2][4];


void* stat_addr=(void*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_STATUS0_OFFSET);


header send_header[2][3] __attribute__((aligned(16)));
header recv_header[2][3] __attribute__((aligned(16)));

const int send_stat_byte_offs[2][3]={{7, 8, 9}, {7, 8, 9}};
const int recv_stat_byte_offs[2][3]={{0, 2, 4}, {1, 3, 5}};


void* sram_base_addr;

extern int __real_rts_get_scratchpad_window(void **location, unsigned *size);

#ifndef USE_SRAM


int __wrap_rts_get_scratchpad_window(void **location, unsigned *size)
{
  char* real_location;
  unsigned real_size;
  __real_rts_get_scratchpad_window((void**) &real_location, &real_size);
  *location=(void*) (real_location+3*fifo4_size);
  *size=real_size-3*fifo4_size;
}


#endif

void shmem_comm_init()
{
  if (sizeof(int)!=sizeof(void*))
  {
    printf("Error:  sizeof(int)!=sizeof(void*)\n");
    exit(1);
  }
  send_fifo_addr[0][0]=(char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_DATAIN_0_OFFSET);
  send_fifo_addr[0][1]=(char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_DATAIN_1_OFFSET);
  send_fifo_addr[0][2]=(char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_DATAIN_2_OFFSET);
  send_fifo_addr[1][0]=(char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_DATAIN_0_OFFSET);
  send_fifo_addr[1][1]=(char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_DATAIN_1_OFFSET);
  send_fifo_addr[1][2]=(char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_DATAIN_2_OFFSET);

  if (which_cpu==0)
  {
    recv_fifo_addr[0][0]=(char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_DATAOUT_0_OFFSET);
    recv_fifo_addr[1][0]=(char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_DATAOUT_1_OFFSET);
    recv_fifo_addr[0][1]=(char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_DATAOUT_2_OFFSET);
    recv_fifo_addr[1][1]=(char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_DATAOUT_3_OFFSET);
    recv_fifo_addr[0][2]=(char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_DATAOUT_4_OFFSET);
    recv_fifo_addr[1][2]=(char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_DATAOUT_5_OFFSET);
  }
  else
  {
    recv_fifo_addr[0][0]=(char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_DATAOUT_0_OFFSET);
    recv_fifo_addr[1][0]=(char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_DATAOUT_1_OFFSET);
    recv_fifo_addr[0][1]=(char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_DATAOUT_2_OFFSET);
    recv_fifo_addr[1][1]=(char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_DATAOUT_3_OFFSET);
    recv_fifo_addr[0][2]=(char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_DATAOUT_4_OFFSET);
    recv_fifo_addr[1][2]=(char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_DATAOUT_5_OFFSET);
  }

  send_stat_addr[0][0] = (char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_STATUS0_OFFSET);
  send_stat_addr[0][1] = (char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_STATUS0_OFFSET);
  send_stat_addr[0][2] = (char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_STATUS0_OFFSET);
  send_stat_addr[1][0] = (char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_STATUS0_OFFSET);
  send_stat_addr[1][1] = (char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_STATUS0_OFFSET);
  send_stat_addr[1][2] = (char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_STATUS0_OFFSET);

  if (which_cpu==0)
  {
    recv_stat_addr[0][0] = (char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_STATUS0_OFFSET);
    recv_stat_addr[0][1] = (char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_STATUS0_OFFSET);
    recv_stat_addr[0][2] = (char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_STATUS0_OFFSET);
    recv_stat_addr[1][0] = (char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_STATUS0_OFFSET);
    recv_stat_addr[1][1] = (char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_STATUS0_OFFSET);
    recv_stat_addr[1][2] = (char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_STATUS0_OFFSET);
  }
  else
  {
    recv_stat_addr[0][0] = (char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_STATUS0_OFFSET);
    recv_stat_addr[0][1] = (char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_STATUS0_OFFSET);
    recv_stat_addr[0][2] = (char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_STATUS0_OFFSET);
    recv_stat_addr[1][0] = (char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_STATUS0_OFFSET);
    recv_stat_addr[1][1] = (char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_STATUS0_OFFSET);
    recv_stat_addr[1][2] = (char*) (BGL_MEM_TORUS_G0_BASE+BGL_MEM_TORUS_STATUS0_OFFSET);
  }
  for (int f=0; f<2; f++)
    for (int mu=0; mu<3; mu++)
    {
      int nb[3];
      for (int i=0; i<3; i++)
	nb[i]=t_position[i];
      if (f==0)
	nb[mu]=(nb[mu]+1)%grid[logical_dir[mu]];
      else
	nb[mu]=(nb[mu]+grid[logical_dir[mu]]-1)%grid[logical_dir[mu]];
      send_header[f][mu].hh.type=0;
      send_header[f][mu].hh.hintXPlus=(f==0 && mu==0);
      send_header[f][mu].hh.hintXMinus=(f==1 && mu==0);
      send_header[f][mu].hh.hintYPlus=(f==0 && mu==1);
      send_header[f][mu].hh.hintYMinus=(f==1 && mu==1);
      send_header[f][mu].hh.hintZPlus=(f==0 && mu==2);
      send_header[f][mu].hh.hintZMinus=(f==1 && mu==2);
      send_header[f][mu].hh.deposit=0;
      send_header[f][mu].hh.fifoID=which_cpu;   /* 0/1 */
      send_header[f][mu].hh.size=3;
      send_header[f][mu].hh.avail=0;
      send_header[f][mu].hh.dynamicRoute=0;
      send_header[f][mu].hh.vc=2;
      send_header[f][mu].hh.destX=nb[0];
      send_header[f][mu].hh.destY=nb[1];
      send_header[f][mu].hh.destZ=nb[2];
      send_header[f][mu].hh.seqno=0;
      send_header[f][mu].hh.chksum=0;
      send_header[f][mu].sh.val1=0;
      send_header[f][mu].sh.val2=0;
    }

  char* sram_ptr;
  unsigned int sram_mem_size=2*(fifo4_size);
  unsigned int scratch_size;

#ifndef USE_SRAM
  int sc=__real_rts_get_scratchpad_window((void**) &sram_ptr, &scratch_size);
#else
  char* scr_ptr;
  unsigned int scr_size;
  __real_rts_get_scratchpad_window((void**) &scr_ptr, &scr_size);

  if (which_cpu==1)
  {
    scr_ptr[0]=2;
    while (scr_ptr[0]!=1);
    scr_ptr[0]=0;
    sram_ptr=((char**) scr_ptr)[1];
    /*H.S.* printf("got sram %x\n", sram_ptr); */
  }
  else
  {
    sram_ptr=rts_malloc_sram(2*(fifo4_size));
    ((char**) scr_ptr)[1]=sram_ptr;
    if (sram_ptr==0)
    {
      printf("error: no sram memory\n");
      exit(1);
    }
    while (scr_ptr[0]!=2);
    scr_ptr[0]=1;
    while (scr_ptr[0]!=0);
    ((char**) scr_ptr)[0]=sram_ptr;
    /*H.S.* printf("sent sram %x\n", sram_ptr); */
  }

/*   if (which_cpu==0) */
/*   { */
/*     sram_ptr=rts_malloc_sram(2*(fifo4_size)); */
/*     if ((long) sram_ptr!=(long) BGL_MEM_SRAM_BASE) */
/*     { */
/*       printf("error: rts_malloc_sram returned unexpected value: %x != %x\n", */
/* 	     sram_ptr, BGL_MEM_SRAM_BASE); */
/*       exit(1); */
/*     } */
/*   } */
/*   else */
/*     sram_ptr=(char*) BGL_MEM_SRAM_BASE; */
  scratch_size=sram_mem_size;
//  printf("node %i sram %x %x\n", which_cpu, sram_ptr, BGL_MEM_SRAM_BASE);
#endif
  if (((int) sram_ptr) & fifo4_mask!=0)
  {
    printf("error: not aligned\n");
    int sram_ptr_i=(int) sram_ptr;
    sram_ptr_i+=fifo4_size;
    sram_ptr_i&=fifo4_mask;
    sram_ptr=(char*) sram_ptr_i;
    scratch_size-=fifo4_size;
    exit(1);
  }
  if (scratch_size<sram_mem_size)
  {
    printf("error: no memory\n");
    exit(1);
  }
  if (which_cpu==0)
  {
    send_fifo4_addr=sram_ptr;
    recv_fifo4_addr=sram_ptr+fifo4_size;
    ((int*) (send_fifo4_addr))[0]=16; // rd offs
    ((int*) (send_fifo4_addr))[1]=16; // wr offs
    ((int*) (recv_fifo4_addr))[0]=16;
    ((int*) (recv_fifo4_addr))[1]=16;
  }
  else
  {
    send_fifo4_addr=sram_ptr+fifo4_size;
    recv_fifo4_addr=sram_ptr;
  }
  ((int*) (send_fifo4_addr))[2]=0;     // D-counter
  ((int*) (send_fifo4_addr+128))[0]=0; // gs-counter (gs-data: 256)



// synchronize cpus
  if (which_cpu==0)
  {
    ((int*) (send_fifo4_addr))[3]=2;
    while (((int*) (send_fifo4_addr))[3]!=1)
      ;
    ((int*) (send_fifo4_addr))[3]=0;
  }
  else
  {
    while (((int*) (recv_fifo4_addr))[3]!=2)
      ;
    ((int*) (recv_fifo4_addr))[3]=1;
    while (((int*) (recv_fifo4_addr))[3]!=0)
      ;
 }


//  sleep(5);
//  MPI_Barrier(MPI_COMM_WORLD);
}


int idx0(int* x)
{
  return x[0]+lattice[0]*(x[1]+lattice[1]*(x[2]+lattice[2]*(x[3])));
}

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)
{
#ifndef ODDEVEN
  return idx0(x);

#else

  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*(wilson_data.volume/2);
#endif

}


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

void coords(int i, int* x, int nb)
{
#ifndef ODDEVEN
  coords0(i, x);
  return;
#else
  int oe=i/(wilson_data.volume/2);
  i=2*(i%(wilson_data.volume/2));
  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];
      }
    }
  }
#endif
}




/* fw=1 : idx0 to idx */
/* fw=0 : idx  to idx0 */

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




int b_idx(int* x, int mu)
{
  if (mu==0)
    return x[1]+lattice[1]*(x[2]+lattice[2]*(x[3]));
  if (mu==1)
    return (lattice[0]-1-x[0])+lattice[0]*(x[2]+lattice[2]*(x[3]));
  if (mu==2)
    return x[0]+lattice[0]*(x[1]+lattice[1]*(x[3]));
  if (mu==3)
    return x[0]+lattice[0]*(x[1]+lattice[1]*(x[2]));
}




const int max_ahead=20;
const int bnd_mask=0x80000000;


const int bnd_mask_send=0x40000000;
const int inv_bnd_mask=0x3fffffff;
const int nb_factor=48;
const int nb_factor_l=4;







void init_neighb(int bqcd)
{
  neighb=(int*) malloc(12*wilson_data.volume*sizeof(int));
  for (int f=0; f<2; f++)
    for (int mu=0; mu<4; mu++)
      rm_list[f][mu]=(int*) malloc(sizeof(int)*wilson_data.volume);
  for (int f=0; f<2; f++)
    for (int mu=0; mu<4; mu++)
      {
	rm_list_idx[f][mu]=(int*) malloc(sizeof(int));
	rm_list_num[f][mu]=(int*) malloc(sizeof(int));
      }
  for (int f=0; f<2; f++)
    for (int mu=0; mu<4; mu++)
      *rm_list_idx[f][mu]=0;


  int* nb_ptr=neighb;

  for (int a=0; a<wilson_data.volume; a++)
  {
    int x[4], y[4];
    int x_nb[4];
    int y_nb[4];
    coords(a, x, -1);
    for (int mu=0; mu<4; mu++)
    {
      coords(a, x_nb, mu);
      for (int i=0; i<4; i++)
	y[i]=x[i];
      for (int i=0; i<4; i++)
	y_nb[i]=x_nb[i];
      y[mu]=(x[mu]+1)%lattice[mu];
      y_nb[mu]=(x_nb[mu]+1)%lattice[mu];
      nb_ptr[mu]=idx(y);

#ifdef MESH
      if (t_position[torus_dir[mu]]!=grid[mu]-1 || torus_dir[mu]==3)
#endif
	if (y[mu]==0 && boundary[mu])
	  nb_ptr[mu]|= bnd_mask;
#ifdef MESH
      if (t_position[torus_dir[mu]]!=0 || torus_dir[mu]==3)
#endif
	if (y_nb[mu]==0 && boundary[mu])
	{
	  nb_ptr[mu]|= bnd_mask_send;
	  rm_list[0][mu][(*rm_list_idx[0][mu])++]=nb_factor_l*idx(y_nb);
	}

      y[mu]=(x[mu]+lattice[mu]-1)%lattice[mu];
      y_nb[mu]=(x_nb[mu]+lattice[mu]-1)%lattice[mu];
      nb_ptr[4+2*mu]=idx(y);
#ifdef MESH
      if (t_position[torus_dir[mu]]!=0 || torus_dir[mu]==3)
#endif
	if (y[mu]==lattice[mu]-1 && boundary[mu])
	  nb_ptr[4+2*mu] |= bnd_mask;
#ifdef MESH
      if (t_position[torus_dir[mu]]!=grid[mu]-1 || torus_dir[mu]==3)
#endif
	if (y_nb[mu]==lattice[mu]-1 && boundary[mu])
	{
	  nb_ptr[4+2*mu] |= bnd_mask_send;
	  rm_list[1][mu][(*rm_list_idx[1][mu])++]=nb_factor_l*idx(y_nb);
	}
      if (bqcd)
      {
	int oe;
	if (a<wilson_data.volume/2)
	  oe=0;
	else
	  oe=1;
	nb_ptr[5+2*mu]=get_nn(a % (wilson_data.volume/2), oe, mu, 1)+(2*mu+1-oe)*wilson_data.volh_tot;
      }
      else
      {
#ifndef MU_SLOW
	nb_ptr[5+2*mu]=4*idx(y)+mu;
#else
        nb_ptr[5+2*mu]=idx(y)+mu*wilson_data.volume;
#endif
	if (y[mu]==lattice[mu]-1 && boundary[mu])
	  nb_ptr[5+2*mu]=4*wilson_data.volume+bnd_offs[mu]+b_idx(y, mu);
      }
    }
    nb_ptr+=12;
  }
  for (int f=0; f<2; f++)
    for (int mu=0; mu<4; mu++)
      *rm_list_num[f][mu]=*rm_list_idx[f][mu];


}

void init_lat(int* l, int* g)
{
  for (int i=0; i<4; i++)
    lattice[i]=l[i];
  for (int i=0; i<4; i++)
    grid[i]=g[i];
  for (int i=0; i<4; i++)
    logical_dir[torus_dir[i]]=i;
  wilson_data.volume=lattice[0]*lattice[1]*lattice[2]*lattice[3];
  int global_lattice[4];

  for (int i=0; i<4; i++)
    global_lattice[i]=lattice[i]*grid[i];

  for (int i=0; i<4; i++)
    boundary[i]=lattice[i]!=global_lattice[i];

  position[logical_dir[3]]=my_rank%grid[logical_dir[3]];
  position[logical_dir[2]]=my_rank/(grid[logical_dir[3]]*grid[logical_dir[0]]*grid[logical_dir[1]]);
  position[logical_dir[1]]=(my_rank/(grid[logical_dir[3]]*grid[logical_dir[0]]))%grid[logical_dir[1]];
  position[logical_dir[0]]=(my_rank/grid[logical_dir[3]])%grid[logical_dir[0]];
  global_oe=(position[0]*lattice[0]+position[1]*lattice[1]+
	     position[2]*lattice[2]+position[3]*lattice[3])%2;
  for (int mu=0; mu<4; mu++)
    global_oe_nb[mu]=(global_oe+lattice[mu])%2;
  which_cpu=position[logical_dir[3]];
  t_position[3]=my_rank%grid[logical_dir[3]];
  t_position[0]=(my_rank/grid[logical_dir[3]])%grid[logical_dir[0]];
  t_position[1]=(my_rank/(grid[logical_dir[3]]*grid[logical_dir[0]]))%grid[logical_dir[1]];
  t_position[2]=(my_rank/(grid[logical_dir[3]]*grid[logical_dir[0]]*grid[logical_dir[1]]))%grid[logical_dir[2]];
  wilson_data.t_position[0]=t_position[0];
  wilson_data.t_position[1]=t_position[1];
  wilson_data.t_position[2]=t_position[2];
  wilson_data.t_position[3]=t_position[3];
  wilson_data.t_size[0]=grid[logical_dir[0]];
  wilson_data.t_size[1]=grid[logical_dir[1]];
  wilson_data.t_size[2]=grid[logical_dir[2]];
  wilson_data.t_size[3]=grid[logical_dir[3]];
}



/* nn: pointer to nn defined in bqcd */
/* as nn nn(volh_tot, EVEN:ODD, DIM, FWD:BWD) */


int get_nn(int x, int oe, int mu, int f)
{
  int idx=x+wilson_data.volh_tot*(oe+2*(mu+4*f));
  return bqcd_nn[idx]-1;
}



void wilson_init(int* l, int* g, int bqcd)
{
  BGLPersonality personality;
  rts_get_personality(&personality, sizeof(personality));
  if (BGLPersonality_virtualNodeMode(&personality)!=1)
  {
    printf("error: no virtual node mode\n");
    exit(1);
  }



  init_lat(l, g);
  for (int i=0; i<4; i++)
    wilson_data.phys_dir[i]=torus_dir[i];

  if ((BGLPersonality_xCoord(&personality)!=t_position[0]) ||
      (BGLPersonality_yCoord(&personality)!=t_position[1]) ||
      (BGLPersonality_zCoord(&personality)!=t_position[2]) ||
      (rts_get_processor_id()             !=t_position[3]))
  
  {
    printf("error: wrong processor mapping :\nmpi rank %3i: torus coordinates %i %i %i %i, should be  %i %i %i %i\n",
	   my_rank,
	   BGLPersonality_xCoord(&personality),
	   BGLPersonality_yCoord(&personality),
	   BGLPersonality_zCoord(&personality),
	   rts_get_processor_id(),
	   t_position[0], 
	   t_position[1], 
	   t_position[2], 
	   t_position[3] 
      );
    exit(1);
  }

  for (int i=0; i<4; i++)
    if (boundary[i])
      bnd_size[i]=wilson_data.volume/lattice[i];
    else
      bnd_size[i]=0.;
  bnd_offs[0]=0.;
  bnd_offs[1]=bnd_offs[0]+bnd_size[0];
  bnd_offs[2]=bnd_offs[1]+bnd_size[1];
  bnd_offs[3]=bnd_offs[2]+bnd_size[2];
  tot_bnd_size=bnd_size[0]+bnd_size[1]+bnd_size[2]+bnd_size[3];


  for (int mu=0; mu<4; mu++)
  {
    int n[4];
    for (int i=0; i<4; i++)
      n[i]=position[i];
    n[mu]=(position[mu]+1)%grid[mu];

    grid_nb[0][mu]=n[logical_dir[3]]+
      grid[logical_dir[3]]*
      (n[logical_dir[0]]+grid[logical_dir[0]]*
       (n[logical_dir[1]]+grid[logical_dir[1]]*n[logical_dir[2]]));
    n[mu]=(position[mu]+grid[mu]-1)%grid[mu];
    grid_nb[1][mu]=n[logical_dir[3]]+grid[logical_dir[3]]*
      (n[logical_dir[0]]+grid[logical_dir[0]]*
       (n[logical_dir[1]]+grid[logical_dir[1]]*n[logical_dir[2]]));
  }

  wilson_data.neighb_offs=(short int*) malloc(5*sizeof(short int)*(wilson_data.volume+max_ahead));
  wilson_data.neighb_offs_even=(short int*) malloc(5*sizeof(short int)*(wilson_data.volume/2+max_ahead));
  wilson_data.neighb_offs_odd=(short int*) malloc(5*sizeof(short int)*(wilson_data.volume/2+max_ahead));

  wilson_data.neighb_offs_rm=(short int*) malloc(12*sizeof(short int)*(wilson_data.volume+max_ahead));
  wilson_data.neighb_offs_rm_even=(short int*) malloc(8*sizeof(short int)*(wilson_data.volume/2+max_ahead));
  wilson_data.neighb_offs_rm_odd=(short int*) malloc(8*sizeof(short int)*(wilson_data.volume/2+max_ahead));

  wilson_data.neighb_offs_l=(short int*) malloc(12*sizeof(short int)*(wilson_data.volume+max_ahead));
  wilson_data.neighb_offs_l_even=(short int*) malloc(8*sizeof(short int)*(wilson_data.volume/2+max_ahead));
  wilson_data.neighb_offs_l_odd=(short int*) malloc(8*sizeof(short int)*(wilson_data.volume/2+max_ahead));


  init_neighb(bqcd);
  short int* nb_rm_ptr=wilson_data.neighb_offs_rm;
  short int* nb_rm_oe_ptr=wilson_data.neighb_offs_rm_even;
  short int* nb_l_ptr=wilson_data.neighb_offs_l;
  short int* nb_l_oe_ptr=wilson_data.neighb_offs_l_even;
  int prev_psi=0;
  int prev_psi_rm=0;
  int prev_U=-1;



  for (int f=0; f<2; f++)
    for (int mu=0; mu<4; mu++)
      *rm_list_idx[f][mu]=0;
  short int last_rm_ptr_even;  
  short int last_l_ptr_even;
  short int last_U_nb_even;

  for (int i=0; i<wilson_data.volume; i++)
  {
    if (i==wilson_data.volume/2)
    {
      last_U_nb_even=prev_U;
      if (nb_rm_oe_ptr!=wilson_data.neighb_offs_rm_even)
	last_rm_ptr_even=prev_psi_rm;
      else
	last_rm_ptr_even=0;
      if (nb_l_oe_ptr!=wilson_data.neighb_offs_l_even)
	last_l_ptr_even=prev_psi;
      else
	last_l_ptr_even=0;
      nb_rm_oe_ptr=wilson_data.neighb_offs_rm_odd;
      nb_l_oe_ptr=wilson_data.neighb_offs_l_odd;
    }
    wilson_data.neighb_offs[5*i]=0;
    for (int j=0; j<4; j++)
    {
      if ((neighb[12*i+j] & bnd_mask_send) &&
	  (neighb[12*i+j] & bnd_mask))
      {
	wilson_data.neighb_offs[5*i]|=1 << (15-j);
	if (*rm_list_idx[0][j]<*rm_list_num[0][j])
	{
	  wilson_data.neighb_offs[5*i]|=1 << (7-j);
	  *nb_rm_ptr++=(rm_list[0][j][*rm_list_idx[0][j]]-prev_psi_rm) & inv_bnd_mask;
	  *nb_rm_oe_ptr++=(rm_list[0][j][*rm_list_idx[0][j]]-prev_psi_rm) & inv_bnd_mask;
	  prev_psi_rm=rm_list[0][j][(*rm_list_idx[0][j])++]+nb_factor_l;
	}
      }
      else if ((neighb[12*i+j] & bnd_mask_send) &&
	  !(neighb[12*i+j] & bnd_mask))
      {
	wilson_data.neighb_offs[5*i]|=1 << (7-j);

	*nb_l_ptr++=nb_factor_l*(neighb[12*i+j])-prev_psi;
	*nb_l_oe_ptr++=nb_factor_l*(neighb[12*i+j])-prev_psi;

	prev_psi=nb_factor_l*(neighb[12*i+j]+1);
	*nb_rm_ptr++=(rm_list[0][j][*rm_list_idx[0][j]]-prev_psi_rm) & inv_bnd_mask;
	*nb_rm_oe_ptr++=(rm_list[0][j][*rm_list_idx[0][j]]-prev_psi_rm) & inv_bnd_mask;
	prev_psi_rm=rm_list[0][j][(*rm_list_idx[0][j])++]+nb_factor_l;

      }
      else if (!(neighb[12*i+j] & bnd_mask_send) &&
	       (neighb[12*i+j] & bnd_mask))
	wilson_data.neighb_offs[5*i]|=1 << (15-j);
      else if (!(neighb[12*i+j] & bnd_mask_send) &&
	       !(neighb[12*i+j] & bnd_mask))

      {


	*nb_l_ptr++=nb_factor_l*(neighb[12*i+j])-prev_psi;
	*nb_l_oe_ptr++=nb_factor_l*(neighb[12*i+j])-prev_psi;
	prev_psi=nb_factor_l*(neighb[12*i+j]+1);
      }
    }
    for (int j=0; j<4; j++)
    {
      if ((neighb[12*i+4+2*j] & bnd_mask_send) &&
	  (neighb[12*i+4+2*j] & bnd_mask))
      {
	wilson_data.neighb_offs[5*i]|=1 << (11-j);
	if (*rm_list_idx[1][j]<*rm_list_num[1][j])
	{
	  wilson_data.neighb_offs[5*i]|=1 << (3-j);
	  *nb_rm_ptr++=(rm_list[1][j][*rm_list_idx[1][j]]-prev_psi_rm) & inv_bnd_mask;
	  *nb_rm_oe_ptr++=(rm_list[1][j][*rm_list_idx[1][j]]-prev_psi_rm) & inv_bnd_mask;
	  prev_psi_rm=rm_list[1][j][(*rm_list_idx[1][j])++]+nb_factor_l;
	}
      }
      else if ((neighb[12*i+4+2*j] & bnd_mask_send) &&
	       !(neighb[12*i+4+2*j] & bnd_mask))
      {
	wilson_data.neighb_offs[5*i]|=1 << (3-j);
        *nb_l_ptr++=nb_factor_l*(neighb[12*i+4+2*j])-prev_psi;
        *nb_l_oe_ptr++=nb_factor_l*(neighb[12*i+4+2*j])-prev_psi;
	prev_psi=nb_factor_l*(neighb[12*i+4+2*j]+1);
	*nb_rm_ptr++=(rm_list[1][j][*rm_list_idx[1][j]]-prev_psi_rm) & inv_bnd_mask;
	*nb_rm_oe_ptr++=(rm_list[1][j][*rm_list_idx[1][j]]-prev_psi_rm) & inv_bnd_mask;
	prev_psi_rm=rm_list[1][j][(*rm_list_idx[1][j])++]+nb_factor_l;
      }
      else if (!(neighb[12*i+4+2*j] & bnd_mask_send) &&
	       (neighb[12*i+4+2*j] & bnd_mask))
      {
	wilson_data.neighb_offs[5*i]|=1 << (11-j);
      }
      else if (!(neighb[12*i+4+2*j] & bnd_mask_send) &&
	       !(neighb[12*i+4+2*j] & bnd_mask))
      {
        *nb_l_ptr++=nb_factor_l*(neighb[12*i+4+2*j])-prev_psi;
        *nb_l_oe_ptr++=nb_factor_l*(neighb[12*i+4+2*j])-prev_psi;
	prev_psi=nb_factor_l*(neighb[12*i+4+2*j]+1);
      }
      wilson_data.neighb_offs[5*i+1+j]=1*(neighb[12*i+5+2*j]-prev_U-1);
      prev_U=neighb[12*i+5+2*j];
    }
  }

  for (int i=0; i<5*(wilson_data.volume/2); i++)
    wilson_data.neighb_offs_even[i]=wilson_data.neighb_offs[i];
  for (int i=0; i<5*(wilson_data.volume/2); i++)
    wilson_data.neighb_offs_odd[i]=wilson_data.neighb_offs[5*(wilson_data.volume/2)+i];

  for (int i=wilson_data.volume; i<wilson_data.volume+max_ahead; i++)
  {
    wilson_data.neighb_offs[5*i]=0;
    for (int j=0; j<4; j++)
      wilson_data.neighb_offs[5*i+1+j]=-1;
  }

  for (int i=wilson_data.volume/2; i<wilson_data.volume/2+max_ahead; i++)
  {
    wilson_data.neighb_offs_even[5*i]=0;
    for (int j=0; j<4; j++)
      wilson_data.neighb_offs_even[5*i+1+j]=-1;
  }
  for (int i=wilson_data.volume/2; i<wilson_data.volume/2+max_ahead; i++)
  {
    wilson_data.neighb_offs_odd[5*i]=0;
    for (int j=0; j<4; j++)
      wilson_data.neighb_offs_odd[5*i+1+j]=-1;
  }
  wilson_data.neighb_offs_rm_odd[0]+=last_rm_ptr_even;
  wilson_data.neighb_offs_odd[1]+=1*(last_U_nb_even+1);
  wilson_data.neighb_offs_l_odd[0]+=last_l_ptr_even;


  shmem_comm_init();
}
#define UNDER_MPI__
void bgl_init_comm()
{
#ifdef UNDER_MPI
  MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
  MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
#else
  BGLPersonality personality;
  rts_get_personality(&personality, sizeof(personality));
  if (BGLPersonality_virtualNodeMode(&personality)!=1)
  {
    printf("error: no virtual node mode\n");
    exit(1);
  }
  my_rank=rts_get_processor_id()+2*(
    BGLPersonality_xCoord(&personality)+BGLPersonality_xSize(&personality)*(
      BGLPersonality_yCoord(&personality)+BGLPersonality_ySize(&personality)*(
	BGLPersonality_zCoord(&personality))));
  num_procs=2*BGLPersonality_xSize(&personality)*BGLPersonality_xSize(&personality)*
    BGLPersonality_zSize(&personality);
#ifndef MESH
  if (BGLPersonality_isTorusX(&personality)!=1 ||
      BGLPersonality_isTorusY(&personality)!=1 ||
      BGLPersonality_isTorusZ(&personality)!=1)
  {
    printf("error: not a torus\n");
/*     exit(1); */
  }
  if (torus_dir[0]!=3 || torus_dir[1]!=0 || 
      torus_dir[2]!=1 || torus_dir[3]!=2)
  {
    printf("error: wrong torus directions (must be 3-0-1-2)\n");
    exit(1);
  }
#endif

#endif
}

void wilson_init_bqcd(int* l, int* g, int* vh, int* nn)
{
  bgl_init_comm();
/*   MPI_Comm_size(MPI_COMM_WORLD, &num_procs); */
/*   MPI_Comm_rank(MPI_COMM_WORLD, &my_rank); */
  wilson_data.volh_tot=*vh;
  bqcd_nn=nn;
/*   printf("[%i] wd %x\n", myrank, wilson_data); */
  if ((*vh)*8>32600)
    if (my_rank==0)
    {
      printf("local volume too large\n");
      exit(1);
    }
   
  wilson_init(l, g, 1);
}

void wilson_init_test(int* l, int* g)
{
  wilson_init(l, g, 0);
}

void error_abort()
{
  char str[200];
  sprintf(str, "error_%04i", my_rank);
  FILE* f=fopen(str, "w");
  for (int i=0; i<32; i++)
    fprintf(f, "gpr %2i : % 12i\t%8x\n", i, wilson_data.gpr[i],
	    wilson_data.gpr[i]);
  fprintf(f, "\n");
  fprintf(f, "ctr    : % 12i\t%8x\n", wilson_data.ctr,
	  wilson_data.ctr);
  fprintf(f, "cr     : % 12i\t%8x\n", wilson_data.cr,
	  wilson_data.cr);
  fprintf(f, "lr     : % 12i\t%8x\n", wilson_data.lr,
	  wilson_data.lr);
  fprintf(f, "\n");
  for (int i=0; i<32; i++)
    fprintf(f, "fpr %2i : % 28.18e  % 28.18e\t%8x %8x  %8x %8x\n",
	    i,
	    wilson_data.fpr[2*i], wilson_data.fpr[2*i+1],
	    ((int*) wilson_data.fpr)[4*i],
	    ((int*) wilson_data.fpr)[4*i+1],
	    ((int*) wilson_data.fpr)[4*i+2],
	    ((int*) wilson_data.fpr)[4*i+3]);
  fprintf(f, "\n");
  fprintf(f, "fs     : % 28.18e  % 28.18e\t%8x %8x  %8x %8x\n",
	  ((double*) wilson_data.fifo_stat_orig)[0],
	  ((double*) wilson_data.fifo_stat_orig)[1],
	  wilson_data.fifo_stat_orig[0],
	  wilson_data.fifo_stat_orig[1],
	  wilson_data.fifo_stat_orig[2],
	  wilson_data.fifo_stat_orig[3]);
  fprintf(f, "fs     : % 28.18e  % 28.18e\t%8x %8x  %8x %8x\n",
	  ((double*) wilson_data.fifo_stat_orig)[2],
	  ((double*) wilson_data.fifo_stat_orig)[3],
	  wilson_data.fifo_stat_orig[4],
	  wilson_data.fifo_stat_orig[5],
	  wilson_data.fifo_stat_orig[6],
	  wilson_data.fifo_stat_orig[7]);
  fprintf(f, "fs     : % 28.18e  % 28.18e\t%8x %8x  %8x %8x\n",
	  ((double*) wilson_data.fifo_stat_orig)[4],
	  ((double*) wilson_data.fifo_stat_orig)[5],
	  wilson_data.fifo_stat_orig[8],
	  wilson_data.fifo_stat_orig[9],
	  wilson_data.fifo_stat_orig[10],
	  wilson_data.fifo_stat_orig[11]);
  fprintf(f, "fs     : % 28.18e  % 28.18e\t%8x %8x  %8x %8x\n",
	  ((double*) wilson_data.fifo_stat_orig)[6],
	  ((double*) wilson_data.fifo_stat_orig)[7],
	  wilson_data.fifo_stat_orig[12],
	  wilson_data.fifo_stat_orig[13],
	  wilson_data.fifo_stat_orig[14],
	  wilson_data.fifo_stat_orig[15]);
  fclose(f);
  sprintf(str, "error_n_%04i", my_rank);
  f=fopen(str, "w");
  fprintf(f, "even:\n");
  for (int i=0; i<wilson_data.volume/2+max_ahead; i++)
    fprintf(f, "%5i\t%8hx\t% 8hi % 8hi % 8hi % 8hi\n",
	    i, wilson_data.neighb_offs_even[5*i],
	    wilson_data.neighb_offs_even[5*i+1],
	    wilson_data.neighb_offs_even[5*i+2],
	    wilson_data.neighb_offs_even[5*i+3],
	    wilson_data.neighb_offs_even[5*i+4]);
  fprintf(f, "odd:\n");
  for (int i=0; i<wilson_data.volume/2+max_ahead; i++)
    fprintf(f, "%5i\t%8hx\t% 8hi % 8hi % 8hi % 8hi\n",
	    i, wilson_data.neighb_offs_odd[5*i],
	    wilson_data.neighb_offs_odd[5*i+1],
	    wilson_data.neighb_offs_odd[5*i+2],
	    wilson_data.neighb_offs_odd[5*i+3],
	    wilson_data.neighb_offs_odd[5*i+4]);
  fclose(f);
  sleep(120);
  abort();
}


void fifo_status()
{
  sleep(3);
  if (my_rank!=0 && my_rank!=2 && my_rank!=3)
    return;
  for (int f=0; f<2; f++)
    for (int mu=0; mu<3; mu++)
      printf("[%i] f %i  mu %i   s %i   r %i (%x)\n",
	     my_rank, f, mu,
	     test_stat(stat_addr, send_stat_byte_offs[f][mu]),
	     test_stat(stat_addr, recv_stat_byte_offs[f][mu]),
	     stat_addr);
}

void bgl_sleep(int* n)
{
  sleep(*n);
}

void bgl_get_my_rank(int* r)
{
  *r=my_rank;
}

void bgl_get_num_procs(int* n)
{
  *n=num_procs;
}

void bgl_inner(int* inner)
{
  *inner=1;
  for (int mu=0; mu<3; mu++)
    if (wilson_data.t_position[mu]==0 ||
	wilson_data.t_position[mu]==wilson_data.t_size[mu]-1)
      *inner=0;
}

void bgl_abort()
{
  exit(1);
}

