/*
  poly.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_poly.h"
#include "rb_gsl_array.h"
#include "rb_gsl_common.h"
VALUE cgsl_poly;
VALUE cgsl_poly_dd;
VALUE cgsl_poly_taylor;
VALUE cgsl_poly_workspace;
void Init_gsl_poly2();

static VALUE rb_gsl_poly_eval(VALUE obj, VALUE xx)
{
  gsl_poly *p = NULL;
  gsl_vector *v = NULL, *vnew = NULL;
  gsl_matrix *m = NULL, *mnew = NULL;
  VALUE x, ary;
  int i, j;
  Data_Get_Struct(obj, gsl_poly, p);
  if (CLASS_OF(xx) == rb_cRange) xx = rb_gsl_range2ary(xx);
  switch (TYPE(xx)) {
  case T_FIXNUM:
  case T_BIGNUM:
  case T_FLOAT:
    return rb_float_new(gsl_poly_eval(p->data, p->size, NUM2DBL(xx)));
    break;
  case T_ARRAY:
    ary = rb_ary_new2(RARRAY(xx)->len);
    for (i = 0; i < RARRAY(xx)->len; i++) {
      x = rb_ary_entry(xx, i);
      Need_Float(x);
      rb_ary_store(ary, i, rb_float_new(gsl_poly_eval(p->data, p->size, NUM2DBL(x))));
    }
    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++) {
	gsl_vector_set(vnew, i, gsl_poly_eval(p->data, p->size, gsl_vector_get(v, i)));
      }
      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++) {
	  gsl_matrix_set(mnew, i, j, 
			 gsl_poly_eval(p->data, p->size, gsl_matrix_get(m, i, j)));
	}
      }
      return Data_Wrap_Struct(cgsl_matrix, 0, gsl_matrix_free, mnew);
    } else {
      rb_raise(rb_eTypeError, "wrong argument type");
    }
    break;
  }
  return Qnil; /* never reach here */
}

/* ax*x + bx + c = 0 */
static VALUE rb_gsl_poly_solve_quadratic(int argc, VALUE *argv, VALUE obj)
{
  double x0, x1;
  gsl_poly *v = NULL;
  int n;
  switch (argc) {
  case 3:
    n = gsl_poly_solve_quadratic(NUM2DBL(argv[0]), NUM2DBL(argv[1]), NUM2DBL(argv[2]),
				 &x0, &x1);
    break;
  case 1:
    switch (TYPE(argv[0])) {
    case T_ARRAY:
      n = gsl_poly_solve_quadratic(NUM2DBL(rb_ary_entry(argv[0], 0)), 
				   NUM2DBL(rb_ary_entry(argv[0], 1)),
				   NUM2DBL(rb_ary_entry(argv[0], 2)),
				   &x0, &x1);
      break;
    default:
      CHECK_VECTOR(argv[0]);
      Data_Get_Struct(argv[0], gsl_poly, v);
      n = gsl_poly_solve_quadratic(gsl_vector_get(v, 0),
				   gsl_vector_get(v, 1),
				   gsl_vector_get(v, 2),
				   &x0, &x1);
    }
    break;
  default:
    rb_raise(rb_eArgError, 
	     "wrong number of arguments (3 numbers or 1 array or 1 vector)");
  }
  return rb_ary_new3(2, rb_float_new(x0), rb_float_new(x1));
}

static VALUE rb_gsl_poly_complex_solve_quadratic(int argc, VALUE *argv, VALUE obj)
{
  gsl_complex *z0 = NULL, *z1 = NULL;
  gsl_vector *v = NULL;
  VALUE obj0, obj1;
  int n;
  z0 = ALLOC(gsl_complex);
  z1 = ALLOC(gsl_complex);
  switch (argc) {
  case 3:
    n = gsl_poly_complex_solve_quadratic(NUM2DBL(argv[0]), 
					 NUM2DBL(argv[1]), NUM2DBL(argv[2]),
					 z0, z1);
    break;
  case 1:
    switch (TYPE(argv[0])) {
    case T_ARRAY:
      n = gsl_poly_complex_solve_quadratic(NUM2DBL(rb_ary_entry(argv[0], 0)), 
					   NUM2DBL(rb_ary_entry(argv[0], 1)),
					   NUM2DBL(rb_ary_entry(argv[0], 2)),
					   z0, z1);
      break;
    default:
      CHECK_VECTOR(argv[0]);
      Data_Get_Struct(argv[0], gsl_vector, v);
      n = gsl_poly_complex_solve_quadratic(gsl_vector_get(v, 0),
					   gsl_vector_get(v, 1),
					   gsl_vector_get(v, 2),
					   z0, z1);
    }
    break;
  default:
    rb_raise(rb_eArgError, 
	     "wrong number of arguments (3 numbers or 1 array or 1 vector)");
  }
  obj0 = Data_Wrap_Struct(cgsl_complex, 0, rb_gsl_complex_free, z0);
  obj1 = Data_Wrap_Struct(cgsl_complex, 0, rb_gsl_complex_free, z1);
  return rb_ary_new3(2, obj0, obj1);
}

