/* vector/gsl_vector_complex_float.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007 Gerard Jungman, 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.
 */

#ifndef __GSL_VECTOR_COMPLEX_FLOAT_H__
#define __GSL_VECTOR_COMPLEX_FLOAT_H__

#if !defined( GSL_FUN )
#  if !defined( GSL_DLL )
#    define GSL_FUN extern
#  elif defined( BUILD_GSL_DLL )
#    define GSL_FUN extern __declspec(dllexport)
#  else
#    define GSL_FUN extern __declspec(dllimport)
#  endif
#endif

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_complex.h>
#include <gsl/gsl_check_range.h>
#include <gsl/gsl_vector_float.h>
#include <gsl/gsl_vector_complex.h>
#include <gsl/gsl_block_complex_float.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

typedef struct
{
  size_t size;
  size_t stride;
  float *data;
  gsl_block_complex_float *block;
  int owner;
} gsl_vector_complex_float;

typedef struct
{
  gsl_vector_complex_float vector;
} _gsl_vector_complex_float_view;

typedef _gsl_vector_complex_float_view gsl_vector_complex_float_view;

typedef struct
{
  gsl_vector_complex_float vector;
} _gsl_vector_complex_float_const_view;

typedef const _gsl_vector_complex_float_const_view gsl_vector_complex_float_const_view;

/* Allocation */

GSL_FUN gsl_vector_complex_float *gsl_vector_complex_float_alloc (const size_t n);
GSL_FUN gsl_vector_complex_float *gsl_vector_complex_float_calloc (const size_t n);

GSL_FUN gsl_vector_complex_float *
gsl_vector_complex_float_alloc_from_block (gsl_block_complex_float * b,
                                           const size_t offset,
                                           const size_t n,
                                           const size_t stride);

GSL_FUN gsl_vector_complex_float *
gsl_vector_complex_float_alloc_from_vector (gsl_vector_complex_float * v,
                                             const size_t offset,
                                             const size_t n,
                                             const size_t stride);

GSL_FUN void gsl_vector_complex_float_free (gsl_vector_complex_float * v);

/* Views */

GSL_FUN _gsl_vector_complex_float_view
gsl_vector_complex_float_view_array (float *base,
                                     size_t n);

GSL_FUN _gsl_vector_complex_float_view
gsl_vector_complex_float_view_array_with_stride (float *base,
                                                 size_t stride,
                                                 size_t n);

GSL_FUN _gsl_vector_complex_float_const_view
gsl_vector_complex_float_const_view_array (const float *base,
                                           size_t n);

GSL_FUN _gsl_vector_complex_float_const_view
gsl_vector_complex_float_const_view_array_with_stride (const float *base,
                                                       size_t stride,
                                                       size_t n);

GSL_FUN _gsl_vector_complex_float_view
gsl_vector_complex_float_subvector (gsl_vector_complex_float *base,
                                         size_t i,
                                         size_t n);

GSL_FUN _gsl_vector_complex_float_view
gsl_vector_complex_float_subvector_with_stride (gsl_vector_complex_float *v,
                                                size_t i,
                                                size_t stride,
                                                size_t n);

GSL_FUN _gsl_vector_complex_float_const_view
gsl_vector_complex_float_const_subvector (const gsl_vector_complex_float *base,
                                               size_t i,
                                               size_t n);

GSL_FUN _gsl_vector_complex_float_const_view
gsl_vector_complex_float_const_subvector_with_stride (const gsl_vector_complex_float *v,
                                                      size_t i,
                                                      size_t stride,
                                                      size_t n);

GSL_FUN _gsl_vector_float_view
gsl_vector_complex_float_real (gsl_vector_complex_float *v);

GSL_FUN _gsl_vector_float_view
gsl_vector_complex_float_imag (gsl_vector_complex_float *v);

GSL_FUN _gsl_vector_float_const_view
gsl_vector_complex_float_const_real (const gsl_vector_complex_float *v);

GSL_FUN _gsl_vector_float_const_view
gsl_vector_complex_float_const_imag (const gsl_vector_complex_float *v);

/* Operations */

GSL_FUN void gsl_vector_complex_float_set_zero (gsl_vector_complex_float * v);
GSL_FUN void gsl_vector_complex_float_set_all (gsl_vector_complex_float * v,
                                       gsl_complex_float z);
GSL_FUN int gsl_vector_complex_float_set_basis (gsl_vector_complex_float * v, size_t i);

GSL_FUN int gsl_vector_complex_float_fread (FILE * stream,
                                    gsl_vector_complex_float * v);
GSL_FUN int gsl_vector_complex_float_fwrite (FILE * stream,
                                     const gsl_vector_complex_float * v);
GSL_FUN int gsl_vector_complex_float_fscanf (FILE * stream,
                                     gsl_vector_complex_float * v);
GSL_FUN int gsl_vector_complex_float_fprintf (FILE * stream,
                                      const gsl_vector_complex_float * v,
                                      const char *format);

GSL_FUN int gsl_vector_complex_float_memcpy (gsl_vector_complex_float * dest, const gsl_vector_complex_float * src);

GSL_FUN int gsl_vector_complex_float_reverse (gsl_vector_complex_float * v);

GSL_FUN int gsl_vector_complex_float_swap (gsl_vector_complex_float * v, gsl_vector_complex_float * w);
GSL_FUN int gsl_vector_complex_float_swap_elements (gsl_vector_complex_float * v, const size_t i, const size_t j);

GSL_FUN int gsl_vector_complex_float_equal (const gsl_vector_complex_float * u,
                                    const gsl_vector_complex_float * v);

GSL_FUN int gsl_vector_complex_float_isnull (const gsl_vector_complex_float * v);
GSL_FUN int gsl_vector_complex_float_ispos (const gsl_vector_complex_float * v);
GSL_FUN int gsl_vector_complex_float_isneg (const gsl_vector_complex_float * v);
GSL_FUN int gsl_vector_complex_float_isnonneg (const gsl_vector_complex_float * v);

GSL_FUN int gsl_vector_complex_float_add (gsl_vector_complex_float * a, const gsl_vector_complex_float * b);
GSL_FUN int gsl_vector_complex_float_sub (gsl_vector_complex_float * a, const gsl_vector_complex_float * b);
GSL_FUN int gsl_vector_complex_float_mul (gsl_vector_complex_float * a, const gsl_vector_complex_float * b);
GSL_FUN int gsl_vector_complex_float_div (gsl_vector_complex_float * a, const gsl_vector_complex_float * b);
GSL_FUN int gsl_vector_complex_float_scale (gsl_vector_complex_float * a, const gsl_complex_float x);
GSL_FUN int gsl_vector_complex_float_add_constant (gsl_vector_complex_float * a, const gsl_complex_float x);
GSL_FUN int gsl_vector_complex_float_axpby (const gsl_complex_float alpha, const gsl_vector_complex_float * x, const gsl_complex_float beta, gsl_vector_complex_float * y);

GSL_FUN INLINE_DECL gsl_complex_float gsl_vector_complex_float_get (const gsl_vector_complex_float * v, const size_t i);
GSL_FUN INLINE_DECL void gsl_vector_complex_float_set (gsl_vector_complex_float * v, const size_t i, gsl_complex_float z);
GSL_FUN INLINE_DECL gsl_complex_float *gsl_vector_complex_float_ptr (gsl_vector_complex_float * v, const size_t i);
GSL_FUN INLINE_DECL const gsl_complex_float *gsl_vector_complex_float_const_ptr (const gsl_vector_complex_float * v, const size_t i);

#ifdef HAVE_INLINE

INLINE_FUN
gsl_complex_float
gsl_vector_complex_float_get (const gsl_vector_complex_float * v,
                              const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      gsl_complex_float zero = {{0, 0}};
      GSL_ERROR_VAL ("index out of range", GSL_EINVAL, zero);
    }
#endif
  return *GSL_COMPLEX_FLOAT_AT (v, i);
}

INLINE_FUN
void
gsl_vector_complex_float_set (gsl_vector_complex_float * v,
                              const size_t i, gsl_complex_float z)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_VOID ("index out of range", GSL_EINVAL);
    }
#endif
  *GSL_COMPLEX_FLOAT_AT (v, i) = z;
}

INLINE_FUN
gsl_complex_float *
gsl_vector_complex_float_ptr (gsl_vector_complex_float * v,
                              const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return GSL_COMPLEX_FLOAT_AT (v, i);
}

INLINE_FUN
const gsl_complex_float *
gsl_vector_complex_float_const_ptr (const gsl_vector_complex_float * v,
                                    const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return GSL_COMPLEX_FLOAT_AT (v, i);
}

#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_VECTOR_COMPLEX_FLOAT_H__ */


#pragma once

#include <type_traits>
#include <vector>

#include <gsl-lite/gsl-lite.hpp>

namespace thrustshift {

/*! \brief Make multiple spans from a range of objects which contain ranges.
 *
 *  \param r
 *  \param get_range This functor must **not** return a copy of any object
 *
 *  ```cpp
 *  struct S {
 *
 *    std::vector<int> v;
 *  };
 *  ...
 *
 *  std::vector<S> ss;
 *  // fill ss
 *
 *  //// WRONG
 *  auto spans_with_invalid_ptrs = make_spans_from_ranges(ss, [](const auto& e) { return e.v; });
 *
 *  //// CORRECT
 *  auto spans = make_spans_from_ranges(ss, [](const auto& e) { return gsl_lite::make_span(e.v); });
 *  ```
 *
 *  The wrong lambda is returning a copy of member vector `v`, which is then deallocated.
 *
 */
template <class T, class RangeOfRanges, class GetRangeCallback>
std::vector<gsl_lite::span<T>> make_spans_from_ranges(
    RangeOfRanges&& r,
    GetRangeCallback&& get_range) {
	std::vector<gsl_lite::span<T>> spans(r.size());
	for (size_t i = 0; i < spans.size(); ++i) {
		spans[i] =
		    gsl_lite::make_span(get_range(r[i]).data(), get_range(r[i]).size());
	}
	return spans;
}

template <class T, class RangeOfRanges>
std::vector<gsl_lite::span<T>> make_spans_from_ranges(RangeOfRanges&& r) {
	auto get_range = [](auto& c) { return gsl_lite::make_span(c); };
	return make_spans_from_ranges<T>(std::forward<RangeOfRanges>(r), get_range);
}

template <class T, class RangeOfPtrs>
std::vector<gsl_lite::span<T>> make_spans_from_ptrs(RangeOfPtrs&& r, size_t N) {

	std::vector<gsl_lite::span<T>> spans(r.size());
	for (size_t i = 0; i < spans.size(); ++i) {
		spans[i] = gsl_lite::make_span(r[i].get(), N);
	}
	return spans;
}

template <typename T>
gsl_lite::span<T> subtract(gsl_lite::span<T>& pool, size_t size_of_new_span) {
	gsl_Expects(pool.size() >= size_of_new_span);
	auto piece = pool.first(size_of_new_span);
	pool = pool.subspan(size_of_new_span);
	return piece;
}

} // namespace thrustshift


///
/// @file This file contains OpenMP and BLAS thread count configuration
/// interface.
///
/// @author Mirko Myllykoski (mirkom@cs.umu.se), Umeå University
///
/// @internal LICENSE
///
/// Copyright (c) 2019-2020, Umeå Universitet
///
/// 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.
///

#include <starneig_test_config.h>
#include <starneig/configuration.h>
#include "threads.h"
#include "parse.h"
#include "omp.h"
#include <hwloc.h>
#ifdef MKL_SET_NUM_THREADS_LOCAL_FOUND
#include <mkl.h>
#endif
#if defined(OPENBLAS_SET_NUM_THREADS_FOUND) || \
defined(GOTO_SET_NUM_THREADS_FOUND)
#include <cblas.h>
#endif

