#include <stdio.h>
#include <stdlib.h>
#include <argp.h>
#include <string.h>
#include <assert.h>
#include <stdint.h>
#include "utils.h"

#ifdef USE_MKL
#pragma message "Using the MKL for BLAS."
#include <mkl.h>
#else
#pragma message "Not using the MKL for BLAS."
#include <cblas.h>
#endif

#define MAX_LINES 2000
#define NB_RUNS 1
#define MAX_NAME_SIZE 256

#define MAX_OFFSET 1000000
#define WRITE_MEMORY_SIZE 100000000

FILE* active_file = NULL;
char* basename = "calibration";
char* dir_name  = ".";

static double *matrix_A;
static double *matrix_B;
static double *matrix_C;

unsigned long long base_time;

typedef struct {
  char* sizefile;
  int loop;
  char* resultfile;
} my_args;

static char doc[] = "Runs BLAS benchmarks to compute values used for SMPI calibration";

static struct argp_option options[] = {
  {"sizeFile", 's', "SIZEFILE", 0, "filename of the size list"},
  {"resultfile", 'o', "RESULTFILE", 0, "filename of the results"},
  {"loop", 'l', "NB_LOOPS", 0, "number of (non-recorded) loops to run before and after the execution"},
  { 0 }
};

static int parse_options (int key, char *arg, struct argp_state *state)
{
  my_args *arguments = state->input;
  switch (key){
  case 'l':
    arguments->loop = atoi(arg);
    break;
  case 's':
    arguments->sizefile = arg;
    break;
  case 'o':
    arguments->resultfile = arg;
    break;

  default: return ARGP_ERR_UNKNOWN;
  }
  return 0;
}

struct argp argp = { options, parse_options, NULL, doc };
my_args arguments;

/*
 * Return 0b000...01...1, with n ones at the end.
 */
uint64_t __get_mask(unsigned n) {
    if(n == 0)
        return 0;
    assert(n >= 0 && n < 64);
    uint64_t mask = 1;
    return (mask << n) - 1;
}

/*
 * Return 0b000...01...10...0.
 * The bits at positions [start, stop] are equal to 1, the others to 0 (start is the lower order).
 */
uint64_t get_mask(unsigned start, unsigned stop) {
    assert(0 <= start && start <= stop && stop < 64);
    uint64_t ones = __get_mask(stop);
    uint64_t zeroes = ~__get_mask(start);
    return ones & zeroes;
}

/*
 * Apply the given mask to a double.
 */
double apply_mask(double x, uint64_t mask) {
    uint64_t *tmp = (uint64_t*)&x;
    (*tmp) |= mask;
    return *((double*)tmp);
}

void __print_bits(uint64_t n, unsigned i) {
    if(i == 64)
        return;
    __print_bits(n / 2, i+1);
    if(n % 2)
        printf("1");
    else
        printf("0");
}

void print_bits(uint64_t n) {
    printf("0b");
    __print_bits(n, 0);
    printf("\n");
}

void print_bits_f(double x) {
    uint64_t *tmp = (uint64_t*)&x;
    print_bits(*tmp);
}

double *allocate_matrix(size_t size1, size_t size2) {
    size_t alloc_size = (size1*size2 + MAX_OFFSET) * sizeof(double);
    double *result = (double*) malloc(alloc_size);
    if(!result) {
      perror("malloc");
      exit(errno);
    }
#ifdef MASK_SIZE
    uint64_t mask = get_mask(0, MASK_SIZE);
#pragma message "Using a mask for the values of the matrix."
#else
    // Note that this particular mask does not do anything: apply_mask(x, get_mask(0, 0)) == x
    // One need to change the stop and/or start indices to really apply a mask.
    uint64_t mask = get_mask(0, 0);
#endif
    printf("Using the mask: ");
    print_bits(mask);
    double x = 3.1415926535897932384626433;
    assert(x == apply_mask(x, get_mask(0, 0)));
    assert(x != apply_mask(x, get_mask(0, 1)));
    for(int i = 0; i < size1*size2; i++) {
        result[i] = apply_mask((double)rand()/(double)(RAND_MAX), mask);
    }
    return result;
}

