/*
  interp.c
  Ruby/GSL: Ruby extension library for GSL (GNU Scientific Library)
    (C) Copyright 2001-2004 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 "rb_gsl_interp.h"

static VALUE cgsl_interp_accel;
EXTERN VALUE cgsl_vector;

typedef struct {
  gsl_interp *p;
  gsl_interp_accel *a;
} rb_gsl_interp;

typedef struct {
  gsl_spline *s;
  gsl_interp_accel *a;
} rb_gsl_spline;

enum {
  GSL_INTERP_LINEAR,
  GSL_INTERP_POLYNOMIAL,
  GSL_INTERP_CSPLINE,
  GSL_INTERP_CSPLINE_PERIODIC,
  GSL_INTERP_AKIMA,
  GSL_INTERP_AKIMA_PERIODIC,
};

static void rb_gsl_interp_free(rb_gsl_interp *sp);
static void rb_gsl_spline_free(rb_gsl_spline *sp);
static const gsl_interp_type* get_interp_type(VALUE t);

static VALUE rb_gsl_interp_new(VALUE klass, VALUE t, VALUE s)
{
  const gsl_interp_type * T;
  rb_gsl_interp *rgi = NULL; 
  size_t size;
  size = FIX2INT(s);
  rgi= ALLOC(rb_gsl_interp);
  T = get_interp_type(t);
  rgi->p = gsl_interp_alloc(T, size);
  rgi->a = gsl_interp_accel_alloc();
  return Data_Wrap_Struct(klass, 0, rb_gsl_interp_free, rgi);
}

static void rb_gsl_interp_free(rb_gsl_interp *sp)
{
  gsl_interp_free(sp->p);
  gsl_interp_accel_free(sp->a);
  free((rb_gsl_interp *) sp);
}

static VALUE rb_gsl_interp_init(VALUE obj, VALUE xxa, VALUE yya)
{
  rb_gsl_interp *rgi = NULL;
  gsl_vector *xa = NULL, *ya = NULL;
  CHECK_VECTOR(xxa);
  CHECK_VECTOR(yya);
  Data_Get_Struct(obj, rb_gsl_interp, rgi);
  Data_Get_Struct(xxa, gsl_vector, xa);
  Data_Get_Struct(yya, gsl_vector, ya);
  gsl_interp_init(rgi->p, xa->data, ya->data, xa->size);
  return obj;
}

static VALUE rb_gsl_interp_name(VALUE obj)
{
  rb_gsl_interp *rgi = NULL;
  Data_Get_Struct(obj, rb_gsl_interp, rgi);
  return rb_str_new2(gsl_interp_name(rgi->p));
}

static VALUE rb_gsl_interp_min_size(VALUE obj)
{
  rb_gsl_interp *rgi = NULL;
  Data_Get_Struct(obj, rb_gsl_interp, rgi);
  return INT2FIX(gsl_interp_min_size(rgi->p));
}

/* singleton */
static VALUE rb_gsl_interp_bsearch(VALUE obj, VALUE vv, VALUE xx,
				   VALUE il, VALUE ih)
{
  gsl_vector *v = NULL;
  double x;
  size_t indexl, indexh;
  CHECK_VECTOR(vv);
  Need_Float(xx);
  CHECK_FIXNUM(il); CHECK_FIXNUM(ih);
  Data_Get_Struct(vv, gsl_vector, v);
  x = RFLOAT(xx)->value;
  indexl = FIX2INT(il);
  indexh = FIX2INT(ih);
  return INT2FIX(gsl_interp_bsearch(v->data, x, indexl, indexh));
}

static VALUE rb_gsl_interp_accel(VALUE obj)
{
  rb_gsl_interp *rgi = NULL;
  Data_Get_Struct(obj, rb_gsl_interp, rgi);
  return Data_Wrap_Struct(cgsl_interp_accel, 0, NULL, rgi->a);
}

static VALUE rb_gsl_interp_find(VALUE obj, VALUE vv, VALUE xx)
{
  rb_gsl_interp *rgi = NULL;
  gsl_vector *v = NULL;
  double x;
  CHECK_VECTOR(vv);
  Need_Float(xx);
  Data_Get_Struct(obj, rb_gsl_interp, rgi);
  Data_Get_Struct(vv, gsl_vector, v);
  x = RFLOAT(xx)->value;
  return INT2FIX(gsl_interp_accel_find(rgi->a, v->data, v->size, x));
}

