/*
  siman.c
  Ruby/GSL: Ruby extension library for GSL (GNU Scientific Library)
    (C) Copyright 2001-2006 by Yoshiki Tsunesada

  Ruby/GSL is free software: you can redistribute it and/or modify it
  under the terms of the GNU General Public License.
  This library is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY.
*/

#include <gsl/gsl_rng.h>
#include <gsl/gsl_siman.h>
#include "include/rb_gsl.h"
#include "include/rb_gsl_array.h"
#include "include/rb_gsl_function.h"
#include "include/rb_gsl_rng.h"
#include "include/rb_gsl_common.h"

static VALUE cgsl_siman_Efunc;
static VALUE cgsl_siman_step;
static VALUE cgsl_siman_metric;
static VALUE cgsl_siman_print;
static VALUE cgsl_siman_solver;
static VALUE cgsl_siman_params;

/***** siman_solver *****/
typedef struct __siman_solver {
  VALUE proc_efunc;
  VALUE proc_step;
  VALUE proc_metric;
  VALUE proc_print;
  gsl_vector *vx;
} siman_solver;

static siman_solver* gsl_siman_solver_alloc(size_t size);
static void gsl_siman_solver_free(siman_solver *ss);
static void gsl_siman_solver_mark(siman_solver *s);

static void gsl_siman_solver_mark(siman_solver *s)
{
  rb_gc_mark(s->proc_efunc);
  rb_gc_mark(s->proc_step);
  rb_gc_mark(s->proc_metric);
  rb_gc_mark(s->proc_print);
}

static siman_solver* gsl_siman_solver_alloc(size_t size)
{
  siman_solver *ss = NULL;
  ss = ALLOC(siman_solver);
  if (size > 0) {
    ss->vx = gsl_vector_alloc(size);
  } else {
    ss->vx = NULL;
  }
  return ss;
}

static void gsl_siman_solver_free(siman_solver *ss)
{
  if (ss->vx) gsl_vector_free(ss->vx);
  free((siman_solver *) ss);
}

static VALUE rb_gsl_siman_solver_new(int argc, VALUE *argv, VALUE klass)
{
  siman_solver *ss = NULL;
  if (argc == 1) ss = gsl_siman_solver_alloc(FIX2INT(argv[0]));
  else ss = gsl_siman_solver_alloc(0);
  return Data_Wrap_Struct(klass, gsl_siman_solver_mark, gsl_siman_solver_free, ss);
}

/***** siman_Efunc *****/
typedef struct ___siman_Efunc {
  double (*siman_Efunc_t)(void *);
  VALUE proc;
} siman_Efunc;

static siman_Efunc* siman_Efunc_alloc();
static void siman_Efunc_free(siman_Efunc *se);
static double rb_gsl_siman_Efunc_t(void *xp);
static VALUE rb_gsl_siman_Efunc_set(int argc, VALUE *argv, VALUE obj);
static void siman_Efunc_mark(siman_Efunc *se);

static siman_Efunc* siman_Efunc_alloc()
{
  siman_Efunc *se = NULL;
  se = ALLOC(siman_Efunc);
  se->siman_Efunc_t = rb_gsl_siman_Efunc_t;
  return se;
}

static void siman_Efunc_mark(siman_Efunc *se)
{
  rb_gc_mark(se->proc);
}

static void siman_Efunc_free(siman_Efunc *se)
{
  free((siman_Efunc *) se);
}

static VALUE rb_gsl_siman_Efunc_new(int argc, VALUE *argv, VALUE klass)
{
  VALUE obj;
  siman_Efunc *se = NULL;
  se = siman_Efunc_alloc();
  obj = Data_Wrap_Struct(klass, siman_Efunc_mark, siman_Efunc_free, se);
  rb_gsl_siman_Efunc_set(argc, argv, obj);
  return obj;
}

static double rb_gsl_siman_Efunc_t(void *data)
{
  VALUE proc, params, result;
  siman_solver *ss = NULL;
  ss = (siman_solver *) data;
  proc = (VALUE) ss->proc_efunc;
  params = Data_Wrap_Struct(cgsl_vector, 0, NULL, ss->vx);
  result = rb_funcall(proc, RBGSL_ID_call, 1, params);
  return NUM2DBL(result);
}