void write_memory(void) {
    static char *buff = NULL;
    if(!buff) {
        buff = malloc(WRITE_MEMORY_SIZE);
        if(!buff) {
            perror("malloc");
            exit(errno);
        }
    }
    memset(buff, rand()%256, WRITE_MEMORY_SIZE);
}

FILE *open_file(const char* filename){
    FILE *file = fopen(filename, "w");
    if(!file) {
        perror("open_file");
        exit(errno);
    }
    return file;
}

void get_dgemm(FILE *file, int *sizes, int nb_it, unsigned long long base_time, int write_file) {
  unsigned long long start_time, total_time;
  double alpha = 1., beta=1.;
  for(int i=0; i<nb_it; i++) {
    start_time=get_time();
    size_t offset = rand() % MAX_OFFSET;
    cblas_dgemm(CblasColMajor, CblasNoTrans, CblasTrans, sizes[0], sizes[1], sizes[2], alpha, matrix_A+offset, sizes[3],
            matrix_B+offset, sizes[4], beta, matrix_C+offset, sizes[5]);
    total_time=get_time()-start_time;
    if(write_file)
      print_in_file(file, "dgemm", sizes, 6, start_time-base_time, total_time);
  }
}

static const char *names[] = {"dgemm", NULL};
static const void (*functions[])(FILE*, int*, int, unsigned long long, int) = {get_dgemm};

void test_op(FILE *result_file, experiment_t *exp, int nb_runs, unsigned long long base_time, int write_file) {
  functions[exp->op_id](result_file, exp->sizes, nb_runs, base_time, write_file);
}

int get_max_size(experiment_t *exp, int nb_exp, int idx) {
  int max = -1;
  for(int i = 0; i < nb_exp; i++) {
    int size = exp[i].sizes[idx];
    if(max < size)
      max = size;
  }
  return max;
}

int main(int argc, char** argv){

  srand(42);
  bzero (&arguments, sizeof(my_args));

  if (argp_parse (&argp, argc, argv, 0, 0, &arguments) == ARGP_KEY_ERROR){
    fprintf(stderr,"error during the parsing of parameters\n");
    return 1;
  }

  int nb_loops = arguments.loop;

  if(arguments.sizefile==NULL){
    fprintf(stderr, "Please provide a name for a file containing a list of sizes\n");
    return -1;
  }
  if(arguments.resultfile==NULL){
    fprintf(stderr, "Please provide a name for a result file\n");
    return -1;
  }

  FILE *result_file = NULL;
  if(arguments.resultfile)
    result_file = open_file(arguments.resultfile);

  int nb_exp, largest_size;

  experiment_t *experiments = parse_experiment_file(names, arguments.sizefile, &nb_exp, &largest_size, -1, 300000, 6);

  printf("nb_exp=%d, largest_size=%d\n", nb_exp, largest_size);

  size_t max_size = (size_t)largest_size*(size_t)largest_size;
  int max_M = get_max_size(experiments, nb_exp, 0);
  int max_N = get_max_size(experiments, nb_exp, 1);
  int max_K = get_max_size(experiments, nb_exp, 2);
  printf("max(M)=%d | max(N)=%d | max(K)=%d\n", max_M, max_N, max_K);
  printf("Alloc size: was %.2e bytes\n", (double)(max_size)*sizeof(double)*3);
  double alloc_size = (double)max_M*(double)max_K;
  alloc_size += (double)max_K*(double)max_N;
  alloc_size += (double)max_M*(double)max_N;
  printf("Alloc size: now %.2e bytes\n", alloc_size*sizeof(double));

  matrix_A = allocate_matrix(max_M, max_K);
  matrix_B = allocate_matrix(max_K, max_N);
  matrix_C = allocate_matrix(max_M, max_N);

  //Init time
  base_time=get_time();
  for(int i = 0; i < nb_loops; i++) {
    for(int j = 0; j < nb_exp; j++) {
      test_op(result_file, &experiments[j], NB_RUNS, base_time, 0);
    }
  }
  for(int j = 0; j < nb_exp; j++) {
    test_op(result_file, &experiments[j], NB_RUNS, base_time, 1);
  }
  for(int i = 0; i < nb_loops; i++) {
    for(int j = 0; j < nb_exp; j++) {
      test_op(result_file, &experiments[j], NB_RUNS, base_time, 0);
    }
  }

  fflush(result_file);
  fclose(result_file);
  free(experiments);
  free(matrix_A);
  free(matrix_B);
  free(matrix_C);
  return 0;
}


