/*
!===============================================================================
!
! ildg.c - read/write configuration in ILDG lime format
!
!-------------------------------------------------------------------------------
!
! Copyright (C) 2007-2017 Hinnerk Stueben
!               2013 Yoshifumi Nakamura
!
! 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/>.
!
!-------------------------------------------------------------------------------
!
! ildg_open_r()  : open ILDG file for reading
! ildg_open_w()  : open ILDG file for writing
! ildg_read()    : read one xy-slice
! ildg_write()   : write one xy-slice
! ildg_close_r() : close ILDG file after reading
! ildg_close_w() : close ILDG file after writing
! ildg_read_info() : read restart info
!
! ildg_landau_close_w(): for QED Landau Gauge fixing program
!-------------------------------------------------------------------------------
*/
# include <sys/types.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include "c_defs.h"
# include "c_util.h"
# include "cksum.h"
# include "lime.h"
# include "ildg.h"
# include "ildg_xml.h"
# include "defs_ildg.h"

static FILE *fp_r;
static FILE *fp_w;
static LimeReader *r;
static LimeWriter *w;
static LimeRecordHeader *h;

static char xmldoc[LEN_XMLDOC];
static int len_xmldoc = LEN_XMLDOC - 1;

static char bqcd_input[LEN_BQCD_INPUT];
static int len_bqcd_input = LEN_BQCD_INPUT - 1;

static char bqcd_counters[LEN_BQCD_COUNTERS];
static int len_bqcd_counters = LEN_BQCD_COUNTERS - 1;

static char bqcd_cksum[LEN_BQCD_CKSUM];
static int len_bqcd_cksum = LEN_BQCD_CKSUM - 1;

static char bqcd_ran_name[LEN_BQCD_RAN_NAME];
static int len_bqcd_ran_name = LEN_BQCD_RAN_NAME - 1;

static char *bqcd_ran_state;
static int len_bqcd_ran_state;

static double plaquette;
static char bqcd_plaquette[LEN_BQCD_PLAQUETTE];
static int len_bqcd_plaquette = LEN_BQCD_PLAQUETTE - 1;

static char record_ildg_format[]      = "ildg-format";
static char record_ildg_binary_data[] = "ildg-binary-data";
static char record_ildg_data_lfn[]    = "ildg-data-lfn";

static char record_bqcd_input[]       = "bqcd-input";
static char record_bqcd_counters[]    = "bqcd-counters";
static char record_bqcd_ran_name[]    = "bqcd-ran-name";
static char record_bqcd_ran_state[]   = "bqcd-ran-state";
static char record_bqcd_cksum[]       = "bqcd-cksum";
static char record_bqcd_plaquette[]   = "bqcd-plaquette";
static char record_bqcd_plaquette_qed[] = "bqcd-plaquette-qed";

static char record_qcdsf_u1_format[]      = "qcdsf-u1-format";
static char record_qcdsf_u1_binary_data[] = "qcdsf-u1-binary-data";

static char record_qcdsf_axion_format[]      = "qcdsf-axion-format";
static char record_qcdsf_axion_binary_data[] = "qcdsf-axion-binary-data";

void ildg_get_bqcd_input(char *s, int *len);
void ildg_get_bqcd_counters(int *run, int *job, int *traj);
void ildg_get_bqcd_ran_name(char *s, int *len);
void ildg_get_bqcd_ran_state_strlen(int *len);
void ildg_get_bqcd_ran_state(char *s, int *len);
void ildg_get_bqcd_ranfield_info(int *lz, int *lt);
void ildg_get_bqcd_ranfield_state(char *s, int *len, int *lz, int *lt);
void ildg_get_bqcd_plaquette(double *plaq);
void ildg_get_bqcd_plaquette_qed(double *plaq);

void ildg_put_bqcd_input(char *s, int *len);
void ildg_put_bqcd_counters(char *s, int *len);
void ildg_put_bqcd_ran_name(char *s, int *len);
void ildg_put_bqcd_ran_state(char *s, int *len);
void ildg_put_bqcd_ranfield_info(int *lz, int *lt);
void ildg_put_bqcd_ranfield_state(char *s, int *len, int *lz, int *lt);
void ildg_put_bqcd_cksum(char *s, int *len);
void ildg_put_bqcd_plaquette(char *s, int *len);

