// Copyright (c) 2015-2016, Massachusetts Institute of Technology
// Copyright (c) 2016-2017 Sandia Corporation
// Copyright (c) 2017 NTESS, LLC.

// This file is part of the Compressed Continuous Computation (C3) Library
// Author: Alex A. Gorodetsky
// Contact: alex@alexgorodetsky.com

// All rights reserved.

// 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.

// 3. Neither the name of the copyright holder nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission.

// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER 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.

//Code

#ifndef LINALG_H
#define LINALG_H

#ifdef __APPLE__
    #include <Accelerate/Accelerate.h>
    /* #include "/System/Library/Frameworks/Accelerate.framework/Versions/Current/Frameworks/vecLib.framework/Headers/clapack.h" */

    #define dgetri_(X, Y, Z, A , B, C, D ) \
            ( dgetri_( (__CLPK_integer *) X, Y, (__CLPK_integer *) Z, (__CLPK_integer *) A, \
                        B, (__CLPK_integer *)C , (__CLPK_integer *) D) )
    #define dgetrf_(X, Y, Z, A ,B, C ) \
            ( dgetrf_( (__CLPK_integer *) X,(__CLPK_integer *) Y, Z, (__CLPK_integer *) A, \
                       (__CLPK_integer *) B, (__CLPK_integer *)C ))
    #define dorgqr_(X,Y,Z,A,B,C,D,E,F) \
            ( dorgqr_( (__CLPK_integer *) X, (__CLPK_integer *) Y, (__CLPK_integer *) Z, A, \
                       (__CLPK_integer *) B, C , D, (__CLPK_integer *) E, (__CLPK_integer *)F) )
    #define dgeqrf_(X, Y, Z, A , B, C, D, E ) \
            ( dgeqrf_( (__CLPK_integer *) X,  (__CLPK_integer *)Y, Z, (__CLPK_integer *) A, \
                        B, C, (__CLPK_integer *) D, (__CLPK_integer *) E) )

    #define dorgrq_(X,Y,Z,A,B,C,D,E,F) \
            ( dorgrq_( (__CLPK_integer *) X, (__CLPK_integer *) Y, (__CLPK_integer *) Z, A, \
                       (__CLPK_integer *) B, C , D, (__CLPK_integer *) E, (__CLPK_integer *)F) )
    #define dgerqf_(X, Y, Z, A , B, C, D, E ) \
            ( dgerqf_( (__CLPK_integer *) X,  (__CLPK_integer *)Y, Z, (__CLPK_integer *) A, \
                        B, C, (__CLPK_integer *) D, (__CLPK_integer *) E) )

    #define dgesdd_(X,Y,Z,A,B,C,D,E,F,G,H,I,J,K) \
            ( dgesdd_(X, (__CLPK_integer *)Y, (__CLPK_integer *)Z, A, (__CLPK_integer *) B,\
                C,D,(__CLPK_integer *) E, F, (__CLPK_integer *) G, H, (__CLPK_integer *) I,\
                (__CLPK_integer *) J, (__CLPK_integer *) K ) )

    #define dgebal_(X, Y, Z, A , B, C, D, E ) \
            ( dgebal_( X,  (__CLPK_integer *)Y, Z, (__CLPK_integer *) A, \
                   (__CLPK_integer *) B, (__CLPK_integer *)C, D, (__CLPK_integer *) E) )

    #define dpotrf_(X,Y,Z,A,B) \
            ( dpotrf_(X, (__CLPK_integer *)Y, Z, (__CLPK_integer *) A, (__CLPK_integer *) B ))

    #define dpotri_(X,Y,Z,A,B) \
            ( dpotri_(X, (__CLPK_integer *)Y, Z, (__CLPK_integer *) A, (__CLPK_integer *) B ))

    #define dtrtri_(X,Y,Z,A,B,C) \
            ( dtrtri_(X,Y, (__CLPK_integer *)Z, A, (__CLPK_integer *) B, (__CLPK_integer *) C ))

    #define dgesv_(X,Y,Z,A,B,C,D,E) \
            ( dgesv_((__CLPK_integer *)X, (__CLPK_integer *)Y, Z, (__CLPK_integer *) A, (__CLPK_integer *) B, \
                C, (__CLPK_integer *) D, (__CLPK_integer *) E))

    #define dhseqr_(X, Y, Z, A , B, C, D, E,F,G,H,I,J,K ) \
            ( dhseqr_( X, Y, (__CLPK_integer *)Z, (__CLPK_integer *)A, \
                   (__CLPK_integer *) B, C, (__CLPK_integer *)D, E, F, G, (__CLPK_integer *) H, \
                   I, (__CLPK_integer *) J, (__CLPK_integer *)K ) )

    #define dsyev_(A,B,C,D,E,F,G,H,J) \
        ( dsyev_(A,B,(__CLPK_integer *) C,D,(__CLPK_integer *) E,F, \
            G, (__CLPK_integer *) H, (__CLPK_integer *) J) )

    #define dgeev_(X, Y, Z, A , B, C, D, E,F,G,H,I,J,K ) \
            ( dgeev_( X, Y, (__CLPK_integer *)Z, A, \
                   (__CLPK_integer *) B, C, D, E, (__CLPK_integer *) F, G, (__CLPK_integer *) H, \
                   I, (__CLPK_integer *) J, (__CLPK_integer *)K ) )

    #define dstegr_(X,Y,Z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q) \
            ( dstegr_(X,Y,(__CLPK_integer *)Z,A,B,C,D,\
                   (__CLPK_integer *)E,(__CLPK_integer *)F,G,(__CLPK_integer *)H, \
                   I,J,(__CLPK_integer *)K,(__CLPK_integer *)L,M,(__CLPK_integer *)N, \
                   (__CLPK_integer *)O,(__CLPK_integer *)P,(__CLPK_integer *)Q))

    #define dgelsd_(X,Y,Z,A,B,C,D,E,F,G,H,I,J,K)                         \
            ( dgelsd_( (__CLPK_integer *)X, (__CLPK_integer *)Y, (__CLPK_integer *)Z, \
                       (__CLPK_doublereal *)A, (__CLPK_integer *)B, (__CLPK_doublereal *)C,  \
                       (__CLPK_integer *)D, (__CLPK_doublereal *) E, \
	                   (__CLPK_doublereal *)F, (__CLPK_integer *)G, (__CLPK_doublereal *)H, \
                       (__CLPK_integer *)I, (__CLPK_integer *)J, (__CLPK_integer *)K))
#else
    /* #include <gsl/gsl_cblas.h> */
    #include <cblas.h>

void dgetri_(int * X, double *Y, int * Z, int * A, double *B, int *C , int * D);
void dgetrf_(int * X,int * Y, double*Z, int * A, int * B, int *C );
void dorgqr_(int * X, int * Y, int * Z, double *A,int * B, double *C , double *D, int * E, int *F);
void dgeqrf_(int *X, int *Y, double *Z, int * A, double * B, double * C, int * D, int * E);
void dgeev_(char * X, char *Y, int * Z, double *A, int * B, double * C, double *D, double *E,
            int * F, double *G, int * H, double *, int *, int *K);
void dgebal_(char *X,  int *Y, double *Z, int * A, int * B, int *C, double *D, int * E);
void dhseqr_(char *X, char *Y, int *Z, int *A,int * B, double *C, int *D,
             double *E, double *F, double *G, int * H, double *, int *, int *K );
void dorgrq_(int * X, int * Y, int * Z, double *A,int * B, double *C , double *D, int * E, int *F);
void dgerqf_(int * X,  int *Y, double *Z, int * A, double *B, double *C, int * D, int * E);
void dgesdd_(char *X, int * Y, int *Z, double *A, int * B,double * C,double * D,
             int * E,double * F, int * G, double *H, int *, int *, int * K );
void dstev_(char *X,  int *Y, double *Z, double *A, double *B, int *C, double *D, int * E);
void dsyev_(char *A,char *B,int * C,double *D,int * E,double *F, double *G, int* H, int * J);
void dpotrf_(char *X, int*Y, double *Z, int * A, int * B );
void dpotri_(char *X, int*Y, double *Z, int * A, int* B);
void dtrtri_(char *X,char*Y, int *Z, double *A, int * B, int * C);
void dgesv_(int *X, int *Y, double*Z, int * A, int * B,double*C, int * D, int * E);
void dgelsd_(int *X, int *Y, int *Z, double *A, int *B, double *C,
             int *D, double * E, double *F, int *G, double *H, int *,int *, int *K);

#endif

#include "matrix_util.h"

void c3linalg_multiple_vec_mat(size_t, size_t, size_t, const double *, size_t,
                               const double *, size_t, double *,size_t);
void c3linalg_multiple_mat_vec(size_t, size_t, size_t, const double *, size_t,
                               const double *, size_t, double *,size_t);
int qr(size_t, size_t, double *, size_t);
void rq_with_rmult(size_t, size_t, double *, size_t, size_t, size_t, double *, size_t);
void svd(size_t, size_t, size_t, double *, double *, double *, double *);
size_t truncated_svd(size_t, size_t, size_t, double *, double **, double **, double **, double);
size_t pinv(size_t, size_t, size_t, double *, double *, double);

double norm2(double *, int);
double norm2diff(double *, double *, int);
double mean(double *, size_t);
double mean_size_t(size_t *, size_t);

struct mat * kron(const struct mat *, const struct mat *);
void kron_col(int, int, double *, int, int, int, double *, int, double *, int);
void vec_kron(size_t, size_t, double *, size_t, size_t, size_t,
        double *, size_t, double *, double, double *);
void vec_kronl(size_t, size_t, double *, size_t, size_t, size_t,
        double *, size_t, long double *, double, long double *);

// decompositions
struct fiber_list{
    size_t index;
    double * vals;
    struct fiber_list * next;
};
struct fiber_info{
    size_t nfibers;
    struct fiber_list * head;
};
void AddFiber(struct fiber_list **, size_t, double *, size_t);
int IndexExists(struct fiber_list *, size_t);
double * getIndex(struct fiber_list *, size_t);
void DeleteFiberList(struct fiber_list **);

struct sk_decomp {
    size_t n;
    size_t m;
    size_t rank;
    size_t * rows_kept;
    size_t * cols_kept;
    size_t cross_rank;
    double * cross_inv;
    struct fiber_info * row_vals;
    struct fiber_info * col_vals;
    int success;
};

void init_skf(struct sk_decomp **, size_t, size_t, size_t);
void sk_decomp_to_full(struct sk_decomp *, double *);
void free_skf(struct sk_decomp **);

/* int comp_pivots(const double *, int, int, int *); */
int maxvol_rhs(const double *, size_t, size_t, size_t *, double *); //
int skeleton(double *, size_t, size_t, size_t, size_t *, size_t *, double);
int skeleton_func(double (*A)(int,int, int, void*), void *, size_t,
                size_t, size_t, size_t *, size_t *, double);
int
skeleton_func2(int (*Ap)(double *, double, size_t, size_t, double *,
                void *),
                   void *, struct sk_decomp **,  double *, double *,
                   double);
void linear_ls(size_t, size_t, double *, double *, double *);
#endif


#pragma once

#include <gsl/gsl>
#include <fmt/format.h>

#include "temple_enums.h"

// Define objHndl as a struct that contains just the handle value
#pragma pack(push, 1)
struct objHndl {
	uint64_t handle;

	explicit operator bool() const {
		return !!handle;
	}

	objHndl& operator=(uint64_t handle) {
		this->handle = handle;
		return *this;
	}

	uint32_t GetHandleLower() const {
		return (uint32_t)(handle & 0xffffffff);
	}

	uint32_t GetHandleUpper() const {
		return (uint32_t)((handle >> 32) & 0xffffffff);
	}

	static objHndl FromUpperAndLower(uint32_t upper, uint32_t lower) {
		return{
			(((uint64_t) upper) << 32)
			| (((uint64_t)lower) & 0xFFFFFFFF)
		};
	}