static VALUE rb_gsl_interp_accel_find(VALUE obj, VALUE vv, VALUE xx)
{
  gsl_interp_accel *a = NULL;
  gsl_vector *v = NULL;
  double x;
  CHECK_VECTOR(vv);
  Need_Float(xx);
  Data_Get_Struct(obj, gsl_interp_accel, a);
  Data_Get_Struct(vv, gsl_vector, v);
  Need_Float(xx);
  x = RFLOAT(xx)->value;
  return INT2FIX(gsl_interp_accel_find(a, v->data, v->size, x));
}

static VALUE rb_gsl_interp_evaluate(VALUE obj, VALUE xxa, VALUE yya, VALUE xx,
				    double (*eval)(const gsl_interp *, const double [], 
						   const double [], double, 
						   gsl_interp_accel *))
{
  rb_gsl_interp *rgi = NULL;
  gsl_vector *xa = NULL, *ya = NULL;
  gsl_vector *v = NULL, *vnew = NULL;
  gsl_matrix *m = NULL, *mnew = NULL;
  VALUE ary, x;
  double val;
  int n, i, j;
  CHECK_VECTOR(xxa);
  CHECK_VECTOR(yya);
  Data_Get_Struct(obj, rb_gsl_interp, rgi);
  Data_Get_Struct(xxa, gsl_vector, xa);
  Data_Get_Struct(yya, gsl_vector, ya);
  if (CLASS_OF(xx) == rb_cRange) xx = rb_gsl_range2ary(xx);
  switch (TYPE(xx)) {
  case T_FIXNUM:
  case T_BIGNUM:
  case T_FLOAT:
    Need_Float(xx);
    return rb_float_new((*eval)(rgi->p, xa->data, ya->data, NUM2DBL(xx), rgi->a));
    break;
  case T_ARRAY:
    n = RARRAY(xx)->len;
    ary = rb_ary_new2(n);
    for (i = 0; i < n; i++) {
      x = rb_ary_entry(xx, i);
      Need_Float(x);
      val = (*eval)(rgi->p, xa->data, ya->data, NUM2DBL(x), rgi->a);
      rb_ary_store(ary, i, rb_float_new(val));
    }
    return ary;
    break;
  default:
    if (VECTOR_P(xx)) {
      Data_Get_Struct(xx, gsl_vector, v);
      vnew = gsl_vector_alloc(v->size);
      for (i = 0; i < v->size; i++) {
	val = (*eval)(rgi->p, xa->data, ya->data, gsl_vector_get(v, i), rgi->a);
	gsl_vector_set(vnew, i, val);
      }
      return Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, vnew);
    } else if (MATRIX_P(xx)) {
      Data_Get_Struct(xx, gsl_matrix, m);
      mnew = gsl_matrix_alloc(m->size1, m->size2);
      for (i = 0; i < m->size1; i++) {
	for (j = 0; j < m->size2; j++) {
	  val = (*eval)(rgi->p, xa->data, ya->data, gsl_matrix_get(m, i, j), rgi->a);
	  gsl_matrix_set(mnew, i, j, val);
	}
      }
      return Data_Wrap_Struct(cgsl_matrix, 0, gsl_matrix_free, mnew);
    } else {
      rb_raise(rb_eTypeError, "wrong argument type");
    }
    break;
  }

  /* never reach here */
  return Qnil;
}

static VALUE rb_gsl_interp_eval(VALUE obj, VALUE xxa, VALUE yya, VALUE xx)
{
  return rb_gsl_interp_evaluate(obj, xxa, yya, xx, gsl_interp_eval);
}

static VALUE rb_gsl_interp_eval_e(VALUE obj, VALUE xxa, VALUE yya, VALUE xx)
{
  rb_gsl_interp *rgi = NULL;
  gsl_vector *xa = NULL, *ya = NULL;
  double x, y;
  CHECK_VECTOR(xxa);
  CHECK_VECTOR(yya);
  Need_Float(xx);
  Data_Get_Struct(obj, rb_gsl_interp, rgi);
  Data_Get_Struct(xxa, gsl_vector, xa);
  Data_Get_Struct(yya, gsl_vector, ya);
  x = RFLOAT(xx)->value;
  gsl_interp_eval_e(rgi->p, xa->data, ya->data, x, rgi->a, &y);
  return rb_float_new(y);
}