static struct {
    int worker_threads;
    int blas_threads;
    int lapack_threads;
    int scalapack_threads;
} status = {
    .worker_threads = 1,
    .blas_threads = 1,
    .lapack_threads = 1,
    .scalapack_threads = 1
};

static int get_core_count()
{
    hwloc_topology_t topology;
    hwloc_topology_init(&topology);
    hwloc_topology_load(topology);

    hwloc_cpuset_t res = hwloc_bitmap_alloc();

    hwloc_cpuset_t mask = hwloc_bitmap_alloc();
    hwloc_get_cpubind(topology, mask, HWLOC_CPUBIND_THREAD);

    int depth_cores = hwloc_get_type_depth(topology, HWLOC_OBJ_CORE);
    int num_cores = hwloc_get_nbobjs_by_depth(topology, depth_cores);

    int num_hwloc_cpus = 0;

    // iterate over all COREs
    for (int i = 0; i < num_cores; i++) {
        hwloc_obj_t core = hwloc_get_obj_by_depth(topology, depth_cores, i);

        // if the CORE has PUs inside it, ...
        if (core->first_child && core->first_child->type == HWLOC_OBJ_PU) {
            // iterate over them
            hwloc_obj_t pu = core->first_child;
            while (pu) {
                // if the PU is in the binding mask, ...
                hwloc_bitmap_and(res, mask, pu->cpuset);
                if (!hwloc_bitmap_iszero(res)) {
                    // count the PU as a CORE
                    num_hwloc_cpus++;
                    break;
                }
                pu = pu->next_sibling;
            }
        }
        else {
            // if the CORE is in the binding mask, ...
            hwloc_bitmap_and(res, mask, core->cpuset);
            if (!hwloc_bitmap_iszero(res)) {
                // count the CORE
                num_hwloc_cpus++;
            }
        }
    }

    hwloc_bitmap_free(mask);
    hwloc_bitmap_free(res);
    hwloc_topology_destroy(topology);

    return num_hwloc_cpus;
}

static void set_blas_threads(int threads)
{
#if defined(MKL_SET_NUM_THREADS_LOCAL_FOUND)
    mkl_set_num_threads_local(threads);
#elif defined(OPENBLAS_SET_NUM_THREADS_FOUND)
    openblas_set_num_threads(threads);
#elif defined(GOTO_SET_NUM_THREADS_FOUND)
    goto_set_num_threads(threads);
#endif
}

void thread_print_usage(int argc, char * const *argv)
{
    printf(
        "  --test-workers [(num),default] -- Test program StarPU worker count\n"
        "  --blas-threads [(num),default] -- Test program BLAS thread count\n"
        "  --lapack-threads [(num),default] -- LAPACK solver thread count\n"
        "  --scalapack-threads [(num),default] -- ScaLAPACK solver thread "
        "count\n"
    );
}

void thread_print_args(int argc, char * const *argv)
{
    print_multiarg("--test-workers", argc, argv, "default", NULL);
    print_multiarg("--blas-threads", argc, argv, "default", NULL);
    print_multiarg("--lapack-threads", argc, argv, "default", NULL);
    print_multiarg("--scalapack-threads", argc, argv, "default", NULL);
}

int thread_check_args(int argc, char * const *argv, int *argr)
{
    struct multiarg_t worker_threads =
        read_multiarg("--test-workers", argc, argv, argr, "default", NULL);

    if (worker_threads.type == MULTIARG_INVALID ||
    (worker_threads.type == MULTIARG_INT && worker_threads.int_value < 1)) {
        fprintf(stderr, "Invalid number of StarPU worker threads.\n");
        return 1;
    }

    struct multiarg_t blas_threads =
        read_multiarg("--blas-threads", argc, argv, argr, "default", NULL);

    if (blas_threads.type == MULTIARG_INVALID ||
    (blas_threads.type == MULTIARG_INT && blas_threads.int_value < 1)) {
        fprintf(stderr, "Invalid number of BLAS threads.\n");
        return 1;
    }

    struct multiarg_t lapack_threads =
        read_multiarg("--lapack-threads", argc, argv, argr, "default", NULL);

    if (lapack_threads.type == MULTIARG_INVALID ||
    (lapack_threads.type == MULTIARG_INT && lapack_threads.int_value < 1)) {
        fprintf(stderr, "Invalid number of LAPACK threads.\n");
        return 1;
    }

    struct multiarg_t scalapack_threads =
        read_multiarg("--scalapack-threads", argc, argv, argr, "default", NULL);

    if (scalapack_threads.type == MULTIARG_INVALID ||
    (scalapack_threads.type == MULTIARG_INT &&
    scalapack_threads.int_value < 1)) {
        fprintf(stderr, "Invalid number of ScaLAPACK threads.\n");
        return 1;
    }

    return 0;
}

void threads_init(int argc, char * const *argv)
{
    struct multiarg_t worker_threads =
        read_multiarg("--test-workers", argc, argv, NULL, "default", NULL);
    if (worker_threads.type == MULTIARG_INT)
        status.worker_threads = worker_threads.int_value;
    else
        status.worker_threads = get_core_count();
    printf(
        "THREADS: Using %d StarPU worker threads during initialization and "
        "validation.\n", status.worker_threads);

    struct multiarg_t blas_threads =
        read_multiarg("--blas-threads", argc, argv, NULL, "default", NULL);
    if (blas_threads.type == MULTIARG_INT)
        status.blas_threads = blas_threads.int_value;
    else
        status.blas_threads = get_core_count();
    printf(
        "THREADS: Using %d BLAS threads during initialization and "
        "validation.\n", status.blas_threads);

    struct multiarg_t lapack_threads =
        read_multiarg("--lapack-threads", argc, argv, NULL, "default", NULL);
    if (lapack_threads.type == MULTIARG_INT)
        status.lapack_threads = lapack_threads.int_value;
    else
        status.lapack_threads = get_core_count();
    printf(
        "THREADS: Using %d BLAS threads in LAPACK solvers.\n",
        status.lapack_threads);

    struct multiarg_t scalapack_threads =
        read_multiarg("--scalapack-threads", argc, argv, NULL, "default", NULL);
    if (scalapack_threads.type == MULTIARG_INT)
        status.scalapack_threads = scalapack_threads.int_value;
    else
        status.scalapack_threads = 1;
    printf(
        "THREADS: Using %d BLAS threads in ScaLAPACK solvers.\n",
        status.scalapack_threads);

    threads_set_mode(THREADS_MODE_DEFAULT);
}

void threads_set_mode(thread_mode_t mode)
{
    switch (mode) {
        case THREADS_MODE_BLAS:
            set_blas_threads(status.blas_threads);
            break;
        case THREADS_MODE_LAPACK:
            set_blas_threads(status.lapack_threads);
            break;
        case THREADS_MODE_SCALAPACK:
            set_blas_threads(status.scalapack_threads);
            break;
        default:
            set_blas_threads(1);
    }
}

int threads_get_workers()
{
    return status.worker_threads;
}

starneig_flag_t threads_get_fast_dm()
{
    if (1 < threads_get_workers())
        return STARNEIG_FAST_DM;
    return STARNEIG_HINT_DM;
}


#pragma once

#include <gsl/span>

namespace miner {

    template<class T, std::ptrdiff_t Extent = gsl::dynamic_extent>
    using span = gsl::span<T, Extent>;

    template<std::ptrdiff_t Extent = gsl::dynamic_extent>
    using ByteSpan = span<uint8_t, Extent>;

    template<std::ptrdiff_t Extent = gsl::dynamic_extent>
    using cByteSpan = span<const uint8_t, Extent>;
}


#ifndef CACHED_IMAGE_H
#define CACHED_IMAGE_H

#ifndef solaris
#  include <stdint.h>
#endif
#include "asf_meta.h"
#include "float_image.h"
#include <stdio.h>
#include <sys/types.h>

#include <glib.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_histogram.h>

//---------------------------------------------------------------------------
// Adding a new supported data type:
//  cache.c:
//    data_size()
//    cached_image_get_pixel()
//    cached_image_get_rgb()
//  stats.c:
//    generate_thumbnail_data()
//  big_image.c:
//    update_pixel_info()
//  read_X.c: (for clients that will handle the data type)
//    open_X_data()
//
typedef enum {
    UNDEFINED = 0,
    GREYSCALE_FLOAT = 1,
    GREYSCALE_BYTE = 2,
    RGB_BYTE = 3,
    RGB_FLOAT = 4
} ssv_data_type_t;

// Stats structure -- this one is used by both greyscale and RGB
typedef struct {
    double map_min, map_max;
    double avg, stddev;
    double act_min, act_max; // absolute min/max of all values
    int hist[256];           // histogram
    double no_data_value;    // value indicating "no data"
    int have_no_data;        // TRUE if "no_data_value" is present
    double no_data_min;
    double no_data_max;
    int have_no_data_range;
    int truncate;
} ImageStats;

// Stats structure -- this one is used only for RGB images, keeps track
//                    of stats for each channel
typedef struct {
    double map_min, map_max;
    double avg, stddev;
    double act_min, act_max; // absolute min/max of all values
    double no_data_value;
    int have_no_data;
    double no_data_max;
    double no_data_min;
    int have_no_data_range;
    int truncate;
} ImageStatsRGB;

// NOTE: At the moment, the Pixbuf that contains the displayed data
// is plain RGB, not RGB-A, so adding support for transparency would
// also involve changing big_image.c/make_big_image(), and you'd have
// to add a method to the cache interface that returned the alpha
// channel in addition to the rgb values.

//---------------------------------------------------------------------------
// This is the client interface -- the set of function pointers, etc,
// that encapsulate how the image cache gets data from the file.
// See: -read.c/read_file() for how the cache is hooked up to the
//          client depending on what the file type is
//      -read_template.c for an example of how to add another client
typedef int ReadClientFn(int row_start, int n_rows_to_get,
                         void *dest, void *read_client_info,
                         meta_parameters *meta, int data_type);
typedef int ThumbFn(int thumb_size_x, int thumb_size_y, meta_parameters *meta,
                    void *read_client_info, void *dest, int data_type);
typedef void FreeFn(void *read_client_info);

typedef struct {
    ReadClientFn *read_fn;
    ThumbFn *thumb_fn;
    FreeFn *free_fn;
    void *read_client_info;
    ssv_data_type_t data_type;
    int require_full_load;
} ClientInterface;

//---------------------------------------------------------------------------
// Here is the ImageCache stuff.  The global ImageCache that holds the
// loaded image is "data_ci".  This is all private data.
typedef struct {
  int nl, ns;               // Image dimensions.
  ClientInterface *client;  // pointers to data read implementations
  int n_tiles;              // Number of tiles in memory
  int reached_max_tiles;    // Have we loaded as many tiles as we can?
  int rows_per_tile;        // Number of rows in each tile
  int entire_image_fits;    // TRUE if we can load the entire image
  int *rowstarts;           // Row numbers starting each tile
  unsigned char **cache;    // Cached values (floats, unsigned chars ...)
  int *access_counts;       // Updated when a tile is accessed
  int n_access;             // used to find oldest tile
  ssv_data_type_t data_type;// type of data we have
  meta_parameters *meta;    // metadata -- don't own this pointer
  ImageStats *stats;        // not owned by us, not populated by us
  ImageStatsRGB *stats_r;   // not owned by us, not populated by us
  ImageStatsRGB *stats_g;   // not owned by us, not populated by us
  ImageStatsRGB *stats_b;   // not owned by us, not populated by us
} CachedImage;