	static const objHndl null;

};
#pragma pack(pop)

/**
 * Compares two object handles for equality. They are equal if their
 * handle value is equal.
 */
inline bool operator ==(const objHndl &a, const objHndl &b) {
	return a.handle == b.handle;
}
inline bool operator !=(const objHndl &a, const objHndl &b) {
	return a.handle != b.handle;
}
inline bool operator <(const objHndl &a, const objHndl &b) {
	return a.handle < b.handle;
}
inline bool operator >(const objHndl &a, const objHndl &b) {
	return a.handle > b.handle;
}

void format_arg(fmt::BasicFormatter<char> &f, const char *&format_str, const objHndl &s);

namespace std {
	template <> struct hash<objHndl> {
		size_t operator()(const objHndl &x) const {
			return std::hash<uint64_t>()(x.handle);
		}
	};
}

typedef uint32_t _fieldIdx;
typedef uint32_t _fieldSubIdx;
typedef uint32_t _mapNum;
typedef uint32_t _key;

#pragma pack(push, 8)

enum class ObjectIdKind : uint16_t {
	Null = 0,
	Prototype = 1,
	Permanent = 2,
	Positional = 3,
	Handle = 0xFFFE,
	Blocked = 0xFFFF
};

union ObjectIdBody {
	GUID guid;
	uint32_t protoId;
	objHndl handle;
	struct {
		int x;
		int y;
		int tempId;
		int mapId;
	} pos;
};

struct ObjectId {
	ObjectIdKind subtype = ObjectIdKind::Null;
	int unk = 0;
	ObjectIdBody body;

	bool IsNull() const {
		return subtype == ObjectIdKind::Null;
	}

	bool IsPermanent() const {
		return subtype == ObjectIdKind::Permanent;
	}

	bool IsPrototype() const {
		return subtype == ObjectIdKind::Prototype;
	}

	bool IsPositional() const {
		return subtype == ObjectIdKind::Positional;
	}

	bool IsHandle() const {
		return subtype == ObjectIdKind::Handle;
	}

	bool IsBlocked() const {
		return subtype == ObjectIdKind::Blocked;
	}

	// Can this object id be persisted and later restored to a handle?
	bool IsPersistable() const {
		return IsNull() || IsPermanent() || IsPrototype() || IsPositional();
	}

	int GetPrototypeId() const {
		Expects(IsPrototype());
		return body.protoId;
	}

	objHndl GetHandle() const {
		Expects(IsHandle());
		return body.handle;
	}

	operator bool() const {
		return !IsNull();
	}

	bool operator ==(const ObjectId &other) const;

	std::string ToString() const;

	// Randomly generates a GUID and returns an object id that contains it
	static ObjectId CreatePermanent();

	// Creates a positional object id
	static ObjectId CreatePositional(int mapId, int tileX, int tileY, int tempId);

	// Creates a prototype object id
	static ObjectId CreatePrototype(uint16_t prototypeId);

	// Creates a null object id
	static ObjectId CreateNull() {
		ObjectId result;
		result.subtype = ObjectIdKind::Null;
		return result;
	}

	static ObjectId CreateHandle(objHndl handle);

};
#pragma pack(pop)

const int testSizeofObjectId = sizeof(ObjectId); // should be 24


/*
MPCOTool:
The Multi-Purposes Calibration and Optimization Tool. A software to perform
calibrations or optimizations of empirical parameters.

AUTHORS: Javier Burguete and Borja Latorre.

Copyright 2012-2019, 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 optimize.c
 * \brief Source file to define the optimization functions.
 * \authors Javier Burguete and Borja Latorre.
 * \copyright Copyright 2012-2019, all rights reserved.
 */
#define _GNU_SOURCE
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/param.h>
#include <gsl/gsl_rng.h>
#include <libxml/parser.h>
#include <libintl.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <json-glib/json-glib.h>
#ifdef G_OS_WIN32
#include <windows.h>
#elif !defined(__BSD_VISIBLE) && !defined(NetBSD)
#include <alloca.h>
#endif
#if HAVE_MPI
#include <mpi.h>
#endif
#include "genetic/genetic.h"
#include "utils.h"
#include "experiment.h"
#include "variable.h"
#include "input.h"
#include "optimize.h"

#define DEBUG_OPTIMIZE 0        ///< Macro to debug optimize functions.

/**
 * \def RM
 * \brief Macro to define the shell remove command.
 */
#ifdef G_OS_WIN32
#define RM "del"
#else
#define RM "rm"
#endif

Optimize optimize[1];           ///< Optimization data.
unsigned int nthreads_climbing;
///< Number of threads for the hill climbing method.

static void (*optimize_algorithm) ();
///< Pointer to the function to perform a optimization algorithm step.
static double (*optimize_estimate_climbing) (unsigned int variable,
                                             unsigned int estimate);
///< Pointer to the function to estimate the climbing.
static double (*optimize_norm) (unsigned int simulation);
///< Pointer to the error norm function.

/**
 * Function to write the simulation input file.
 */
static inline void
optimize_input (unsigned int simulation,        ///< Simulation number.
                char *input,    ///< Input file name.
                GMappedFile * stencil)  ///< Template of the input file name.
{
  char buffer[32], value[32];
  GRegex *regex;
  FILE *file;
  char *buffer2, *buffer3 = NULL, *content;
  gsize length;
  unsigned int i;

#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_input: start\n");
#endif

  // Checking the file
  if (!stencil)
    goto optimize_input_end;

  // Opening stencil
  content = g_mapped_file_get_contents (stencil);
  length = g_mapped_file_get_length (stencil);
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_input: length=%lu\ncontent:\n%s", length, content);
#endif
  file = g_fopen (input, "w");

  // Parsing stencil
  for (i = 0; i < optimize->nvariables; ++i)
    {
#if DEBUG_OPTIMIZE
      fprintf (stderr, "optimize_input: variable=%u\n", i);
#endif
      snprintf (buffer, 32, "@variable%u@", i + 1);
      regex = g_regex_new (buffer, (GRegexCompileFlags) 0, (GRegexMatchFlags) 0,
                           NULL);
      if (i == 0)
        {
          buffer2 = g_regex_replace_literal (regex, content, length, 0,
                                             optimize->label[i],
                                             (GRegexMatchFlags) 0, NULL);
#if DEBUG_OPTIMIZE
          fprintf (stderr, "optimize_input: buffer2\n%s", buffer2);
#endif
        }
      else
        {
          length = strlen (buffer3);
          buffer2 = g_regex_replace_literal (regex, buffer3, length, 0,
                                             optimize->label[i],
                                             (GRegexMatchFlags) 0, NULL);
          g_free (buffer3);
        }
      g_regex_unref (regex);
      length = strlen (buffer2);
      snprintf (buffer, 32, "@value%u@", i + 1);
      regex = g_regex_new (buffer, (GRegexCompileFlags) 0, (GRegexMatchFlags) 0,
                           NULL);
      snprintf (value, 32, format[optimize->precision[i]],
                optimize->value[simulation * optimize->nvariables + i]);

#if DEBUG_OPTIMIZE
      fprintf (stderr, "optimize_input: value=%s\n", value);
#endif
      buffer3 = g_regex_replace_literal (regex, buffer2, length, 0, value,
                                         (GRegexMatchFlags) 0, NULL);
      g_free (buffer2);
      g_regex_unref (regex);
    }

  // Saving input file
  fwrite (buffer3, strlen (buffer3), sizeof (char), file);
  g_free (buffer3);
  fclose (file);

optimize_input_end:
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_input: end\n");
#endif
  return;
}

/**
 * Function to parse input files, simulating and calculating the objective
 *   function.
 *
 * \return Objective function value.
 */
static double
optimize_parse (unsigned int simulation,        ///< Simulation number.
                unsigned int experiment)        ///< Experiment number.
{
  unsigned int i;
  double e;
  char buffer[512], input[MAX_NINPUTS][32], output[32], result[32], *buffer2,
    *buffer3, *buffer4;
  FILE *file_result;

#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_parse: start\n");
  fprintf (stderr, "optimize_parse: simulation=%u experiment=%u\n",
           simulation, experiment);
#endif

  // Opening input files
  for (i = 0; i < optimize->ninputs; ++i)
    {
      snprintf (&input[i][0], 32, "input-%u-%u-%u", i, simulation, experiment);
#if DEBUG_OPTIMIZE
      fprintf (stderr, "optimize_parse: i=%u input=%s\n", i, &input[i][0]);
#endif
      optimize_input (simulation, &input[i][0], optimize->file[i][experiment]);
    }
  for (; i < MAX_NINPUTS; ++i)
    strcpy (&input[i][0], "");
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_parse: parsing end\n");
#endif

  // Performing the simulation
  snprintf (output, 32, "output-%u-%u", simulation, experiment);
  buffer2 = g_path_get_dirname (optimize->simulator);
  buffer3 = g_path_get_basename (optimize->simulator);
  buffer4 = g_build_filename (buffer2, buffer3, NULL);
  snprintf (buffer, 512, "\"%s\" %s %s %s %s %s %s %s %s %s",
            buffer4, input[0], input[1], input[2], input[3], input[4],
            input[5], input[6], input[7], output);
  g_free (buffer4);
  g_free (buffer3);
  g_free (buffer2);
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_parse: %s\n", buffer);
#endif
  if (system (buffer) == -1)
    error_message = buffer;

  // Checking the objective value function
  if (optimize->evaluator)
    {
      snprintf (result, 32, "result-%u-%u", simulation, experiment);
      buffer2 = g_path_get_dirname (optimize->evaluator);
      buffer3 = g_path_get_basename (optimize->evaluator);
      buffer4 = g_build_filename (buffer2, buffer3, NULL);
      snprintf (buffer, 512, "\"%s\" %s %s %s",
                buffer4, output, optimize->experiment[experiment], result);
      g_free (buffer4);
      g_free (buffer3);
      g_free (buffer2);
#if DEBUG_OPTIMIZE
      fprintf (stderr, "optimize_parse: %s\n", buffer);
      fprintf (stderr, "optimize_parse: result=%s\n", result);
#endif
      if (system (buffer) == -1)
        error_message = buffer;
      file_result = g_fopen (result, "r");
      e = atof (fgets (buffer, 512, file_result));
      fclose (file_result);
    }
  else
    {
#if DEBUG_OPTIMIZE
      fprintf (stderr, "optimize_parse: output=%s\n", output);
#endif
      strcpy (result, "");
      file_result = g_fopen (output, "r");
      e = atof (fgets (buffer, 512, file_result));
      fclose (file_result);
    }

  // Removing files
#if !DEBUG_OPTIMIZE
  for (i = 0; i < optimize->ninputs; ++i)
    {
      if (optimize->file[i][0])
        {
          snprintf (buffer, 512, RM " %s", &input[i][0]);
          if (system (buffer) == -1)
            error_message = buffer;
        }
    }
  snprintf (buffer, 512, RM " %s %s", output, result);
  if (system (buffer) == -1)
    error_message = buffer;
#endif

  // Processing pending events
  if (show_pending)
    show_pending ();

#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_parse: end\n");
#endif

  // Returning the objective function
  return e * optimize->weight[experiment];
}

/**
 * Function to calculate the Euclidian error norm.
 *
 * \return Euclidian error norm.
 */
static double
optimize_norm_euclidian (unsigned int simulation)       ///< simulation number.
{
  double e, ei;
  unsigned int i;
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_norm_euclidian: start\n");
#endif
  e = 0.;
  for (i = 0; i < optimize->nexperiments; ++i)
    {
      ei = optimize_parse (simulation, i);
      e += ei * ei;
    }
  e = sqrt (e);
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_norm_euclidian: error=%lg\n", e);
  fprintf (stderr, "optimize_norm_euclidian: end\n");
#endif
  return e;
}