static VALUE rb_gsl_interp_eval_deriv(VALUE obj, VALUE xxa, VALUE yya, VALUE xx)
{
  return rb_gsl_interp_evaluate(obj, xxa, yya, xx, gsl_interp_eval_deriv);
}

static VALUE rb_gsl_interp_eval_deriv_e(VALUE obj, VALUE xxa, VALUE yya, VALUE xx)
{
  rb_gsl_interp *rgi = NULL;
  gsl_vector *xa = NULL, *ya = NULL;
  double x, y;
  CHECK_VECTOR(xxa);
  CHECK_VECTOR(yya);
  Need_Float(xx);
  Data_Get_Struct(obj, rb_gsl_interp, rgi);
  Data_Get_Struct(xxa, gsl_vector, xa);
  Data_Get_Struct(yya, gsl_vector, ya);
  x = RFLOAT(xx)->value;
  gsl_interp_eval_deriv_e(rgi->p, xa->data, ya->data, x, rgi->a, &y);
  return rb_float_new(y);
}

static VALUE rb_gsl_interp_eval_deriv2(VALUE obj, VALUE xxa, VALUE yya, VALUE xx)
{
  return rb_gsl_interp_evaluate(obj, xxa, yya, xx, gsl_interp_eval_deriv2);
}

static VALUE rb_gsl_interp_eval_deriv2_e(VALUE obj, VALUE xxa, VALUE yya, VALUE xx)
{
  rb_gsl_interp *rgi = NULL;
  gsl_vector *xa = NULL, *ya = NULL;
  double x, y;
  CHECK_VECTOR(xxa);
  CHECK_VECTOR(yya);
  Need_Float(xx);
  Data_Get_Struct(obj, rb_gsl_interp, rgi);
  Data_Get_Struct(xxa, gsl_vector, xa);
  Data_Get_Struct(yya, gsl_vector, ya);
  x = RFLOAT(xx)->value;
  gsl_interp_eval_deriv2_e(rgi->p, xa->data, ya->data, x, rgi->a, &y);
  return rb_float_new(y);
}

static VALUE rb_gsl_interp_eval_integ(VALUE obj, VALUE xxa, VALUE yya, VALUE aa, VALUE bb)
{
  rb_gsl_interp *rgi = NULL;
  gsl_vector *xa = NULL, *ya = NULL;
  double a, b;
  CHECK_VECTOR(xxa);
  CHECK_VECTOR(yya);
  Need_Float(aa);
  Need_Float(bb);
  Data_Get_Struct(obj, rb_gsl_interp, rgi);
  Data_Get_Struct(xxa, gsl_vector, xa);
  Data_Get_Struct(yya, gsl_vector, ya);
  a = RFLOAT(aa)->value;
  b = RFLOAT(bb)->value;
  return rb_float_new(gsl_interp_eval_integ(rgi->p, xa->data, ya->data, a, b, rgi->a));
}

static VALUE rb_gsl_interp_eval_integ_e(VALUE obj, VALUE xxa, VALUE yya, VALUE aa, VALUE bb)
{
  rb_gsl_interp *rgi = NULL;
  gsl_vector *xa = NULL, *ya = NULL;
  double y, a, b;
  CHECK_VECTOR(xxa);
  CHECK_VECTOR(yya);
  Need_Float(aa);
  Need_Float(bb);
  Data_Get_Struct(obj, rb_gsl_interp, rgi);
  Data_Get_Struct(xxa, gsl_vector, xa);
  Data_Get_Struct(yya, gsl_vector, ya);
  a = RFLOAT(aa)->value;
  b = RFLOAT(bb)->value;
  gsl_interp_eval_integ_e(rgi->p, xa->data, ya->data, a, b, rgi->a, &y);
  return rb_float_new(y);
}

/******/

static VALUE rb_gsl_spline_new(VALUE klass, VALUE t, VALUE s)
{
  VALUE obj;
  rb_gsl_spline *sp = NULL;
  const gsl_interp_type * T;
  size_t size;
  sp = ALLOC(rb_gsl_spline);
  size = FIX2INT(s);
  T = get_interp_type(t);
  sp->s = gsl_spline_alloc(T, size);
  sp->a = gsl_interp_accel_alloc();
  obj = Data_Wrap_Struct(klass, 0, rb_gsl_spline_free, sp);
  return obj;
}