static VALUE rb_gsl_siman_Efunc_set(int argc, VALUE *argv, VALUE obj)
{
  siman_Efunc *se = NULL;
  Data_Get_Struct(obj, siman_Efunc, se);
  switch (argc) {
  case 0:
    if (rb_block_given_p()) se->proc = rb_block_proc();
    break;
  case 1:
    if (rb_obj_is_kind_of(argv[0], rb_cProc)) se->proc = argv[0];
    else rb_raise(rb_eTypeError, "Proc expected");
    break;
  default:
    rb_raise(rb_eArgError, "wrong number of arguments (%d for 0 or 1)", argc);
  }
  return obj;
}

/***** siman_copy *****/
static void rb_gsl_siman_copy_t(void *source, void *dest);
static void* rb_gsl_siman_copy_construct_t(void *data);
static void rb_gsl_siman_destroy_t(void *data);

static void rb_gsl_siman_copy_t(void *source, void *dest)
{
  siman_solver *sssrc = NULL, *ssdest = NULL;
  sssrc = (siman_solver *) source;
  ssdest = (siman_solver *) dest;
  gsl_vector_memcpy(ssdest->vx, sssrc->vx);
}

/***** siman_copy_construct *****/
static void* rb_gsl_siman_copy_construct_t(void *data)
{
  siman_solver *ssdest = NULL;
  siman_solver *sssrc = NULL;
  sssrc = (siman_solver *) data;
  ssdest = (siman_solver *) gsl_siman_solver_alloc(sssrc->vx->size);
  ssdest->proc_efunc = sssrc->proc_efunc;
  ssdest->proc_step = sssrc->proc_step;
  ssdest->proc_metric = sssrc->proc_metric;
  ssdest->proc_print = sssrc->proc_print;
  gsl_vector_memcpy(ssdest->vx, sssrc->vx);
  return ssdest;
}

/***** siman_destroy *****/
static void rb_gsl_siman_destroy_t(void *data)
{
  siman_solver *ss = NULL;
  ss = (siman_solver *) data;
  gsl_siman_solver_free(ss);
}

/****** siman_print *****/
typedef struct ___siman_print {
  void (*siman_print_t)(void *);
  VALUE proc;
} siman_print;

static siman_print* siman_print_alloc();
static void siman_print_free(siman_print *se);
static void rb_gsl_siman_print_t(void *xp);
static VALUE rb_gsl_siman_print_set(int argc, VALUE *argv, VALUE obj);
static void siman_print_mark(siman_print *se);

static siman_print* siman_print_alloc()
{
  siman_print *se = NULL;
  se = ALLOC(siman_print);
  se->siman_print_t = &rb_gsl_siman_print_t;
  return se;
}

static void siman_print_mark(siman_print *se)
{
  rb_gc_mark(se->proc);
}

static void siman_print_free(siman_print *se)
{
  free((siman_print *) se);
}

static VALUE rb_gsl_siman_print_new(int argc, VALUE *argv, VALUE klass)
{
  VALUE obj;
  siman_print *se = NULL;
  se = siman_print_alloc();
  obj = Data_Wrap_Struct(klass, siman_print_mark, siman_print_free, se);
  rb_gsl_siman_print_set(argc, argv, obj);
  return obj;
}

static void rb_gsl_siman_print_t(void *data)
{
  VALUE proc, params;
  siman_solver *ss = NULL;
  ss = (siman_solver *) data;
  proc = ss->proc_print;
  if (NIL_P(proc)) return;
  params = Data_Wrap_Struct(cgsl_vector, 0, NULL, ss->vx);
  rb_funcall(proc, RBGSL_ID_call, 1, params);
}

static VALUE rb_gsl_siman_print_set(int argc, VALUE *argv, VALUE obj)
{
  siman_print *se = NULL;
  Data_Get_Struct(obj, siman_print, se);
  switch (argc) {
  case 0:
    if (rb_block_given_p()) se->proc = rb_block_proc();
    break;
  case 1:
    if (rb_obj_is_kind_of(argv[0], rb_cProc)) se->proc = argv[0];
    break;
  default:
    rb_raise(rb_eArgError, "wrong number of arguments (%d for 0 or 1)", argc);
  }
  return obj;
}