/*----------------------------------------------------------------------------*/
void ILDG_OPEN_R(char *filename, 
                 int *Lx, int *Ly, int *Lz, int *Lt,
                 int *precision,
                 INT8 *bytes_total)
{
  /*  filename         (input)
   *  Lx, Ly, Lz, Lt   lattice size (output)
   *  precision        32|64 (output)
   *  bytes_total     bytes of configuration (output)
   */

  fp_r = fopen(filename, "r");
  if (fp_r == NULL) 
      die("could not open %s for reading", filename);

  r = limeCreateReader(fp_r);

  ildg_find_record(record_ildg_format);
  ildg_read_string(record_ildg_format, xmldoc, len_xmldoc);

  ildg_xml_check_value(xmldoc, "<version>", "1.0");
  ildg_xml_check_value(xmldoc, "<field>", "su3gauge");
  ildg_xml_get_int(xmldoc, "<precision>", precision);
  ildg_xml_get_int(xmldoc, "<lx>", Lx);
  ildg_xml_get_int(xmldoc, "<ly>", Ly);
  ildg_xml_get_int(xmldoc, "<lz>", Lz);
  ildg_xml_get_int(xmldoc, "<lt>", Lt);

  ildg_find_record(record_ildg_binary_data);

  *bytes_total = limeReaderBytes(r);

  CKSUM_INIT();
}

/*----------------------------------------------------------------------------*/
void ILDG_OPEN_W(char *filename, 
                 int *Lx, int *Ly, int *Lz, int *Lt,
                 int *precision,
                 INT8 *bytes_total)
{
  /*  filename         (input)
   *  Lx, Ly, Lz, Lt   lattice size (input)
   *  precision        32|64 (input)
   *  bytes_total     bytes of configuration (input)
   */

  n_uint64_t nbytes;

  fp_w = fopen(filename, "w");
  if (fp_w == NULL)
      die("could not open %s for writing", filename);

  w = limeCreateWriter(fp_w);

  sprintf(xmldoc, 
          "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
          "<ildgFormat>\n"
          "  <version>1.0</version>\n"
          "  <field>su3gauge</field>\n"
          "  <precision>%d</precision>\n"
          "  <lx>%d</lx> <ly>%d</ly> <lz>%d</lz> <lt>%d</lt>\n"
          "</ildgFormat>\n",
          *precision, *Lx, *Ly, *Lz, *Lt);

  nbytes = strlen(xmldoc);

  h = limeCreateHeader(1, 0, record_ildg_format, nbytes);

  limeWriteRecordHeader(h, w);
  limeDestroyHeader(h);
  limeWriteRecordData(xmldoc, &nbytes, w);

  nbytes = *bytes_total;

  h = limeCreateHeader(0, 1, record_ildg_binary_data, nbytes);
  limeWriteRecordHeader(h, w);
  limeDestroyHeader(h);

  CKSUM_INIT();
}

/*----------------------------------------------------------------------------*/
void ILDG_READ(void *buffer, INT8 *bytes)
{
  /*  buffer        (input)
   *  bytes         (input)
   */

  int status;
  n_uint64_t nbytes = *bytes;

  status = limeReaderReadData(buffer, &nbytes, r);
  if (status != LIME_SUCCESS)
    die("failed to read Data (status=%d)", status);
  if (nbytes != *bytes)
    die("failed to read Data, read bytes /= requested bytes   (status=%d)", status);

  CKSUM_ADD(buffer, bytes);
}

/*----------------------------------------------------------------------------*/
void ILDG_WRITE(void *buffer, INT8 *bytes)
{
  /*  buffer        (output)
   *  bytes         (input)
   */
  int status;
  n_uint64_t nbytes = *bytes;

  status = limeWriteRecordData(buffer, &nbytes, w);
  if (status != LIME_SUCCESS)
    die("failed to write Data (status=%d)", status);
  
  CKSUM_ADD(buffer, bytes);
}

/*----------------------------------------------------------------------------*/
void ILDG_LANDAU_R(void *buffer, INT8 *bytes)
{
  /*  buffer        (input)
   *  bytes         (input)
   */

  int status;
  n_uint64_t nbytes = *bytes;

  status = limeReaderReadData(buffer, &nbytes, r);
  if (status != LIME_SUCCESS)
    die("failed to read Data (status=%d)", status);
  if (nbytes != *bytes)
    die("failed to read Data, read bytes /= requested bytes   (status=%d)", status);
}