/* roots/utility.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007 Reid Priedhorsky, Brian Gough
 *
 * This program 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.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

/* utility.c -- various root finding utility routines */

/* config headers */
#include <config.h>

/* standard headers */
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <float.h>

/* gsl headers */
#include <gsl/gsl_errno.h>
#include <gsl/gsl_roots.h>

/* roots headers */
#include "roots.h"

/* Validate arguments common to gsl_root_bisection and gsl_root_falsepos.
   Return GSL_SUCCESS if all arguments are okay, complain appropriately (i.e.
   call GSL_ERROR and return GSL_FAILURE) otherwise. */
int
_gsl_root_validate_bfp_args (double *root, double (*f) (double),
                             double *lower_bound,
                             double *upper_bound, double epsrel,
                             double epsabs, unsigned int max_iterations,
                             double max_deltay)
{
  /* Is the maximum delta-y too small? */
  if (max_deltay < GSL_ROOT_MIN_MAX_DELTAY)
    GSL_ERROR ("maximum delta-y negative, zero, or too small", GSL_EBADTOL);

  /* Did the user give a lower bound that not less than the upper bound? */
  if (*lower_bound >= *upper_bound)
    GSL_ERROR ("lower bound larger than upper_bound", GSL_EINVAL);

  /* The rest of the arguments are common. */
  return _gsl_root_validate_args (root, f, lower_bound, upper_bound,
                                  epsrel, epsabs, max_iterations);
}

/* Validate arguments commond to gsl_root_secant and gsl_root_newtons. Return
   GSL_SUCCESS if all arguments are okay, complain appropriately (i.e. call
   GSL_ERROR and return GSL_FAILURE) otherwise. */
int
_gsl_root_validate_sn_args (double *root, double (*f) (double),
                            double *guess1,
                            double *guess2, double epsrel,
                            double epsabs, unsigned int max_iterations,
                            double max_step_size)
{
  /* Is the maximum step size ridiculous? */
  if (max_step_size <= 0.0)
    GSL_ERROR ("maximum step size <= 0", GSL_EBADTOL);

  /* The rest of the arguments are common. */
  return _gsl_root_validate_args (root, f, guess1, guess2, epsrel,
                                  epsabs, max_iterations);
}

/* Validate the arguments common to all four low level functions. Return
   GSL_SUCCESS if all of the following arguments hold true, call GSL_ERROR and
   return GSL_FAILURE otherwise.

   * No pointer arguments are null.
   * The maximum number of iterations is non-zero.
   * Relative and absolute error are non-negative.
   * The relative error is not too small. */
int
_gsl_root_validate_args (double *root, double (*f) (double), double *where1,
                         double *where2, double epsrel,
                         double epsabs, unsigned int max_iterations)
{
  /* Are any pointers null? */
  if ((root == NULL) || (f == NULL) || (where1 == NULL)
      || (where2 == NULL))
    GSL_ERROR ("pointer argument null", GSL_EINVAL);
  /* Did the user tell us to do no iterations? */
  if (max_iterations == 0)
    GSL_ERROR ("maximum iterations 0", GSL_EINVAL);
  /* Did the user try to pawn a negative tolerance off on us? */
  if (epsrel < 0.0 || epsabs < 0.0)
    GSL_ERROR ("relative or absolute tolerance negative", GSL_EBADTOL);
  /* Is the relative error too small? */
  if (epsrel < GSL_DBL_EPSILON * GSL_ROOT_EPSILON_BUFFER)
    GSL_ERROR ("relative tolerance too small", GSL_EBADTOL);

  /* All is well. */
  return GSL_SUCCESS;
}

/* Verify that the supplied interval is guaranteed by the Intermediate Value
   Theorem to contain a root and complain appropriately if it is not. (It
   might actually be a discontinuity, but we check for that elsewhere.) Return
   GSL_SUCCESS if all is well, otherwise, call GSL_ERROR and return
   GSL_FAILURE. */
int
_gsl_root_ivt_guar (double (*f) (double), double lower_bound,
                    double upper_bound)
{
  double fl, fu;

  _BARF_FPCALL (f, lower_bound, fl);
  _BARF_FPCALL (f, upper_bound, fu);

  if (fl * fu > 0.0)
    {
      GSL_ERROR ("interval not guaranteed to contain a root", GSL_EINVAL);
    }
  else
    {
      return GSL_SUCCESS;
    }
}