CachedImage * cached_image_new_from_file(
    const char *file, meta_parameters *meta, ClientInterface *client,
    ImageStats *stats, ImageStatsRGB *stats_r, ImageStatsRGB *stats_g,
    ImageStatsRGB *stats_b);

float cached_image_get_pixel (CachedImage *self, int line, int samp);
void cached_image_get_rgb(CachedImage *self, int line, int samp,
                          unsigned char *r, unsigned char *g,
                          unsigned char *b);
void cached_image_get_rgb_float(CachedImage *self, int line, int samp,
                                float *r, float *g, float *b);

void load_thumbnail_data(CachedImage *self, int thumb_size_x, int thumb_size_y,
                         void *dest);

void cached_image_free (CachedImage *self);

#endif


/*
 Copyright (C) 2002 M. Marques, A. Castro, A. Rubio, G. Bertsch

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

 $Id$
*/

#include <config.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <dirent.h>
#include <sys/time.h>
#include <gsl/gsl_rng.h>

#include "string_f.h"

unsigned long int random_seed()
{
 unsigned long int seed;
 FILE *devrandom;

 if ((devrandom = fopen("/dev/urandom","r")) == NULL) {
#ifdef HAVE_GETTIMEOFDAY
   struct timeval tv;
   gettimeofday(&tv, 0);
   seed = tv.tv_sec + tv.tv_usec;
#else
   seed = 0;
#endif
 } else {
   fread(&seed, sizeof(seed), 1, devrandom);
   fclose(devrandom);
 }

 return seed;
}

void FC_FUNC_(oct_printrecipe, OCT_PRINTRECIPE)
  (STR_F_TYPE _dir, STR_F_TYPE filename STR_ARG2)
{

#if HAVE_SCANDIR && HAVE_ALPHASORT
  char *lang, *tmp, dir[512];
  struct dirent **namelist;
  int ii, nn;
  gsl_rng *rng;

  /* get language */
  lang = getenv("LANG");
  if(lang == NULL) lang = "en";

  /* convert directory from Fortran to C string */
  TO_C_STR1(_dir, tmp);
  strcpy(dir, tmp);
  free(tmp);

  strcat(dir, "/recipes");

  /* check out if lang dir exists */
  nn = scandir(dir, &namelist, 0, alphasort);
  if (nn < 0){
    printf("Directory does not exist: %s", dir);
    return;
  }

  for(ii=0; ii<nn; ii++)
    if(strncmp(lang, namelist[ii]->d_name, 2) == 0){
      strcat(dir, "/");
      strcat(dir, namelist[ii]->d_name);
      break;
    }

  if(ii == nn)
    strcat(dir, "/en"); /* default */

  /* clean up */
  for(ii=0; ii<nn; ii++)
    free(namelist[ii]);
  free(namelist);

  /* now we read the recipes */
  nn = scandir(dir, &namelist, 0, alphasort);

  /* initialize random numbers */
  gsl_rng_env_setup();
  rng = gsl_rng_alloc(gsl_rng_default);
  gsl_rng_set(rng, random_seed());
  ii = gsl_rng_uniform_int(rng, nn - 2);
  gsl_rng_free(rng);

  strcat(dir, "/");
  strcat(dir, namelist[ii+2]->d_name); /* skip ./ and ../ */

  /* clean up again */
  for(ii=0; ii<nn; ii++)
    free(namelist[ii]);
  free(namelist);

  TO_F_STR2(dir, filename);

#else
  printf("Sorry, recipes cannot be printed unless scandir and alphasort are available with your C compiler.\n");
#endif
}


/* vector/gsl_vector_uchar.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007 Gerard Jungman, 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.
 */

#ifndef __GSL_VECTOR_UCHAR_H__
#define __GSL_VECTOR_UCHAR_H__

#if !defined( GSL_FUN )
#  if !defined( GSL_DLL )
#    define GSL_FUN extern
#  elif defined( BUILD_GSL_DLL )
#    define GSL_FUN extern __declspec(dllexport)
#  else
#    define GSL_FUN extern __declspec(dllimport)
#  endif
#endif

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_inline.h>
#include <gsl/gsl_check_range.h>
#include <gsl/gsl_block_uchar.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

typedef struct
{
  size_t size;
  size_t stride;
  unsigned char *data;
  gsl_block_uchar *block;
  int owner;
}
gsl_vector_uchar;

typedef struct
{
  gsl_vector_uchar vector;
} _gsl_vector_uchar_view;

typedef _gsl_vector_uchar_view gsl_vector_uchar_view;

typedef struct
{
  gsl_vector_uchar vector;
} _gsl_vector_uchar_const_view;

typedef const _gsl_vector_uchar_const_view gsl_vector_uchar_const_view;

/* Allocation */

GSL_FUN gsl_vector_uchar *gsl_vector_uchar_alloc (const size_t n);
GSL_FUN gsl_vector_uchar *gsl_vector_uchar_calloc (const size_t n);

GSL_FUN gsl_vector_uchar *gsl_vector_uchar_alloc_from_block (gsl_block_uchar * b,
                                                     const size_t offset,
                                                     const size_t n,
                                                     const size_t stride);

GSL_FUN gsl_vector_uchar *gsl_vector_uchar_alloc_from_vector (gsl_vector_uchar * v,
                                                      const size_t offset,
                                                      const size_t n,
                                                      const size_t stride);

GSL_FUN void gsl_vector_uchar_free (gsl_vector_uchar * v);

/* Views */

GSL_FUN _gsl_vector_uchar_view
gsl_vector_uchar_view_array (unsigned char *v, size_t n);

GSL_FUN _gsl_vector_uchar_view
gsl_vector_uchar_view_array_with_stride (unsigned char *base,
                                         size_t stride,
                                         size_t n);

GSL_FUN _gsl_vector_uchar_const_view
gsl_vector_uchar_const_view_array (const unsigned char *v, size_t n);

GSL_FUN _gsl_vector_uchar_const_view
gsl_vector_uchar_const_view_array_with_stride (const unsigned char *base,
                                               size_t stride,
                                               size_t n);

GSL_FUN _gsl_vector_uchar_view
gsl_vector_uchar_subvector (gsl_vector_uchar *v,
                            size_t i,
                            size_t n);

GSL_FUN _gsl_vector_uchar_view
gsl_vector_uchar_subvector_with_stride (gsl_vector_uchar *v,
                                        size_t i,
                                        size_t stride,
                                        size_t n);

GSL_FUN _gsl_vector_uchar_const_view
gsl_vector_uchar_const_subvector (const gsl_vector_uchar *v,
                                  size_t i,
                                  size_t n);

GSL_FUN _gsl_vector_uchar_const_view
gsl_vector_uchar_const_subvector_with_stride (const gsl_vector_uchar *v,
                                              size_t i,
                                              size_t stride,
                                              size_t n);

/* Operations */

GSL_FUN void gsl_vector_uchar_set_zero (gsl_vector_uchar * v);
GSL_FUN void gsl_vector_uchar_set_all (gsl_vector_uchar * v, unsigned char x);
GSL_FUN int gsl_vector_uchar_set_basis (gsl_vector_uchar * v, size_t i);

GSL_FUN int gsl_vector_uchar_fread (FILE * stream, gsl_vector_uchar * v);
GSL_FUN int gsl_vector_uchar_fwrite (FILE * stream, const gsl_vector_uchar * v);
GSL_FUN int gsl_vector_uchar_fscanf (FILE * stream, gsl_vector_uchar * v);
GSL_FUN int gsl_vector_uchar_fprintf (FILE * stream, const gsl_vector_uchar * v,
                              const char *format);

GSL_FUN int gsl_vector_uchar_memcpy (gsl_vector_uchar * dest, const gsl_vector_uchar * src);

GSL_FUN int gsl_vector_uchar_reverse (gsl_vector_uchar * v);

GSL_FUN int gsl_vector_uchar_swap (gsl_vector_uchar * v, gsl_vector_uchar * w);
GSL_FUN int gsl_vector_uchar_swap_elements (gsl_vector_uchar * v, const size_t i, const size_t j);

GSL_FUN unsigned char gsl_vector_uchar_max (const gsl_vector_uchar * v);
GSL_FUN unsigned char gsl_vector_uchar_min (const gsl_vector_uchar * v);
GSL_FUN void gsl_vector_uchar_minmax (const gsl_vector_uchar * v, unsigned char * min_out, unsigned char * max_out);

GSL_FUN size_t gsl_vector_uchar_max_index (const gsl_vector_uchar * v);
GSL_FUN size_t gsl_vector_uchar_min_index (const gsl_vector_uchar * v);
GSL_FUN void gsl_vector_uchar_minmax_index (const gsl_vector_uchar * v, size_t * imin, size_t * imax);

GSL_FUN int gsl_vector_uchar_add (gsl_vector_uchar * a, const gsl_vector_uchar * b);
GSL_FUN int gsl_vector_uchar_sub (gsl_vector_uchar * a, const gsl_vector_uchar * b);
GSL_FUN int gsl_vector_uchar_mul (gsl_vector_uchar * a, const gsl_vector_uchar * b);
GSL_FUN int gsl_vector_uchar_div (gsl_vector_uchar * a, const gsl_vector_uchar * b);
GSL_FUN int gsl_vector_uchar_scale (gsl_vector_uchar * a, const unsigned char x);
GSL_FUN int gsl_vector_uchar_add_constant (gsl_vector_uchar * a, const double x);
GSL_FUN int gsl_vector_uchar_axpby (const unsigned char alpha, const gsl_vector_uchar * x, const unsigned char beta, gsl_vector_uchar * y);
GSL_FUN unsigned char gsl_vector_uchar_sum (const gsl_vector_uchar * a);

GSL_FUN int gsl_vector_uchar_equal (const gsl_vector_uchar * u,
                            const gsl_vector_uchar * v);

GSL_FUN int gsl_vector_uchar_isnull (const gsl_vector_uchar * v);
GSL_FUN int gsl_vector_uchar_ispos (const gsl_vector_uchar * v);
GSL_FUN int gsl_vector_uchar_isneg (const gsl_vector_uchar * v);
GSL_FUN int gsl_vector_uchar_isnonneg (const gsl_vector_uchar * v);

GSL_FUN INLINE_DECL unsigned char gsl_vector_uchar_get (const gsl_vector_uchar * v, const size_t i);
GSL_FUN INLINE_DECL void gsl_vector_uchar_set (gsl_vector_uchar * v, const size_t i, unsigned char x);
GSL_FUN INLINE_DECL unsigned char * gsl_vector_uchar_ptr (gsl_vector_uchar * v, const size_t i);
GSL_FUN INLINE_DECL const unsigned char * gsl_vector_uchar_const_ptr (const gsl_vector_uchar * v, const size_t i);

#ifdef HAVE_INLINE

INLINE_FUN
unsigned char
gsl_vector_uchar_get (const gsl_vector_uchar * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_VAL ("index out of range", GSL_EINVAL, 0);
    }
#endif
  return v->data[i * v->stride];
}

INLINE_FUN
void
gsl_vector_uchar_set (gsl_vector_uchar * v, const size_t i, unsigned char x)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_VOID ("index out of range", GSL_EINVAL);
    }
#endif
  v->data[i * v->stride] = x;
}

INLINE_FUN
unsigned char *
gsl_vector_uchar_ptr (gsl_vector_uchar * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return (unsigned char *) (v->data + i * v->stride);
}

INLINE_FUN
const unsigned char *
gsl_vector_uchar_const_ptr (const gsl_vector_uchar * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return (const unsigned char *) (v->data + i * v->stride);
}
#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_VECTOR_UCHAR_H__ */


#pragma once
#include <cstddef>
#include <gsl/gsl.h>