static VALUE rb_gsl_poly_solve_cubic(int argc, VALUE *argv, VALUE obj)
{
  double x0, x1, x2;
  gsl_vector *v = NULL;
  int n;
  switch (argc) {
  case 3:
    n = gsl_poly_solve_cubic(NUM2DBL(argv[0]), NUM2DBL(argv[1]), NUM2DBL(argv[2]),
			     &x0, &x1, &x2);
    break;
  case 1:
    switch (TYPE(argv[0])) {
    case T_ARRAY:
      n = gsl_poly_solve_cubic(NUM2DBL(rb_ary_entry(argv[0], 0)), 
				   NUM2DBL(rb_ary_entry(argv[0], 1)),
				   NUM2DBL(rb_ary_entry(argv[0], 2)),
				   &x0, &x1, &x2);
      break;
    default:
      CHECK_VECTOR(argv[0]);
      Data_Get_Struct(argv[0], gsl_vector, v);
      n = gsl_poly_solve_cubic(gsl_vector_get(v, 0),
				   gsl_vector_get(v, 1),
				   gsl_vector_get(v, 2),
				   &x0, &x1, &x2);
    }
    break;
  default:
    rb_raise(rb_eArgError, 
	     "wrong number of arguments (3 numbers or 1 array or 1 vector)");
  }
  return rb_ary_new3(3, rb_float_new(x0), rb_float_new(x1),
		     rb_float_new(x2));
}

static VALUE rb_gsl_poly_complex_solve_cubic(int argc, VALUE *argv, VALUE obj)
{
  gsl_complex *z0 = NULL, *z1 = NULL, *z2 = NULL;
  gsl_vector *v = NULL;
  VALUE obj0, obj1, obj2;
  int n;
  z0 = ALLOC(gsl_complex);
  z1 = ALLOC(gsl_complex);
  z2 = ALLOC(gsl_complex);
  switch (argc) {
  case 3:
    n = gsl_poly_complex_solve_cubic(NUM2DBL(argv[0]), NUM2DBL(argv[1]), NUM2DBL(argv[2]),
				     z0, z1, z2);
    break;
  case 1:
    switch (TYPE(argv[0])) {
    case T_ARRAY:
      n = gsl_poly_complex_solve_cubic(NUM2DBL(rb_ary_entry(argv[0], 0)), 
				   NUM2DBL(rb_ary_entry(argv[0], 1)),
				   NUM2DBL(rb_ary_entry(argv[0], 2)),
				   z0, z1, z2);
      break;
    default:
      CHECK_VECTOR(argv[0]);
      Data_Get_Struct(argv[0], gsl_vector, v);
      n = gsl_poly_complex_solve_cubic(gsl_vector_get(v, 0),
				   gsl_vector_get(v, 1),
				   gsl_vector_get(v, 2),
				   z0, z1, z2);
    }
    break;
  default:
    rb_raise(rb_eArgError, 
	     "wrong number of arguments (3 numbers or 1 array or 1 vector)");
  }

  obj0 = Data_Wrap_Struct(cgsl_complex, 0, rb_gsl_complex_free, z0);
  obj1 = Data_Wrap_Struct(cgsl_complex, 0, rb_gsl_complex_free, z1);
  obj2 = Data_Wrap_Struct(cgsl_complex, 0, rb_gsl_complex_free, z2);
  return rb_ary_new3(3, obj0, obj1, obj2);
}