/* Check if the user has the root but doesn't know it. If lower_bound or
   upper_bound is a root of f, or the interval [upper_bound, lower_bound] is
   within tolerance, return 1 and set *root appropriately. Otherwise, return
   0. On error, call GSL_ERROR and return GSL_FAILURE. Only worry about
   max_deltay if it is greater than 0 (which implies that you should validate
   arguments _before_ calling this function). */
int
_gsl_root_silly_user (double *root, double (*f) (double), double lower_bound,
                      double upper_bound, double epsrel,
                      double epsabs, double max_deltay)
{
  double fl, fu;

  /* Is lower_bound the root? */
  _BARF_FPCALL (f, lower_bound, fl);
  if (fl == 0.0)
    {
      *root = lower_bound;
      return 1;
    }

  /* Is upper_bound the root? */
  _BARF_FPCALL (f, upper_bound, fu);
  if (fu == 0.0)
    {
      *root = upper_bound;
      return 1;
    }

  /* Are lower_bound and upper_bound within tolerance? */
  _BARF_TOLS (lower_bound, upper_bound, 2 * epsrel, 2 * epsabs);
  if (max_deltay > 0.0)
    _BARF_DELTAY (fl, fu, max_deltay);
  if (_WITHIN_TOL (lower_bound, upper_bound, 2 * epsrel,
                   2 * epsabs))
    {
      *root = (lower_bound + upper_bound) / 2.0;
      return 1;
    }

  /* No? Bummer. */
  return 0;
}


/* ieee-utils/make_rep.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Brian Gough
 *
 * This program 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 2 of the License, or (at
 * your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <config.h>
#include <gsl/gsl_ieee_utils.h>

#include "endian.c"
#include "standardize.c"

static void sprint_nybble(int i, char *s) ;
static void sprint_byte(int i, char *s) ;
static int determine_ieee_type (int non_zero, int exponent, int max_exponent);

/* For the IEEE float format the bits are found from the following
   masks,

   sign      = 0x80000000
   exponent  = 0x7f800000
   mantisssa = 0x007fffff

   For the IEEE double format the masks are,

   sign      = 0x8000000000000000
   exponent  = 0x7ff0000000000000
   mantissa  = 0x000fffffffffffff

   */

void
gsl_ieee_float_to_rep (const float * x, gsl_ieee_float_rep * r)
{
  int e, non_zero;

  union {
    float f;
    struct  {
      unsigned char byte[4] ;
    } ieee ;
  } u;

  u.f = *x ;

  if (little_endian_p())
    make_float_bigendian(&(u.f)) ;

  /* note that r->sign is signed, u.ieee.byte is unsigned */

  if (u.ieee.byte[3]>>7)
    {
      r->sign = 1 ;
    }
  else
    {
      r->sign = 0 ;
    }

  e = (u.ieee.byte[3] & 0x7f) << 1 | (u.ieee.byte[2] & 0x80)>>7 ;

  r->exponent = e - 127 ;

  sprint_byte((u.ieee.byte[2] & 0x7f) << 1,r->mantissa) ;
  sprint_byte(u.ieee.byte[1],r->mantissa + 7) ;
  sprint_byte(u.ieee.byte[0],r->mantissa + 15) ;

  r->mantissa[23] = '\0' ;

  non_zero = u.ieee.byte[0] || u.ieee.byte[1] || (u.ieee.byte[2] & 0x7f);

  r->type = determine_ieee_type (non_zero, e, 255) ;
}