/**
 * Function to calculate the maximum error norm.
 *
 * \return Maximum error norm.
 */
static double
optimize_norm_maximum (unsigned int simulation) ///< simulation number.
{
  double e, ei;
  unsigned int i;
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_norm_maximum: start\n");
#endif
  e = 0.;
  for (i = 0; i < optimize->nexperiments; ++i)
    {
      ei = fabs (optimize_parse (simulation, i));
      e = fmax (e, ei);
    }
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_norm_maximum: error=%lg\n", e);
  fprintf (stderr, "optimize_norm_maximum: end\n");
#endif
  return e;
}

/**
 * Function to calculate the P error norm.
 *
 * \return P error norm.
 */
static double
optimize_norm_p (unsigned int simulation)       ///< simulation number.
{
  double e, ei;
  unsigned int i;
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_norm_p: start\n");
#endif
  e = 0.;
  for (i = 0; i < optimize->nexperiments; ++i)
    {
      ei = fabs (optimize_parse (simulation, i));
      e += pow (ei, optimize->p);
    }
  e = pow (e, 1. / optimize->p);
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_norm_p: error=%lg\n", e);
  fprintf (stderr, "optimize_norm_p: end\n");
#endif
  return e;
}

/**
 * Function to calculate the taxicab error norm.
 *
 * \return Taxicab error norm.
 */
static double
optimize_norm_taxicab (unsigned int simulation) ///< simulation number.
{
  double e;
  unsigned int i;
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_norm_taxicab: start\n");
#endif
  e = 0.;
  for (i = 0; i < optimize->nexperiments; ++i)
    e += fabs (optimize_parse (simulation, i));
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_norm_taxicab: error=%lg\n", e);
  fprintf (stderr, "optimize_norm_taxicab: end\n");
#endif
  return e;
}

/**
 * Function to print the results.
 */
static void
optimize_print ()
{
  unsigned int i;
  char buffer[512];
#if HAVE_MPI
  if (optimize->mpi_rank)
    return;
#endif
  printf ("%s\n", _("Best result"));
  fprintf (optimize->file_result, "%s\n", _("Best result"));
  printf ("error = %.15le\n", optimize->error_old[0]);
  fprintf (optimize->file_result, "error = %.15le\n", optimize->error_old[0]);
  for (i = 0; i < optimize->nvariables; ++i)
    {
      snprintf (buffer, 512, "%s = %s\n",
                optimize->label[i], format[optimize->precision[i]]);
      printf (buffer, optimize->value_old[i]);
      fprintf (optimize->file_result, buffer, optimize->value_old[i]);
    }
  fflush (optimize->file_result);
}

/**
 * Function to save in a file the variables and the error.
 */
static void
optimize_save_variables (unsigned int simulation,       ///< Simulation number.
                         double error)  ///< Error value.
{
  unsigned int i;
  char buffer[64];
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_save_variables: start\n");
#endif
  for (i = 0; i < optimize->nvariables; ++i)
    {
      snprintf (buffer, 64, "%s ", format[optimize->precision[i]]);
      fprintf (optimize->file_variables, buffer,
               optimize->value[simulation * optimize->nvariables + i]);
    }
  fprintf (optimize->file_variables, "%.14le\n", error);
  fflush (optimize->file_variables);
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_save_variables: end\n");
#endif
}

/**
 * Function to save the best simulations.
 */
static void
optimize_best (unsigned int simulation, ///< Simulation number.
               double value)    ///< Objective function value.
{
  unsigned int i, j;
  double e;
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_best: start\n");
  fprintf (stderr, "optimize_best: nsaveds=%u nbest=%u\n",
           optimize->nsaveds, optimize->nbest);
#endif
  if (optimize->nsaveds < optimize->nbest
      || value < optimize->error_best[optimize->nsaveds - 1])
    {
      if (optimize->nsaveds < optimize->nbest)
        ++optimize->nsaveds;
      optimize->error_best[optimize->nsaveds - 1] = value;
      optimize->simulation_best[optimize->nsaveds - 1] = simulation;
      for (i = optimize->nsaveds; --i;)
        {
          if (optimize->error_best[i] < optimize->error_best[i - 1])
            {
              j = optimize->simulation_best[i];
              e = optimize->error_best[i];
              optimize->simulation_best[i] = optimize->simulation_best[i - 1];
              optimize->error_best[i] = optimize->error_best[i - 1];
              optimize->simulation_best[i - 1] = j;
              optimize->error_best[i - 1] = e;
            }
          else
            break;
        }
    }
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_best: end\n");
#endif
}

/**
 * Function to optimize sequentially.
 */
static void
optimize_sequential ()
{
  unsigned int i;
  double e;
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_sequential: start\n");
  fprintf (stderr, "optimize_sequential: nstart=%u nend=%u\n",
           optimize->nstart, optimize->nend);
#endif
  for (i = optimize->nstart; i < optimize->nend; ++i)
    {
      e = optimize_norm (i);
      optimize_best (i, e);
      optimize_save_variables (i, e);
      if (e < optimize->threshold)
        {
          optimize->stop = 1;
          break;
        }
#if DEBUG_OPTIMIZE
      fprintf (stderr, "optimize_sequential: i=%u e=%lg\n", i, e);
#endif
    }
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_sequential: end\n");
#endif
}

/**
 * Function to optimize on a thread.
 *
 * \return NULL.
 */
static void *
optimize_thread (ParallelData * data)   ///< Function data.
{
  unsigned int i, thread;
  double e;
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_thread: start\n");
#endif
  thread = data->thread;
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_thread: thread=%u start=%u end=%u\n", thread,
           optimize->thread[thread], optimize->thread[thread + 1]);
#endif
  for (i = optimize->thread[thread]; i < optimize->thread[thread + 1]; ++i)
    {
      e = optimize_norm (i);
      g_mutex_lock (mutex);
      optimize_best (i, e);
      optimize_save_variables (i, e);
      if (e < optimize->threshold)
        optimize->stop = 1;
      g_mutex_unlock (mutex);
      if (optimize->stop)
        break;
#if DEBUG_OPTIMIZE
      fprintf (stderr, "optimize_thread: i=%u e=%lg\n", i, e);
#endif
    }
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_thread: end\n");
#endif
  g_thread_exit (NULL);
  return NULL;
}

/**
 * Function to merge the 2 optimization results.
 */
static inline void
optimize_merge (unsigned int nsaveds,   ///< Number of saved results.
                unsigned int *simulation_best,
                ///< Array of best simulation numbers.
                double *error_best)
                ///< Array of best objective function values.
{
  unsigned int i, j, k, s[optimize->nbest];
  double e[optimize->nbest];
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_merge: start\n");
#endif
  i = j = k = 0;
  do
    {
      if (i == optimize->nsaveds)
        {
          s[k] = simulation_best[j];
          e[k] = error_best[j];
          ++j;
          ++k;
          if (j == nsaveds)
            break;
        }
      else if (j == nsaveds)
        {
          s[k] = optimize->simulation_best[i];
          e[k] = optimize->error_best[i];
          ++i;
          ++k;
          if (i == optimize->nsaveds)
            break;
        }
      else if (optimize->error_best[i] > error_best[j])
        {
          s[k] = simulation_best[j];
          e[k] = error_best[j];
          ++j;
          ++k;
        }
      else
        {
          s[k] = optimize->simulation_best[i];
          e[k] = optimize->error_best[i];
          ++i;
          ++k;
        }
    }
  while (k < optimize->nbest);
  optimize->nsaveds = k;
  memcpy (optimize->simulation_best, s, k * sizeof (unsigned int));
  memcpy (optimize->error_best, e, k * sizeof (double));
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_merge: end\n");
#endif
}

/**
 * Function to synchronise the optimization results of MPI tasks.
 */
#if HAVE_MPI
static void
optimize_synchronise ()
{
  unsigned int i, nsaveds, simulation_best[optimize->nbest], stop;
  double error_best[optimize->nbest];
  MPI_Status mpi_stat;
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_synchronise: start\n");
#endif
  if (optimize->mpi_rank == 0)
    {
      for (i = 1; (int) i < ntasks; ++i)
        {
          MPI_Recv (&nsaveds, 1, MPI_INT, i, 1, MPI_COMM_WORLD, &mpi_stat);
          MPI_Recv (simulation_best, nsaveds, MPI_INT, i, 1,
                    MPI_COMM_WORLD, &mpi_stat);
          MPI_Recv (error_best, nsaveds, MPI_DOUBLE, i, 1,
                    MPI_COMM_WORLD, &mpi_stat);
          optimize_merge (nsaveds, simulation_best, error_best);
          MPI_Recv (&stop, 1, MPI_UNSIGNED, i, 1, MPI_COMM_WORLD, &mpi_stat);
          if (stop)
            optimize->stop = 1;
        }
      for (i = 1; (int) i < ntasks; ++i)
        MPI_Send (&optimize->stop, 1, MPI_UNSIGNED, i, 1, MPI_COMM_WORLD);
    }
  else
    {
      MPI_Send (&optimize->nsaveds, 1, MPI_INT, 0, 1, MPI_COMM_WORLD);
      MPI_Send (optimize->simulation_best, optimize->nsaveds, MPI_INT, 0, 1,
                MPI_COMM_WORLD);
      MPI_Send (optimize->error_best, optimize->nsaveds, MPI_DOUBLE, 0, 1,
                MPI_COMM_WORLD);
      MPI_Send (&optimize->stop, 1, MPI_UNSIGNED, 0, 1, MPI_COMM_WORLD);
      MPI_Recv (&stop, 1, MPI_UNSIGNED, 0, 1, MPI_COMM_WORLD, &mpi_stat);
      if (stop)
        optimize->stop = 1;
    }
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_synchronise: end\n");
#endif
}
#endif

/**
 * Function to optimize with the sweep algorithm.
 */
static void
optimize_sweep ()
{
  unsigned int i, j, k, l;
  double e;
  GThread *thread[nthreads];
  ParallelData data[nthreads];
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_sweep: start\n");
#endif
  for (i = 0; i < optimize->nsimulations; ++i)
    {
      k = i;
      for (j = 0; j < optimize->nvariables; ++j)
        {
          l = k % optimize->nsweeps[j];
          k /= optimize->nsweeps[j];
          e = optimize->rangemin[j];
          if (optimize->nsweeps[j] > 1)
            e += l * (optimize->rangemax[j] - optimize->rangemin[j])
              / (optimize->nsweeps[j] - 1);
          optimize->value[i * optimize->nvariables + j] = e;
        }
    }
  optimize->nsaveds = 0;
  if (nthreads <= 1)
    optimize_sequential ();
  else
    {
      for (i = 0; i < nthreads; ++i)
        {
          data[i].thread = i;
          thread[i]
            = g_thread_new (NULL, (GThreadFunc) optimize_thread, &data[i]);
        }
      for (i = 0; i < nthreads; ++i)
        g_thread_join (thread[i]);
    }
#if HAVE_MPI
  // Communicating tasks results
  optimize_synchronise ();
#endif
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_sweep: end\n");
#endif
}

/**
 * Function to optimize with the Monte-Carlo algorithm.
 */
static void
optimize_MonteCarlo ()
{
  unsigned int i, j;
  GThread *thread[nthreads];
  ParallelData data[nthreads];
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_MonteCarlo: start\n");
#endif
  for (i = 0; i < optimize->nsimulations; ++i)
    for (j = 0; j < optimize->nvariables; ++j)
      optimize->value[i * optimize->nvariables + j]
        = optimize->rangemin[j] + gsl_rng_uniform (optimize->rng)
        * (optimize->rangemax[j] - optimize->rangemin[j]);
  optimize->nsaveds = 0;
  if (nthreads <= 1)
    optimize_sequential ();
  else
    {
      for (i = 0; i < nthreads; ++i)
        {
          data[i].thread = i;
          thread[i]
            = g_thread_new (NULL, (GThreadFunc) optimize_thread, &data[i]);
        }
      for (i = 0; i < nthreads; ++i)
        g_thread_join (thread[i]);
    }
#if HAVE_MPI
  // Communicating tasks results
  optimize_synchronise ();
#endif
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_MonteCarlo: end\n");
#endif
}