/*----------------------------------------------------------------------------*/
void ILDG_LANDAU_W(void *buffer, INT8 *bytes)
{
  /*  buffer        (output)
   *  bytes         (input)
   */
  int status;
  n_uint64_t nbytes = *bytes;

  status = limeWriteRecordData(buffer, &nbytes, w);
  if (status != LIME_SUCCESS)
    die("failed to write Data (status=%d)", status);
}

/*----------------------------------------------------------------------------*/
void ILDG_CLOSE_R(INT8 *cksum, INT8 *bytes_total, char *lfn, int *len)
{
  /*  cksum         CRC check sum (output)
   *  bytes_total   (output) (from cksum())
   *  lfn           logical file name (output)
   *  len           input: max. length of "lfn"
   *                output: actual length of "lfn"
   */

  n_uint64_t nbytes;
  int status;
  INT8 cksum_r;

  fseek(fp_r, 0, SEEK_SET);
  do {
      status = limeReaderNextRecord(r);
      if (status == LIME_EOF)
          break;
  } while (strncmp(limeReaderType(r), record_ildg_data_lfn, 13) != 0);


  if (status == LIME_EOF)
      strcpy(lfn, "UNDEFINED");
  else
      ildg_read_string(record_ildg_data_lfn, lfn, *len);

  *len = strlen(lfn);

  CKSUM_GET(cksum, bytes_total);

  if (MY_PE() == 0) {
      if (ildg_try_record(record_bqcd_cksum)) {
	  ildg_read_string(record_bqcd_cksum, bqcd_cksum, len_bqcd_cksum);
	  bqcd_cksum[len_bqcd_cksum] = '\0';
	  sscanf(bqcd_cksum, "%ld", &cksum_r);
	  if (*cksum != cksum_r) die("cksum error");
      } else {
	  warn("can't verify cksum");
      }
  }

  limeDestroyReader(r);
  fclose(fp_r);

}
/*----------------------------------------------------------------------------*/
void ildg_landau_close_r(INT8 *cksum)
{
  /*  cksum         CRC check sum (input)
   */

  INT8 cksum_r;

  if (MY_PE() == 0) {
      fseek(fp_r, 0, SEEK_SET);
      if (ildg_try_record(record_bqcd_cksum)) {
	  ildg_read_string(record_bqcd_cksum, bqcd_cksum, len_bqcd_cksum);
	  bqcd_cksum[len_bqcd_cksum] = '\0';
	  sscanf(bqcd_cksum, "%ld", &cksum_r);
	  if (*cksum != cksum_r) die("cksum error");
      } else {
	  warn("can't verify cksum");
      }
  }

  limeDestroyReader(r);
  fclose(fp_r);

}