void
gsl_ieee_double_to_rep (const double * x, gsl_ieee_double_rep * r)
{

  int e, non_zero;

  union
  {
    double d;
    struct  {
      unsigned char byte[8];
    } ieee ;
  } u;

  u.d= *x ;

  if (little_endian_p())
    make_double_bigendian(&(u.d)) ;

  /* note that r->sign is signed, u.ieee.byte is unsigned */

  if (u.ieee.byte[7]>>7)
    {
      r->sign = 1 ;
    }
  else
    {
      r->sign = 0 ;
    }

  e =(u.ieee.byte[7] & 0x7f)<<4 ^ (u.ieee.byte[6] & 0xf0)>>4 ;

  r->exponent = e - 1023 ;

  sprint_nybble(u.ieee.byte[6],r->mantissa) ;
  sprint_byte(u.ieee.byte[5],r->mantissa + 4) ;
  sprint_byte(u.ieee.byte[4],r->mantissa + 12) ;
  sprint_byte(u.ieee.byte[3],r->mantissa + 20) ;
  sprint_byte(u.ieee.byte[2],r->mantissa + 28) ;
  sprint_byte(u.ieee.byte[1],r->mantissa + 36) ;
  sprint_byte(u.ieee.byte[0],r->mantissa + 44) ;

  r->mantissa[52] = '\0' ;

  non_zero = (u.ieee.byte[0] || u.ieee.byte[1] || u.ieee.byte[2]
              || u.ieee.byte[3] || u.ieee.byte[4] || u.ieee.byte[5]
              || (u.ieee.byte[6] & 0x0f)) ;

  r->type = determine_ieee_type (non_zero, e, 2047) ;
}

/* A table of character representations of nybbles */

static char nybble[16][5]={ /* include space for the \0 */
  "0000", "0001", "0010", "0011",
  "0100", "0101", "0110", "0111",
  "1000", "1001", "1010", "1011",
  "1100", "1101", "1110", "1111"
}  ;

static void
sprint_nybble(int i, char *s)
{
  char *c ;
  c=nybble[i & 0x0f ];
  *s=c[0] ;  *(s+1)=c[1] ;  *(s+2)=c[2] ;  *(s+3)=c[3] ;
}

static void
sprint_byte(int i, char *s)
{
  char *c ;
  c=nybble[(i & 0xf0)>>4];
  *s=c[0] ;  *(s+1)=c[1] ;  *(s+2)=c[2] ;  *(s+3)=c[3] ;
  c=nybble[i & 0x0f];
  *(s+4)=c[0] ;  *(s+5)=c[1] ;  *(s+6)=c[2] ;  *(s+7)=c[3] ;
}

static int
determine_ieee_type (int non_zero, int exponent, int max_exponent)
{
  if (exponent == max_exponent)
    {
      if (non_zero)
        {
          return GSL_IEEE_TYPE_NAN ;
        }
      else
        {
          return GSL_IEEE_TYPE_INF ;
        }
    }
  else if (exponent == 0)
    {
      if (non_zero)
        {
          return GSL_IEEE_TYPE_DENORMAL ;
        }
      else
        {
          return GSL_IEEE_TYPE_ZERO ;
        }
    }
  else
    {
      return GSL_IEEE_TYPE_NORMAL ;
    }
}


/*
Ballistic: a software to benchmark ballistic models.

AUTHORS: Javier Burguete Tolosa.

Copyright 2018, AUTHORS.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice,
    this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/

/**
 * \file ballistic.c
 * \brief Source file with the main function.
 * \author Javier Burguete Tolosa.
 * \copyright Copyright 2018.
 */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <gsl/gsl_rng.h>
#include <libxml/parser.h>
#include <glib.h>
#include "config.h"
#include "utils.h"
#include "equation.h"
#include "method.h"
#include "runge-kutta.h"
#include "multi-steps.h"

#define DEBUG_BALLISTIC 0       ///< macro to debug the ballistic functions.

long double convergence_factor;
///< convergence factor.
unsigned int ntrajectories;
///< number of projectil trajectories to calculate.
unsigned int convergence;
///< number of convergence steps.

/**
 * Function to read the basic input data.
 *
 * \return 1 on success, 0 on error.
 */