/* singleton method */
static VALUE rb_gsl_poly_complex_solve(int argc, VALUE *argv, VALUE obj)
{
  size_t size, size2;
  gsl_poly_complex_workspace *w = NULL;
  gsl_vector *v = NULL, *z = NULL;
  gsl_complex *c = NULL;
  VALUE ary;
  int status, i, flag = 0, flagv = 0;

  switch (TYPE(argv[0])) {
  case T_ARRAY:
    size = RARRAY(argv[0])->len;
    v = gsl_vector_alloc(size);
    for (i = 0; i < size; i++) gsl_vector_set(v, i, NUM2DBL(rb_ary_entry(argv[0], i)));
    flagv = 1;
    break;
  case T_FIXNUM:
  case T_BIGNUM:
  case T_FLOAT:
    if (rb_obj_is_kind_of(argv[argc-1], cgsl_poly_workspace)) size = argc - 1;
    else size = argc;
    v = gsl_vector_alloc(size);
    for (i = 0; i < size; i++) gsl_vector_set(v, i, NUM2DBL(argv[i]));
    flagv = 1;
    break;
  default:
    if (rb_obj_is_kind_of(argv[0], cgsl_vector)) {
      Data_Get_Struct(argv[0], gsl_vector, v);
      size = v->size;
    } else {
      rb_raise(rb_eTypeError, "wrong argument type (Array, Vector, or Numeric expected");
    }
  }

  size2 = 2*(size - 1);
  z = gsl_vector_alloc(size2);
  if (rb_obj_is_kind_of(argv[argc-1], cgsl_poly_workspace)) {
    Data_Get_Struct(argv[argc-1], gsl_poly_complex_workspace, w);
    flag = 0;
  } else {
    w = gsl_poly_complex_workspace_alloc(size);
    flag = 1;
  }
  
  status = gsl_poly_complex_solve(v->data, size, w, z->data);

  if (flag == 1) gsl_poly_complex_workspace_free(w);
  if (flagv == 1) gsl_vector_free(v);
  ary = rb_ary_new2(size - 1);
  for (i = 0; i < size - 1; i++) {
    c = ALLOC(gsl_complex);
    c->dat[0] = gsl_vector_get(z, i*2);
    c->dat[1] = gsl_vector_get(z, i*2+1);
    rb_ary_store(ary, i, Data_Wrap_Struct(cgsl_complex, 0, rb_gsl_complex_free, c));
  }
  gsl_vector_free(z);

  return ary;
}

/* a2 x*x + a1 x + a0 = 0 */
static VALUE rb_gsl_poly_solve_quadratic2(VALUE obj)
{
  gsl_vector *v = NULL;
  double a2, a1, a0;
  double d;                /* determinant */
  double x0, x1;
  int n;
  gsl_complex *z0 = NULL, *z1 = NULL;
  VALUE obj0, obj1;

  Data_Get_Struct(obj, gsl_vector, v);
  if (v->size < 3) {
    rb_raise(rb_eArgError, "the order of the object is less than 3.");
  }
  a2 = gsl_vector_get(v, 2);   /* coefficients */
  a1 = gsl_vector_get(v, 1);
  a0 = gsl_vector_get(v, 0);
  d = a1*a1 - 4.0*a2*a0;    /* determinant */

  if (d >= 0.0) {
    n = gsl_poly_solve_quadratic(a2, a1, a0, &x0, &x1);
    return rb_ary_new3(2, rb_float_new(x0), rb_float_new(x1));
  } else {
    z0 = ALLOC(gsl_complex);
    z1 = ALLOC(gsl_complex);
    
    n = gsl_poly_complex_solve_quadratic(a2, a1, a0, z0, z1);
    
    obj0 = Data_Wrap_Struct(cgsl_complex, 0, rb_gsl_complex_free, z0);
    obj1 = Data_Wrap_Struct(cgsl_complex, 0, rb_gsl_complex_free, z1);

    return rb_ary_new3(2, obj0, obj1);
  }

}