/**
 * Function to optimize with the orthogonal sampling algorithm.
 */
static void
optimize_orthogonal ()
{
  unsigned int i, j, k, l;
  double e;
  GThread *thread[nthreads];
  ParallelData data[nthreads];
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_orthogonal: start\n");
#endif
  for (i = 0; i < optimize->nsimulations; ++i)
    {
      k = i;
      for (j = 0; j < optimize->nvariables; ++j)
        {
          l = k % optimize->nsweeps[j];
          k /= optimize->nsweeps[j];
          e = optimize->rangemin[j];
          if (optimize->nsweeps[j] > 1)
            e += (l + gsl_rng_uniform (optimize->rng))
              * (optimize->rangemax[j] - optimize->rangemin[j])
              / optimize->nsweeps[j];
          optimize->value[i * optimize->nvariables + j] = e;
        }
    }
  optimize->nsaveds = 0;
  if (nthreads <= 1)
    optimize_sequential ();
  else
    {
      for (i = 0; i < nthreads; ++i)
        {
          data[i].thread = i;
          thread[i]
            = g_thread_new (NULL, (GThreadFunc) optimize_thread, &data[i]);
        }
      for (i = 0; i < nthreads; ++i)
        g_thread_join (thread[i]);
    }
#if HAVE_MPI
  // Communicating tasks results
  optimize_synchronise ();
#endif
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_orthogonal: end\n");
#endif
}

/**
 * Function to save the best simulation in a hill climbing method.
 */
static void
optimize_best_climbing (unsigned int simulation,        ///< Simulation number.
                        double value)   ///< Objective function value.
{
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_best_climbing: start\n");
  fprintf (stderr,
           "optimize_best_climbing: simulation=%u value=%.14le best=%.14le\n",
           simulation, value, optimize->error_best[0]);
#endif
  if (value < optimize->error_best[0])
    {
      optimize->error_best[0] = value;
      optimize->simulation_best[0] = simulation;
#if DEBUG_OPTIMIZE
      fprintf (stderr,
               "optimize_best_climbing: BEST simulation=%u value=%.14le\n",
               simulation, value);
#endif
    }
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_best_climbing: end\n");
#endif
}

/**
 * Function to estimate the hill climbing sequentially.
 */
static inline void
optimize_climbing_sequential (unsigned int simulation)  ///< Simulation number.
{
  double e;
  unsigned int i, j;
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_climbing_sequential: start\n");
  fprintf (stderr, "optimize_climbing_sequential: nstart_climbing=%u "
           "nend_climbing=%u\n",
           optimize->nstart_climbing, optimize->nend_climbing);
#endif
  for (i = optimize->nstart_climbing; i < optimize->nend_climbing; ++i)
    {
      j = simulation + i;
      e = optimize_norm (j);
      optimize_best_climbing (j, e);
      optimize_save_variables (j, e);
      if (e < optimize->threshold)
        {
          optimize->stop = 1;
          break;
        }
#if DEBUG_OPTIMIZE
      fprintf (stderr, "optimize_climbing_sequential: i=%u e=%lg\n", i, e);
#endif
    }
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_climbing_sequential: end\n");
#endif
}

/**
 * Function to estimate the hill climbing on a thread.
 *
 * \return NULL
 */
static void *
optimize_climbing_thread (ParallelData * data)  ///< Function data.
{
  unsigned int i, thread;
  double e;
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_climbing_thread: start\n");
#endif
  thread = data->thread;
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_climbing_thread: thread=%u start=%u end=%u\n",
           thread,
           optimize->thread_climbing[thread],
           optimize->thread_climbing[thread + 1]);
#endif
  for (i = optimize->thread_climbing[thread];
       i < optimize->thread_climbing[thread + 1]; ++i)
    {
      e = optimize_norm (i);
      g_mutex_lock (mutex);
      optimize_best_climbing (i, e);
      optimize_save_variables (i, e);
      if (e < optimize->threshold)
        optimize->stop = 1;
      g_mutex_unlock (mutex);
      if (optimize->stop)
        break;
#if DEBUG_OPTIMIZE
      fprintf (stderr, "optimize_climbing_thread: i=%u e=%lg\n", i, e);
#endif
    }
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_climbing_thread: end\n");
#endif
  g_thread_exit (NULL);
  return NULL;
}

/**
 * Function to estimate a component of the hill climbing vector.
 */
static double
optimize_estimate_climbing_random (unsigned int variable,
                                   ///< Variable number.
                                   unsigned int estimate
                                   __attribute__((unused)))
  ///< Estimate number.
{
  double x;
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_estimate_climbing_random: start\n");
#endif
  x = optimize->climbing[variable]
    + (1. - 2. * gsl_rng_uniform (optimize->rng)) * optimize->step[variable];
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_estimate_climbing_random: climbing%u=%lg\n",
           variable, x);
  fprintf (stderr, "optimize_estimate_climbing_random: end\n");
#endif
  return x;
}

/**
 * Function to estimate a component of the hill climbing vector.
 */
static double
optimize_estimate_climbing_coordinates (unsigned int variable,
                                        ///< Variable number.
                                        unsigned int estimate)
                                        ///< Estimate number.
{
  double x;
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_estimate_climbing_coordinates: start\n");
#endif
  x = optimize->climbing[variable];
  if (estimate >= (2 * variable) && estimate < (2 * variable + 2))
    {
      if (estimate & 1)
        x += optimize->step[variable];
      else
        x -= optimize->step[variable];
    }
#if DEBUG_OPTIMIZE
  fprintf (stderr,
           "optimize_estimate_climbing_coordinates: climbing%u=%lg\n",
           variable, x);
  fprintf (stderr, "optimize_estimate_climbing_coordinates: end\n");
#endif
  return x;
}

/**
 * Function to do a step of the hill climbing method.
 */
static inline void
optimize_step_climbing (unsigned int simulation)        ///< Simulation number.
{
  GThread *thread[nthreads_climbing];
  ParallelData data[nthreads_climbing];
  unsigned int i, j, k, b;
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_step_climbing: start\n");
#endif
  for (i = 0; i < optimize->nestimates; ++i)
    {
      k = (simulation + i) * optimize->nvariables;
      b = optimize->simulation_best[0] * optimize->nvariables;
#if DEBUG_OPTIMIZE
      fprintf (stderr, "optimize_step_climbing: simulation=%u best=%u\n",
               simulation + i, optimize->simulation_best[0]);
#endif
      for (j = 0; j < optimize->nvariables; ++j, ++k, ++b)
        {
#if DEBUG_OPTIMIZE
          fprintf (stderr,
                   "optimize_step_climbing: estimate=%u best%u=%.14le\n",
                   i, j, optimize->value[b]);
#endif
          optimize->value[k]
            = optimize->value[b] + optimize_estimate_climbing (j, i);
          optimize->value[k] = fmin (fmax (optimize->value[k],
                                           optimize->rangeminabs[j]),
                                     optimize->rangemaxabs[j]);
#if DEBUG_OPTIMIZE
          fprintf (stderr,
                   "optimize_step_climbing: estimate=%u variable%u=%.14le\n",
                   i, j, optimize->value[k]);
#endif
        }
    }
  if (nthreads_climbing == 1)
    optimize_climbing_sequential (simulation);
  else
    {
      for (i = 0; i <= nthreads_climbing; ++i)
        {
          optimize->thread_climbing[i]
            = simulation + optimize->nstart_climbing
            + i * (optimize->nend_climbing - optimize->nstart_climbing)
            / nthreads_climbing;
#if DEBUG_OPTIMIZE
          fprintf (stderr,
                   "optimize_step_climbing: i=%u thread_climbing=%u\n",
                   i, optimize->thread_climbing[i]);
#endif
        }
      for (i = 0; i < nthreads_climbing; ++i)
        {
          data[i].thread = i;
          thread[i] = g_thread_new
            (NULL, (GThreadFunc) optimize_climbing_thread, &data[i]);
        }
      for (i = 0; i < nthreads_climbing; ++i)
        g_thread_join (thread[i]);
    }
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_step_climbing: end\n");
#endif
}

/**
 * Function to optimize with a hill climbing method.
 */
static inline void
optimize_climbing ()
{
  unsigned int i, j, k, b, s, adjust;
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_climbing: start\n");
#endif
  for (i = 0; i < optimize->nvariables; ++i)
    optimize->climbing[i] = 0.;
  b = optimize->simulation_best[0] * optimize->nvariables;
  s = optimize->nsimulations;
  adjust = 1;
  for (i = 0; i < optimize->nsteps; ++i, s += optimize->nestimates, b = k)
    {
#if DEBUG_OPTIMIZE
      fprintf (stderr, "optimize_climbing: step=%u old_best=%u\n",
               i, optimize->simulation_best[0]);
#endif
      optimize_step_climbing (s);
      k = optimize->simulation_best[0] * optimize->nvariables;
#if DEBUG_OPTIMIZE
      fprintf (stderr, "optimize_climbing: step=%u best=%u\n",
               i, optimize->simulation_best[0]);
#endif
      if (k == b)
        {
          if (adjust)
            for (j = 0; j < optimize->nvariables; ++j)
              optimize->step[j] *= 0.5;
          for (j = 0; j < optimize->nvariables; ++j)
            optimize->climbing[j] = 0.;
          adjust = 1;
        }
      else
        {
          for (j = 0; j < optimize->nvariables; ++j)
            {
#if DEBUG_OPTIMIZE
              fprintf (stderr,
                       "optimize_climbing: best%u=%.14le old%u=%.14le\n",
                       j, optimize->value[k + j], j, optimize->value[b + j]);
#endif
              optimize->climbing[j]
                = (1. - optimize->relaxation) * optimize->climbing[j]
                + optimize->relaxation
                * (optimize->value[k + j] - optimize->value[b + j]);
#if DEBUG_OPTIMIZE
              fprintf (stderr, "optimize_climbing: climbing%u=%.14le\n",
                       j, optimize->climbing[j]);
#endif
            }
          adjust = 0;
        }
    }
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_climbing: end\n");
#endif
}

/**
 * Function to calculate the objective function of an entity.
 *
 * \return objective function value.
 */
static double
optimize_genetic_objective (Entity * entity)    ///< entity data.
{
  unsigned int j;
  double objective;
  char buffer[64];
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_genetic_objective: start\n");
#endif
  for (j = 0; j < optimize->nvariables; ++j)
    {
      optimize->value[entity->id * optimize->nvariables + j]
        = genetic_get_variable (entity, optimize->genetic_variable + j);
    }
  objective = optimize_norm (entity->id);
  g_mutex_lock (mutex);
  for (j = 0; j < optimize->nvariables; ++j)
    {
      snprintf (buffer, 64, "%s ", format[optimize->precision[j]]);
      fprintf (optimize->file_variables, buffer,
               genetic_get_variable (entity, optimize->genetic_variable + j));
    }
  fprintf (optimize->file_variables, "%.14le\n", objective);
  g_mutex_unlock (mutex);
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_genetic_objective: end\n");
#endif
  return objective;
}

/**
 * Function to optimize with the genetic algorithm.
 */