namespace util{

template<typename T>
using span_dyn = gsl::span<T>;
template<typename T, int64_t M>
using span_1d = gsl::span<T,M>;
template<typename T, int64_t M, int64_t N>
using span_2d = gsl::span<T,M,N>;
template<typename T, int64_t L, int64_t M, int64_t N>
using span_3d = gsl::span<T,L,M,N>;

//Not works and param.cpp directly uses gsl.h
template<typename... Args>
auto as_span(Args&&... args){
    return gsl::as_span(std::forward<Args>(args)...);
}

template<int64_t T>
using dim = gsl::dim<T>;

template<std::ptrdiff_t Extent = gsl::dynamic_range>
using cstring_span = gsl::basic_string_span<const char, Extent>;

}//namespace util


#pragma once

#include "ShaderCompiler.h"

#include <gsl/gsl>
#include <spirv_cross.hpp>
#include <spirv_parser.hpp>
#include <unordered_map>
#include <string>

namespace Babylon::ShaderCompilerCommon
{
    template<typename AppendageT>
    inline void AppendBytes(std::vector<uint8_t>& bytes, const AppendageT appendage)
    {
        auto ptr = reinterpret_cast<const uint8_t*>(&appendage);
        auto stride = static_cast<std::ptrdiff_t>(sizeof(AppendageT));
        bytes.insert(bytes.end(), ptr, ptr + stride);
    }

    template<typename AppendageT = std::string&>
    inline void AppendBytes(std::vector<uint8_t>& bytes, const std::string& string)
    {
        auto ptr = reinterpret_cast<const uint8_t*>(string.data());
        auto stride = static_cast<std::ptrdiff_t>(string.length());
        bytes.insert(bytes.end(), ptr, ptr + stride);
    }

    template<typename ElementT>
    inline void AppendBytes(std::vector<uint8_t>& bytes, const gsl::span<ElementT>& data)
    {
        auto ptr = reinterpret_cast<const uint8_t*>(data.data());
        auto stride = static_cast<std::ptrdiff_t>(data.size() * sizeof(ElementT));
        bytes.insert(bytes.end(), ptr, ptr + stride);
    }

    struct NonSamplerUniformsInfo
    {
        struct Uniform
        {
            enum class TypeEnum
            {
                Vec4,
                Mat4
            };

            std::string Name{};
            uint32_t Offset{};
            uint16_t RegisterSize{};
            TypeEnum Type{};
        };

        uint16_t ByteSize{};
        std::vector<Uniform> Uniforms{};
    };

    void AppendUniformBuffer(std::vector<uint8_t>& bytes, const NonSamplerUniformsInfo& uniformBuffer, bool isFragment);
    void AppendSamplers(std::vector<uint8_t>& bytes, const spirv_cross::Compiler& compiler, const spirv_cross::SmallVector<spirv_cross::Resource>& samplers, std::unordered_map<std::string, uint8_t>& stages);
    NonSamplerUniformsInfo CollectNonSamplerUniforms(spirv_cross::Parser& parser, const spirv_cross::Compiler& compiler);

    struct ShaderInfo
    {
        std::unique_ptr<spirv_cross::Parser> Parser;
        std::unique_ptr<const spirv_cross::Compiler> Compiler;
        gsl::span<uint8_t> Bytes;
        std::unordered_map<std::string, std::string> AttributeRenaming;
    };

    ShaderCompiler::BgfxShaderInfo CreateBgfxShader(ShaderInfo vertexShaderInfo, ShaderInfo fragmentShaderInfo);
}


/*
 * This file is part of the GROMACS molecular simulation package.
 *
 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 * Copyright (c) 2001-2004, The GROMACS development team,
 * check out http://www.gromacs.org for more information.
 * Copyright (c) 2012,2013, by the GROMACS development team, led by
 * David van der Spoel, Berk Hess, Erik Lindahl, and including many
 * others, as listed in the AUTHORS file in the top-level source
 * directory and at http://www.gromacs.org.
 *
 * GROMACS is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1
 * of the License, or (at your option) any later version.
 *
 * GROMACS 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with GROMACS; if not, see
 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 *
 * If you want to redistribute modifications to GROMACS, please
 * consider that scientific software is very special. Version
 * control is crucial - bugs must be traceable. We will be happy to
 * consider code for inclusion in the official distribution, but
 * derived work must not be called official GROMACS. Details are found
 * in the README & COPYING files - if they are missing, get the
 * official version at http://www.gromacs.org.
 *
 * To help us fund GROMACS development, we humbly ask that you cite
 * the research papers on the package. Check out http://www.gromacs.org.
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include "statutil.h"
#include "sysstuff.h"
#include "typedefs.h"
#include "smalloc.h"
#include "macros.h"
#include "gmx_fatal.h"
#include "vec.h"
#include "copyrite.h"
#include "futil.h"
#include "readinp.h"
#include "statutil.h"
#include "txtdump.h"
#include "gstat.h"
#include "xvgr.h"
#include "physics.h"
#include "gmx_ana.h"

enum {
    epAuf, epEuf, epAfu, epEfu, epNR
};
enum {
    eqAif, eqEif, eqAfi, eqEfi, eqAui, eqEui, eqAiu, eqEiu, eqNR
};
static char *eep[epNR] = { "Af", "Ef", "Au", "Eu" };
static char *eeq[eqNR] = { "Aif", "Eif", "Afi", "Efi", "Aui", "Eui", "Aiu", "Eiu" };

typedef struct {
    int       nreplica;  /* Number of replicas in the calculation                   */
    int       nframe;    /* Number of time frames                                   */
    int       nstate;    /* Number of states the system can be in, e.g. F,I,U       */
    int       nparams;   /* Is 2, 4 or 8                                            */
    gmx_bool *bMask;     /* Determine whether this replica is part of the d2 comp.  */
    gmx_bool  bSum;
    gmx_bool  bDiscrete; /* Use either discrete folding (0/1) or a continuous       */
    /* criterion */
    int       nmask;     /* Number of replicas taken into account                   */
    real      dt;        /* Timestep between frames                                 */
    int       j0, j1;    /* Range of frames used in calculating delta               */
    real    **temp, **data, **data2;
    int     **state;     /* State index running from 0 (F) to nstate-1 (U)          */
    real    **beta, **fcalt, **icalt;
    real     *time, *sumft, *sumit, *sumfct, *sumict;
    real     *params;
    real     *d2_replica;
} t_remd_data;

#ifdef HAVE_LIBGSL
#include <gsl/gsl_multimin.h>

static char *itoa(int i)
{
    static char ptr[12];

    sprintf(ptr, "%d", i);
    return ptr;
}

static char *epnm(int nparams, int index)
{
    static char buf[32], from[8], to[8];
    int         nn, ni, ii;

    range_check(index, 0, nparams);
    if ((nparams == 2) || (nparams == 4))
    {
        return eep[index];
    }
    else if ((nparams > 4) && (nparams % 4 == 0))
    {
        return eeq[index];
    }
    else
    {
        gmx_fatal(FARGS, "Don't know how to handle %d parameters", nparams);
    }

    return NULL;
}

static gmx_bool bBack(t_remd_data *d)
{
    return (d->nparams > 2);
}

static real is_folded(t_remd_data *d, int irep, int jframe)
{
    if (d->state[irep][jframe] == 0)
    {
        return 1.0;
    }
    else
    {
        return 0.0;
    }
}

static real is_unfolded(t_remd_data *d, int irep, int jframe)
{
    if (d->state[irep][jframe] == d->nstate-1)
    {
        return 1.0;
    }
    else
    {
        return 0.0;
    }
}

static real is_intermediate(t_remd_data *d, int irep, int jframe)
{
    if ((d->state[irep][jframe] == 1) && (d->nstate > 2))
    {
        return 1.0;
    }
    else
    {
        return 0.0;
    }
}

static void integrate_dfdt(t_remd_data *d)
{
    int    i, j;
    double beta, ddf, ddi, df, db, fac, sumf, sumi, area;

    d->sumfct[0] = 0;
    d->sumict[0] = 0;
    for (i = 0; (i < d->nreplica); i++)
    {
        if (d->bMask[i])
        {
            if (d->bDiscrete)
            {
                ddf = 0.5*d->dt*is_folded(d, i, 0);
                ddi = 0.5*d->dt*is_intermediate(d, i, 0);
            }
            else
            {
                ddf = 0.5*d->dt*d->state[i][0];
                ddi = 0.0;
            }
            d->fcalt[i][0] = ddf;
            d->icalt[i][0] = ddi;
            d->sumfct[0]  += ddf;
            d->sumict[0]  += ddi;
        }
    }
    for (j = 1; (j < d->nframe); j++)
    {
        if (j == d->nframe-1)
        {
            fac = 0.5*d->dt;
        }
        else
        {
            fac = d->dt;
        }
        sumf = sumi = 0;
        for (i = 0; (i < d->nreplica); i++)
        {
            if (d->bMask[i])
            {
                beta = d->beta[i][j];
                if ((d->nstate <= 2) || d->bDiscrete)
                {
                    if (d->bDiscrete)
                    {
                        df = (d->params[epAuf]*exp(-beta*d->params[epEuf])*
                              is_unfolded(d, i, j));
                    }
                    else
                    {
                        area = (d->data2 ? d->data2[i][j] : 1.0);
                        df   =  area*d->params[epAuf]*exp(-beta*d->params[epEuf]);
                    }
                    if (bBack(d))
                    {
                        db = 0;
                        if (d->bDiscrete)
                        {
                            db = (d->params[epAfu]*exp(-beta*d->params[epEfu])*
                                  is_folded(d, i, j));
                        }
                        else
                        {
                            gmx_fatal(FARGS, "Back reaction not implemented with continuous");
                        }
                        ddf = fac*(df-db);
                    }
                    else
                    {
                        ddf = fac*df;
                    }
                    d->fcalt[i][j] = d->fcalt[i][j-1] + ddf;
                    sumf          += ddf;
                }
                else
                {
                    ddf = fac*((d->params[eqAif]*exp(-beta*d->params[eqEif])*
                                is_intermediate(d, i, j)) -
                               (d->params[eqAfi]*exp(-beta*d->params[eqEfi])*
                                is_folded(d, i, j)));
                    ddi = fac*((d->params[eqAui]*exp(-beta*d->params[eqEui])*
                                is_unfolded(d, i, j)) -
                               (d->params[eqAiu]*exp(-beta*d->params[eqEiu])*
                                is_intermediate(d, i, j)));
                    d->fcalt[i][j] = d->fcalt[i][j-1] + ddf;
                    d->icalt[i][j] = d->icalt[i][j-1] + ddi;
                    sumf          += ddf;
                    sumi          += ddi;
                }
            }
        }
        d->sumfct[j] = d->sumfct[j-1] + sumf;
        d->sumict[j] = d->sumict[j-1] + sumi;
    }
    if (debug)
    {
        fprintf(debug, "@type xy\n");
        for (j = 0; (j < d->nframe); j++)
        {
            fprintf(debug, "%8.3f  %12.5e\n", d->time[j], d->sumfct[j]);
        }
        fprintf(debug, "&\n");
    }
}

static void sum_ft(t_remd_data *d)
{
    int    i, j;
    double fac;

    for (j = 0; (j < d->nframe); j++)
    {
        d->sumft[j] = 0;
        d->sumit[j] = 0;
        if ((j == 0) || (j == d->nframe-1))
        {
            fac = d->dt*0.5;
        }
        else
        {
            fac = d->dt;
        }
        for (i = 0; (i < d->nreplica); i++)
        {
            if (d->bMask[i])
            {
                if (d->bDiscrete)
                {
                    d->sumft[j] += fac*is_folded(d, i, j);
                    d->sumit[j] += fac*is_intermediate(d, i, j);
                }
                else
                {
                    d->sumft[j] += fac*d->state[i][j];
                }
            }
        }
    }
}