static inline int
convergence_read_xml (xmlNode * node)      ///< XML node.
{
	const char *message[] = {
		"Bad trajectories number",
		"Bad convergence steps",
		"Bad convergence factor"
	};
	int e, error_code;

#if DEBUG_BALLISTIC
  fprintf (stderr, "convergence_read_xml: start\n");
#endif
  ntrajectories = xml_node_get_uint (node, XML_TRAJECTORIES, &error_code);
  if (error_code || !ntrajectories)
	  {
			e = 0;
      goto fail;
		}
  convergence = xml_node_get_uint (node, XML_CONVERGENCE, &error_code);
  if (error_code || !convergence)
	  {
			e = 1;
      goto fail;
		}
  convergence_factor = xml_node_get_float (node, XML_FACTOR, &error_code);
  if (error_code || convergence_factor <= 0.)
	  {
			e = 2;
      goto fail;
		}
#if DEBUG_BALLISTIC
  fprintf (stderr, "convergence_read_xml: success\n");
  fprintf (stderr, "convergence_read_xml: end\n");
#endif
  return 1;

fail:
	error_add (message[e]);
#if DEBUG_BALLISTIC
  fprintf (stderr, "convergence_read_xml: error\n");
  fprintf (stderr, "convergence_read_xml: end\n");
#endif
  return 0;
}

/**
 * Function to open a numerical method on a XML node.
 *
 * \return 0 on error, 1 on Runge-Kutta method, 2 on multi-steps method.
 */
static inline int
method_open_xml (MultiSteps * ms,
		             RungeKutta * rk,
								 xmlNode * node)
{
	char *message[] = {
    "No numerical method XML node",
    "Bad Runge-Kutta data",
    "Bad multi-steps data",
    "Unknown numerical method"
	};
	int e, m;

#if DEBUG_BALLISTIC
  fprintf (stderr, "method_open_xml: start\n");
#endif
  if (!node)
    {
      e = 0;
      goto fail;
    }
  if (!xmlStrcmp (node->name, XML_RUNGE_KUTTA))
    {
      if (!runge_kutta_read_xml (rk, node))
        {
          e = 1;
          goto fail;
        }
      runge_kutta_init_variables (rk);
			m = 1;
    }
  else if (!xmlStrcmp (node->name, XML_MULTI_STEPS))
    {
      if (!multi_steps_read_xml (ms, node))
        {
          e = 2;
          goto fail;
        }
      multi_steps_init_variables (ms);
			m = 2;
    }
  else
    {
      e = 3;
      goto fail;
    }
#if DEBUG_BALLISTIC
  fprintf (stderr, "method_open_xml: success\n");
  fprintf (stderr, "method_open_xml: end\n");
#endif
	return m;

fail:
	error_add (message[e]);
#if DEBUG_BALLISTIC
  fprintf (stderr, "method_open_xml: error\n");
  fprintf (stderr, "method_open_xml: end\n");
#endif
	return 0;
}

/**
 * Function to show the error message.
 */
static void
show_error ()
{
  printf ("ERROR!\n%s", error_message);
  g_free (error_message);
	error_message = NULL;
}

/**
 * Function to print the solution values.
 */
static void
print_solution (char *label,    ///< label.
                long double *r0,        ///< position vector.
                long double *r1)        ///< velocity vector.
{
  printf ("%s\n", label);
  printf ("x = %.19Le\n", r0[0]);
  printf ("y = %.19Le\n", r0[1]);
  printf ("z = %.19Le\n", r0[2]);
  printf ("vx = %.19Le\n", r1[0]);
  printf ("vy = %.19Le\n", r1[1]);
  printf ("vz = %.19Le\n", r1[2]);
}

/**
 * Function to print the numerical errors.
 */
static void
print_error (char *label,       ///< label.
             long double *r1,   ///< numerical solution vector.
             long double *r2)   ///< analytical solution vector.
{
  printf ("%s = %.19Le\n", label, distance (r1, r2));
}

/**
 * Function to perform a convergence analysis of a method.
 *
 * \return 0 on success, error code on error.
 */