/***** siman_step *****/
typedef struct ___siman_step {
  void (*siman_step_t)(const gsl_rng *r, void *xp, double step_size);
  VALUE proc;
} siman_step;

static siman_step* siman_step_alloc();
static void siman_step_free(siman_step *se);
static void rb_gsl_siman_step_t(const gsl_rng *r, void *xp, double step_size);
static VALUE rb_gsl_siman_step_set(int argc, VALUE *argv, VALUE obj);
static void siman_step_mark(siman_step *se);

static siman_step* siman_step_alloc()
{
  siman_step *se = NULL;
  se = ALLOC(siman_step);
  se->siman_step_t = rb_gsl_siman_step_t;
  return se;
}

static void siman_step_mark(siman_step *se)
{
  rb_gc_mark(se->proc);
}

static void siman_step_free(siman_step *se)
{
  free((siman_step *) se);
}

static VALUE rb_gsl_siman_step_new(int argc, VALUE *argv, VALUE klass)
{
  VALUE obj;
  siman_step *se = NULL;
  se = siman_step_alloc();
  obj = Data_Wrap_Struct(klass, siman_step_mark, siman_step_free, se);
  rb_gsl_siman_step_set(argc, argv, obj);
  return obj;
}

static void rb_gsl_siman_step_t(const gsl_rng *r, void *data, double step_size)
{
  VALUE proc, params;
  VALUE rng;
  siman_solver *ss = NULL;
  ss = (siman_solver *) data;
  proc = (VALUE) ss->proc_step;
  rng = Data_Wrap_Struct(cgsl_rng, 0, NULL, (gsl_rng *) r);
  params = Data_Wrap_Struct(cgsl_vector, 0, NULL, ss->vx);
  rb_funcall(proc, RBGSL_ID_call, 3, rng, params, rb_float_new(step_size));
}

static VALUE rb_gsl_siman_step_set(int argc, VALUE *argv, VALUE obj)
{
  siman_step *se = NULL;
  Data_Get_Struct(obj, siman_step, se);
  switch (argc) {
  case 0:
    if (rb_block_given_p()) se->proc = rb_block_proc();
    break;
  case 1:
    if (rb_obj_is_kind_of(argv[0], rb_cProc)) se->proc = argv[0];
    break;
  default:
    rb_raise(rb_eArgError, "wrong number of arguments (%d for 0 or 1)", argc);
  }
  return obj;
}

/***** siman_metric *****/
typedef struct ___siman_metric {
  double (*siman_metric_t)(void *, void *);
  VALUE proc;
} siman_metric;

static siman_metric* siman_metric_alloc();
static void siman_metric_free(siman_metric *se);
static double rb_gsl_siman_metric_t(void *xp, void *yp);
static VALUE rb_gsl_siman_metric_set(int argc, VALUE *argv, VALUE obj);
static void siman_metric_mark(siman_metric *se);

static siman_metric* siman_metric_alloc()
{
  siman_metric *se = NULL;
  se = ALLOC(siman_metric);
  if (se == NULL) rb_raise(rb_eRuntimeError, "ALLOC failed");
  se->siman_metric_t = &rb_gsl_siman_metric_t;
  return se;
}

static void siman_metric_mark(siman_metric *se)
{
  rb_gc_mark(se->proc);
}

static void siman_metric_free(siman_metric *se)
{
  free((siman_metric *) se);
}

static VALUE rb_gsl_siman_metric_new(int argc, VALUE *argv, VALUE klass)
{
  VALUE obj;
  siman_metric *se = NULL;
  se = siman_metric_alloc();
  obj = Data_Wrap_Struct(klass, siman_metric_mark, siman_metric_free, se);
  rb_gsl_siman_metric_set(argc, argv, obj);
  return obj;
}