/*----------------------------------------------------------------------------*/
void ILDG_CLOSE_W(INT8 *cksum, INT8 *bytes_total, char *lfn)
{
  /*  cksum         CRC check sum (output)
   *  bytes_total   (output) (from cksum())
   *  lfn           logical file name (input)
   */

  n_uint64_t nbytes;
  int run, job, traj, len;
  int z, t, lz, lt;

  nbytes = strlen(lfn);
  h = limeCreateHeader(1, 1, record_ildg_data_lfn, nbytes);
  limeWriteRecordHeader(h, w);
  limeDestroyHeader(h);
  limeWriteRecordData(lfn, &nbytes, w);

  ildg_get_bqcd_input(bqcd_input, &len_bqcd_input);
  nbytes = len_bqcd_input;
  h = limeCreateHeader(1, 0, record_bqcd_input, nbytes);
  limeWriteRecordHeader(h, w);
  limeDestroyHeader(h);
  limeWriteRecordData(bqcd_input, &nbytes, w);

  ildg_get_bqcd_counters(&run, &job, &traj);
  sprintf(bqcd_counters, "%10d %10d %10d ", run, job, traj); 
  nbytes = len_bqcd_counters;
  h = limeCreateHeader(0, 0, record_bqcd_counters, nbytes);
  limeWriteRecordHeader(h, w);
  limeDestroyHeader(h);
  limeWriteRecordData(bqcd_counters, &nbytes, w);

  ildg_get_bqcd_ran_name(bqcd_ran_name, &len_bqcd_ran_name);
  nbytes = len_bqcd_ran_name;
  h = limeCreateHeader(0, 0, record_bqcd_ran_name, nbytes);
  limeWriteRecordHeader(h, w);
  limeDestroyHeader(h);
  limeWriteRecordData(bqcd_ran_name, &nbytes, w);

  ildg_get_bqcd_ran_state_strlen(&len_bqcd_ran_state);
  bqcd_ran_state = malloc((size_t) len_bqcd_ran_state);
  if (bqcd_ran_state == NULL) die("out of memory");
  ildg_get_bqcd_ran_state(bqcd_ran_state, &len_bqcd_ran_state);
  ildg_get_bqcd_ranfield_info(&lz, &lt);
  nbytes = len_bqcd_ran_state;
  h = limeCreateHeader(0, 0, record_bqcd_ran_state, (lz * lt + 1) * nbytes);
  limeWriteRecordHeader(h, w);
  limeDestroyHeader(h);
  limeWriteRecordData(bqcd_ran_state, &nbytes, w);
  for (t = 0; t < lt; t++){
      for (z = 0; z < lz; z++) {
	  ildg_get_bqcd_ranfield_state(bqcd_ran_state, &len_bqcd_ran_state, &z, &t);
	  limeWriteRecordData(bqcd_ran_state, &nbytes, w);
      }
  }
  free(bqcd_ran_state);

  ildg_get_bqcd_plaquette(&plaquette);
  sprintf(bqcd_plaquette, "%23.15e ", plaquette);
  nbytes = len_bqcd_plaquette;
  h = limeCreateHeader(0, 0, record_bqcd_plaquette, nbytes);
  limeWriteRecordHeader(h, w);
  limeDestroyHeader(h);
  limeWriteRecordData(bqcd_plaquette, &nbytes, w);

  ildg_get_bqcd_plaquette_qed(&plaquette);
  if (plaquette > 0) {
  sprintf(bqcd_plaquette, "%23.15e ", plaquette);
  nbytes = len_bqcd_plaquette;
  h = limeCreateHeader(0, 0, record_bqcd_plaquette_qed, nbytes);
  limeWriteRecordHeader(h, w);
  limeDestroyHeader(h);
  limeWriteRecordData(bqcd_plaquette, &nbytes, w);
  }

  CKSUM_GET(cksum, bytes_total);
  sprintf(bqcd_cksum, "%ld ", *cksum);  
  nbytes = strlen(bqcd_cksum);
  h = limeCreateHeader(0, 1, record_bqcd_cksum, nbytes);
  limeWriteRecordHeader(h, w);
  limeDestroyHeader(h);
  limeWriteRecordData(bqcd_cksum, &nbytes, w);

  limeDestroyWriter(w);
  fclose(fp_w);
}