static inline int
convergence_run (xmlNode * node,   ///< XML node.
                 char *output)  ///< results file name.
{
	const char *message[] = {
		NULL,
		"Bad convergence data",
		"No equation XML node",
		"Unknown numerical method",
		"Bad numerical method data"
	};
  MultiSteps ms[1];
  RungeKutta rk[1];
  Equation eq[1];
  Method *m;
  gsl_rng *rng;
  FILE *file;
  long double sr0[3], sr1[3];
  long double t, l0r0, l2r0, l0r1, l2r1, e;
	int er, me;
  unsigned int i, j;
#if DEBUG_BALLISTIC
  fprintf (stderr, "convergence_run: start\n");
#endif
	er = 0;
  if (!convergence_read_xml (node))
	  {
      er = 1;
			goto fail;
		}
#if DEBUG_BALLISTIC
  fprintf (stderr, "convergence_run: initing equation\n");
#endif
	node = node->children;
	if (!node)
	  {
			er = 2;
			goto fail;
		}
  if (!equation_read_xml (eq, node, 0))
	  {
			er = 3;
      goto fail;
		}
#if DEBUG_BALLISTIC
  fprintf (stderr, "convergence_run: initing method\n");
#endif
	node = node->next;
	me = method_open_xml (ms, rk, node);
	switch (me)
	  {
		case 1:
      m = RUNGE_KUTTA_METHOD (rk);
			break;
		case 2:
      m = MULTI_STEPS_METHOD (ms);
			break;
		default:
			er = 4;
			goto fail;
		}
  rng = gsl_rng_alloc (gsl_rng_taus2);
  file = fopen (output, "w");
  for (j = 0; j < convergence; ++j)
    {
      gsl_rng_set (rng, 0l);
      nevaluations = 0l;
      l0r0 = l2r0 = l0r1 = l2r1 = 0.L;
      for (i = 0; i < ntrajectories; ++i)
        {
#if DEBUG_BALLISTIC
          fprintf (stderr, "convergence_run: initing equation data\n");
#endif
          equation_init (eq, rng);
#if DEBUG_BALLISTIC
          fprintf (stderr, "convergence_run: initing variables\n");
#endif
          equation_solution (eq, r0, r1, 0.);
          equation_acceleration (eq, r0, r1, r2, 0.L);
#if DEBUG_BALLISTIC
          fprintf (stderr, "convergence_run: running\n");
#endif
          if (me == 1)
            t = runge_kutta_run (rk, eq);
          else
            t = multi_steps_run (ms, eq);
#if DEBUG_BALLISTIC
          fprintf (stderr, "convergence_run: solutions\n");
          print_solution ("Numerical solution", r0, r1);
          printf ("Time = %.19Le\n", t);
#endif
          switch (eq->land_type)
            {
            case 0:
              equation_solution (eq, sr0, sr1, eq->tf);
              break;
            default:
              t = equation_solve (eq, sr0, sr1);
            }
#if DEBUG_BALLISTIC
          print_solution ("Analytical solution", sr0, sr1);
          printf ("Time = %.19Le\n", t);
          print_error ("Position error", r0, sr0);
          print_error ("Velocity error", r1, sr1);
#endif
          e = distance (r0, sr0);
          l0r0 = fmaxl (l0r0, e);
          l2r0 += e * e;
          e = distance (r1, sr1);
          l0r1 = fmaxl (l0r1, e);
          l2r1 += e * e;

        }
#if DEBUG_BALLISTIC
      fprintf (stderr, "convergence_run: saving results\n");
#endif
      l2r0 = sqrtl (l2r0 / ntrajectories);
      l2r1 = sqrtl (l2r1 / ntrajectories);
      fprintf (file, "%lu %.19Le %.19Le %.19Le %.19Le %.19Le %.19Le\n",
               nevaluations, l0r0, l2r0, l0r1, l2r1, kt, m->emt);
      switch (eq->size_type)
        {
        case 0:
          dt *= convergence_factor;
          break;
        default:
          kt *= convergence_factor;
        }
      m->emt *= convergence_factor;
      if (me == 2)
        RUNGE_KUTTA_METHOD (MULTI_STEPS_RUNGE_KUTTA (ms))->emt
          *= convergence_factor;
    }
  fclose (file);
  printf ("Time = %.19Le\n", t);
#if DEBUG_BALLISTIC
  fprintf (stderr, "convergence_run: deleting method\n");
#endif
  if (me == 1)
    runge_kutta_delete (rk);
  else
    multi_steps_delete (ms);
  gsl_rng_free (rng);
fail:
	if (er)
	  {
			error_add (message[er]);
		  show_error ();
		}
#if DEBUG_BALLISTIC
  fprintf (stderr, "convergence_run: end\n");
#endif
  return 0;
}

/**
 * Function to calculate a ballistic trajectory.
 *
 * \return 0 on success, error code on error.
 */