static double calc_d2(t_remd_data *d)
{
    int    i, j;
    double dd2, d2 = 0, dr2, tmp;

    integrate_dfdt(d);

    if (d->bSum)
    {
        for (j = d->j0; (j < d->j1); j++)
        {
            if (d->bDiscrete)
            {
                d2  += sqr(d->sumft[j]-d->sumfct[j]);
                if (d->nstate > 2)
                {
                    d2 += sqr(d->sumit[j]-d->sumict[j]);
                }
            }
            else
            {
                d2  += sqr(d->sumft[j]-d->sumfct[j]);
            }
        }
    }
    else
    {
        for (i = 0; (i < d->nreplica); i++)
        {
            dr2 = 0;
            if (d->bMask[i])
            {
                for (j = d->j0; (j < d->j1); j++)
                {
                    tmp  = sqr(is_folded(d, i, j)-d->fcalt[i][j]);
                    d2  += tmp;
                    dr2 += tmp;
                    if (d->nstate > 2)
                    {
                        tmp  = sqr(is_intermediate(d, i, j)-d->icalt[i][j]);
                        d2  += tmp;
                        dr2 += tmp;
                    }
                }
                d->d2_replica[i] = dr2/(d->j1-d->j0);
            }
        }
    }
    dd2 = (d2/(d->j1-d->j0))/(d->bDiscrete ? d->nmask : 1);

    return dd2;
}

static double my_f(const gsl_vector *v, void *params)
{
    t_remd_data *d       = (t_remd_data *) params;
    double       penalty = 0;
    int          i;

    for (i = 0; (i < d->nparams); i++)
    {
        d->params[i] = gsl_vector_get(v, i);
        if (d->params[i] < 0)
        {
            penalty += 10;
        }
    }
    if (penalty > 0)
    {
        return penalty;
    }
    else
    {
        return calc_d2(d);
    }
}

static void optimize_remd_parameters(FILE *fp, t_remd_data *d, int maxiter,
                                     real tol)
{
    real   size, d2;
    int    iter   = 0;
    int    status = 0;
    int    i;

    const gsl_multimin_fminimizer_type *T;
    gsl_multimin_fminimizer            *s;

    gsl_vector                         *x, *dx;
    gsl_multimin_function               my_func;

    my_func.f      = &my_f;
    my_func.n      = d->nparams;
    my_func.params = (void *) d;

    /* Starting point */
    x = gsl_vector_alloc (my_func.n);
    for (i = 0; (i < my_func.n); i++)
    {
        gsl_vector_set (x, i, d->params[i]);
    }

    /* Step size, different for each of the parameters */
    dx = gsl_vector_alloc (my_func.n);
    for (i = 0; (i < my_func.n); i++)
    {
        gsl_vector_set (dx, i, 0.1*d->params[i]);
    }

    T = gsl_multimin_fminimizer_nmsimplex;
    s = gsl_multimin_fminimizer_alloc (T, my_func.n);

    gsl_multimin_fminimizer_set (s, &my_func, x, dx);
    gsl_vector_free (x);
    gsl_vector_free (dx);

    printf ("%5s", "Iter");
    for (i = 0; (i < my_func.n); i++)
    {
        printf(" %12s", epnm(my_func.n, i));
    }
    printf (" %12s %12s\n", "NM Size", "Chi2");

    do
    {
        iter++;
        status = gsl_multimin_fminimizer_iterate (s);

        if (status != 0)
        {
            gmx_fatal(FARGS, "Something went wrong in the iteration in minimizer %s",
                      gsl_multimin_fminimizer_name(s));
        }

        d2     = gsl_multimin_fminimizer_minimum(s);
        size   = gsl_multimin_fminimizer_size(s);
        status = gsl_multimin_test_size(size, tol);

        if (status == GSL_SUCCESS)
        {
            printf ("Minimum found using %s at:\n",
                    gsl_multimin_fminimizer_name(s));
        }

        printf ("%5d", iter);
        for (i = 0; (i < my_func.n); i++)
        {
            printf(" %12.4e", gsl_vector_get (s->x, i));
        }
        printf (" %12.4e %12.4e\n", size, d2);
    }
    while ((status == GSL_CONTINUE) && (iter < maxiter));

    gsl_multimin_fminimizer_free (s);
}

static void preprocess_remd(FILE *fp, t_remd_data *d, real cutoff, real tref,
                            real ucut, gmx_bool bBack, real Euf, real Efu,
                            real Ei, real t0, real t1, gmx_bool bSum, gmx_bool bDiscrete,
                            int nmult)
{
    int  i, j, ninter;
    real dd, tau_f, tau_u;

    ninter = (ucut > cutoff) ? 1 : 0;
    if (ninter && (ucut <= cutoff))
    {
        gmx_fatal(FARGS, "You have requested an intermediate but the cutoff for intermediates %f is smaller than the normal cutoff(%f)", ucut, cutoff);
    }

    if (!bBack)
    {
        d->nparams = 2;
        d->nstate  = 2;
    }
    else
    {
        d->nparams = 4*(1+ninter);
        d->nstate  = 2+ninter;
    }
    d->bSum      = bSum;
    d->bDiscrete = bDiscrete;
    snew(d->beta, d->nreplica);
    snew(d->state, d->nreplica);
    snew(d->bMask, d->nreplica);
    snew(d->d2_replica, d->nreplica);
    snew(d->sumft, d->nframe);
    snew(d->sumit, d->nframe);
    snew(d->sumfct, d->nframe);
    snew(d->sumict, d->nframe);
    snew(d->params, d->nparams);
    snew(d->fcalt, d->nreplica);
    snew(d->icalt, d->nreplica);

    /* convert_times(d->nframe,d->time); */

    if (t0 < 0)
    {
        d->j0 = 0;
    }
    else
    {
        for (d->j0 = 0; (d->j0 < d->nframe) && (d->time[d->j0] < t0); d->j0++)
        {
            ;
        }
    }
    if (t1 < 0)
    {
        d->j1 = d->nframe;
    }
    else
    {
        for (d->j1 = 0; (d->j1 < d->nframe) && (d->time[d->j1] < t1); d->j1++)
        {
            ;
        }
    }
    if ((d->j1-d->j0) < d->nparams+2)
    {
        gmx_fatal(FARGS, "Start (%f) or end time (%f) for fitting inconsistent. Reduce t0, increase t1 or supply more data", t0, t1);
    }
    fprintf(fp, "Will optimize from %g to %g\n",
            d->time[d->j0], d->time[d->j1-1]);
    d->nmask = d->nreplica;
    for (i = 0; (i < d->nreplica); i++)
    {
        snew(d->beta[i], d->nframe);
        snew(d->state[i], d->nframe);
        snew(d->fcalt[i], d->nframe);
        snew(d->icalt[i], d->nframe);
        d->bMask[i] = TRUE;
        for (j = 0; (j < d->nframe); j++)
        {
            d->beta[i][j] = 1.0/(BOLTZ*d->temp[i][j]);
            dd            = d->data[i][j];
            if (bDiscrete)
            {
                if (dd <= cutoff)
                {
                    d->state[i][j] = 0;
                }
                else if ((ucut > cutoff) && (dd <= ucut))
                {
                    d->state[i][j] = 1;
                }
                else
                {
                    d->state[i][j] = d->nstate-1;
                }
            }
            else
            {
                d->state[i][j] = dd*nmult;
            }
        }
    }
    sum_ft(d);

    /* Assume forward rate constant is half the total time in this
     * simulation and backward is ten times as long */
    if (bDiscrete)
    {
        tau_f            = d->time[d->nframe-1];
        tau_u            = 4*tau_f;
        d->params[epEuf] = Euf;
        d->params[epAuf] = exp(d->params[epEuf]/(BOLTZ*tref))/tau_f;
        if (bBack)
        {
            d->params[epEfu] = Efu;
            d->params[epAfu] = exp(d->params[epEfu]/(BOLTZ*tref))/tau_u;
            if (ninter > 0)
            {
                d->params[eqEui] = Ei;
                d->params[eqAui] = exp(d->params[eqEui]/(BOLTZ*tref))/tau_u;
                d->params[eqEiu] = Ei;
                d->params[eqAiu] = exp(d->params[eqEiu]/(BOLTZ*tref))/tau_u;
            }
        }
        else
        {
            d->params[epAfu]  = 0;
            d->params[epEfu]  = 0;
        }
    }
    else
    {
        d->params[epEuf] = Euf;
        if (d->data2)
        {
            d->params[epAuf] = 0.1;
        }
        else
        {
            d->params[epAuf] = 20.0;
        }
    }
}

static real tau(real A, real E, real T)
{
    return exp(E/(BOLTZ*T))/A;
}

static real folded_fraction(t_remd_data *d, real tref)
{
    real tauf, taub;

    tauf = tau(d->params[epAuf], d->params[epEuf], tref);
    taub = tau(d->params[epAfu], d->params[epEfu], tref);

    return (taub/(tauf+taub));
}

static void print_tau(FILE *gp, t_remd_data *d, real tref)
{
    real tauf, taub, ddd, fff, DG, DH, TDS, Tm, Tb, Te, Fb, Fe, Fm;
    int  i, np = d->nparams;

    ddd = calc_d2(d);
    fprintf(gp, "Final value for Chi2 = %12.5e (%d replicas)\n", ddd, d->nmask);
    tauf = tau(d->params[epAuf], d->params[epEuf], tref);
    fprintf(gp, "%s = %12.5e %s = %12.5e (kJ/mole)\n",
            epnm(np, epAuf), d->params[epAuf],
            epnm(np, epEuf), d->params[epEuf]);
    if (bBack(d))
    {
        taub = tau(d->params[epAfu], d->params[epEfu], tref);
        fprintf(gp, "%s = %12.5e %s = %12.5e (kJ/mole)\n",
                epnm(np, epAfu), d->params[epAfu],
                epnm(np, epEfu), d->params[epEfu]);
        fprintf(gp, "Equilibrium properties at T = %g\n", tref);
        fprintf(gp, "tau_f = %8.3f ns, tau_b = %8.3f ns\n", tauf/1000, taub/1000);
        fff = taub/(tauf+taub);
        DG  = BOLTZ*tref*log(fff/(1-fff));
        DH  = d->params[epEfu]-d->params[epEuf];
        TDS = DH-DG;
        fprintf(gp, "Folded fraction     F = %8.3f\n", fff);
        fprintf(gp, "Unfolding energies: DG = %8.3f  DH = %8.3f TDS = %8.3f\n",
                DG, DH, TDS);
        Tb = 260;
        Te = 420;
        Tm = 0;
        Fm = 0;
        Fb = folded_fraction(d, Tb);
        Fe = folded_fraction(d, Te);
        while ((Te-Tb > 0.001) && (Fm != 0.5))
        {
            Tm = 0.5*(Tb+Te);
            Fm = folded_fraction(d, Tm);
            if (Fm > 0.5)
            {
                Fb = Fm;
                Tb = Tm;
            }
            else if (Fm < 0.5)
            {
                Te = Tm;
                Fe = Fm;
            }
        }
        if ((Fb-0.5)*(Fe-0.5) <= 0)
        {
            fprintf(gp, "Melting temperature Tm = %8.3f K\n", Tm);
        }
        else
        {
            fprintf(gp, "No melting temperature detected between 260 and 420K\n");
        }
        if (np > 4)
        {
            char *ptr;
            fprintf(gp, "Data for intermediates at T = %g\n", tref);
            fprintf(gp, "%8s  %10s  %10s  %10s\n", "Name", "A", "E", "tau");
            for (i = 0; (i < np/2); i++)
            {
                tauf = tau(d->params[2*i], d->params[2*i+1], tref);
                ptr  = epnm(d->nparams, 2*i);
                fprintf(gp, "%8s  %10.3e  %10.3e  %10.3e\n", ptr+1,
                        d->params[2*i], d->params[2*i+1], tauf/1000);
            }
        }
    }
    else
    {
        fprintf(gp, "Equilibrium properties at T = %g\n", tref);
        fprintf(gp, "tau_f = %8.3f\n", tauf);
    }
}