static void rb_gsl_spline_free(rb_gsl_spline *sp)
{
  gsl_spline_free(sp->s);
  gsl_interp_accel_free(sp->a);
  free((rb_gsl_spline *) sp);
}

static VALUE rb_gsl_spline_init(VALUE obj, VALUE xxa, VALUE yya)
{
  rb_gsl_spline *sp = NULL;
  gsl_spline *p = NULL;
  gsl_vector *xa = NULL, *ya = NULL;
  int i, size;
  Data_Get_Struct(obj, rb_gsl_spline, sp);
  p = sp->s;
  if (TYPE(xxa) == T_ARRAY) {
    size = RARRAY(xxa)->len;
    xa = gsl_vector_alloc(size);
    for (i = 0; i < size; i++) gsl_vector_set(xa, i, NUM2DBL(rb_ary_entry(xxa, i)));
  } else if (rb_obj_is_kind_of(xxa, cgsl_vector)) {
    CHECK_VECTOR(xxa);
    Data_Get_Struct(xxa, gsl_vector, xa);
    size = xa->size;
  } else {
    rb_raise(rb_eTypeError, "not a vector");
  }
  if (TYPE(yya) == T_ARRAY) {
    ya = gsl_vector_alloc(size);
    for (i = 0; i < size; i++) gsl_vector_set(ya, i, NUM2DBL(rb_ary_entry(yya, i)));
  } else if (rb_obj_is_kind_of(yya, cgsl_vector)) {
    CHECK_VECTOR(yya);
    Data_Get_Struct(yya, gsl_vector, ya);
  } else {
    rb_raise(rb_eTypeError, "not a vector");
  }
  gsl_spline_init(p, xa->data, ya->data, size);
  return obj;
}

static VALUE rb_gsl_spline_accel(VALUE obj)
{
  rb_gsl_spline *rgi = NULL;
  Data_Get_Struct(obj, rb_gsl_spline, rgi);
  return Data_Wrap_Struct(cgsl_interp_accel, 0, NULL, rgi->a);
}

static VALUE rb_gsl_spline_evaluate(VALUE obj, VALUE xx,
				    double (*eval)(const gsl_spline *, double, 
						   gsl_interp_accel *))
{
  rb_gsl_spline *rgs = NULL;
  gsl_vector *v = NULL, *vnew = NULL;
  gsl_matrix *m = NULL, *mnew = NULL;
  VALUE ary, x;
  double val;
  int n, i, j;
  Data_Get_Struct(obj, rb_gsl_spline, rgs);
  if (CLASS_OF(xx) == rb_cRange) xx = rb_gsl_range2ary(xx);
  switch (TYPE(xx)) {
  case T_FIXNUM:
  case T_BIGNUM:
  case T_FLOAT:
    Need_Float(xx);
    return rb_float_new((*eval)(rgs->s, NUM2DBL(xx), rgs->a));
    break;
  case T_ARRAY:
    n = RARRAY(xx)->len;
    ary = rb_ary_new2(n);
    for (i = 0; i < n; i++) {
      x = rb_ary_entry(xx, i);
      Need_Float(x);
      val = (*eval)(rgs->s, NUM2DBL(x), rgs->a);
      rb_ary_store(ary, i, rb_float_new(val));
    }
    return ary;
    break;
  default:
    if (VECTOR_P(xx)) {
      Data_Get_Struct(xx, gsl_vector, v);
      vnew = gsl_vector_alloc(v->size);
      for (i = 0; i < v->size; i++) {
	val = (*eval)(rgs->s, gsl_vector_get(v, i), rgs->a);
	gsl_vector_set(vnew, i, val);
      }
      return Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, vnew);
    } else if (MATRIX_P(xx)) {
      Data_Get_Struct(xx, gsl_matrix, m);
      mnew = gsl_matrix_alloc(m->size1, m->size2);
      for (i = 0; i < m->size1; i++) {
	for (j = 0; j < m->size2; j++) {
	  val = (*eval)(rgs->s, gsl_matrix_get(m, i, j), rgs->a);
	  gsl_matrix_set(mnew, i, j, val);
	}
      }
      return Data_Wrap_Struct(cgsl_matrix, 0, gsl_matrix_free, mnew);
    } else {
      rb_raise(rb_eTypeError, "wrong argument type");
    }
    break;
  }

  /* never reach here */
  return Qnil;
}