static VALUE rb_gsl_poly_complex_solve_quadratic2(VALUE obj)
{
  gsl_vector *v = NULL;
  double a2, a1, a0;
  double d;                /* determinant */
  int n;
  gsl_complex *z0 = NULL, *z1 = NULL;
  VALUE obj0, obj1;

  Data_Get_Struct(obj, gsl_vector, v);
  if (v->size < 3) {
    rb_raise(rb_eArgError, "the order of the object is less than 3.");
  }
  a2 = gsl_vector_get(v, 2);   /* coefficients */
  a1 = gsl_vector_get(v, 1);
  a0 = gsl_vector_get(v, 0);
  d = a1*a1 - 4.0*a2*a0;    /* determinant */
 
  z0 = ALLOC(gsl_complex);
  z1 = ALLOC(gsl_complex);
  
  n = gsl_poly_complex_solve_quadratic(a2, a1, a0, z0, z1);
  
  obj0 = Data_Wrap_Struct(cgsl_complex, 0, rb_gsl_complex_free, z0);
  obj1 = Data_Wrap_Struct(cgsl_complex, 0, rb_gsl_complex_free, z1);
  
  return rb_ary_new3(2, obj0, obj1);
}

/* x**3 + a2 x**2 + a1 x + a0 = 0 */
static VALUE rb_gsl_poly_solve_cubic2(VALUE obj)
{
  gsl_vector *v = NULL;
  double a3, a2, a1, a0;
  double x0, x1, x2;
  int n;

  Data_Get_Struct(obj, gsl_vector, v);
  if (v->size < 4) {
    rb_raise(rb_eArgError, "the order of the object is less than 4.");
  }
  a3 = gsl_vector_get(v, 3);   
  a2 = gsl_vector_get(v, 2)/a3;   /* coefficients */
  a1 = gsl_vector_get(v, 1)/a3;
  a0 = gsl_vector_get(v, 0)/a3;
  n = gsl_poly_solve_cubic(a2, a1, a0, &x0, &x1, &x2);
  return rb_ary_new3(3, rb_float_new(x0), rb_float_new(x1), rb_float_new(x2));
}

static VALUE rb_gsl_poly_complex_solve_cubic2(VALUE obj)
{
  gsl_vector *v = NULL;
  double a3, a2, a1, a0;
  int n;
  gsl_complex *z0 = NULL, *z1 = NULL, *z2 = NULL;
  VALUE obj0, obj1, obj2;

  Data_Get_Struct(obj, gsl_vector, v);
  if (v->size < 4) {
    rb_raise(rb_eArgError, "the order of the object is less than 4.");
  }
  a3 = gsl_vector_get(v, 3);   
  a2 = gsl_vector_get(v, 2)/a3;   /* coefficients */
  a1 = gsl_vector_get(v, 1)/a3;
  a0 = gsl_vector_get(v, 0)/a3;

  z0 = ALLOC(gsl_complex);
  z1 = ALLOC(gsl_complex);
  z2 = ALLOC(gsl_complex);
  
  n = gsl_poly_complex_solve_cubic(a2, a1, a0, z0, z1, z2);
  
  obj0 = Data_Wrap_Struct(cgsl_complex, 0, rb_gsl_complex_free, z0);
  obj1 = Data_Wrap_Struct(cgsl_complex, 0, rb_gsl_complex_free, z1);
  obj2 = Data_Wrap_Struct(cgsl_complex, 0, rb_gsl_complex_free, z2);
  
  return rb_ary_new3(3, obj0, obj1, obj2);
}

/* method */
static VALUE rb_gsl_poly_complex_solve2(int argc, VALUE *argv, VALUE obj)
{
  gsl_vector *v = NULL, *z = NULL;
  size_t size, size2;
  gsl_poly_complex_workspace *w = NULL;
  gsl_complex *c = NULL;
  VALUE ary;
  int n, i, flag = 0;

  Data_Get_Struct(obj, gsl_vector, v);
  size = v->size;
  size2 = 2*(size - 1);
  z = gsl_vector_alloc(size2);

  if (argc == 1 && rb_obj_is_kind_of(argv[0], cgsl_poly_workspace)) {
    Data_Get_Struct(argv[0], gsl_poly_complex_workspace, w);
    flag = 0;
  } else {
    w = gsl_poly_complex_workspace_alloc(size);
    flag = 1;
  }
  
  n = gsl_poly_complex_solve(v->data, size, w, z->data);

  /*  ary = make_rarray_from_cvector(z);*/
  ary = rb_ary_new2(size - 1);
  for (i = 0; i < size - 1; i++) {
    c = ALLOC(gsl_complex);
    c->dat[0] = gsl_vector_get(z, i*2);
    c->dat[1] = gsl_vector_get(z, i*2+1);
    rb_ary_store(ary, i, Data_Wrap_Struct(cgsl_complex, 0, rb_gsl_complex_free, c));
  }
  gsl_vector_free(z);
  if (flag == 1) gsl_poly_complex_workspace_free(w);

  return ary;
}