static void
optimize_genetic ()
{
  double *best_variable = NULL;
  char *best_genome = NULL;
  double best_objective = 0.;
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_genetic: start\n");
  fprintf (stderr, "optimize_genetic: ntasks=%u nthreads=%u\n", ntasks,
           nthreads);
  fprintf (stderr,
           "optimize_genetic: nvariables=%u population=%u generations=%u\n",
           optimize->nvariables, optimize->nsimulations, optimize->niterations);
  fprintf (stderr,
           "optimize_genetic: mutation=%lg reproduction=%lg adaptation=%lg\n",
           optimize->mutation_ratio, optimize->reproduction_ratio,
           optimize->adaptation_ratio);
#endif
  genetic_algorithm_default (optimize->nvariables,
                             optimize->genetic_variable,
                             optimize->nsimulations,
                             optimize->niterations,
                             optimize->mutation_ratio,
                             optimize->reproduction_ratio,
                             optimize->adaptation_ratio,
                             optimize->seed,
                             optimize->threshold,
                             &optimize_genetic_objective,
                             &best_genome, &best_variable, &best_objective);
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_genetic: the best\n");
#endif
  optimize->error_old = (double *) g_malloc (sizeof (double));
  optimize->value_old
    = (double *) g_malloc (optimize->nvariables * sizeof (double));
  optimize->error_old[0] = best_objective;
  memcpy (optimize->value_old, best_variable,
          optimize->nvariables * sizeof (double));
  g_free (best_genome);
  g_free (best_variable);
  optimize_print ();
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_genetic: end\n");
#endif
}

/**
 * Function to save the best results on iterative methods.
 */
static inline void
optimize_save_old ()
{
  unsigned int i, j;
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_save_old: start\n");
  fprintf (stderr, "optimize_save_old: nsaveds=%u\n", optimize->nsaveds);
#endif
  memcpy (optimize->error_old, optimize->error_best,
          optimize->nbest * sizeof (double));
  for (i = 0; i < optimize->nbest; ++i)
    {
      j = optimize->simulation_best[i];
#if DEBUG_OPTIMIZE
      fprintf (stderr, "optimize_save_old: i=%u j=%u\n", i, j);
#endif
      memcpy (optimize->value_old + i * optimize->nvariables,
              optimize->value + j * optimize->nvariables,
              optimize->nvariables * sizeof (double));
    }
#if DEBUG_OPTIMIZE
  for (i = 0; i < optimize->nvariables; ++i)
    fprintf (stderr, "optimize_save_old: best variable %u=%lg\n",
             i, optimize->value_old[i]);
  fprintf (stderr, "optimize_save_old: end\n");
#endif
}

/**
 * Function to merge the best results with the previous step best results on
 *   iterative methods.
 */
static inline void
optimize_merge_old ()
{
  unsigned int i, j, k;
  double v[optimize->nbest * optimize->nvariables], e[optimize->nbest],
    *enew, *eold;
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_merge_old: start\n");
#endif
  enew = optimize->error_best;
  eold = optimize->error_old;
  i = j = k = 0;
  do
    {
      if (*enew < *eold)
        {
          memcpy (v + k * optimize->nvariables,
                  optimize->value
                  + optimize->simulation_best[i] * optimize->nvariables,
                  optimize->nvariables * sizeof (double));
          e[k] = *enew;
          ++k;
          ++enew;
          ++i;
        }
      else
        {
          memcpy (v + k * optimize->nvariables,
                  optimize->value_old + j * optimize->nvariables,
                  optimize->nvariables * sizeof (double));
          e[k] = *eold;
          ++k;
          ++eold;
          ++j;
        }
    }
  while (k < optimize->nbest);
  memcpy (optimize->value_old, v, k * optimize->nvariables * sizeof (double));
  memcpy (optimize->error_old, e, k * sizeof (double));
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_merge_old: end\n");
#endif
}

/**
 * Function to refine the search ranges of the variables in iterative
 *   algorithms.
 */
static inline void
optimize_refine ()
{
  unsigned int i, j;
  double d;
#if HAVE_MPI
  MPI_Status mpi_stat;
#endif
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_refine: start\n");
#endif
#if HAVE_MPI
  if (!optimize->mpi_rank)
    {
#endif
      for (j = 0; j < optimize->nvariables; ++j)
        {
          optimize->rangemin[j] = optimize->rangemax[j]
            = optimize->value_old[j];
        }
      for (i = 0; ++i < optimize->nbest;)
        {
          for (j = 0; j < optimize->nvariables; ++j)
            {
              optimize->rangemin[j]
                = fmin (optimize->rangemin[j],
                        optimize->value_old[i * optimize->nvariables + j]);
              optimize->rangemax[j]
                = fmax (optimize->rangemax[j],
                        optimize->value_old[i * optimize->nvariables + j]);
            }
        }
      for (j = 0; j < optimize->nvariables; ++j)
        {
          d = optimize->tolerance
            * (optimize->rangemax[j] - optimize->rangemin[j]);
          switch (optimize->algorithm)
            {
            case ALGORITHM_MONTE_CARLO:
              d *= 0.5;
              break;
            default:
              if (optimize->nsweeps[j] > 1)
                d /= optimize->nsweeps[j] - 1;
              else
                d = 0.;
            }
          optimize->rangemin[j] -= d;
          optimize->rangemin[j]
            = fmax (optimize->rangemin[j], optimize->rangeminabs[j]);
          optimize->rangemax[j] += d;
          optimize->rangemax[j]
            = fmin (optimize->rangemax[j], optimize->rangemaxabs[j]);
          printf ("%s min=%lg max=%lg\n", optimize->label[j],
                  optimize->rangemin[j], optimize->rangemax[j]);
          fprintf (optimize->file_result, "%s min=%lg max=%lg\n",
                   optimize->label[j], optimize->rangemin[j],
                   optimize->rangemax[j]);
        }
#if HAVE_MPI
      for (i = 1; (int) i < ntasks; ++i)
        {
          MPI_Send (optimize->rangemin, optimize->nvariables, MPI_DOUBLE, i,
                    1, MPI_COMM_WORLD);
          MPI_Send (optimize->rangemax, optimize->nvariables, MPI_DOUBLE, i,
                    1, MPI_COMM_WORLD);
        }
    }
  else
    {
      MPI_Recv (optimize->rangemin, optimize->nvariables, MPI_DOUBLE, 0, 1,
                MPI_COMM_WORLD, &mpi_stat);
      MPI_Recv (optimize->rangemax, optimize->nvariables, MPI_DOUBLE, 0, 1,
                MPI_COMM_WORLD, &mpi_stat);
    }
#endif
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_refine: end\n");
#endif
}

/**
 * Function to do a step of the iterative algorithm.
 */
static void
optimize_step ()
{
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_step: start\n");
#endif
  optimize_algorithm ();
  if (optimize->nsteps)
    optimize_climbing ();
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_step: end\n");
#endif
}

/**
 * Function to iterate the algorithm.
 */
static inline void
optimize_iterate ()
{
  unsigned int i;
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_iterate: start\n");
#endif
  optimize->error_old = (double *) g_malloc (optimize->nbest * sizeof (double));
  optimize->value_old =
    (double *) g_malloc (optimize->nbest * optimize->nvariables *
                         sizeof (double));
  optimize_step ();
  optimize_save_old ();
  optimize_refine ();
  optimize_print ();
  for (i = 1; i < optimize->niterations && !optimize->stop; ++i)
    {
      optimize_step ();
      optimize_merge_old ();
      optimize_refine ();
      optimize_print ();
    }
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_iterate: end\n");
#endif
}

/**
 * Function to free the memory used by the Optimize struct.
 */
void
optimize_free ()
{
  unsigned int i, j;
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_free: start\n");
#endif
  for (j = 0; j < optimize->ninputs; ++j)
    {
      for (i = 0; i < optimize->nexperiments; ++i)
        g_mapped_file_unref (optimize->file[j][i]);
      g_free (optimize->file[j]);
    }
  g_free (optimize->error_old);
  g_free (optimize->value_old);
  g_free (optimize->value);
  g_free (optimize->genetic_variable);
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_free: end\n");
#endif
}

/**
 * Function to open and perform a optimization.
 */
void
optimize_open ()
{
  GTimeZone *tz;
  GDateTime *t0, *t;
  unsigned int i, j;

#if DEBUG_OPTIMIZE
  char *buffer;
  fprintf (stderr, "optimize_open: start\n");
#endif

  // Getting initial time
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_open: getting initial time\n");
#endif
  tz = g_time_zone_new_utc ();
  t0 = g_date_time_new_now (tz);

  // Obtaining and initing the pseudo-random numbers generator seed
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_open: getting initial seed\n");
#endif
  if (optimize->seed == DEFAULT_RANDOM_SEED)
    optimize->seed = input->seed;
  gsl_rng_set (optimize->rng, optimize->seed);

  // Replacing the working directory
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_open: replacing the working directory\n");
#endif
  g_chdir (input->directory);

  // Getting results file names
  optimize->result = input->result;
  optimize->variables = input->variables;

  // Obtaining the simulator file
  optimize->simulator = input->simulator;

  // Obtaining the evaluator file
  optimize->evaluator = input->evaluator;

  // Reading the algorithm
  optimize->algorithm = input->algorithm;
  switch (optimize->algorithm)
    {
    case ALGORITHM_MONTE_CARLO:
      optimize_algorithm = optimize_MonteCarlo;
      break;
    case ALGORITHM_SWEEP:
      optimize_algorithm = optimize_sweep;
      break;
    case ALGORITHM_ORTHOGONAL:
      optimize_algorithm = optimize_orthogonal;
      break;
    default:
      optimize_algorithm = optimize_genetic;
      optimize->mutation_ratio = input->mutation_ratio;
      optimize->reproduction_ratio = input->reproduction_ratio;
      optimize->adaptation_ratio = input->adaptation_ratio;
    }
  optimize->nvariables = input->nvariables;
  optimize->nsimulations = input->nsimulations;
  optimize->niterations = input->niterations;
  optimize->nbest = input->nbest;
  optimize->tolerance = input->tolerance;
  optimize->nsteps = input->nsteps;
  optimize->nestimates = 0;
  optimize->threshold = input->threshold;
  optimize->stop = 0;
  if (input->nsteps)
    {
      optimize->relaxation = input->relaxation;
      switch (input->climbing)
        {
        case CLIMBING_METHOD_COORDINATES:
          optimize->nestimates = 2 * optimize->nvariables;
          optimize_estimate_climbing = optimize_estimate_climbing_coordinates;
          break;
        default:
          optimize->nestimates = input->nestimates;
          optimize_estimate_climbing = optimize_estimate_climbing_random;
        }
    }

#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_open: nbest=%u\n", optimize->nbest);
#endif
  optimize->simulation_best
    = (unsigned int *) alloca (optimize->nbest * sizeof (unsigned int));
  optimize->error_best = (double *) alloca (optimize->nbest * sizeof (double));

  // Reading the experimental data
#if DEBUG_OPTIMIZE
  buffer = g_get_current_dir ();
  fprintf (stderr, "optimize_open: current directory=%s\n", buffer);
  g_free (buffer);
#endif
  optimize->nexperiments = input->nexperiments;
  optimize->ninputs = input->experiment->ninputs;
  optimize->experiment
    = (char **) alloca (input->nexperiments * sizeof (char *));
  optimize->weight = (double *) alloca (input->nexperiments * sizeof (double));
  for (i = 0; i < input->experiment->ninputs; ++i)
    optimize->file[i] = (GMappedFile **)
      g_malloc (input->nexperiments * sizeof (GMappedFile *));
  for (i = 0; i < input->nexperiments; ++i)
    {
#if DEBUG_OPTIMIZE
      fprintf (stderr, "optimize_open: i=%u\n", i);
#endif
      optimize->experiment[i] = input->experiment[i].name;
      optimize->weight[i] = input->experiment[i].weight;
#if DEBUG_OPTIMIZE
      fprintf (stderr, "optimize_open: experiment=%s weight=%lg\n",
               optimize->experiment[i], optimize->weight[i]);
#endif
      for (j = 0; j < input->experiment->ninputs; ++j)
        {
#if DEBUG_OPTIMIZE
          fprintf (stderr, "optimize_open: stencil%u\n", j + 1);
#endif
          optimize->file[j][i]
            = g_mapped_file_new (input->experiment[i].stencil[j], 0, NULL);
        }
    }

  // Reading the variables data
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_open: reading variables\n");
#endif
  optimize->label = (char **) alloca (input->nvariables * sizeof (char *));
  j = input->nvariables * sizeof (double);
  optimize->rangemin = (double *) alloca (j);
  optimize->rangeminabs = (double *) alloca (j);
  optimize->rangemax = (double *) alloca (j);
  optimize->rangemaxabs = (double *) alloca (j);
  optimize->step = (double *) alloca (j);
  j = input->nvariables * sizeof (unsigned int);
  optimize->precision = (unsigned int *) alloca (j);
  optimize->nsweeps = (unsigned int *) alloca (j);
  optimize->nbits = (unsigned int *) alloca (j);
  for (i = 0; i < input->nvariables; ++i)
    {
      optimize->label[i] = input->variable[i].name;
      optimize->rangemin[i] = input->variable[i].rangemin;
      optimize->rangeminabs[i] = input->variable[i].rangeminabs;
      optimize->rangemax[i] = input->variable[i].rangemax;
      optimize->rangemaxabs[i] = input->variable[i].rangemaxabs;
      optimize->precision[i] = input->variable[i].precision;
      optimize->step[i] = input->variable[i].step;
      optimize->nsweeps[i] = input->variable[i].nsweeps;
      optimize->nbits[i] = input->variable[i].nbits;
    }
  if (input->algorithm == ALGORITHM_SWEEP
      || input->algorithm == ALGORITHM_ORTHOGONAL)
    {
      optimize->nsimulations = 1;
      for (i = 0; i < input->nvariables; ++i)
        {
          optimize->nsimulations *= optimize->nsweeps[i];
#if DEBUG_OPTIMIZE
          fprintf (stderr, "optimize_open: nsweeps=%u nsimulations=%u\n",
                   optimize->nsweeps[i], optimize->nsimulations);
#endif
        }
    }
  if (optimize->nsteps)
    optimize->climbing
      = (double *) alloca (optimize->nvariables * sizeof (double));

  // Setting error norm
  switch (input->norm)
    {
    case ERROR_NORM_EUCLIDIAN:
      optimize_norm = optimize_norm_euclidian;
      break;
    case ERROR_NORM_MAXIMUM:
      optimize_norm = optimize_norm_maximum;
      break;
    case ERROR_NORM_P:
      optimize_norm = optimize_norm_p;
      optimize->p = input->p;
      break;
    default:
      optimize_norm = optimize_norm_taxicab;
    }

  // Allocating values
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_open: allocating variables\n");
  fprintf (stderr, "optimize_open: nvariables=%u algorithm=%u\n",
           optimize->nvariables, optimize->algorithm);
#endif
  optimize->genetic_variable = NULL;
  if (optimize->algorithm == ALGORITHM_GENETIC)
    {
      optimize->genetic_variable = (GeneticVariable *)
        g_malloc (optimize->nvariables * sizeof (GeneticVariable));
      for (i = 0; i < optimize->nvariables; ++i)
        {
#if DEBUG_OPTIMIZE
          fprintf (stderr, "optimize_open: i=%u min=%lg max=%lg nbits=%u\n",
                   i, optimize->rangemin[i], optimize->rangemax[i],
                   optimize->nbits[i]);
#endif
          optimize->genetic_variable[i].minimum = optimize->rangemin[i];
          optimize->genetic_variable[i].maximum = optimize->rangemax[i];
          optimize->genetic_variable[i].nbits = optimize->nbits[i];
        }
    }
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_open: nvariables=%u nsimulations=%u\n",
           optimize->nvariables, optimize->nsimulations);