/*----------------------------------------------------------------------------*/
void ildg_landau_close_w(INT8 *cksum, char *lfn, double *plaq, double *plaq_qed)
{
  /*  cksum         CRC check sum (input)
   *  lfn           logical file name (input)
   *  plaq          plaquette (output)
   *  plaq_qed      plaquette_qed (output)
   */

  n_uint64_t nbytes;
  int run, job, traj, len;

  nbytes = strlen(lfn);
  h = limeCreateHeader(1, 1, record_ildg_data_lfn, nbytes);
  limeWriteRecordHeader(h, w);
  limeDestroyHeader(h);
  limeWriteRecordData(lfn, &nbytes, w);

  ildg_find_record(record_bqcd_input);
  ildg_read_string(record_bqcd_input, bqcd_input, len_bqcd_input);
  nbytes = len_bqcd_input;
  h = limeCreateHeader(1, 0, record_bqcd_input, nbytes);
  limeWriteRecordHeader(h, w);
  limeDestroyHeader(h);
  limeWriteRecordData(bqcd_input, &nbytes, w);

  ildg_find_record(record_bqcd_counters);
  ildg_read_string(record_bqcd_counters, bqcd_counters, len_bqcd_counters);
  nbytes = len_bqcd_counters;
  h = limeCreateHeader(0, 0, record_bqcd_counters, nbytes);
  limeWriteRecordHeader(h, w);
  limeDestroyHeader(h);
  limeWriteRecordData(bqcd_counters, &nbytes, w);

  ildg_find_record(record_bqcd_ran_name);
  ildg_read_string(record_bqcd_ran_name, bqcd_ran_name, len_bqcd_ran_name);
  nbytes = len_bqcd_ran_name;
  h = limeCreateHeader(0, 0, record_bqcd_ran_name, nbytes);
  limeWriteRecordHeader(h, w);
  limeDestroyHeader(h);
  limeWriteRecordData(bqcd_ran_name, &nbytes, w);

  ildg_find_record(record_bqcd_ran_state);
  len_bqcd_ran_state = limeReaderBytes(r);
  bqcd_ran_state = malloc((size_t) len_bqcd_ran_state);
  if (bqcd_ran_state == NULL) die("out of memory");
  ildg_read_string(record_bqcd_ran_state, bqcd_ran_state, len_bqcd_ran_state);
  nbytes = len_bqcd_ran_state;
  h = limeCreateHeader(0, 0, record_bqcd_ran_state, nbytes);
  limeWriteRecordHeader(h, w);
  limeDestroyHeader(h);
  limeWriteRecordData(bqcd_ran_state, &nbytes, w);
  free(bqcd_ran_state);

  ildg_find_record(record_bqcd_plaquette);
  ildg_read_string(record_bqcd_plaquette, bqcd_plaquette, len_bqcd_plaquette);
  nbytes = len_bqcd_plaquette;
  h = limeCreateHeader(0, 0, record_bqcd_plaquette, nbytes);
  limeWriteRecordHeader(h, w);
  limeDestroyHeader(h);
  limeWriteRecordData(bqcd_plaquette, &nbytes, w);
  bqcd_plaquette[len_bqcd_plaquette] = '\0';
  sscanf(bqcd_plaquette, "%lf", plaq);

  if (ildg_try_record(record_bqcd_plaquette_qed)) {
  ildg_read_string(record_bqcd_plaquette_qed, bqcd_plaquette, len_bqcd_plaquette);
  nbytes = len_bqcd_plaquette;
  h = limeCreateHeader(0, 0, record_bqcd_plaquette_qed, nbytes);
  limeWriteRecordHeader(h, w);
  limeDestroyHeader(h);
  limeWriteRecordData(bqcd_plaquette, &nbytes, w);
  bqcd_plaquette[len_bqcd_plaquette] = '\0';
  sscanf(bqcd_plaquette, "%lf", plaq_qed);
  }

  sprintf(bqcd_cksum, "%ld ", *cksum);  
  nbytes = strlen(bqcd_cksum);
  h = limeCreateHeader(0, 1, record_bqcd_cksum, nbytes);
  limeWriteRecordHeader(h, w);
  limeDestroyHeader(h);
  limeWriteRecordData(bqcd_cksum, &nbytes, w);

  limeDestroyWriter(w);
  fclose(fp_w);
}

/*----------------------------------------------------------------------------*/
int ildg_try_record(char *record)
{
  /*  record         (input)
   */

  int status;

  fseek(fp_r, 0, SEEK_SET);
  do {
      status = limeReaderNextRecord(r);
      if (status == LIME_EOF)
          return 0;
  } while (strncmp(limeReaderType(r), record, (int) strlen(record)) != 0);

  return 1;
}

/*----------------------------------------------------------------------------*/
void ildg_find_record(char *record)
{
  /*  record         (input)
   */

  int status;

  fseek(fp_r, 0, SEEK_SET);
  do {
      status = limeReaderNextRecord(r);
      if (status == LIME_EOF)
          die("unexpected end-of-file");
  } while (strncmp(limeReaderType(r), record, (int) strlen(record)) != 0);
}

/*----------------------------------------------------------------------------*/
void ildg_read_string(char *record, char *s, int len)
{
  /*  record         (input)
   *  s              string read (output)
   *  len            max. length of "s"
   */

  int status;
  n_uint64_t nbytes;

  nbytes = limeReaderBytes(r);
  if (nbytes > len)
    die("string (length=%d) too short for reading record %s", len, record);

  status = limeReaderReadData(s, &nbytes, r);
  if (status != LIME_SUCCESS)
      die("failed to read record %s (status=%d)", record, status);
  s[nbytes] = '\0';
}