static inline int
ballistic_run (xmlNode * node)    ///< XML node.
{
  const char *message[] = {
    NULL,
    "Unable to open the XML root element",
    "Bad XML file",
    "No equation XML node",
    "Bad equation data",
  };
  MultiSteps ms[1];
  RungeKutta rk[1];
  Equation eq[1];
  long double sr0[3], sr1[3];
  long double t;
  int e, m;
#if DEBUG_BALLISTIC
  fprintf (stderr, "ballistic_run: start\n");
#endif
  e = 0;
  node = node->children;
  if (!node)
    {
      e = 3;
      goto end;
    }
  if (!equation_read_xml (eq, node, 1))
    {
      e = 4;
      goto end;
    }
  node = node->next;
  if (!node)
    {
      e = 5;
      goto end;
    }
  if (!xmlStrcmp (node->name, XML_RUNGE_KUTTA))
    {
      if (!runge_kutta_read_xml (rk, node))
        {
          e = 6;
          goto end;
        }
      runge_kutta_init_variables (rk);
			m = 1;
    }
  else if (!xmlStrcmp (node->name, XML_MULTI_STEPS))
    {
      if (!multi_steps_read_xml (ms, node))
        {
          e = 7;
          goto end;
        }
      multi_steps_init_variables (ms);
			m = 2;
    }
  else
    {
      e = 8;
      goto end;
    }
  nevaluations = 0l;
#if DEBUG_BALLISTIC
  fprintf (stderr, "ballistic_run: initing variables\n");
#endif
  equation_solution (eq, r0, r1, 0.);
  equation_acceleration (eq, r0, r1, r2, 0.L);
#if DEBUG_BALLISTIC
  fprintf (stderr, "ballistic_run: running\n");
#endif
  switch (m)
	  {
		case 1:
			t = runge_kutta_run (rk, eq);
			break;
		default:
			t = multi_steps_run (ms, eq);
		}
#if DEBUG_BALLISTIC
  fprintf (stderr, "ballistic_run: solutions\n");
#endif
  print_solution ("Numerical solution", r0, r1);
  printf ("Time = %.19Le\n", t);
  switch (eq->land_type)
    {
    case 0:
      equation_solution (eq, sr0, sr1, eq->tf);
      break;
    default:
      t = equation_solve (eq, sr0, sr1);
    }
  print_solution ("Analytical solution", sr0, sr1);
  printf ("Time = %.19Le\n", t);
  print_error ("Position error", r0, sr0);
  print_error ("Velocity error", r1, sr1);
  printf ("Time = %.19Le\n", t);
#if DEBUG_BALLISTIC
  fprintf (stderr, "ballistic_run: deleting method\n");
#endif
  switch (m)
	  {
		case 1:
      runge_kutta_delete (rk);
			break;
		default:
      multi_steps_delete (ms);
		}
end:
#if DEBUG_BALLISTIC
  fprintf (stderr, "ballistic_run: end\n");
#endif
	if (e)
    error_add (message[e]);
  return e;
}

/**
 * Main function
 *
 * \return 0 on success, error code otherwise.
 */
int
main (int argn,                 ///< number of arguments.
      char **argc)              ///< array of argument chars.
{
	const char *message[] = {
		NULL,
    "The syntax is:\n./ballistic input_file output_file\n",
		"Unable to open the input file",
		"Bad XML root element",
	  "Bad ballistic run",
	  "Bad convergence run",
		"Unknown model"
	};
  xmlDoc *doc;
	xmlNode *node;
  int e;
#if DEBUG_BALLISTIC
  fprintf (stderr, "main: start\n");
#endif
	e = 0;
	if (argn != 3)
	  {
			e = 1;
			goto end;
		}
  xmlKeepBlanksDefault (0);
  doc = xmlParseFile (argc[1]);
  if (!doc)
	  {
			e = 2;
			goto end;
		}
  node = xmlDocGetRootElement (doc);
  if (!node)
    {
      e = 3;
      goto end;
    }
  if (!xmlStrcmp (node->name, XML_BALLISTIC))
	  {
      e = ballistic_run (node);
			if (e)
			  {
					e = 4;
					goto end;
				}
		}
	else if (!xmlStrcmp (node->name, XML_CONVERGENCE))
	  {
      e = convergence_run (node, argc[2]);
			if (e)
			  {
					e = 5;
					goto end;
				}
		}
	else
	  e = 6;
  xmlFreeDoc (doc);
end:
  if (e)
    {
			error_add (message[e]);
      show_error ();
    }
  return e;
}