#endif
  optimize->value = (double *)
    g_malloc ((optimize->nsimulations
               + optimize->nestimates * optimize->nsteps)
              * optimize->nvariables * sizeof (double));

  // Calculating simulations to perform for each task
#if HAVE_MPI
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_open: rank=%u ntasks=%u\n",
           optimize->mpi_rank, ntasks);
#endif
  optimize->nstart = optimize->mpi_rank * optimize->nsimulations / ntasks;
  optimize->nend = (1 + optimize->mpi_rank) * optimize->nsimulations / ntasks;
  if (optimize->nsteps)
    {
      optimize->nstart_climbing
        = optimize->mpi_rank * optimize->nestimates / ntasks;
      optimize->nend_climbing
        = (1 + optimize->mpi_rank) * optimize->nestimates / ntasks;
    }
#else
  optimize->nstart = 0;
  optimize->nend = optimize->nsimulations;
  if (optimize->nsteps)
    {
      optimize->nstart_climbing = 0;
      optimize->nend_climbing = optimize->nestimates;
    }
#endif
#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_open: nstart=%u nend=%u\n", optimize->nstart,
           optimize->nend);
#endif

  // Calculating simulations to perform for each thread
  optimize->thread
    = (unsigned int *) alloca ((1 + nthreads) * sizeof (unsigned int));
  for (i = 0; i <= nthreads; ++i)
    {
      optimize->thread[i] = optimize->nstart
        + i * (optimize->nend - optimize->nstart) / nthreads;
#if DEBUG_OPTIMIZE
      fprintf (stderr, "optimize_open: i=%u thread=%u\n", i,
               optimize->thread[i]);
#endif
    }
  if (optimize->nsteps)
    optimize->thread_climbing = (unsigned int *)
      alloca ((1 + nthreads_climbing) * sizeof (unsigned int));

  // Opening result files
  optimize->file_result = g_fopen (optimize->result, "w");
  optimize->file_variables = g_fopen (optimize->variables, "w");

  // Performing the algorithm
  switch (optimize->algorithm)
    {
      // Genetic algorithm
    case ALGORITHM_GENETIC:
      optimize_genetic ();
      break;

      // Iterative algorithm
    default:
      optimize_iterate ();
    }

  // Getting calculation time
  t = g_date_time_new_now (tz);
  optimize->calculation_time = 0.000001 * g_date_time_difference (t, t0);
  g_date_time_unref (t);
  g_date_time_unref (t0);
  g_time_zone_unref (tz);
  printf ("%s = %.6lg s\n", _("Calculation time"), optimize->calculation_time);
  fprintf (optimize->file_result, "%s = %.6lg s\n",
           _("Calculation time"), optimize->calculation_time);

  // Closing result files
  fclose (optimize->file_variables);
  fclose (optimize->file_result);

#if DEBUG_OPTIMIZE
  fprintf (stderr, "optimize_open: end\n");
#endif
}


#ifndef ENTITY_H_
#define ENTITY_H_

#include <vector>
#include <memory>
#include <utility>
#include <gsl.h>
#include "Component.h"

using std::vector;
using std::unique_ptr;
using std::make_unique;
using std::move;
using std::forward;

class Entity
{
public:

    static void test();

    template <typename C, typename... Args>
    void addComponent(Args &&... args);

    template <typename C>
    C * getComponent() const;

private:

    Component * getComponent(Component::Id _id, Component::Id _mask) const;

    vector<unique_ptr<Component>> m_components;
    vector<Component::Id> m_componentIds;      // invariant : m_components.size() == m_componentIds.size()
};

template<typename C, typename ...Args>
inline void Entity::addComponent(Args && ...args)
{
    Expects(IdInfoOf<C>::value.isRegistered() != 0);
    m_components.emplace_back(make_unique<C>(forward<Args>(args)...));
    m_componentIds.emplace_back(IdInfoOf<C>::value.getId());
}

template<typename C>
inline C * Entity::getComponent() const
{
    auto & idInfo = IdInfoOf<C>::value;
    auto id = idInfo.getId();
    auto mask = idInfo.getMaskId();
    return static_cast<C*>(getComponent(id, mask));
}

#endif // ENTITY_H_


#pragma once

#include <cstddef>
#include <memory>
#include <iostream>
#include <array>
#include <gsl\gsl>
#include "../utils/CRSP.h"
#include "./Pool.h"
#include "../math/OtherMath.h"
#include "../types/def.h"

namespace config {
    /**
    Default pool size for our Small Memory Allocator pools.
    */
    constexpr size_t sma_pool_size = 1024 * 1024;
	constexpr size_t sma_pool_amount = 10;
}  // namespace config

/**
Our allocator class that will be the general interface for any future
allocation. Inherits from the Curiously Recurring Singleton Pattern in order to
have just one global one.

@see CRSP
*/
class SmallMemoryAllocator : public CRSP<SmallMemoryAllocator> {
private:
    friend class CRSP<SmallMemoryAllocator>;

    SmallMemoryAllocator();
    ~SmallMemoryAllocator();

public:
    Pool * get_pool_for_size(size_t size);

    bool fits(size_t size) const;

    void* alloc(size_t size);

    template <typename T>
    T* alloc() {
        return reinterpret_cast<T*>(alloc(sizeof(T)));
    }

    void dealloc(void* elem);

    void print_status(std::ostream& stream);

    void print_status(void* elem, std::ostream& stream) const;

private:
	size_t get_pool_index(size_t size) const;

    std::array<std::unique_ptr<Pool>, config::sma_pool_amount> m_pool_array{};
	std::array<size_t, config::sma_pool_amount + 1> m_extra_requested_elements{};
    std::array<size_t, config::sma_pool_amount> m_max_allocated_elements{};

    byte* m_general_pool{ nullptr };
};


#pragma once

#include <gsl/gsl>

/*
	Helper class for reading structured binary data from a
	memory stream.
*/
class BinaryReader {
public:

	explicit BinaryReader(gsl::span<uint8_t> data) : mData(data) {}

	template<typename T>
	T Read() {
		Expects(mData.size() >= sizeof(T));
		auto result{ *reinterpret_cast<T*>(&mData[0]) };
		mData = mData.subspan(sizeof(T));
		return result;
	}

	std::string ReadFixedString(size_t length) {
		Expects(mData.size() >= (int) length);
		std::string result(length, '\0');
		memcpy(&result[0], &mData[0], length);
		result.resize(result.length());
		mData = mData.subspan(length);
		return result;
	}

	bool AtEnd() const {
		return mData.size() == 0;
	}

private:
	gsl::span<uint8_t> mData;
};


#pragma once

#include "Utils.h"

#include <algorithm>
#include <array>
#include <atomic>
#include <deque>
#include <functional>
#include <initializer_list>
#include <limits>
#include <memory>
#include <mutex>
#include <set>
#include <vector>

#include <gsl/gsl>