/*----------------------------------------------------------------------------*/
void ildg_read_string2(char *s, int len)
{
  /*  
   *  s              string read (output)
   *  len            numberof bytes to be read in "s"
   */

  int status;
  n_uint64_t nbytes;

  nbytes = len;
  status = limeReaderReadData(s, &nbytes, r);
  if (status != LIME_SUCCESS)
      die("failed to read string (status=%d)", status);
  s[nbytes] = '\0';
}

/*----------------------------------------------------------------------------*/
void ILDG_READ_INFO(char *filename)
{
  int z, t, lz, lt, status;
  n_uint64_t nbytes;

  fp_r = fopen(filename, "r");
  if (fp_r == NULL) 
      die("could not open %s for reading", filename);

  r = limeCreateReader(fp_r);

  if (ildg_try_record(record_bqcd_input)) {
      ildg_read_string(record_bqcd_input, bqcd_input, len_bqcd_input);
      ildg_put_bqcd_input(bqcd_input, &len_bqcd_input);
  }
  if (ildg_try_record(record_bqcd_counters)) {
      ildg_read_string(record_bqcd_counters, bqcd_counters, len_bqcd_counters);
      ildg_put_bqcd_counters(bqcd_counters, &len_bqcd_counters);
  }
  if (ildg_try_record(record_bqcd_ran_name)) {
      ildg_read_string(record_bqcd_ran_name, bqcd_ran_name, len_bqcd_ran_name);
      ildg_put_bqcd_ran_name(bqcd_ran_name, &len_bqcd_ran_name);
  }
  if (ildg_try_record(record_bqcd_ran_state)) {
      ildg_get_bqcd_ran_state_strlen(&len_bqcd_ran_state);
      bqcd_ran_state = malloc((size_t) (len_bqcd_ran_state + 1));
      if (bqcd_ran_state == NULL) die("out of memory");
      ildg_get_bqcd_ranfield_info(&lz, &lt);
      check(limeReaderBytes(r) == (lz * lt + 1) * len_bqcd_ran_state);
      ildg_read_string2(bqcd_ran_state, len_bqcd_ran_state);
      ildg_put_bqcd_ran_state(bqcd_ran_state, &len_bqcd_ran_state);
      for (t = 0; t < lt; t++){
	  for (z = 0; z < lz; z++) {
	      ildg_read_string2(bqcd_ran_state, len_bqcd_ran_state);
	      ildg_put_bqcd_ranfield_state(bqcd_ran_state, &len_bqcd_ran_state, &z, &t);
	  }
      }
      free(bqcd_ran_state);
  }
  if (ildg_try_record(record_bqcd_plaquette)) {
      ildg_read_string(record_bqcd_plaquette, bqcd_plaquette, len_bqcd_plaquette);
      ildg_put_bqcd_plaquette(bqcd_plaquette, &len_bqcd_plaquette);
  }
  if (ildg_try_record(record_bqcd_cksum)) {
      ildg_read_string(record_bqcd_cksum, bqcd_cksum, len_bqcd_cksum);
      ildg_put_bqcd_cksum(bqcd_cksum, &len_bqcd_cksum);
  }

  limeDestroyReader(r);
  fclose(fp_r);
}

/*----------------------------------------------------------------------------*/
/*
 * added by Yoshifumi Nakamura 2013/April/03
 * for QCD+QED
 */

void QCDSF_U1_R( int *Lx, int *Ly, int *Lz, int *Lt,
                 int *precision,
                 INT8 *bytes_total)
{
  /*  Lx, Ly, Lz, Lt   lattice size (output)
   *  precision        32|64 (output)
   *  bytes_total     bytes of configuration (output)
   */

  r = limeCreateReader(fp_r);

  ildg_find_record(record_qcdsf_u1_format);
  ildg_read_string(record_qcdsf_u1_format, xmldoc, len_xmldoc);

  ildg_xml_check_value(xmldoc, "<version>", "1.0");
  ildg_xml_check_value(xmldoc, "<field>", "u1gauge");
  ildg_xml_get_int(xmldoc, "<precision>", precision);
  ildg_xml_get_int(xmldoc, "<lx>", Lx);
  ildg_xml_get_int(xmldoc, "<ly>", Ly);
  ildg_xml_get_int(xmldoc, "<lz>", Lz);
  ildg_xml_get_int(xmldoc, "<lt>", Lt);

  ildg_find_record(record_qcdsf_u1_binary_data);

  *bytes_total = limeReaderBytes(r);

}