static VALUE rb_gsl_spline_eval(VALUE obj, VALUE xx)
{
  return rb_gsl_spline_evaluate(obj, xx, gsl_spline_eval);
}

static VALUE rb_gsl_spline_eval_deriv(VALUE obj, VALUE xx)
{
  return rb_gsl_spline_evaluate(obj, xx, gsl_spline_eval_deriv);
}

static VALUE rb_gsl_spline_eval_deriv2(VALUE obj, VALUE xx)
{
  return rb_gsl_spline_evaluate(obj, xx, gsl_spline_eval_deriv2);
}

static VALUE rb_gsl_spline_eval_integ(VALUE obj, VALUE aa, VALUE bb)
{
  rb_gsl_spline *sp = NULL;
  gsl_spline *s = NULL;
  gsl_interp_accel *acc = NULL;
  double a, b;
  Need_Float(aa);
  Need_Float(bb);
  Data_Get_Struct(obj, rb_gsl_spline, sp);
  s = sp->s;
  acc = sp->a;
  a = NUM2DBL(aa);
  b = NUM2DBL(bb);
  return rb_float_new(gsl_spline_eval_integ(s, a, b, acc));
}

static const gsl_interp_type* get_interp_type(VALUE t)
{
  int type;
  char name[32];

  switch (TYPE(t)) {
  case T_FIXNUM:
    type = FIX2INT(t);
    switch (type) {
    case GSL_INTERP_LINEAR: return gsl_interp_linear; break;
    case GSL_INTERP_POLYNOMIAL: return gsl_interp_polynomial; break;
    case GSL_INTERP_CSPLINE: return gsl_interp_cspline; break;
    case GSL_INTERP_CSPLINE_PERIODIC: return gsl_interp_cspline_periodic; break;
    case GSL_INTERP_AKIMA: return gsl_interp_akima; break;
    case GSL_INTERP_AKIMA_PERIODIC: return gsl_interp_akima_periodic; break;
    default:
      rb_raise(rb_eTypeError, "unknown type %d\n", type);
    }
    break;
  case T_STRING:
    strcpy(name, STR2CSTR(t));
    if (str_tail_grep(name, "linear") == 0) {
      return gsl_interp_linear;
    } else if (str_tail_grep(name, "polynomial") == 0) {
      return gsl_interp_polynomial;
    } else if (str_tail_grep(name, "cspline") == 0) {
      return gsl_interp_cspline;
    } else if (str_tail_grep(name, "cspline_periodic") == 0) {
      return gsl_interp_cspline_periodic;
    } else if (str_tail_grep(name, "akima") == 0) {
      return gsl_interp_akima;
    } else if (str_tail_grep(name, "akima_periodic") == 0) {
      return gsl_interp_akima_periodic;
    } else {
      rb_raise(rb_eTypeError, "Unknown type");
    }
    break;
  default:
    rb_raise(rb_eTypeError, "Unknown type");
  }
}

static VALUE rb_gsl_spline_name(VALUE obj)
{
  rb_gsl_spline *sp = NULL;
  Data_Get_Struct(obj, rb_gsl_spline, sp);
  return rb_str_new2(gsl_interp_name(sp->s->interp));
}

static VALUE rb_gsl_spline_find(VALUE obj, VALUE vv, VALUE xx)
{
  rb_gsl_spline *sp = NULL;
  gsl_vector *v = NULL;
  double x;
  CHECK_VECTOR(vv);
  Need_Float(xx);
  Data_Get_Struct(vv, gsl_vector, v);
  Data_Get_Struct(obj, rb_gsl_spline, sp);
  x = RFLOAT(xx)->value;
  return INT2FIX(gsl_interp_accel_find(sp->a, v->data, v->size, x));
}