static VALUE rb_gsl_poly_workspace_new(VALUE klass, VALUE n)
{
  gsl_poly_complex_workspace *w = NULL;
  w = gsl_poly_complex_workspace_alloc(FIX2INT(n));
  return Data_Wrap_Struct(klass, 0, gsl_poly_complex_workspace_free, w);
}

typedef struct __poly_dd {
  gsl_vector *dd;
  gsl_vector *xa;
  VALUE vdd, vxa;
} poly_dd;

static void poly_dd_free(poly_dd *dd);
static poly_dd* poly_dd_alloc(size_t n);

static poly_dd* poly_dd_alloc(size_t n)
{
  poly_dd *dd = NULL;

  dd = ALLOC(poly_dd);
  dd->dd = gsl_vector_alloc(n);
  dd->xa = gsl_vector_alloc(n);
  dd->vdd = Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, dd->dd);
  dd->vxa = Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, dd->xa);
  return dd;
}

static void poly_dd_free(poly_dd *dd)
{
  free((poly_dd *) dd);
}

/* singleton method of the class Poly */
static VALUE rb_gsl_poly_dd_init(VALUE obj, VALUE vxa, VALUE vya)
{
  gsl_vector *xa = NULL, *ya = NULL;
  poly_dd *dd = NULL;

  Data_Get_Struct(vxa, gsl_vector, xa);
  Data_Get_Struct(vya, gsl_vector, ya);
  dd = poly_dd_alloc(xa->size);
  gsl_poly_dd_init(dd->dd->data, xa->data, ya->data, xa->size);
  gsl_vector_memcpy(dd->xa, xa);
  return Data_Wrap_Struct(cgsl_poly_dd, 0, poly_dd_free, dd);
}

static VALUE rb_gsl_poly_dd_eval(VALUE obj, VALUE xx)
{
  /*  poly_dd *dd;
  Data_Get_Struct(obj, poly_dd, dd);
  return rb_float_new(gsl_poly_dd_eval(dd->dd->data, dd->xa->data, dd->dd->size,
  NUM2DBL(x)));*/
  gsl_vector *v = NULL, *vnew = NULL;
  gsl_matrix *m = NULL, *mnew = NULL;
  VALUE x, ary;
  size_t size;
  int i, j;
  poly_dd *dd = NULL;
  Data_Get_Struct(obj, poly_dd, dd);
  switch (TYPE(xx)) {
  case T_FIXNUM:
  case T_BIGNUM:
  case T_FLOAT:
    return rb_float_new(gsl_poly_dd_eval(dd->dd->data, dd->xa->data, dd->dd->size,
					 NUM2DBL(xx)));
    break;
  case T_ARRAY:
    size = RARRAY(xx)->len;
    ary = rb_ary_new2(size);
    for (i = 0; i < size; i++) {
      x = rb_ary_entry(xx, i);
      Need_Float(x);
      rb_ary_store(ary, i, 
		   rb_float_new(gsl_poly_dd_eval(dd->dd->data, dd->xa->data, 
						 dd->dd->size, NUM2DBL(x))));
    }
    return ary;
    break;
  default:
    if (VECTOR_P(xx)) {
      Data_Get_Struct(xx, gsl_vector, v);
      size = v->size;
      vnew = gsl_vector_alloc(v->size);
      for (i = 0; i < size; i++) {
	gsl_vector_set(vnew, i, 
		       gsl_poly_dd_eval(dd->dd->data, dd->xa->data, 
					dd->dd->size, gsl_vector_get(v, i)));
      }
      return Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, vnew);
    } else if (MATRIX_P(xx)) {
      Data_Get_Struct(xx, gsl_matrix, m);
      size = m->size1;
      mnew = gsl_matrix_alloc(m->size1, m->size2);
      for (i = 0; i < m->size1; i++) {
	for (j = 0; j < m->size2; j++) {
	  gsl_matrix_set(mnew, i, j, 
			 gsl_poly_dd_eval(dd->dd->data, dd->xa->data, 
					  dd->dd->size, gsl_matrix_get(m, i, j)));
	}
      }
      return Data_Wrap_Struct(cgsl_matrix, 0, gsl_matrix_free, mnew);
    } else {
      rb_raise(rb_eTypeError, "wrong argument type");
    }
    break;
  }
  return Qnil; /* never reach here */
}