static double rb_gsl_siman_metric_t(void *data, void *yp)
{
  VALUE proc, vxp, vyp, result;
  siman_solver *ss = NULL, *ssy = NULL;
  ss = (siman_solver *) data;
  ssy = (siman_solver *) yp;
  proc = ss->proc_metric;
  vxp = Data_Wrap_Struct(cgsl_vector, 0, NULL, ss->vx);
  vyp = Data_Wrap_Struct(cgsl_vector, 0, NULL, ssy->vx);
  result = rb_funcall(proc, RBGSL_ID_call, 2, vxp, vyp);
  return NUM2DBL(result);
}

static VALUE rb_gsl_siman_metric_set(int argc, VALUE *argv, VALUE obj)
{
  siman_metric *se = NULL;
  Data_Get_Struct(obj, siman_metric, se);
  switch (argc) {
  case 0:
    if (rb_block_given_p()) se->proc = rb_block_proc();
    break;
  case 1:
    if (rb_obj_is_kind_of(argv[0], rb_cProc)) se->proc = argv[0];
    break;
  default:
    rb_raise(rb_eArgError, "wrong number of arguments (%d for 0 or 1)", argc);
  }
  return obj;
}

/***** siman_params_t *****/
static gsl_siman_params_t* gsl_siman_params_alloc();
static void gsl_siman_params_free(gsl_siman_params_t *params);

static gsl_siman_params_t* gsl_siman_params_alloc()
{
  gsl_siman_params_t *params = NULL;
  params = ALLOC(gsl_siman_params_t);
  return params;
}

static void gsl_siman_params_free(gsl_siman_params_t *params)
{
  free((gsl_siman_params_t *) params);
}

static VALUE rb_gsl_siman_params_set(int argc, VALUE *argv, VALUE obj);

static VALUE rb_gsl_siman_params_new(int argc, VALUE *argv, VALUE klass)
{
  gsl_siman_params_t *params = NULL;
  VALUE obj;
  params = gsl_siman_params_alloc();
  obj = Data_Wrap_Struct(klass, 0, gsl_siman_params_free, params);
  rb_gsl_siman_params_set(argc, argv, obj);
  return obj;
}

static VALUE rb_gsl_siman_params_set(int argc, VALUE *argv, VALUE obj)
{
  gsl_siman_params_t *params = NULL;
  Data_Get_Struct(obj, gsl_siman_params_t, params);
  switch (argc) {
  case 7:
    params->t_min = NUM2DBL(argv[6]);
  /* no break */
  case 6:
    params->mu_t = NUM2DBL(argv[5]);
  /* no break */
  case 5:
    params->t_initial = NUM2DBL(argv[4]);
  /* no break */
  case 4:
    params->k = NUM2DBL(argv[3]);
  /* no break */
  case 3:
    params->step_size = NUM2DBL(argv[2]);
  /* no break */
  case 2:
    params->iters_fixed_T = NUM2INT(argv[1]);
  /* no break */
  case 1:
    params->n_tries = NUM2INT(argv[0]);
  }
  return obj;
}

static VALUE rb_gsl_siman_params_n_tries(VALUE obj)
{
  gsl_siman_params_t *params = NULL;
  Data_Get_Struct(obj, gsl_siman_params_t, params);
  return INT2FIX(params->n_tries);
}

static VALUE rb_gsl_siman_params_set_n_tries(VALUE obj, VALUE n)
{
  gsl_siman_params_t *params = NULL;
  Data_Get_Struct(obj, gsl_siman_params_t, params);
  params->n_tries = NUM2INT(n);
  return obj;
}

static VALUE rb_gsl_siman_params_iters_fixed_T(VALUE obj)
{
  gsl_siman_params_t *params = NULL;
  Data_Get_Struct(obj, gsl_siman_params_t, params);
  return INT2FIX(params->iters_fixed_T);
}

static VALUE rb_gsl_siman_params_set_iters_fixed_T(VALUE obj, VALUE n)
{
  gsl_siman_params_t *params = NULL;
  Data_Get_Struct(obj, gsl_siman_params_t, params);
  params->iters_fixed_T = NUM2INT(n);
  return obj;
}