/*----------------------------------------------------------------------------*/
void QCDSF_U1_W(int *Lx, int *Ly, int *Lz, int *Lt,
                int *precision,
                INT8 *bytes_total)
{
  /*  Lx, Ly, Lz, Lt   lattice size (input)
   *  precision        32|64 (input)
   *  bytes_total     bytes of configuration (input)
   */

  n_uint64_t nbytes;

  sprintf(xmldoc, 
          "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
          "<ildgFormat>\n"
          "  <version>1.0</version>\n"
          "  <field>u1gauge</field>\n"
          "  <precision>%d</precision>\n"
          "  <lx>%d</lx> <ly>%d</ly> <lz>%d</lz> <lt>%d</lt>\n"
          "</ildgFormat>\n",
          *precision, *Lx, *Ly, *Lz, *Lt);

  nbytes = strlen(xmldoc);

  h = limeCreateHeader(1, 0, record_qcdsf_u1_format, nbytes);

  limeWriteRecordHeader(h, w);
  limeDestroyHeader(h);
  limeWriteRecordData(xmldoc, &nbytes, w);

  nbytes = *bytes_total;

  h = limeCreateHeader(0, 1, record_qcdsf_u1_binary_data, nbytes);
  limeWriteRecordHeader(h, w);
  limeDestroyHeader(h);

}

/*----------------------------------------------------------------------------*/
void ILDG_FIND_U1_BINARY_DATA(void)
{
  ildg_find_record(record_qcdsf_u1_binary_data);
}

/*----------------------------------------------------------------------------*/
void QCDSF_AXION_R( int *Lx, int *Ly, int *Lz, int *Lt,
                 int *precision,
                 INT8 *bytes_total)
{
  /*  Lx, Ly, Lz, Lt   lattice size (output)
   *  precision        32|64 (output)
   *  bytes_total     bytes of configuration (output)
   */

  r = limeCreateReader(fp_r);

  ildg_find_record(record_qcdsf_axion_format);
  ildg_read_string(record_qcdsf_axion_format, xmldoc, len_xmldoc);

  ildg_xml_check_value(xmldoc, "<version>", "1.0");
  ildg_xml_check_value(xmldoc, "<field>", "axion");
  ildg_xml_get_int(xmldoc, "<precision>", precision);
  ildg_xml_get_int(xmldoc, "<lx>", Lx);
  ildg_xml_get_int(xmldoc, "<ly>", Ly);
  ildg_xml_get_int(xmldoc, "<lz>", Lz);
  ildg_xml_get_int(xmldoc, "<lt>", Lt);

  ildg_find_record(record_qcdsf_axion_binary_data);

  *bytes_total = limeReaderBytes(r);

}

/*----------------------------------------------------------------------------*/
void QCDSF_AXION_W(int *Lx, int *Ly, int *Lz, int *Lt,
                int *precision,
                INT8 *bytes_total)
{
  /*  Lx, Ly, Lz, Lt   lattice size (input)
   *  precision        32|64 (input)
   *  bytes_total     bytes of configuration (input)
   */

  n_uint64_t nbytes;

  sprintf(xmldoc, 
          "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
          "<ildgFormat>\n"
          "  <version>1.0</version>\n"
          "  <field>axion</field>\n"
          "  <precision>%d</precision>\n"
          "  <lx>%d</lx> <ly>%d</ly> <lz>%d</lz> <lt>%d</lt>\n"
          "</ildgFormat>\n",
          *precision, *Lx, *Ly, *Lz, *Lt);

  nbytes = strlen(xmldoc);

  h = limeCreateHeader(1, 0, record_qcdsf_axion_format, nbytes);

  limeWriteRecordHeader(h, w);
  limeDestroyHeader(h);
  limeWriteRecordData(xmldoc, &nbytes, w);

  nbytes = *bytes_total;

  h = limeCreateHeader(0, 1, record_qcdsf_axion_binary_data, nbytes);
  limeWriteRecordHeader(h, w);
  limeDestroyHeader(h);

}

/*----------------------------------------------------------------------------*/
void ILDG_FIND_AXION_BINARY_DATA(void)
{
  ildg_find_record(record_qcdsf_axion_binary_data);
}

/*============================================================================*/