namespace indexes::utils {
template <typename epoch_t, typename ReclaimedType,
          int ReclamationThreshold = 1000, typename Enable = void>
class EpochManager;

// Provides support for epoch based reclaimation.
template <typename epoch_t, typename ReclaimedType, int ReclamationThreshold>
class EpochManager<
    epoch_t, ReclaimedType, ReclamationThreshold,
    typename std::enable_if_t<
        (ReclamationThreshold > 0) && std::is_integral_v<epoch_t> &&
        !std::is_same_v<epoch_t, bool> && !std::is_same_v<epoch_t, char>>> {
  // Calling thread must be registered by calling `RegisterThread` before using
  // epoch based safe memory reclamation.

  using ReclaimedPtrType = ReclaimedType *;

public:
  // It is undefined behaviour to use epoch manager, without registering the
  // thread. Returns false, if active registered thread count is more than
  // `MAX_THREADS`. If returned false, thread is not registered.

  // enter_epoch guarantees that all shared objects, accessed by the calling
  // thread, after enter_epoch is called are safe.
  // Recursive calls are allowed
  inline void enter_epoch() {
    auto &pd = m_local_epoch[ThreadRegistry::ThreadID()];

    if (pd.nested_level == 0) {
      pd.epoch.store(now(), std::memory_order_release);
      std::atomic_thread_fence(std::memory_order_acquire);
    }
    pd.nested_level++;
  }

  // exit_epoch marks quiescent state of the calling thread, when nesting
  // becomes 0.
  // Enables reclaimation of objects retired before calling thread's epoch.
  inline void exit_epoch() {
    auto &pd = m_local_epoch[ThreadRegistry::ThreadID()];

    pd.nested_level--;
    if (pd.nested_level == 0)
      pd.epoch.store(QUIESCENT_STATE, std::memory_order_release);
  }

  // Switch to new epoch and return old epoch
  inline epoch_t switch_epoch() { return m_global_epoch++; }

  // Returns current threads epoch
  inline epoch_t my_epoch() {
    return m_local_epoch[ThreadRegistry::ThreadID()].epoch.load(
        std::memory_order_relaxed);
  }

  // Return current epoch
  inline epoch_t now() const { return m_global_epoch; }

  // retire objects and start a new epoch.
  // Objects will be reclaimed at a suitable and safe epoch.
  // When reclaimed `reclaimer` will be called for each reclaimed object.
  void
  retire_in_new_epoch(std::function<void(ReclaimedPtrType object)> reclaimer,
                      gsl::span<ReclaimedPtrType> objects) {
    retire(reclaimer, objects, switch_epoch());
  }

  inline void
  retire_in_new_epoch(std::function<void(ReclaimedPtrType object)> reclaimer,
                      ReclaimedPtrType object) {
    retire_in_new_epoch(reclaimer, {&object, 1});
  }

  // retire objects in current (without starting new epoch) epoch.
  // Objects will be reclaimed at a suitable and safe epoch.
  // When reclaimed `reclaimer` will be called for each reclaimed object.
  void retire_in_current_epoch(
      std::function<void(ReclaimedPtrType object)> reclaimer,
      gsl::span<ReclaimedPtrType> objects) {
    retire(reclaimer, objects, now());
  }

  inline void retire_in_current_epoch(
      std::function<void(ReclaimedPtrType object)> reclaimer,
      ReclaimedPtrType object) {
    retire_in_current_epoch(reclaimer, {&object, 1});
  }

  // Reclaim objects which are safe to reclaim.
  // Returns # objects still not reclaimed (but retired).
  // A long running thread using an epoch could prevent
  // reclaimation of objects visible to that thread.
  size_t do_reclaim() {
    return reclaim_in_retire_list(m_retire_list[ThreadRegistry::ThreadID()],
                                  get_min_used_epoch());
  }

  void reclaim_all() {
    epoch_t min_used_epoch = get_min_used_epoch();

    for (auto &retire_list : m_retire_list) {
      reclaim_in_retire_list(retire_list, min_used_epoch);
    }
  }

  // Getter/Setter for reclaimation threshold.
  // # objects to collect before triggering reclaimation.
  // A low value means possibly reduced peak memory consumption, but would less
  // performance, because of frequent calls to `DoReclaim`.
  void set_reclamation_threshold(int threshold) {
    if (threshold > 0)
      m_reclaimation_threshold = threshold;
  }

  int get_reclamation_threshold(int threshold) {
    return m_reclaimation_threshold;
  }

  EpochManager()
      : m_reclaimation_threshold{ReclamationThreshold}, m_global_epoch{0},
        m_local_epoch(ThreadRegistry::MAX_THREADS),
        m_retire_list(ThreadRegistry::MAX_THREADS) {}

  EpochManager(const EpochManager &) = delete;
  EpochManager(EpochManager &&) = delete;

private:
  class Retiree {
  public:
    Retiree(ReclaimedPtrType object,
            std::function<void(ReclaimedPtrType object)> reclaimer,
            epoch_t retired_epoch)
        : m_object(object), m_retired_epoch(retired_epoch),
          m_reclaimer(reclaimer) {}

    // Can reclaim this object after the given `epoch`
    bool can_reclaim(epoch_t epoch) const { return epoch > m_retired_epoch; }

    void reclaim() { return m_reclaimer(m_object); }

  private:
    // Object to reclaim safely.
    ReclaimedPtrType m_object;
    // Epoch on which the object was retired.
    epoch_t m_retired_epoch;
    // Callback to call during reclaimation.
    std::function<void(ReclaimedPtrType object)> m_reclaimer;
  };

  static size_t reclaim_in_retire_list(std::deque<Retiree> &retire_list,
                                       epoch_t min_used_epoch) {
    auto begin = std::begin(retire_list);
    auto reclaim_upto = begin;

    for (auto &retiree : retire_list) {
      if (!retiree.can_reclaim(min_used_epoch))
        break;

      retiree.reclaim();
      reclaim_upto++;
    }

    size_t num_reclaimed = reclaim_upto - begin;

    if (num_reclaimed) {
      retire_list.erase(begin, reclaim_upto);
      retire_list.shrink_to_fit();
    }

    return retire_list.size();
  }

  epoch_t get_min_used_epoch() {
    return std::min_element(std::begin(m_local_epoch),
                            std::begin(m_local_epoch) +
                                ThreadRegistry::MaxThreadID() + 1,
                            [](const auto &a, const auto &b) {
                              return a.epoch.load() < b.epoch.load();
                            })
        ->epoch;
  }

  void retire(std::function<void(ReclaimedPtrType object)> reclaimer,
              gsl::span<ReclaimedPtrType> objects, epoch_t retired_epoch) {
    auto &retire_list = m_retire_list[ThreadRegistry::ThreadID()];

    for (auto object : objects)
      retire_list.emplace_back(object, reclaimer, retired_epoch);

    if (static_cast<int>(retire_list.size()) >= m_reclaimation_threshold)
      do_reclaim();
  }

  // Reclaimation threshold. Default 1000 objects
  std::atomic<int> m_reclaimation_threshold;

  // Global epoch
  std::atomic<epoch_t> m_global_epoch;

  // Quiescent state
  static constexpr epoch_t QUIESCENT_STATE =
      std::numeric_limits<epoch_t>::max();

  struct alignas(128) PrivateData {
    std::atomic<epoch_t> epoch = QUIESCENT_STATE;
    int nested_level = 0;
  };

  // Thread local epoch, denotes the epoch of each thread.
  // Accessed using `slot` by each thread.
  std::vector<PrivateData> m_local_epoch;

  // Thread local retire list. Accessed using `slot` by each thread.
  std::vector<std::deque<Retiree>> m_retire_list;
};
} // namespace indexes::utils


#ifndef _RNG_INTERNAL_H
#define _RNG_INTERNAL_H

#include <gsl/gsl_rng.h>

struct _ccs_rng_data_s {
	const gsl_rng_type *rng_type;
	gsl_rng            *rng;
};

typedef struct _ccs_rng_data_s _ccs_rng_data_t;

struct _ccs_rng_ops_s {
	_ccs_object_ops_t obj_ops;
};
typedef struct _ccs_rng_ops_s _ccs_rng_ops_t;

struct _ccs_rng_s {
	_ccs_object_internal_t obj;
	_ccs_rng_data_t *data;
};

#endif //_RNG_INTERNAL_H


#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <math.h>

#include <gsl/gsl_errno.h>

#include "ccl.h"

// Error handling policy: whether to exit on error (C default) or continue
// (Python or other binding default)
static CCLErrorPolicy _ccl_error_policy = CCL_ERROR_POLICY_EXIT;

// Debug mode policy: whether to print error messages as they are raised. This
// is useful for the Python wrapper, which normally allows errors to be
// overwritten by the C code until control returns to Python. If debug mode is
// switched on, the errors are always printed by the C code when they occur.
// Setting the debug mode to warning allows ccl_raise_warning to print warnings
// but keeps the behavior of ccl_raise_exception as if debug mode is set to off.
static CCLDebugModePolicy _ccl_debug_mode_policy = CCL_DEBUG_MODE_WARNING;

// Set error policy
void ccl_set_error_policy(CCLErrorPolicy error_policy)
{
  _ccl_error_policy = error_policy;
}

// Set debug mode policy
void ccl_set_debug_policy(CCLDebugModePolicy debug_policy)
{
    _ccl_debug_mode_policy = debug_policy;
}

// Convenience function to raise exceptions in an appropriate way
void ccl_raise_exception(int err, const char* msg, ...)
{
  char message[256];

  va_list va;
  va_start(va, msg);
  vsnprintf(message, 250, msg, va);
  va_end(va);

  // Print error message and exit if fatal errors are enabled
  if ((_ccl_error_policy == CCL_ERROR_POLICY_EXIT) && (err)) {
    fprintf(stderr, "ERROR %d: %s\n", err, message);
    exit(1);
  }
  // Print error message and exit if debug output is enabled
  else if ((_ccl_debug_mode_policy == CCL_DEBUG_MODE_ON) && (err)){
    fprintf(stderr, "ERROR %d: %s\n", err, message);
  }
}

// Convenience function to handle warnings
void ccl_raise_warning(int err, const char* msg, ...)
{
  char message[256];

  va_list va;
  va_start(va, msg);
  vsnprintf(message, 250, msg, va);
  va_end(va);

  // For now just print warning to stderr if debug is enabled.
  // TODO: Implement some kind of error stack that can be passed on to, e.g.,
  // the python binding.
  if( (_ccl_debug_mode_policy == CCL_DEBUG_MODE_ON)
      || (_ccl_debug_mode_policy == CCL_DEBUG_MODE_WARNING) ) {
    fprintf(stderr, "WARNING: %s\n", message);
  }
}

// Convenience function to handle warnings
void ccl_raise_gsl_warning(int gslstatus, const char* msg, ...)
{
  char message[256];

  va_list va;
  va_start(va, msg);
  vsnprintf(message, 250, msg, va);
  va_end(va);

  ccl_raise_warning(gslstatus, "%s GSL error: %s", message, gsl_strerror(gslstatus));
  return;
}

void ccl_check_status(ccl_cosmology *cosmo, int * status)
{

  switch (*status) {
  case 0: // all good, nothing to do
    return;
  case CCL_ERROR_LINSPACE:	// spacing allocation error, always terminate
    ccl_raise_exception(*status, cosmo->status_message);
  case CCL_ERROR_SPLINE:	// spline allocation error, always terminate
    ccl_raise_exception(*status, cosmo->status_message);
  case CCL_ERROR_COMPUTECHI:	// compute_chi error //RH
    ccl_raise_exception(*status, cosmo->status_message);
  case CCL_ERROR_HMF_INTERP: // terminate if hmf definition not supported
    ccl_raise_exception(*status, cosmo->status_message);
  case CCL_ERROR_NU_INT: // error in getting the neutrino integral spline: exit. No status_message in cosmo because can't pass cosmology to the function.
    ccl_raise_exception(*status, "Error, in ccl_neutrinos.c. ccl_calculate_nu_phasespace_spline(): Error in setting neutrino phasespace spline.");
  case CCL_ERROR_NU_SOLVE: // error in converting Omeganuh2-> Mnu: exit. No status_message in cosmo because can't pass cosmology to the function.
    ccl_raise_exception(*status, "Error, in ccl_neutrinos.c. Omeganuh2_to_Mnu(): Root finding did not converge.");
    // TODO: Implement softer error handling, e.g. for integral convergence here
  default:
    ccl_raise_exception(*status, cosmo->status_message);
  }
}