static VALUE rb_gsl_siman_params_step_size(VALUE obj)
{
  gsl_siman_params_t *params = NULL;
  Data_Get_Struct(obj, gsl_siman_params_t, params);
  return rb_float_new(params->step_size);
}

static VALUE rb_gsl_siman_params_set_step_size(VALUE obj, VALUE s)
{
  gsl_siman_params_t *params = NULL;
  Data_Get_Struct(obj, gsl_siman_params_t, params);
  params->step_size = NUM2DBL(s);
  return obj;
}

static VALUE rb_gsl_siman_params_k(VALUE obj)
{
  gsl_siman_params_t *params = NULL;
  Data_Get_Struct(obj, gsl_siman_params_t, params);
  return rb_float_new(params->k);
}

static VALUE rb_gsl_siman_params_set_k(VALUE obj, VALUE s)
{
  gsl_siman_params_t *params = NULL;
  Data_Get_Struct(obj, gsl_siman_params_t, params);
  params->k = NUM2DBL(s);
  return obj;
}

static VALUE rb_gsl_siman_params_t_initial(VALUE obj)
{
  gsl_siman_params_t *params = NULL;
  Data_Get_Struct(obj, gsl_siman_params_t, params);
  return rb_float_new(params->t_initial);
}

static VALUE rb_gsl_siman_params_set_t_initial(VALUE obj, VALUE s)
{
  gsl_siman_params_t *params = NULL;
  Data_Get_Struct(obj, gsl_siman_params_t, params);
  params->t_initial = NUM2DBL(s);
  return obj;
}

static VALUE rb_gsl_siman_params_mu_t(VALUE obj)
{
  gsl_siman_params_t *params = NULL;
  Data_Get_Struct(obj, gsl_siman_params_t, params);
  return rb_float_new(params->mu_t);
}

static VALUE rb_gsl_siman_params_set_mu_t(VALUE obj, VALUE s)
{
  gsl_siman_params_t *params = NULL;
  Data_Get_Struct(obj, gsl_siman_params_t, params);
  params->mu_t = NUM2DBL(s);
  return obj;
}

static VALUE rb_gsl_siman_params_t_min(VALUE obj)
{
  gsl_siman_params_t *params = NULL;
  Data_Get_Struct(obj, gsl_siman_params_t, params);
  return rb_float_new(params->t_min);
}

static VALUE rb_gsl_siman_params_set_t_min(VALUE obj, VALUE s)
{
  gsl_siman_params_t *params = NULL;
  Data_Get_Struct(obj, gsl_siman_params_t, params);
  params->t_min = NUM2DBL(s);
  return obj;
}

static VALUE rb_gsl_siman_params_params(VALUE obj)
{
  gsl_siman_params_t *params = NULL;
  Data_Get_Struct(obj, gsl_siman_params_t, params);
  return rb_ary_new3(7, INT2FIX(params->n_tries), INT2FIX(params->iters_fixed_T),
                     rb_float_new(params->step_size), rb_float_new(params->k),
                     rb_float_new(params->t_initial), rb_float_new(params->mu_t),
                     rb_float_new(params->t_min));
}