static void dump_remd_parameters(FILE *gp, t_remd_data *d, const char *fn,
                                 const char *fn2, const char *rfn,
                                 const char *efn, const char *mfn, int skip, real tref,
                                 output_env_t oenv)
{
    FILE       *fp, *hp;
    int         i, j, np = d->nparams;
    real        rhs, tauf, taub, fff, DG;
    real       *params;
    const char *leg[]  = { "Measured", "Fit", "Difference" };
    const char *mleg[] = { "Folded fraction", "DG (kJ/mole)"};
    char      **rleg;
    real        fac[] = { 0.97, 0.98, 0.99, 1.0, 1.01, 1.02, 1.03 };
#define NFAC asize(fac)
    real        d2[NFAC];
    double      norm;

    integrate_dfdt(d);
    print_tau(gp, d, tref);
    norm = (d->bDiscrete ? 1.0/d->nmask : 1.0);

    if (fn)
    {
        fp = xvgropen(fn, "Optimized fit to data", "Time (ps)", "Fraction Folded", oenv);
        xvgr_legend(fp, asize(leg), leg, oenv);
        for (i = 0; (i < d->nframe); i++)
        {
            if ((skip <= 0) || ((i % skip) == 0))
            {
                fprintf(fp, "%12.5e  %12.5e  %12.5e  %12.5e\n", d->time[i],
                        d->sumft[i]*norm, d->sumfct[i]*norm,
                        (d->sumft[i]-d->sumfct[i])*norm);
            }
        }
        ffclose(fp);
    }
    if (!d->bSum && rfn)
    {
        snew(rleg, d->nreplica*2);
        for (i = 0; (i < d->nreplica); i++)
        {
            snew(rleg[2*i], 32);
            snew(rleg[2*i+1], 32);
            sprintf(rleg[2*i], "\\f{4}F(t) %d", i);
            sprintf(rleg[2*i+1], "\\f{12}F \\f{4}(t) %d", i);
        }
        fp = xvgropen(rfn, "Optimized fit to data", "Time (ps)", "Fraction Folded", oenv);
        xvgr_legend(fp, d->nreplica*2, (const char**)rleg, oenv);
        for (j = 0; (j < d->nframe); j++)
        {
            if ((skip <= 0) || ((j % skip) == 0))
            {
                fprintf(fp, "%12.5e", d->time[j]);
                for (i = 0; (i < d->nreplica); i++)
                {
                    fprintf(fp, "  %5f  %9.2e", is_folded(d, i, j), d->fcalt[i][j]);
                }
                fprintf(fp, "\n");
            }
        }
        ffclose(fp);
    }

    if (fn2 && (d->nstate > 2))
    {
        fp = xvgropen(fn2, "Optimized fit to data", "Time (ps)",
                      "Fraction Intermediate", oenv);
        xvgr_legend(fp, asize(leg), leg, oenv);
        for (i = 0; (i < d->nframe); i++)
        {
            if ((skip <= 0) || ((i % skip) == 0))
            {
                fprintf(fp, "%12.5e  %12.5e  %12.5e  %12.5e\n", d->time[i],
                        d->sumit[i]*norm, d->sumict[i]*norm,
                        (d->sumit[i]-d->sumict[i])*norm);
            }
        }
        ffclose(fp);
    }
    if (mfn)
    {
        if (bBack(d))
        {
            fp = xvgropen(mfn, "Melting curve", "T (K)", "", oenv);
            xvgr_legend(fp, asize(mleg), mleg, oenv);
            for (i = 260; (i <= 420); i++)
            {
                tauf = tau(d->params[epAuf], d->params[epEuf], 1.0*i);
                taub = tau(d->params[epAfu], d->params[epEfu], 1.0*i);
                fff  = taub/(tauf+taub);
                DG   = BOLTZ*i*log(fff/(1-fff));
                fprintf(fp, "%5d  %8.3f  %8.3f\n", i, fff, DG);
            }
            ffclose(fp);
        }
    }

    if (efn)
    {
        snew(params, d->nparams);
        for (i = 0; (i < d->nparams); i++)
        {
            params[i] = d->params[i];
        }

        hp = xvgropen(efn, "Chi2 as a function of relative parameter",
                      "Fraction", "Chi2", oenv);
        for (j = 0; (j < d->nparams); j++)
        {
            /* Reset all parameters to optimized values */
            fprintf(hp, "@type xy\n");
            for (i = 0; (i < d->nparams); i++)
            {
                d->params[i] = params[i];
            }
            /* Now modify one of them */
            for (i = 0; (i < NFAC); i++)
            {
                d->params[j] = fac[i]*params[j];
                d2[i]        = calc_d2(d);
                fprintf(gp, "%s = %12g  d2 = %12g\n", epnm(np, j), d->params[j], d2[i]);
                fprintf(hp, "%12g  %12g\n", fac[i], d2[i]);
            }
            fprintf(hp, "&\n");
        }
        ffclose(hp);
        for (i = 0; (i < d->nparams); i++)
        {
            d->params[i] = params[i];
        }
        sfree(params);
    }
    if (!d->bSum)
    {
        for (i = 0; (i < d->nreplica); i++)
        {
            fprintf(gp, "Chi2[%3d] = %8.2e\n", i, d->d2_replica[i]);
        }
    }
}
#endif /*HAVE_LIBGSL*/

int gmx_kinetics(int argc, char *argv[])
{
    const char     *desc[] = {
        "[TT]g_kinetics[tt] reads two [TT].xvg[tt] files, each one containing data for N replicas.",
        "The first file contains the temperature of each replica at each timestep,",
        "and the second contains real values that can be interpreted as",
        "an indicator for folding. If the value in the file is larger than",
        "the cutoff it is taken to be unfolded and the other way around.[PAR]",
        "From these data an estimate of the forward and backward rate constants",
        "for folding is made at a reference temperature. In addition,",
        "a theoretical melting curve and free energy as a function of temperature",
        "are printed in an [TT].xvg[tt] file.[PAR]",
        "The user can give a max value to be regarded as intermediate",
        "([TT]-ucut[tt]), which, when given will trigger the use of an intermediate state",
        "in the algorithm to be defined as those structures that have",
        "cutoff < DATA < ucut. Structures with DATA values larger than ucut will",
        "not be regarded as potential folders. In this case 8 parameters are optimized.[PAR]",
        "The average fraction foled is printed in an [TT].xvg[tt] file together with the fit to it.",
        "If an intermediate is used a further file will show the build of the intermediate and the fit to that process.[PAR]",
        "The program can also be used with continuous variables (by setting",
        "[TT]-nodiscrete[tt]). In this case kinetics of other processes can be",
        "studied. This is very much a work in progress and hence the manual",
        "(this information) is lagging behind somewhat.[PAR]",
        "In order to compile this program you need access to the GNU",
        "scientific library."
    };
    static int      nreplica  = 1;
    static real     tref      = 298.15;
    static real     cutoff    = 0.2;
    static real     ucut      = 0.0;
    static real     Euf       = 10;
    static real     Efu       = 30;
    static real     Ei        = 10;
    static gmx_bool bHaveT    = TRUE;
    static real     t0        = -1;
    static real     t1        = -1;
    static real     tb        = 0;
    static real     te        = 0;
    static real     tol       = 1e-3;
    static int      maxiter   = 100;
    static int      skip      = 0;
    static int      nmult     = 1;
    static gmx_bool bBack     = TRUE;
    static gmx_bool bSplit    = TRUE;
    static gmx_bool bSum      = TRUE;
    static gmx_bool bDiscrete = TRUE;
    t_pargs         pa[]      = {
        { "-time",    FALSE, etBOOL, {&bHaveT},
          "Expect a time in the input" },
        { "-b",       FALSE, etREAL, {&tb},
          "First time to read from set" },
        { "-e",       FALSE, etREAL, {&te},
          "Last time to read from set" },
        { "-bfit",    FALSE, etREAL, {&t0},
          "Time to start the fit from" },
        { "-efit",    FALSE, etREAL, {&t1},
          "Time to end the fit" },
        { "-T",       FALSE, etREAL, {&tref},
          "Reference temperature for computing rate constants" },
        { "-n",       FALSE, etINT, {&nreplica},
          "Read data for this number of replicas. Only necessary when files are written in xmgrace format using @type and & as delimiters." },
        { "-cut",     FALSE, etREAL, {&cutoff},
          "Cut-off (max) value for regarding a structure as folded" },
        { "-ucut",    FALSE, etREAL, {&ucut},
          "Cut-off (max) value for regarding a structure as intermediate (if not folded)" },
        { "-euf",     FALSE, etREAL, {&Euf},
          "Initial guess for energy of activation for folding (kJ/mol)" },
        { "-efu",     FALSE, etREAL, {&Efu},
          "Initial guess for energy of activation for unfolding (kJ/mol)" },
        { "-ei",      FALSE, etREAL, {&Ei},
          "Initial guess for energy of activation for intermediates (kJ/mol)" },
        { "-maxiter", FALSE, etINT, {&maxiter},
          "Max number of iterations" },
        { "-back",    FALSE, etBOOL, {&bBack},
          "Take the back reaction into account" },
        { "-tol",     FALSE, etREAL, {&tol},
          "Absolute tolerance for convergence of the Nelder and Mead simplex algorithm" },
        { "-skip",    FALSE, etINT, {&skip},
          "Skip points in the output [TT].xvg[tt] file" },
        { "-split",   FALSE, etBOOL, {&bSplit},
          "Estimate error by splitting the number of replicas in two and refitting" },
        { "-sum",     FALSE, etBOOL, {&bSum},
          "Average folding before computing [GRK]chi[grk]^2" },
        { "-discrete", FALSE, etBOOL, {&bDiscrete},
          "Use a discrete folding criterion (F <-> U) or a continuous one" },
        { "-mult",    FALSE, etINT, {&nmult},
          "Factor to multiply the data with before discretization" }
    };
#define NPA asize(pa)

    FILE        *fp;
    real         dt_t, dt_d, dt_d2;
    int          nset_t, nset_d, nset_d2, n_t, n_d, n_d2, i;
    const char  *tfile, *dfile, *dfile2;
    t_remd_data  remd;
    output_env_t oenv;

    t_filenm     fnm[] = {
        { efXVG, "-f",    "temp",    ffREAD   },
        { efXVG, "-d",    "data",    ffREAD   },
        { efXVG, "-d2",   "data2",   ffOPTRD  },
        { efXVG, "-o",    "ft_all",  ffWRITE  },
        { efXVG, "-o2",   "it_all",  ffOPTWR  },
        { efXVG, "-o3",   "ft_repl", ffOPTWR  },
        { efXVG, "-ee",   "err_est", ffOPTWR  },
        { efLOG, "-g",    "remd",    ffWRITE  },
        { efXVG, "-m",    "melt",    ffWRITE  }
    };
#define NFILE asize(fnm)

    CopyRight(stderr, argv[0]);
    parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_BE_NICE | PCA_TIME_UNIT,
                      NFILE, fnm, NPA, pa, asize(desc), desc, 0, NULL, &oenv);