/* ------- ROUTINE: ccl_check_status_nocosmo ------
   INPUTS: pointer to a status integer
   TASK: Perform a check on status for the case where it is not possible to have a cosmology object where the status check is required.
*/
void ccl_check_status_nocosmo(int * status)
{
  switch (*status) {
  case 0: // Nothing to do
    return;
  case CCL_ERROR_LINSPACE:
    // Spacing allocation error, always terminate
    ccl_raise_exception(*status, "CCL_ERROR_LINSPACE: Spacing allocation error.");
  case CCL_ERROR_SPLINE:
    // Spline allocation error, always terminate
    ccl_raise_exception(*status, "CCL_ERROR_SPLINE: Spline allocation error.");
  case CCL_ERROR_COMPUTECHI:
    // Compute_chi error
    ccl_raise_exception(*status,
             "CCL_ERROR_COMPUTECHI: Comoving distance chi computation failed.");
  case CCL_ERROR_HMF_INTERP:
    // Terminate if hmf definition not supported
    ccl_raise_exception(*status,
          "CCL_ERROR_HMF_INTERP: Halo mass function definition not supported.");
  case CCL_ERROR_NU_INT:
    // Error in getting the neutrino integral spline: exit. No status_message
    // in cosmo because can't pass cosmology to the function.
    ccl_raise_exception(*status,
      "CCL_ERROR_NU_INT: Error getting the neutrino phase-space integral spline.");
  case CCL_ERROR_NU_SOLVE:
    // Error in converting Omeganuh2-> Mnu: exit. No status_message in cosmo
    // because can't pass cosmology to the function.
    ccl_raise_exception(*status,
                      "CCL_ERROR_NU_SOLVE: Error converting Omeganuh2 -> Mnu.");
  case CCL_ERROR_MNU_UNPHYSICAL:
    // Error in the sum of mnu or Omeganu passed for the hierarchy requested.
	  ccl_raise_exception(*status,
      "CCL_ERROR_MNU_UNPHYSICAL: Sum of neutrinos masses for this Omeganu value is incompatible with the requested mass hierarchy.");
  case CCL_ERROR_NOT_IMPLEMENTED:
    ccl_raise_exception(*status,
      "CCL_ERROR_NOT_IMPLEMENTED: the type of m_nu specified is not supported.");
  default:
    ccl_raise_exception(*status,
             "Unrecognized error code (see gsl_errno.h for error codes 1-32).");
  }
}


#include <stdlib.h>
#include <stdio.h>

//#define VERY_VERBOSE

#define MSG(x) { printf(x); fflush(stdout); }
#define MSGF(x) { printf x ; fflush(stdout); }

#include "buffer.h"

static void test_buffer()
{
}

#include "array.h"

static void test_array()
{
	int k;
	const int * p;
	const int * q;
	OLIO_ARRAY_STACK(st, int, 64);
	olio_array * hp;

	OLIO_ARRAY_ALLOC(hp, int, 0);

	printf("[testing array...]\n"); fflush(stdout);

	MSGF(("st %u\n", st->base.allocated));
	MSGF(("hp %u\n", hp->base.allocated));

#define ARRAY_COUNT 1000

	MSG("appending to arrays...\n");
	for (k = 0; k < ARRAY_COUNT; k++) {
		olio_array_append(st, &k, 1);
		olio_array_append(hp, &k, 1);
	}

	MSG("prepending to arrays...\n");
	for (k = 0; k < ARRAY_COUNT; k++) {
		olio_array_prepend(st, &k, 1);
		olio_array_prepend(hp, &k, 1);
	}

	MSG("inserting to arrays...\n");
	for (k = 0; k <= ARRAY_COUNT * 2; k++) {
		olio_array_insert(st, k * 2, &k, 1);
		olio_array_insert(hp, k * 2, &k, 1);
	}

	MSG("checking array contents...\n");
	p = olio_array_contents(st);
	q = olio_array_contents(hp);
	for (k = 0; k < ARRAY_COUNT; k++) {
		if (p[k * 2 + 1] != ARRAY_COUNT - 1 - k ||
			p[k * 2 + 1 + ARRAY_COUNT * 2] != k)
			MSG("ERROR: odd numbered stack item not as expected\n");

		if (p[k * 2] != k ||
			p[k * 2 + ARRAY_COUNT * 2] != k + ARRAY_COUNT)
			MSG("ERROR: even numbered stack item not as expected\n");

		if (q[k * 2 + 1] != ARRAY_COUNT - 1 - k ||
			q[k * 2 + 1 + ARRAY_COUNT * 2] != k)
			MSG("ERROR: odd numbered heap item not as expected\n");

		if (q[k * 2] != k ||
			q[k * 2 + ARRAY_COUNT * 2] != k + ARRAY_COUNT)
			MSG("ERROR: even numbered heap item not as expected\n");
	}
	if (p[ARRAY_COUNT * 4] != ARRAY_COUNT * 2)
		MSG("ERROR: last item in stack array not as expected\n");
	if (q[ARRAY_COUNT * 4] != ARRAY_COUNT * 2)
		MSG("ERROR: last item in heap array not as expected\n");

#ifdef VERY_VERBOSE
	for (k = 0; k < olio_array_length(st) ||
		k < olio_array_length(hp); k++) {
		MSGF(("%i: st % 4i hp % 4i\n", k,
			(k < olio_array_length(st)) ? p[k] : -1,
			(k < olio_array_length(hp)) ? q[k] : -1));
	}
#endif

	MSGF(("st %p = %p\n", st->base.data, st->stack));
	MSGF(("hp %p = %p\n", hp->base.data, hp->stack));

	MSG("freeing arrays...\n");
	olio_array_free(st);
	olio_array_free(hp);
}

#include "string.h"

static void test_string()
{
}

#include "graph.h"

static void test_graph()
{
	int rc;
	olio_graph g;

	printf("[testing graph...]\n"); fflush(stdout);

	printf("graph initialization...\n"); fflush(stdout);
	olio_graph_init(&g, 10);

	printf("graph setting edges...\n"); fflush(stdout);
	olio_graph_set_edge(&g, 0, 1, 10);
	olio_graph_set_edge(&g, 1, 2, 10);

	printf("graph testing cyclic...\n"); fflush(stdout);
	rc = olio_graph_acyclic(&g);
	if (rc == 0) {
		printf("ERROR: graph NOT acyclic\n"); fflush(stdout);
	}

	printf("graph setting edges...\n"); fflush(stdout);
	olio_graph_set_edge(&g, 2, 0, 10);

	MSG("graph testing cyclic...\n");
	rc = olio_graph_acyclic(&g);
	if (rc != 0) MSG("ERROR: graph NOT cyclic\n");

	MSG("freeing graph...\n");
	olio_graph_free(&g);
}

#include "skiplist.h"

static void test_skiplist()
{
	uint32_t test_data[] = {0xf, 0x436, 0x53547, 0x65, 0x54, 0x5654, 0x8756, 0x8767};

	olio_skiplist sk;
	int i;

	printf("sizeof(entry) = %li\n", sizeof(olio_skiplist_entry));
	printf("sizeof(block) = %li\n", sizeof(olio_skiplist_block));
	printf("sizeof(skiplist) = %li\n", sizeof(olio_skiplist));

	olio_skiplist_init(&sk);
	printf("adding...\n");
	for (i = 0; i < sizeof(test_data) / sizeof(uint32_t); i++) {
		int rv = olio_skiplist_add(&sk, test_data[i], (void*) test_data[i]);
		printf("k: 0x%x, rv: %i\n", test_data[i], rv);
	}

	printf("searching...\n");
	for (i = 0; i < sizeof(test_data) / sizeof(uint32_t); i++) {
		void * d = olio_skiplist_find(&sk, test_data[i]);
		printf("k: 0x%x, data: %p\n", test_data[i], d);
	}

	olio_skiplist_display(&sk);

	printf("freeing...\n");
	olio_skiplist_free(&sk);

	olio_skiplist_display(&sk);

	olio_skiplist_init(&sk);
	printf("adding...\n");
	for (i = 0; i < sizeof(test_data) / sizeof(uint32_t); i++) {
		int rv = olio_skiplist_add(&sk, test_data[i], (void*) test_data[i]);
		printf("k: 0x%x, rv: %i\n", test_data[i], rv);
	}

	printf("deleting...\n");
	for (i = 0; i < sizeof(test_data) / sizeof(uint32_t); i++) {
		void * d;
		int rv = olio_skiplist_remove(&sk, test_data[i], &d);
		printf("k: 0x%x, rv: %i, data: %p\n", test_data[i], rv, d);
		olio_skiplist_display(&sk);
	}

	printf("freeing...\n");
	olio_skiplist_free(&sk);

	olio_skiplist_display(&sk);
}

#include "error.h"

static void test_error()
{
}

#ifdef HAVE_GSL
#include <gsl/gsl_rng.h>
#include "random.h"

gsl_rng * gsl;
olio_random olio;

static void test_random()
{
    uint32_t seed;
    uint32_t a, b;
    long i;

    printf("[testing random...]\n"); fflush(stdout);

    gsl = gsl_rng_alloc(gsl_rng_mt19937);

    //for (seed = 0; seed < 1000; seed++)
    {
	seed = 0;
	gsl_rng_set(gsl, 0);
	olio_random_set_seed(&olio, 4357);
	for (i = 0; i < 10000; i++) {
	    a = gsl_rng_uniform_int(gsl, 0xffffffff);
	    b = olio_random_integer(&olio);
	    if (a != b) {
		printf("mismatch\n");
	    }
	}
    }
    gsl_rng_free(gsl);
}
#else
static void test_random() {}
#endif

#include "phash.h"
#include "hash.h"

static void test_hashes()
{
    unsigned long i;
    const char * s[] = {
	"alpha", "beta", "gamma", "delta", "epsilon",
	"lamda", "mu", "nu", "omicron", "pi", "phi", "psi",
	"tau", "theta", "zeta", NULL
    };

    olio_phash_build m;
    int rc;

    printf("[testing hashes...]\n"); fflush(stdout);

    rc = olio_phash_init(&m, NULL);
    if (rc == -1) {
	printf("error, aborting\n");
	exit(1);
    }

    for (i = 0; s[i] != NULL; i++) {
	rc = olio_phash_add_entry(&m, s[i], strlen(s[i]), i * 1000);
	if (rc == -1) {
	    printf("error, aborting\n");
	    exit(1);
	}
    }

    rc = olio_phash_generate(&m, 1000, 1);
    if (rc < 0) {
	printf("error, aborting\n");
	exit(1);
    }
    if (rc > 0) {
	printf("failed to generate with attempt/extra limits\n");
	exit(1);
    }

    printf("phash generated:\n");
    printf("A hash: %08x\nB hash: %08x\nG size: %u\n",
	   m.phash->a_hash_seed, m.phash->b_hash_seed,
	   m.phash->g_table_size);

    printf("{ ");
    for (i = 0; i < m.phash->g_table_size; i++) {
	int16_t * gv = (int16_t *) m.phash->data;
	printf("%i, ", gv[i]);
    }
    printf("}\n");

    for (i = 0; s[i] != NULL; i++) {
	int32_t v = olio_phash_value(m.phash,
				     s[i], strlen(s[i]));
	printf(" %s -> %li\n",
	       s[i], v);
    }

    olio_phash_free(&m);
}

#include "varint.h"

static void test_varint_sub(uint32_t x, uint8_t bytes_expected)
{
    uint32_t a;
    uint8_t storage[5];
    uint8_t bytes_set, bytes_get;
    uint8_t i;

    bytes_set = olio_varint_set(x, storage);
    a = olio_varint_get(storage, &bytes_get);

    if (a != x || bytes_set != bytes_get || bytes_set != bytes_expected)
    {
	printf("x = %lu\n(%u) 0x", x, bytes_set);
	for (i = 0; i < bytes_set; i++)
	    printf("%02x", storage[i]);
	printf("\na = %lu\n(%u)\n\n", a, bytes_get);
    }
}

static void test_varint()
{
    printf("[testing varint...]\n"); fflush(stdout);

    test_varint_sub(0, 1);
    test_varint_sub(1,1);
    test_varint_sub(127, 1);
    test_varint_sub(200, 2);
    test_varint_sub(32000, 3);
    test_varint_sub(32000000, 3);
    test_varint_sub(100000000, 4);
    test_varint_sub(400000000, 5);
    test_varint_sub(0, 1);
    test_varint_sub(1, 1);
}

int main(int argv, char ** argc)
{
	test_buffer();
	test_array();
	test_string();
	test_graph();
	test_skiplist();
	test_error();
	test_random();
	test_hashes();
	test_varint();
}