/***** solver *****/
static VALUE rb_gsl_siman_solver_solve(VALUE obj, VALUE rng,
                                       VALUE vx0p, VALUE vefunc,
                                       VALUE vstep, VALUE vmetric, VALUE vprint,
                                       VALUE vparams)
{
  gsl_rng *r = NULL;
  siman_solver *ss = NULL;
  siman_Efunc *efunc = NULL;
  siman_step *step = NULL;
  siman_metric *metric = NULL;
  siman_print *print = NULL;
  gsl_vector *vtmp = NULL;
  gsl_siman_params_t *params = NULL;
  int flag = 0;
  /*  Data_Get_Struct(obj, siman_solver, ss);*/
  CHECK_VECTOR(vx0p);
  Data_Get_Struct(vx0p, gsl_vector, vtmp);

  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    ss = gsl_siman_solver_alloc(vtmp->size);
    flag = 1;
    break;
  default:
    Data_Get_Struct(obj, siman_solver, ss);
  }
  if (!rb_obj_is_kind_of(rng, cgsl_rng))
    rb_raise(rb_eTypeError, "wrong argument type %s (GSL::Rng expected)",
             rb_class2name(CLASS_OF(rng)));
  if (!rb_obj_is_kind_of(vefunc, cgsl_siman_Efunc))
    rb_raise(rb_eTypeError, "wrong argument type %s (GSL::Siman::Efunc expected)",
             rb_class2name(CLASS_OF(vefunc)));
  if (!rb_obj_is_kind_of(vstep, cgsl_siman_step))
    rb_raise(rb_eTypeError, "wrong argument type %s (GSL::Siman::Step expected)",
             rb_class2name(CLASS_OF(vstep)));
  if (!rb_obj_is_kind_of(vmetric, cgsl_siman_metric))
    rb_raise(rb_eTypeError, "wrong argument type %s (GSL::Siman::Metric expected)",
             rb_class2name(CLASS_OF(vmetric)));
  Data_Get_Struct(rng, gsl_rng, r);
  Data_Get_Struct(vefunc, siman_Efunc, efunc);
  Data_Get_Struct(vstep, siman_step, step);
  Data_Get_Struct(vmetric, siman_metric, metric);
  if (NIL_P(vprint)) {
    ss->proc_print = Qnil;
  } else {
    if (!rb_obj_is_kind_of(vprint, cgsl_siman_print))
      rb_raise(rb_eTypeError, "wrong argument type %s (GSL::Siman::Print expected)",
               rb_class2name(CLASS_OF(vprint)));
    Data_Get_Struct(vprint, siman_print, print);
    ss->proc_print   = print->proc;
  }
  if (!rb_obj_is_kind_of(vparams, cgsl_siman_params))
    rb_raise(rb_eTypeError, "wrong argument type %s (GSL::Siman::Params expected)",
             rb_class2name(CLASS_OF(vparams)));
  Data_Get_Struct(vparams, gsl_siman_params_t, params);

  ss->proc_efunc   = efunc->proc;
  ss->proc_step    = step->proc;
  ss->proc_metric  = metric->proc;

  gsl_vector_memcpy(ss->vx, vtmp);

  if (NIL_P(vprint)) {
    gsl_siman_solve(r, ss, rb_gsl_siman_Efunc_t,
                    rb_gsl_siman_step_t,
                    rb_gsl_siman_metric_t,
                    NULL,
                    rb_gsl_siman_copy_t,
                    rb_gsl_siman_copy_construct_t,
                    rb_gsl_siman_destroy_t, 0,
                    *params);

  } else {
    gsl_siman_solve(r, ss, rb_gsl_siman_Efunc_t,
                    rb_gsl_siman_step_t,
                    rb_gsl_siman_metric_t,
                    rb_gsl_siman_print_t,
                    rb_gsl_siman_copy_t,
                    rb_gsl_siman_copy_construct_t,
                    rb_gsl_siman_destroy_t, 0,
                    *params);
  }
  gsl_vector_memcpy(vtmp, ss->vx);

  if (flag == 1) gsl_siman_solver_free(ss);
  return obj;
}