#ifdef HAVE_LIBGSL
    please_cite(stdout, "Spoel2006d");
    if (cutoff < 0)
    {
        gmx_fatal(FARGS, "cutoff should be >= 0 (rather than %f)", cutoff);
    }

    tfile   = opt2fn("-f", NFILE, fnm);
    dfile   = opt2fn("-d", NFILE, fnm);
    dfile2  = opt2fn_null("-d2", NFILE, fnm);

    fp = ffopen(opt2fn("-g", NFILE, fnm), "w");

    remd.temp = read_xvg_time(tfile, bHaveT,
                              opt2parg_bSet("-b", NPA, pa), tb,
                              opt2parg_bSet("-e", NPA, pa), te,
                              nreplica, &nset_t, &n_t, &dt_t, &remd.time);
    printf("Read %d sets of %d points in %s, dt = %g\n\n", nset_t, n_t, tfile, dt_t);
    sfree(remd.time);

    remd.data = read_xvg_time(dfile, bHaveT,
                              opt2parg_bSet("-b", NPA, pa), tb,
                              opt2parg_bSet("-e", NPA, pa), te,
                              nreplica, &nset_d, &n_d, &dt_d, &remd.time);
    printf("Read %d sets of %d points in %s, dt = %g\n\n", nset_d, n_d, dfile, dt_d);

    if ((nset_t != nset_d) || (n_t != n_d) || (dt_t != dt_d))
    {
        gmx_fatal(FARGS, "Files %s and %s are inconsistent. Check log file",
                  tfile, dfile);
    }

    if (dfile2)
    {
        remd.data2 = read_xvg_time(dfile2, bHaveT,
                                   opt2parg_bSet("-b", NPA, pa), tb,
                                   opt2parg_bSet("-e", NPA, pa), te,
                                   nreplica, &nset_d2, &n_d2, &dt_d2, &remd.time);
        printf("Read %d sets of %d points in %s, dt = %g\n\n",
               nset_d2, n_d2, dfile2, dt_d2);
        if ((nset_d2 != nset_d) || (n_d != n_d2) || (dt_d != dt_d2))
        {
            gmx_fatal(FARGS, "Files %s and %s are inconsistent. Check log file",
                      dfile, dfile2);
        }
    }
    else
    {
        remd.data2 = NULL;
    }

    remd.nreplica  = nset_d;
    remd.nframe    = n_d;
    remd.dt        = 1;
    preprocess_remd(fp, &remd, cutoff, tref, ucut, bBack, Euf, Efu, Ei, t0, t1,
                    bSum, bDiscrete, nmult);

    optimize_remd_parameters(fp, &remd, maxiter, tol);

    dump_remd_parameters(fp, &remd, opt2fn("-o", NFILE, fnm),
                         opt2fn_null("-o2", NFILE, fnm),
                         opt2fn_null("-o3", NFILE, fnm),
                         opt2fn_null("-ee", NFILE, fnm),
                         opt2fn("-m", NFILE, fnm), skip, tref, oenv);

    if (bSplit)
    {
        printf("Splitting set of replicas in two halves\n");
        for (i = 0; (i < remd.nreplica); i++)
        {
            remd.bMask[i] = FALSE;
        }
        remd.nmask = 0;
        for (i = 0; (i < remd.nreplica); i += 2)
        {
            remd.bMask[i] = TRUE;
            remd.nmask++;
        }
        sum_ft(&remd);
        optimize_remd_parameters(fp, &remd, maxiter, tol);
        dump_remd_parameters(fp, &remd, "test1.xvg", NULL, NULL, NULL, NULL, skip, tref, oenv);

        for (i = 0; (i < remd.nreplica); i++)
        {
            remd.bMask[i] = !remd.bMask[i];
        }
        remd.nmask = remd.nreplica - remd.nmask;

        sum_ft(&remd);
        optimize_remd_parameters(fp, &remd, maxiter, tol);
        dump_remd_parameters(fp, &remd, "test2.xvg", NULL, NULL, NULL, NULL, skip, tref, oenv);

        for (i = 0; (i < remd.nreplica); i++)
        {
            remd.bMask[i] = FALSE;
        }
        remd.nmask = 0;
        for (i = 0; (i < remd.nreplica/2); i++)
        {
            remd.bMask[i] = TRUE;
            remd.nmask++;
        }
        sum_ft(&remd);
        optimize_remd_parameters(fp, &remd, maxiter, tol);
        dump_remd_parameters(fp, &remd, "test1.xvg", NULL, NULL, NULL, NULL, skip, tref, oenv);

        for (i = 0; (i < remd.nreplica); i++)
        {
            remd.bMask[i] = FALSE;
        }
        remd.nmask = 0;
        for (i = remd.nreplica/2; (i < remd.nreplica); i++)
        {
            remd.bMask[i] = TRUE;
            remd.nmask++;
        }
        sum_ft(&remd);
        optimize_remd_parameters(fp, &remd, maxiter, tol);
        dump_remd_parameters(fp, &remd, "test1.xvg", NULL, NULL, NULL, NULL, skip, tref, oenv);
    }
    ffclose(fp);

    view_all(oenv, NFILE, fnm);

    thanx(stderr);
#else
    fprintf(stderr, "This program should be compiled with the GNU scientific library. Please install the library and reinstall GROMACS.\n");
#endif /*HAVE_LIBGSL*/

    return 0;
}


/*
 * Copyright (C) 2017 Jelmer Ypma. All Rights Reserved.
 * This code is published under the L-GPL.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * File:   nloptrAPI.h
 * Author: Jelmer Ypma
 * Date:   3 October 2017
 *
 * This file provides an API for calling internal NLopt code from C within
 * R packages. The C functions that are registered in init_nloptr.c can be
 * accessed by external R packages.
 *
 * 03/10/2017: Initial version exposing nlopt_version.
 */

#ifndef __NLOPTRAPI_H__
#define __NLOPTRAPI_H__

#include <R_ext/Rdynload.h>
#include <R.h>
#include <Rinternals.h>

#include <nlopt.h>

/*
 * C functions can be exposed using the following template:
 *
 * RET_TYPE FUNCNAME(ARGTYPE_1 ARGNAME 1, ARGTYPE_2 ARGNAME_2)
 * {
 *     static RET_TYPE(*fun)(ARGTYPE_1, ARGTYPE_2) = NULL;
 *     if (fun == NULL) fun = (RET_TYPE(*)(ARGTYPE_1, ARGTYPE_2)) R_GetCCallable("nloptr","FUNCNAME");
 *     return fun(ARGNAME_1, ARGNAME_2);
 * }
 *
 */

inline NLOPT_EXTERN(const char *) nlopt_algorithm_name(nlopt_algorithm a)
{
    static const char *(*fun)(nlopt_algorithm) = NULL;
    if (fun == NULL) fun = (const char *(*)(nlopt_algorithm)) R_GetCCallable("nloptr","nlopt_algorithm_name");
    return fun(a);
}

inline NLOPT_EXTERN(void) nlopt_srand(unsigned long seed)
{
    static void(*fun)(unsigned long) = NULL;
    if (fun == NULL) fun = (void(*)(unsigned long)) R_GetCCallable("nloptr","nlopt_srand");
    return fun(seed);
}

inline NLOPT_EXTERN(void) nlopt_srand_time(void)
{
    static void(*fun)(void) = NULL;
    if (fun == NULL) fun = (void(*)(void)) R_GetCCallable("nloptr","nlopt_srand_time");
    return fun();
}

inline NLOPT_EXTERN(void) nlopt_version(int *major, int *minor, int *bugfix)
{
    static void(*fun)(int *, int *, int *) = NULL;
    if (fun == NULL) fun = (void(*)(int *, int *, int *)) R_GetCCallable("nloptr","nlopt_version");
    return fun(major, minor, bugfix);
}

inline NLOPT_EXTERN(nlopt_opt) nlopt_create(nlopt_algorithm algorithm, unsigned n)
{
    static nlopt_opt(*fun)(nlopt_algorithm, unsigned) = NULL;
    if (fun == NULL) fun = (nlopt_opt(*)(nlopt_algorithm, unsigned)) R_GetCCallable("nloptr","nlopt_create");
    return fun(algorithm, n);
}

inline NLOPT_EXTERN(void) nlopt_destroy(nlopt_opt opt)
{
    static void(*fun)(nlopt_opt) = NULL;
    if (fun == NULL) fun = (void(*)(nlopt_opt)) R_GetCCallable("nloptr","nlopt_destroy");
    return fun(opt);
}

inline NLOPT_EXTERN(nlopt_opt) nlopt_copy(const nlopt_opt opt)
{
    static nlopt_opt(*fun)(const nlopt_opt) = NULL;
    if (fun == NULL) fun = (nlopt_opt(*)(const nlopt_opt)) R_GetCCallable("nloptr","nlopt_copy");
    return fun(opt);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_optimize(nlopt_opt opt, double *x, double *opt_f)
{
    static nlopt_result(*fun)(nlopt_opt, double *, double *) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, double *, double *)) R_GetCCallable("nloptr","nlopt_optimize");
    return fun(opt, x, opt_f);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_set_min_objective(nlopt_opt opt, nlopt_func f, void *f_data)
{
    static nlopt_result(*fun)(nlopt_opt, nlopt_func, void *) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, nlopt_func, void *)) R_GetCCallable("nloptr","nlopt_set_min_objective");
    return fun(opt, f, f_data);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_set_max_objective(nlopt_opt opt, nlopt_func f, void *f_data)
{
    static nlopt_result(*fun)(nlopt_opt, nlopt_func, void *) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, nlopt_func, void *)) R_GetCCallable("nloptr","nlopt_set_max_objective");
    return fun(opt, f, f_data);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_set_precond_min_objective(nlopt_opt opt, nlopt_func f, nlopt_precond pre, void *f_data)
{
    static nlopt_result(*fun)(nlopt_opt, nlopt_func, nlopt_precond, void *) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, nlopt_func, nlopt_precond, void *)) R_GetCCallable("nloptr","nlopt_set_precond_min_objective");
    return fun(opt, f, pre, f_data);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_set_precond_max_objective(nlopt_opt opt, nlopt_func f, nlopt_precond pre, void *f_data)
{
    static nlopt_result(*fun)(nlopt_opt, nlopt_func, nlopt_precond, void *) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, nlopt_func, nlopt_precond, void *)) R_GetCCallable("nloptr","nlopt_set_precond_max_objective");
    return fun(opt, f, pre, f_data);
}

inline NLOPT_EXTERN(nlopt_algorithm) nlopt_get_algorithm(const nlopt_opt opt)
{
    static nlopt_algorithm(*fun)(const nlopt_opt) = NULL;
    if (fun == NULL) fun = (nlopt_algorithm(*)(const nlopt_opt)) R_GetCCallable("nloptr","nlopt_get_algorithm");
    return fun(opt);
}

inline NLOPT_EXTERN(unsigned) nlopt_get_dimension(const nlopt_opt opt)
{
    static unsigned(*fun)(const nlopt_opt) = NULL;
    if (fun == NULL) fun = (unsigned(*)(const nlopt_opt)) R_GetCCallable("nloptr","nlopt_get_dimension");
    return fun(opt);
}

/* constraints: */