void Init_gsl_interp()
{
  VALUE cgsl_interp, cgsl_spline;

  cgsl_interp = rb_define_class_under(mgsl, "Interp", rb_cObject);
  cgsl_interp_accel = rb_define_class_under(cgsl_interp, "Accel", rb_cObject);
  cgsl_spline = rb_define_class_under(mgsl, "Spline", rb_cObject);

  rb_define_const(cgsl_interp, "LINEAR", INT2FIX(GSL_INTERP_LINEAR));
  rb_define_const(cgsl_interp, "POLYNOMIAL", INT2FIX(GSL_INTERP_POLYNOMIAL));
  rb_define_const(cgsl_interp, "CSPLINE", INT2FIX(GSL_INTERP_CSPLINE));
  rb_define_const(cgsl_interp, "CSPLINE_PERIODIC", INT2FIX(GSL_INTERP_CSPLINE_PERIODIC));
  rb_define_const(cgsl_interp, "AKIMA", INT2FIX(GSL_INTERP_AKIMA));
  rb_define_const(cgsl_interp, "AKIMA_PERIODIC", INT2FIX(GSL_INTERP_AKIMA_PERIODIC));

  rb_define_const(cgsl_interp, "Linear", INT2FIX(GSL_INTERP_LINEAR));
  rb_define_const(cgsl_interp, "Polynomial", INT2FIX(GSL_INTERP_POLYNOMIAL));
  rb_define_const(cgsl_interp, "CSpline", INT2FIX(GSL_INTERP_CSPLINE));
  rb_define_const(cgsl_interp, "CSpline_Periodic", INT2FIX(GSL_INTERP_CSPLINE_PERIODIC));
  rb_define_const(cgsl_interp, "Akima", INT2FIX(GSL_INTERP_AKIMA));
  rb_define_const(cgsl_interp, "Akima_Periodic", INT2FIX(GSL_INTERP_AKIMA_PERIODIC));

  rb_define_singleton_method(cgsl_interp, "new", rb_gsl_interp_new, 2);
  rb_define_singleton_method(cgsl_interp, "alloc", rb_gsl_interp_new, 2);

  rb_define_singleton_method(cgsl_spline, "new", rb_gsl_spline_new, 2);
  rb_define_singleton_method(cgsl_spline, "alloc", rb_gsl_spline_new, 2);

  /*****/

  rb_define_singleton_method(cgsl_interp, "bsearch", rb_gsl_interp_bsearch, 4);
  rb_define_method(cgsl_interp, "name", rb_gsl_interp_name, 0);
  rb_define_method(cgsl_interp, "min_size", rb_gsl_interp_min_size, 0);
  rb_define_method(cgsl_interp, "init", rb_gsl_interp_init, 2);
  rb_define_method(cgsl_interp, "accel", rb_gsl_interp_accel, 0);
  rb_define_method(cgsl_interp, "eval", rb_gsl_interp_eval, 3);
  rb_define_method(cgsl_interp, "eval_e", rb_gsl_interp_eval_e, 3);
  rb_define_method(cgsl_interp, "eval_deriv", rb_gsl_interp_eval_deriv, 3);
  rb_define_method(cgsl_interp, "eval_deriv_e", rb_gsl_interp_eval_deriv_e, 3);
  rb_define_method(cgsl_interp, "eval_deriv2", rb_gsl_interp_eval_deriv2, 3);
  rb_define_method(cgsl_interp, "eval_deriv2_e", rb_gsl_interp_eval_deriv2_e, 3);
  rb_define_method(cgsl_interp, "eval_integ", rb_gsl_interp_eval_integ, 4);
  rb_define_method(cgsl_interp, "eval_integ_e", rb_gsl_interp_eval_integ_e, 4);

  /*****/

  rb_define_method(cgsl_spline, "init", rb_gsl_spline_init, 2);
  rb_define_method(cgsl_spline, "accel", rb_gsl_spline_accel, 0);
  rb_define_method(cgsl_spline, "eval", rb_gsl_spline_eval, 1);
  rb_define_method(cgsl_spline, "eval_deriv", rb_gsl_spline_eval_deriv, 1);
  rb_define_method(cgsl_spline, "eval_deriv2", rb_gsl_spline_eval_deriv2, 1);
  rb_define_method(cgsl_spline, "eval_integ", rb_gsl_spline_eval_integ, 2);

  rb_define_method(cgsl_spline, "name", rb_gsl_spline_name, 0);
  /*****/
  rb_define_method(cgsl_interp_accel, "find", rb_gsl_interp_accel_find, 2);

  rb_define_method(cgsl_interp, "find", rb_gsl_interp_find, 2);
  rb_define_alias(cgsl_interp, "accel_find", "find");

  rb_define_method(cgsl_spline, "find", rb_gsl_spline_find, 2);
  rb_define_alias(cgsl_spline, "accel_find", "find");
}