void Init_gsl_siman(VALUE module)
{
  VALUE mgsl_siman;

  mgsl_siman = rb_define_module_under(module, "Siman");

  cgsl_siman_Efunc = rb_define_class_under(mgsl_siman, "Efunc", cGSL_Object);
  cgsl_siman_step = rb_define_class_under(mgsl_siman, "Step", cGSL_Object);
  cgsl_siman_metric = rb_define_class_under(mgsl_siman, "Metric", cGSL_Object);
  cgsl_siman_print = rb_define_class_under(mgsl_siman, "Print", cGSL_Object);
  cgsl_siman_params = rb_define_class_under(mgsl_siman, "Params", cGSL_Object);
  cgsl_siman_solver = rb_define_class_under(mgsl_siman, "Solver", cGSL_Object);

  /***** Efunc *****/
  rb_define_singleton_method(cgsl_siman_Efunc, "alloc", rb_gsl_siman_Efunc_new, -1);
  rb_define_method(cgsl_siman_Efunc, "set", rb_gsl_siman_Efunc_set, -1);
  rb_define_alias(cgsl_siman_Efunc, "set_proc", "set");

  /***** Print *****/
  rb_define_singleton_method(cgsl_siman_print, "alloc", rb_gsl_siman_print_new, -1);
  rb_define_method(cgsl_siman_print, "set", rb_gsl_siman_print_set, -1);
  rb_define_alias(cgsl_siman_print, "set_proc", "set");

  rb_define_singleton_method(cgsl_siman_step, "alloc", rb_gsl_siman_step_new, -1);
  rb_define_method(cgsl_siman_step, "set", rb_gsl_siman_step_set, -1);
  rb_define_alias(cgsl_siman_step, "set_proc", "set");

  rb_define_singleton_method(cgsl_siman_metric, "alloc", rb_gsl_siman_metric_new, -1);
  rb_define_method(cgsl_siman_metric, "set", rb_gsl_siman_metric_set, -1);
  rb_define_alias(cgsl_siman_metric, "set_proc", "set");

  /***** params *****/
  rb_define_singleton_method(cgsl_siman_params, "alloc", rb_gsl_siman_params_new, -1);
  rb_define_method(cgsl_siman_params, "set", rb_gsl_siman_params_set, -1);
  rb_define_method(cgsl_siman_params, "params", rb_gsl_siman_params_params, 0);
  rb_define_method(cgsl_siman_params, "n_tries", rb_gsl_siman_params_n_tries, 0);
  rb_define_method(cgsl_siman_params, "set_n_tries", rb_gsl_siman_params_set_n_tries, 1);
  rb_define_alias(cgsl_siman_params, "n_tries=", "set_n_tries");
  rb_define_method(cgsl_siman_params, "iters_fixed_T", rb_gsl_siman_params_iters_fixed_T, 0);
  rb_define_method(cgsl_siman_params, "set_iters_fixed_T", rb_gsl_siman_params_set_iters_fixed_T, 1);
  rb_define_alias(cgsl_siman_params, "iters_fixed_T=", "set_iters_fixed_T");
  rb_define_method(cgsl_siman_params, "step_size", rb_gsl_siman_params_step_size, 0);
  rb_define_method(cgsl_siman_params, "set_step_size", rb_gsl_siman_params_set_step_size, 1);
  rb_define_alias(cgsl_siman_params, "step_size=", "set_step_size");
  rb_define_method(cgsl_siman_params, "k", rb_gsl_siman_params_k, 0);
  rb_define_method(cgsl_siman_params, "set_k", rb_gsl_siman_params_set_k, 1);
  rb_define_alias(cgsl_siman_params, "k=", "set_k");
  rb_define_method(cgsl_siman_params, "t_initial", rb_gsl_siman_params_t_initial, 0);
  rb_define_method(cgsl_siman_params, "set_t_initial", rb_gsl_siman_params_set_t_initial, 1);
  rb_define_alias(cgsl_siman_params, "t_initial=", "set_t_initial");
  rb_define_method(cgsl_siman_params, "mu_t", rb_gsl_siman_params_mu_t, 0);
  rb_define_method(cgsl_siman_params, "set_mu_t", rb_gsl_siman_params_set_mu_t, 1);
  rb_define_alias(cgsl_siman_params, "mu_t=", "set_mu_t");
  rb_define_method(cgsl_siman_params, "t_min", rb_gsl_siman_params_t_min, 0);
  rb_define_method(cgsl_siman_params, "set_t_min", rb_gsl_siman_params_set_t_min, 1);
  rb_define_alias(cgsl_siman_params, "t_min=", "set_t_min");

  /***** solver *****/
  rb_define_singleton_method(cgsl_siman_solver, "alloc", rb_gsl_siman_solver_new, -1);
  rb_define_method(cgsl_siman_solver, "solve", rb_gsl_siman_solver_solve, 7);

  rb_define_singleton_method(cgsl_siman_solver, "solve", rb_gsl_siman_solver_solve, 7);
  rb_define_singleton_method(mgsl_siman, "solve", rb_gsl_siman_solver_solve, 7);
}