static VALUE rb_gsl_poly_dd_taylor(int argc, VALUE *argv, VALUE obj)
{
  poly_dd *dd = NULL;
  gsl_vector *w = NULL, *c = NULL;
  double xp;
  size_t size;
  int flag = 0;

  Data_Get_Struct(obj, poly_dd, dd);
  size = dd->dd->size;
  xp = NUM2DBL(argv[0]);
  if (argc == 0 && rb_obj_is_kind_of(argv[1], cgsl_vector)) {
    Data_Get_Struct(argv[1], gsl_vector, w);
  } else {
    w = gsl_vector_alloc(size);
    flag = 1;
  }
  c = gsl_vector_alloc(size);
  gsl_poly_dd_taylor(c->data, xp, dd->dd->data, dd->xa->data, size, w->data);

  if (flag == 1) gsl_vector_free(w);
  return Data_Wrap_Struct(cgsl_poly_taylor, 0, gsl_vector_free, c);
}

static VALUE rb_gsl_poly_order(VALUE obj)
{
  gsl_vector *v = NULL;
  Data_Get_Struct(obj, gsl_vector, v);
  return INT2FIX(v->size - 1);
}

void Init_gsl_poly()
{
  cgsl_poly = rb_define_class_under(mgsl, "Poly", cgsl_vector);
  cgsl_poly_workspace = rb_define_class_under(cgsl_poly, "Workspace", rb_cObject);
  cgsl_poly_dd = rb_define_class_under(cgsl_poly, "DividedDifferenceRepresentation",
				       rb_cObject);
  cgsl_poly_taylor = rb_define_class_under(cgsl_poly, "Taylor", cgsl_poly);

  rb_define_singleton_method(cgsl_poly_workspace, "new", rb_gsl_poly_workspace_new, 1);

  rb_define_singleton_method(cgsl_poly, "solve_quadratic", rb_gsl_poly_solve_quadratic, -1);
  rb_define_singleton_method(cgsl_poly, "solve_cubic", rb_gsl_poly_solve_cubic, -1);
  rb_define_singleton_method(cgsl_poly, "complex_solve_quadratic", rb_gsl_poly_complex_solve_quadratic, -1);
  rb_define_singleton_method(cgsl_poly, "complex_solve_cubic", rb_gsl_poly_complex_solve_cubic, -1);
  rb_define_singleton_method(cgsl_poly, "complex_solve", rb_gsl_poly_complex_solve, -1);
  rb_define_singleton_method(cgsl_poly, "solve", rb_gsl_poly_complex_solve, -1);
  rb_define_singleton_method(cgsl_poly, "dd_init", rb_gsl_poly_dd_init, 2);

  rb_define_method(cgsl_poly, "eval", rb_gsl_poly_eval, 1);
  rb_define_method(cgsl_poly, "solve_quadratic", 
		   rb_gsl_poly_solve_quadratic2, 0);
  rb_define_method(cgsl_poly, "complex_solve_quadratic", 
		   rb_gsl_poly_complex_solve_quadratic2, 0);

  rb_define_method(cgsl_poly, "solve_cubic", 
		   rb_gsl_poly_solve_cubic2, 0);
  rb_define_method(cgsl_poly, "complex_solve_cubic", 
		   rb_gsl_poly_complex_solve_cubic2, 0);

  rb_define_method(cgsl_poly, "complex_solve", 
		   rb_gsl_poly_complex_solve2, -1);
  rb_define_alias(cgsl_poly, "solve", "complex_solve");

  rb_define_method(cgsl_poly_dd, "eval", rb_gsl_poly_dd_eval, 1);
  rb_define_method(cgsl_poly_dd, "taylor", rb_gsl_poly_dd_taylor, -1);

  rb_define_method(cgsl_poly, "order", rb_gsl_poly_order, 0);

  Init_gsl_poly2();
}