inline NLOPT_EXTERN(nlopt_result) nlopt_set_lower_bounds(nlopt_opt opt, const double *lb)
{
    static nlopt_result(*fun)(nlopt_opt, const double *) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, const double *)) R_GetCCallable("nloptr","nlopt_set_lower_bounds");
    return fun(opt, lb);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_set_lower_bounds1(nlopt_opt opt, double lb)
{
    static nlopt_result(*fun)(nlopt_opt, double) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, double)) R_GetCCallable("nloptr","nlopt_set_lower_bounds1");
    return fun(opt, lb);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_get_lower_bounds(const nlopt_opt opt, double *lb)
{
    static nlopt_result(*fun)(const nlopt_opt, double *) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(const nlopt_opt, double *)) R_GetCCallable("nloptr","nlopt_get_lower_bounds");
    return fun(opt, lb);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_set_upper_bounds(nlopt_opt opt, const double *ub)
{
    static nlopt_result(*fun)(nlopt_opt, const double *) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, const double *)) R_GetCCallable("nloptr","nlopt_set_upper_bounds");
    return fun(opt, ub);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_set_upper_bounds1(nlopt_opt opt, double ub)
{
    static nlopt_result(*fun)(nlopt_opt, double) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, double)) R_GetCCallable("nloptr","nlopt_set_upper_bounds1");
    return fun(opt, ub);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_get_upper_bounds(const nlopt_opt opt, double *ub)
{
    static nlopt_result(*fun)(const nlopt_opt, double *) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(const nlopt_opt, double *)) R_GetCCallable("nloptr","nlopt_get_upper_bounds");
    return fun(opt, ub);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_remove_inequality_constraints(nlopt_opt opt)
{
    static nlopt_result(*fun)(nlopt_opt) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt)) R_GetCCallable("nloptr","nlopt_remove_inequality_constraints");
    return fun(opt);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_add_inequality_constraint(nlopt_opt opt,
             nlopt_func fc,
             void *fc_data,
             double tol)
{
    static nlopt_result(*fun)(nlopt_opt, nlopt_func, void *, double) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, nlopt_func, void *, double)) R_GetCCallable("nloptr","nlopt_add_inequality_constraint");
    return fun(opt, fc, fc_data, tol);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_add_precond_inequality_constraint(
        nlopt_opt opt, nlopt_func fc, nlopt_precond pre, void *fc_data,
        double tol)
{
    static nlopt_result(*fun)(nlopt_opt, nlopt_func, nlopt_precond, void *, double) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, nlopt_func, nlopt_precond, void *, double)) R_GetCCallable("nloptr","nlopt_add_precond_inequality_constraint");
    return fun(opt, fc, pre, fc_data, tol);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_add_inequality_mconstraint(nlopt_opt opt,
             unsigned m,
             nlopt_mfunc fc,
             void *fc_data,
             const double *tol)
{
    static nlopt_result(*fun)(nlopt_opt, unsigned, nlopt_mfunc, void *, const double *) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, unsigned, nlopt_mfunc, void *, const double *)) R_GetCCallable("nloptr","nlopt_add_inequality_mconstraint");
    return fun(opt, m, fc, fc_data, tol);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_remove_equality_constraints(nlopt_opt opt)
{
    static nlopt_result(*fun)(nlopt_opt) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt)) R_GetCCallable("nloptr","nlopt_remove_equality_constraints");
    return fun(opt);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_add_equality_constraint(nlopt_opt opt,
             nlopt_func h,
             void *h_data,
             double tol)
{
    static nlopt_result(*fun)(nlopt_opt, nlopt_func, void *, double) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, nlopt_func, void *, double)) R_GetCCallable("nloptr","nlopt_add_equality_constraint");
    return fun(opt, h, h_data, tol);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_add_precond_equality_constraint(
        nlopt_opt opt, nlopt_func h, nlopt_precond pre, void *h_data,
        double tol)
{
    static nlopt_result(*fun)(nlopt_opt, nlopt_func, nlopt_precond, void *, double) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, nlopt_func, nlopt_precond, void *, double)) R_GetCCallable("nloptr","nlopt_add_precond_equality_constraint");
    return fun(opt, h, pre, h_data, tol);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_add_equality_mconstraint(nlopt_opt opt,
             unsigned m,
             nlopt_mfunc h,
             void *h_data,
             const double *tol)
{
    static nlopt_result(*fun)(nlopt_opt, unsigned, nlopt_mfunc, void *, const double *) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, unsigned, nlopt_mfunc, void *, const double *)) R_GetCCallable("nloptr","nlopt_add_equality_mconstraint");
    return fun(opt, m, h, h_data, tol);
}

/* stopping criteria: */

inline NLOPT_EXTERN(nlopt_result) nlopt_set_stopval(nlopt_opt opt, double stopval)
{
    static nlopt_result(*fun)(nlopt_opt, double) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, double)) R_GetCCallable("nloptr","nlopt_set_stopval");
    return fun(opt, stopval);
}

inline NLOPT_EXTERN(double) nlopt_get_stopval(const nlopt_opt opt)
{
    static double(*fun)(const nlopt_opt) = NULL;
    if (fun == NULL) fun = (double(*)(const nlopt_opt)) R_GetCCallable("nloptr","nlopt_get_stopval");
    return fun(opt);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_set_ftol_rel(nlopt_opt opt, double tol)
{
    static nlopt_result(*fun)(nlopt_opt, double) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, double)) R_GetCCallable("nloptr","nlopt_set_ftol_rel");
    return fun(opt, tol);
}

inline NLOPT_EXTERN(double) nlopt_get_ftol_rel(const nlopt_opt opt)
{
    static double(*fun)(const nlopt_opt) = NULL;
    if (fun == NULL) fun = (double(*)(const nlopt_opt)) R_GetCCallable("nloptr","nlopt_get_ftol_rel");
    return fun(opt);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_set_ftol_abs(nlopt_opt opt, double tol)
{
    static nlopt_result(*fun)(nlopt_opt, double) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, double)) R_GetCCallable("nloptr","nlopt_set_ftol_abs");
    return fun(opt, tol);
}

inline NLOPT_EXTERN(double) nlopt_get_ftol_abs(const nlopt_opt opt)
{
    static double(*fun)(const nlopt_opt) = NULL;
    if (fun == NULL) fun = (double(*)(const nlopt_opt)) R_GetCCallable("nloptr","nlopt_get_ftol_abs");
    return fun(opt);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_set_xtol_rel(nlopt_opt opt, double tol)
{
    static nlopt_result(*fun)(nlopt_opt, double) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, double)) R_GetCCallable("nloptr","nlopt_set_xtol_rel");
    return fun(opt, tol);
}

inline NLOPT_EXTERN(double) nlopt_get_xtol_rel(const nlopt_opt opt)
{
    static double(*fun)(const nlopt_opt) = NULL;
    if (fun == NULL) fun = (double(*)(const nlopt_opt)) R_GetCCallable("nloptr","nlopt_get_xtol_rel");
    return fun(opt);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_set_xtol_abs1(nlopt_opt opt, double tol)
{
    static nlopt_result(*fun)(nlopt_opt, double) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, double)) R_GetCCallable("nloptr","nlopt_set_xtol_abs1");
    return fun(opt, tol);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_set_xtol_abs(nlopt_opt opt, const double *tol)
{
    static nlopt_result(*fun)(nlopt_opt, const double *) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, const double *)) R_GetCCallable("nloptr","nlopt_set_xtol_abs");
    return fun(opt, tol);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_get_xtol_abs(const nlopt_opt opt, double *tol)
{
    static nlopt_result(*fun)(nlopt_opt, double *) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, double *)) R_GetCCallable("nloptr","nlopt_get_xtol_abs");
    return fun(opt, tol);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_set_maxeval(nlopt_opt opt, int maxeval)
{
    static nlopt_result(*fun)(nlopt_opt, int) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, int)) R_GetCCallable("nloptr","nlopt_set_maxeval");
    return fun(opt, maxeval);
}

inline NLOPT_EXTERN(int) nlopt_get_maxeval(const nlopt_opt opt)
{
    static int(*fun)(const nlopt_opt) = NULL;
    if (fun == NULL) fun = (int(*)(const nlopt_opt)) R_GetCCallable("nloptr","nlopt_get_maxeval");
    return fun(opt);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_set_maxtime(nlopt_opt opt, double maxtime)
{
    static nlopt_result(*fun)(nlopt_opt, double) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, double)) R_GetCCallable("nloptr","nlopt_set_maxtime");
    return fun(opt, maxtime);
}

inline NLOPT_EXTERN(double) nlopt_get_maxtime(const nlopt_opt opt)
{
    static double(*fun)(nlopt_opt) = NULL;
    if (fun == NULL) fun = (double(*)(nlopt_opt)) R_GetCCallable("nloptr","nlopt_get_maxtime");
    return fun(opt);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_force_stop(nlopt_opt opt)
{
    static nlopt_result(*fun)(nlopt_opt) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt)) R_GetCCallable("nloptr","nlopt_force_stop");
    return fun(opt);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_set_force_stop(nlopt_opt opt, int val)
{
    static nlopt_result(*fun)(nlopt_opt, int) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, int)) R_GetCCallable("nloptr","nlopt_set_force_stop");
    return fun(opt, val);
}

inline NLOPT_EXTERN(int) nlopt_get_force_stop(const nlopt_opt opt)
{
    static int(*fun)(const nlopt_opt) = NULL;
    if (fun == NULL) fun = (int(*)(const nlopt_opt)) R_GetCCallable("nloptr","nlopt_get_force_stop");
    return fun(opt);
}

/* more algorithm-specific parameters */

inline NLOPT_EXTERN(nlopt_result) nlopt_set_local_optimizer(nlopt_opt opt, const nlopt_opt local_opt)
{
    static nlopt_result(*fun)(nlopt_opt, const nlopt_opt) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, const nlopt_opt)) R_GetCCallable("nloptr","nlopt_set_local_optimizer");
    return fun(opt, local_opt);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_set_population(nlopt_opt opt, unsigned pop)
{
    static nlopt_result(*fun)(nlopt_opt, unsigned) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, unsigned)) R_GetCCallable("nloptr","nlopt_set_population");
    return fun(opt, pop);
}

inline NLOPT_EXTERN(unsigned) nlopt_get_population(const nlopt_opt opt)
{
    static unsigned(*fun)(const nlopt_opt) = NULL;
    if (fun == NULL) fun = (unsigned(*)(const nlopt_opt)) R_GetCCallable("nloptr","nlopt_get_population");
    return fun(opt);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_set_vector_storage(nlopt_opt opt, unsigned dim)
{
    static nlopt_result(*fun)(nlopt_opt, unsigned) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, unsigned)) R_GetCCallable("nloptr","nlopt_set_vector_storage");
    return fun(opt, dim);
}

inline NLOPT_EXTERN(unsigned) nlopt_get_vector_storage(const nlopt_opt opt)
{
    static unsigned(*fun)(const nlopt_opt) = NULL;
    if (fun == NULL) fun = (unsigned(*)(const nlopt_opt)) R_GetCCallable("nloptr","nlopt_get_vector_storage");
    return fun(opt);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_set_default_initial_step(nlopt_opt opt, const double *x)
{
    static nlopt_result(*fun)(nlopt_opt, const double *) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, const double *)) R_GetCCallable("nloptr","nlopt_set_default_initial_step");
    return fun(opt, x);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_set_initial_step(nlopt_opt opt, const double *dx)
{
    static nlopt_result(*fun)(nlopt_opt, const double *) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, const double *)) R_GetCCallable("nloptr","nlopt_set_initial_step");
    return fun(opt, dx);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_set_initial_step1(nlopt_opt opt, double dx)
{
    static nlopt_result(*fun)(nlopt_opt, double) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(nlopt_opt, double)) R_GetCCallable("nloptr","nlopt_set_initial_step1");
    return fun(opt, dx);
}

inline NLOPT_EXTERN(nlopt_result) nlopt_get_initial_step(const nlopt_opt opt, const double *x, double *dx)
{
    static nlopt_result(*fun)(const nlopt_opt, const double *, double *) = NULL;
    if (fun == NULL) fun = (nlopt_result(*)(const nlopt_opt, const double *, double *)) R_GetCCallable("nloptr","nlopt_get_initial_step");
    return fun(opt, x, dx);
}

#endif /* __NLOPTRAPI_H__ */