/*
  complex.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; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#include "rb_gsl_config.h"

#include "rb_gsl_complex.h"
#include "rb_gsl_array.h"

VALUE cgsl_complex;

void rb_gsl_complex_free(gsl_complex *c);
static VALUE rb_gsl_complex_new(int argc, VALUE *argv, VALUE klass)
{
  gsl_complex *c = NULL;
  VALUE obj, re, im;
  obj = Data_Make_Struct(klass, gsl_complex, 0, rb_gsl_complex_free, c); 
  switch (argc) {
  case 1:
    switch (TYPE(argv[0])) {
    case T_ARRAY:
      re = rb_ary_entry(argv[0], 0);
      im = rb_ary_entry(argv[0], 1);
      Need_Float(re); Need_Float(im);
      *c = gsl_complex_rect(NUM2DBL(re), NUM2DBL(im));
      break;
    case T_FLOAT:
    case T_FIXNUM:
    case T_BIGNUM:
      Need_Float(argv[0]);
      *c = gsl_complex_rect(NUM2DBL(argv[0]), 0.0);
      break;
    default:
      rb_raise(rb_eTypeError, "wrong argument type %s", 
	       rb_class2name(CLASS_OF(argv[0])));
    }
    break;
  case 2:
    Need_Float(argv[0]); Need_Float(argv[1]);
    *c = gsl_complex_rect(NUM2DBL(argv[0]),  NUM2DBL(argv[1]));
    break;
  default:
    rb_raise(rb_eArgError, "wrong number of arguments (%d for 1 or 2)", argc);
  }
  return obj;
}

void rb_gsl_complex_free(gsl_complex *c)
{
  free((gsl_complex *) c);
}

gsl_complex* make_complex(double re, double im)
{
  gsl_complex *c = NULL;
  c = ALLOC(gsl_complex);
  *c = gsl_complex_rect(re, im);
  return c;
}

static VALUE rb_gsl_complex_polar(VALUE klass, VALUE r, VALUE theta)
{
  VALUE obj;
  gsl_complex *c = NULL;
  Need_Float(r); Need_Float(theta);
  obj = Data_Make_Struct(klass, gsl_complex, 0, rb_gsl_complex_free, c); 
  *c = gsl_complex_polar(NUM2DBL(r), NUM2DBL(theta));
  return obj;
}

static VALUE rb_gsl_complex_get(VALUE obj, VALUE ii)
{
  gsl_complex *c = NULL;
  int i;
  CHECK_FIXNUM(ii);
  Data_Get_Struct(obj, gsl_complex, c);
  i = FIX2INT(ii);
  switch (i) {
  case 0:
    return  rb_float_new(GSL_REAL(*c));
    break;
  case 1:
    return  rb_float_new(GSL_IMAG(*c));
    break;
  default:
    rb_raise(rb_eArgError, "wrong argument (%d for 0 or 1)", i);
    break;
  }
  return Qnil;
}

static VALUE rb_gsl_complex_real(VALUE obj)
{
  gsl_complex *c = NULL;
  Data_Get_Struct(obj, gsl_complex, c);
  return  rb_float_new(GSL_REAL(*c));
}

static VALUE rb_gsl_complex_imag(VALUE obj)
{
  gsl_complex *c = NULL;
  Data_Get_Struct(obj, gsl_complex, c);
  return  rb_float_new(GSL_IMAG(*c));
}

static VALUE rb_gsl_complex_set(int argc, VALUE *argv, VALUE obj)
{
  gsl_complex *c = NULL;
  VALUE re, im;
  Data_Get_Struct(obj, gsl_complex, c);
  switch (argc) {
  case 1:
    switch (TYPE(argv[0])) {
    case T_ARRAY:
      re = rb_ary_entry(argv[0], 0);
      im = rb_ary_entry(argv[0], 1);
      Need_Float(re);      Need_Float(im);
      *c = gsl_complex_rect(NUM2DBL(re), NUM2DBL(im));
      break;
    case T_FLOAT:
    case T_BIGNUM:
    case T_FIXNUM:
      GSL_SET_REAL(c, NUM2DBL(argv[0]));
      break;
    default:
      rb_raise(rb_eTypeError, "Array or Numeric expected");
    }
    break;
  case 2:
    Need_Float(argv[0]);Need_Float(argv[1]);
    *c = gsl_complex_rect(NUM2DBL(argv[0]), NUM2DBL(argv[1]));
    break;
  default:
    rb_raise(rb_eArgError, "wrong number of arguments (%d for 1 or 2)", argc);
    break;
  }
  return obj;
}

static VALUE rb_gsl_complex_set_real(VALUE obj, VALUE x)
{
  gsl_complex *c = NULL;
  Need_Float(x);
  Data_Get_Struct(obj, gsl_complex, c);
  GSL_SET_REAL(c, NUM2DBL(x));
  return obj;
}

static VALUE rb_gsl_complex_set_imag(VALUE obj, VALUE y)
{
  gsl_complex *c = NULL;
  Need_Float(y);
  Data_Get_Struct(obj, gsl_complex, c);
  GSL_SET_IMAG(c, NUM2DBL(y));
  return obj;
}

static VALUE rb_gsl_complex_print(VALUE obj)
{
  gsl_complex *c = NULL;
  Data_Get_Struct(obj, gsl_complex, c);
  fprintf(stdout, "[%g %g] \n", GSL_REAL(*c), GSL_IMAG(*c));
  return obj;
}

static VALUE rb_gsl_complex_printf(VALUE obj, VALUE s)
{
  gsl_complex *c = NULL;
  char tmp[32], format[64];
  Check_Type(s, T_STRING);
  Data_Get_Struct(obj, gsl_complex, c);
  strcpy(tmp, STR2CSTR(s));
  sprintf(format, "%s %s\n", tmp, tmp);
  fprintf(stdout, format, GSL_REAL(*c), GSL_IMAG(*c));
  return obj;
}

static VALUE rb_gsl_complex_inspect(VALUE obj)
{
  gsl_complex *c = NULL;
  Data_Get_Struct(obj, gsl_complex, c);
  fprintf(stdout, "[%g %g]\n", GSL_REAL(*c), GSL_IMAG(*c));
  return obj;
}

static VALUE rb_gsl_complex_return_double(double (*func)(gsl_complex), VALUE obj);
static VALUE rb_gsl_complex_return_double(double (*func)(gsl_complex), VALUE obj)
{
  gsl_complex *c = NULL;
  Data_Get_Struct(obj, gsl_complex, c);
  return  rb_float_new((*func)(*c));
}

static VALUE rb_gsl_complex_arg(VALUE obj)
{
  return rb_gsl_complex_return_double(gsl_complex_arg, obj);
}

static VALUE rb_gsl_complex_abs(VALUE obj)
{
  return rb_gsl_complex_return_double(gsl_complex_abs, obj);
}

static VALUE rb_gsl_complex_abs2(VALUE obj)
{
  return rb_gsl_complex_return_double(gsl_complex_abs2, obj);
}

static VALUE rb_gsl_complex_logabs(VALUE obj)
{
  return rb_gsl_complex_return_double(gsl_complex_logabs, obj);
}

static VALUE rb_gsl_complex_arithmetics2(gsl_complex (*func)(gsl_complex, double),
					 VALUE obj, VALUE xx);

static VALUE rb_gsl_complex_arithmetics2(gsl_complex (*func)(gsl_complex, double),
					VALUE obj, VALUE xx)
{
  gsl_complex *a = NULL, *c = NULL, tmp;
  VALUE obj2;
  double x;
  Need_Float(xx);
  Data_Get_Struct(obj, gsl_complex, a);
  x = NUM2DBL(xx);
  tmp = (*func)(*a, x);
  obj2 = Data_Make_Struct(cgsl_complex, gsl_complex, 0, rb_gsl_complex_free, c); 
  *c = tmp;
  return obj2;
}

static VALUE rb_gsl_complex_add_real(VALUE obj, VALUE xx)
{
  return rb_gsl_complex_arithmetics2(gsl_complex_add_real, obj, xx);
}

static VALUE rb_gsl_complex_sub_real(VALUE obj, VALUE xx)
{
  return rb_gsl_complex_arithmetics2(gsl_complex_sub_real, obj, xx);
}

static VALUE rb_gsl_complex_mul_real(VALUE obj, VALUE xx)
{
  return rb_gsl_complex_arithmetics2(gsl_complex_mul_real, obj, xx);
}

static VALUE rb_gsl_complex_div_real(VALUE obj, VALUE xx)
{
  return rb_gsl_complex_arithmetics2(gsl_complex_div_real, obj, xx);
}

static VALUE rb_gsl_complex_add_imag(VALUE obj, VALUE xx)
{
  return rb_gsl_complex_arithmetics2(gsl_complex_add_imag, obj, xx);
}

static VALUE rb_gsl_complex_sub_imag(VALUE obj, VALUE xx)
{
  return rb_gsl_complex_arithmetics2(gsl_complex_sub_imag, obj, xx);
}

static VALUE rb_gsl_complex_mul_imag(VALUE obj, VALUE xx)
{
  return rb_gsl_complex_arithmetics2(gsl_complex_mul_imag, obj, xx);
}

static VALUE rb_gsl_complex_div_imag(VALUE obj, VALUE xx)
{
  return rb_gsl_complex_arithmetics2(gsl_complex_div_imag, obj, xx);
}

static VALUE rb_gsl_complex_operate(gsl_complex (*func)(gsl_complex), VALUE obj);
static VALUE rb_gsl_complex_operate2(gsl_complex (*func)(gsl_complex), int argc, VALUE *argv, VALUE obj);

static VALUE rb_gsl_complex_operate(gsl_complex (*func)(gsl_complex), VALUE obj)
{
  gsl_complex *c = NULL, *cnew = NULL, tmp;
  Data_Get_Struct(obj, gsl_complex, c);
  tmp = (*func)(*c);
  cnew = ALLOC(gsl_complex);
  *cnew = tmp;
  return Data_Wrap_Struct(cgsl_complex, 0, rb_gsl_complex_free, cnew); 
}

static VALUE rb_gsl_complex_operate2(gsl_complex (*func)(gsl_complex), int argc, VALUE *argv, VALUE obj)
{
  gsl_complex *c = NULL, *cnew = NULL, tmp;
  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    CHECK_COMPLEX(argv[0]);
    Data_Get_Struct(argv[0], gsl_complex, c);
    break;
  default:
    Data_Get_Struct(obj, gsl_complex, c);
  }
  tmp = (*func)(*c);
  cnew = ALLOC(gsl_complex);
  *cnew = tmp;
  return Data_Wrap_Struct(cgsl_complex, 0, rb_gsl_complex_free, cnew); 
}

static VALUE rb_gsl_complex_conjugate(VALUE obj)
{
  return rb_gsl_complex_operate(gsl_complex_conjugate, obj);
}

static VALUE rb_gsl_complex_inverse(VALUE obj)
{
  return rb_gsl_complex_operate(gsl_complex_inverse, obj);
}

static VALUE rb_gsl_complex_negative(VALUE obj)
{
  return rb_gsl_complex_operate(gsl_complex_negative, obj);
}

static VALUE rb_gsl_complex_sqrt(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_sqrt, argc, argv, obj);
}

static VALUE rb_gsl_complex_sqrt_real(VALUE obj, VALUE x)
{
  gsl_complex *cnew = NULL, tmp;
  Need_Float(x);
  tmp = gsl_complex_sqrt_real(NUM2DBL(x));
  cnew = ALLOC(gsl_complex);
  *cnew = tmp;
  return Data_Wrap_Struct(cgsl_complex, 0, rb_gsl_complex_free, cnew); 
}

static VALUE rb_gsl_complex_pow(int argc, VALUE *argv, VALUE obj)
{
  gsl_complex *c = NULL, *a = NULL, *cnew = NULL, tmp;
  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 2) rb_raise(rb_eArgError, "wrong number of arguments (%d for 2)", argc);
    CHECK_COMPLEX(argv[0]);
    CHECK_COMPLEX(argv[1]);
    Data_Get_Struct(argv[0], gsl_complex, c);
    Data_Get_Struct(argv[1], gsl_complex, a);
    break;
  default:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc);
    CHECK_COMPLEX(obj);
    CHECK_COMPLEX(argv[0]);
    Data_Get_Struct(obj, gsl_complex, c);
    Data_Get_Struct(argv[0], gsl_complex, a);
    break;
  }
  tmp = gsl_complex_pow(*c, *a);
  cnew = ALLOC(gsl_complex);
  *cnew = tmp;
  return Data_Wrap_Struct(cgsl_complex, 0, rb_gsl_complex_free, cnew); 
}

static VALUE rb_gsl_complex_pow_real(int argc, VALUE *argv, VALUE obj)
{
  double a;
  gsl_complex *c = NULL, *cnew = NULL, tmp;
  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 2) rb_raise(rb_eArgError, "wrong number of arguments (%d for 2)", argc);
    CHECK_COMPLEX(argv[0]);
    Need_Float(argv[1]);
    Data_Get_Struct(argv[0], gsl_complex, c);
    a = NUM2DBL(argv[1]);
    break;
  default:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc);
    CHECK_COMPLEX(obj);
    Need_Float(argv[0]);
    Data_Get_Struct(obj, gsl_complex, c);
    a = NUM2DBL(argv[0]);
    break;
  }
  tmp = gsl_complex_pow_real(*c, a);
  cnew = ALLOC(gsl_complex);
  *cnew = tmp;
  return Data_Wrap_Struct(cgsl_complex, 0, rb_gsl_complex_free, cnew); 
}

static VALUE rb_gsl_complex_exp(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_exp, argc, argv, obj);
}

static VALUE rb_gsl_complex_log(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_log, argc, argv, obj);
}

static VALUE rb_gsl_complex_log10(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_log10, argc, argv, obj);
}

static VALUE rb_gsl_complex_log_b(int argc, VALUE *argv, VALUE obj)
{
  gsl_complex *z = NULL, *b = NULL, *cnew = NULL, tmp;
  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 2) rb_raise(rb_eArgError, "wrong number of arguments (%d for 2)", argc);
    CHECK_COMPLEX(argv[0]);
    CHECK_COMPLEX(argv[1]);
    Data_Get_Struct(argv[0], gsl_complex, z);
    Data_Get_Struct(argv[1], gsl_complex, b);
    break;
  default:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc);
    CHECK_COMPLEX(obj);
    CHECK_COMPLEX(argv[0]);
    Data_Get_Struct(obj, gsl_complex, z);
    Data_Get_Struct(argv[0], gsl_complex, b);
    break;
  }
  tmp = gsl_complex_log_b(*z, *b);
  cnew = ALLOC(gsl_complex);
  *cnew = tmp;
  return Data_Wrap_Struct(cgsl_complex, 0, rb_gsl_complex_free, cnew); 
}

static VALUE rb_gsl_complex_sin(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_sin, argc, argv, obj);
}

static VALUE rb_gsl_complex_cos(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_cos, argc, argv, obj);
}

static VALUE rb_gsl_complex_tan(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_tan, argc, argv, obj);
}

static VALUE rb_gsl_complex_sec(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_sec, argc, argv, obj);
}

static VALUE rb_gsl_complex_csc(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_csc, argc, argv, obj);
}

static VALUE rb_gsl_complex_cot(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_cot, argc, argv, obj);
}

/*****/
static VALUE rb_gsl_complex_arcsin(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_arcsin, argc, argv, obj);
}

static VALUE rb_gsl_complex_arcsin_real(VALUE obj, VALUE xx)
{
  gsl_complex tmp, *cnew = NULL;
  double x;
  Need_Float(xx);
  x = NUM2DBL(xx);
  tmp = gsl_complex_arcsin_real(x);
  cnew = ALLOC(gsl_complex);
  *cnew = tmp;
  return Data_Wrap_Struct(cgsl_complex, 0, rb_gsl_complex_free, cnew); 
}

static VALUE rb_gsl_complex_arccos(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_arccos, argc, argv, obj);
}

static VALUE rb_gsl_complex_arccos_real(VALUE obj, VALUE xx)
{
  gsl_complex tmp, *cnew = NULL;
  double x;
  Need_Float(xx);
  x = NUM2DBL(xx);
  tmp = gsl_complex_arccos_real(x);
  cnew = ALLOC(gsl_complex);
  *cnew = tmp;
  return Data_Wrap_Struct(cgsl_complex, 0, rb_gsl_complex_free, cnew); 
}

static VALUE rb_gsl_complex_arctan(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_arctan, argc, argv, obj);
}

static VALUE rb_gsl_complex_arcsec(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_arcsec, argc, argv, obj);
}

static VALUE rb_gsl_complex_arcsec_real(VALUE obj, VALUE xx)
{
  gsl_complex tmp, *cnew = NULL;
  double x;
  Need_Float(xx);
  x = NUM2DBL(xx);
  tmp = gsl_complex_arcsec_real(x);
  cnew = ALLOC(gsl_complex);
  *cnew = tmp;
  return Data_Wrap_Struct(cgsl_complex, 0, rb_gsl_complex_free, cnew); 
}

static VALUE rb_gsl_complex_arccsc(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_arccsc, argc, argv, obj);
}

static VALUE rb_gsl_complex_arccsc_real(VALUE obj, VALUE xx)
{
  gsl_complex tmp, *cnew = NULL;
  double x;
  Need_Float(xx);
  x = NUM2DBL(xx);
  tmp = gsl_complex_arccsc_real(x);
  cnew = ALLOC(gsl_complex);
  *cnew = tmp;
  return Data_Wrap_Struct(cgsl_complex, 0, rb_gsl_complex_free, cnew); 
}

static VALUE rb_gsl_complex_arccot(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_arccot, argc, argv, obj);
}

static VALUE rb_gsl_complex_sinh(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_sinh, argc, argv, obj);
}

static VALUE rb_gsl_complex_cosh(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_cosh, argc, argv, obj);
}

static VALUE rb_gsl_complex_tanh(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_tanh, argc, argv, obj);
}

static VALUE rb_gsl_complex_sech(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_sech, argc, argv, obj);
}

static VALUE rb_gsl_complex_csch(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_csch, argc, argv, obj);
}

static VALUE rb_gsl_complex_coth(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_coth, argc, argv, obj);
}

static VALUE rb_gsl_complex_arcsinh(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_arcsinh, argc, argv, obj);
}

static VALUE rb_gsl_complex_arccosh_real(VALUE obj, VALUE xx)
{
  gsl_complex tmp, *cnew = NULL;
  double x;
  Need_Float(xx);
  x = NUM2DBL(xx);
  tmp = gsl_complex_arccosh_real(x);
  cnew = ALLOC(gsl_complex);
  *cnew = tmp;
  return Data_Wrap_Struct(cgsl_complex, 0, rb_gsl_complex_free, cnew); 
}

static VALUE rb_gsl_complex_arccosh(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_arccosh, argc, argv, obj);
}

static VALUE rb_gsl_complex_arctanh(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_arctanh, argc, argv, obj);
}

static VALUE rb_gsl_complex_arctanh_real(VALUE obj, VALUE xx)
{
  gsl_complex tmp, *cnew = NULL;
  double x;
  Need_Float(xx);
  x = NUM2DBL(xx);
  tmp = gsl_complex_arctanh_real(x);
  cnew = ALLOC(gsl_complex);
  *cnew = tmp;
  return Data_Wrap_Struct(cgsl_complex, 0, rb_gsl_complex_free, cnew); 
}

static VALUE rb_gsl_complex_arcsech(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_arcsech, argc, argv, obj);
}

static VALUE rb_gsl_complex_arccsch(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_arccsch, argc, argv, obj);
}

static VALUE rb_gsl_complex_arccoth(int argc, VALUE *argv, VALUE obj)
{
  return rb_gsl_complex_operate2(gsl_complex_arccoth, argc, argv, obj);
}

int rbgsl_complex_equal(const gsl_complex *z1, const gsl_complex *z2)
{
  if (z1->dat[0] != z2->dat[0]) return 0;
  if (z1->dat[1] != z2->dat[1]) return 0;
  return 1;
}

int rbgsl_complex_zero(const gsl_complex *z1)
{
  if (z1->dat[0] != 0.0) return 0;
  if (z1->dat[1] != 0.0) return 0;
  return 1;
}

static VALUE rb_gsl_complex_equal(VALUE obj, VALUE zz)
{
  gsl_complex *z1 = NULL, *z2 = NULL;
  CHECK_COMPLEX(zz);
  Data_Get_Struct(obj, gsl_complex, z1);
  Data_Get_Struct(zz, gsl_complex, z2);
  if (rbgsl_complex_equal(z1, z2)) return Qtrue;
  else return Qfalse;
}

static VALUE rb_gsl_complex_zero(VALUE obj)
{
  gsl_complex *z = NULL;
  Data_Get_Struct(obj, gsl_complex, z);
  if (rbgsl_complex_zero(z)) return Qtrue;
  else return Qfalse;
}

void Init_gsl_complex(VALUE module)
{
  cgsl_complex = rb_define_class_under(module, "Complex",  rb_cNumeric);
  rb_define_singleton_method(cgsl_complex, "new", rb_gsl_complex_new, -1);
  rb_define_singleton_method(cgsl_complex, "rect", rb_gsl_complex_new, -1);
  rb_define_singleton_method(cgsl_complex, "[]", rb_gsl_complex_new, -1);
  rb_define_singleton_method(cgsl_complex, "polar", rb_gsl_complex_polar, 2);

  rb_define_method(cgsl_complex, "equal?", rb_gsl_complex_equal, 1);
  rb_define_alias(cgsl_complex, "==", "equal?");
  rb_define_method(cgsl_complex, "zero?", rb_gsl_complex_zero, 0);

  rb_define_method(cgsl_complex, "get", rb_gsl_complex_get, 1);
  rb_define_alias(cgsl_complex, "[]", "get");
  rb_define_method(cgsl_complex, "real", rb_gsl_complex_real, 0);
  rb_define_alias(cgsl_complex, "re", "real");
  rb_define_alias(cgsl_complex, "REAL", "real");
  rb_define_method(cgsl_complex, "imag", rb_gsl_complex_imag, 0);
  rb_define_alias(cgsl_complex, "im", "imag");
  rb_define_alias(cgsl_complex, "IMAG", "imag");
  rb_define_method(cgsl_complex, "print", rb_gsl_complex_print, 0);
  rb_define_method(cgsl_complex, "printf", rb_gsl_complex_printf, 1);
  rb_define_method(cgsl_complex, "inspect", rb_gsl_complex_inspect, 0);
  rb_define_alias(cgsl_complex, "show", "print");

  rb_define_method(cgsl_complex, "set", rb_gsl_complex_set, -1);
  rb_define_alias(cgsl_complex, "set_complex", "set");
  rb_define_alias(cgsl_complex, "SET_COMPLEX", "set");
  rb_define_method(cgsl_complex, "set_real", rb_gsl_complex_set_real, 1);
  rb_define_alias(cgsl_complex, "real=", "set_real");
  rb_define_alias(cgsl_complex, "re=", "set_real");
  rb_define_alias(cgsl_complex, "SET_REAL", "set_real");
  rb_define_method(cgsl_complex, "set_imag", rb_gsl_complex_set_imag, 1);
  rb_define_alias(cgsl_complex, "imag=", "set_imag");
  rb_define_alias(cgsl_complex, "im=", "set_imag");
  rb_define_alias(cgsl_complex, "SET_IMAG", "set_imag");

  rb_define_method(cgsl_complex, "arg", rb_gsl_complex_arg, 0);
  rb_define_method(cgsl_complex, "abs", rb_gsl_complex_abs, 0);
  rb_define_method(cgsl_complex, "abs2", rb_gsl_complex_abs2, 0);
  rb_define_method(cgsl_complex, "logabs", rb_gsl_complex_logabs, 0);

  rb_define_method(cgsl_complex, "add_real", rb_gsl_complex_add_real, 1);
  rb_define_method(cgsl_complex, "sub_real", rb_gsl_complex_sub_real, 1);
  rb_define_method(cgsl_complex, "mul_real", rb_gsl_complex_mul_real, 1);
  rb_define_method(cgsl_complex, "div_real", rb_gsl_complex_div_real, 1);
  rb_define_method(cgsl_complex, "add_imag", rb_gsl_complex_add_imag, 1);
  rb_define_method(cgsl_complex, "sub_imag", rb_gsl_complex_sub_imag, 1);
  rb_define_method(cgsl_complex, "mul_imag", rb_gsl_complex_mul_imag, 1);
  rb_define_method(cgsl_complex, "div_imag", rb_gsl_complex_div_imag, 1);

  rb_define_method(cgsl_complex, "conjugate", rb_gsl_complex_conjugate, 0);
  rb_define_method(cgsl_complex, "inverse", rb_gsl_complex_inverse, 0);
  rb_define_method(cgsl_complex, "negative", rb_gsl_complex_negative, 0);

  rb_define_singleton_method(cgsl_complex, "sqrt", rb_gsl_complex_sqrt, -1);
  rb_define_singleton_method(cgsl_complex, "sqrt_real", rb_gsl_complex_sqrt_real, 1);
  rb_define_singleton_method(cgsl_complex, "pow", rb_gsl_complex_pow, -1);
  rb_define_singleton_method(cgsl_complex, "pow_real", rb_gsl_complex_pow_real, -1);

  rb_define_singleton_method(cgsl_complex, "exp", rb_gsl_complex_exp, -1);
  rb_define_singleton_method(cgsl_complex, "log", rb_gsl_complex_log, -1);
  rb_define_singleton_method(cgsl_complex, "log10", rb_gsl_complex_log10, -1);
  rb_define_singleton_method(cgsl_complex, "log_b", rb_gsl_complex_log_b, -1);

  rb_define_singleton_method(cgsl_complex, "sin", rb_gsl_complex_sin, -1);
  rb_define_singleton_method(cgsl_complex, "cos", rb_gsl_complex_cos, -1);
  rb_define_singleton_method(cgsl_complex, "tan", rb_gsl_complex_tan, -1);
  rb_define_singleton_method(cgsl_complex, "sec", rb_gsl_complex_sec, -1);
  rb_define_singleton_method(cgsl_complex, "csc", rb_gsl_complex_csc, -1);
  rb_define_singleton_method(cgsl_complex, "cot", rb_gsl_complex_cot, -1);

  rb_define_singleton_method(cgsl_complex, "arcsin", rb_gsl_complex_arcsin, -1);
  rb_define_singleton_method(cgsl_complex, "arcsin_real", rb_gsl_complex_arcsin_real, 1);
  rb_define_singleton_method(cgsl_complex, "arccos", rb_gsl_complex_arccos, -1);
  rb_define_singleton_method(cgsl_complex, "arccos_real", rb_gsl_complex_arccos_real, 1);
  rb_define_singleton_method(cgsl_complex, "arctan", rb_gsl_complex_arctan, -1);
  rb_define_singleton_method(cgsl_complex, "arcsec", rb_gsl_complex_arcsec, -1);
  rb_define_singleton_method(cgsl_complex, "arcsec_real", rb_gsl_complex_arcsec_real, 1);
  rb_define_singleton_method(cgsl_complex, "arccsc", rb_gsl_complex_arccsc, -1);
  rb_define_singleton_method(cgsl_complex, "arccsc_real", rb_gsl_complex_arccsc_real, 1);
  rb_define_singleton_method(cgsl_complex, "arccot", rb_gsl_complex_arccot, -1);

  rb_define_singleton_method(cgsl_complex, "sinh", rb_gsl_complex_sinh, -1);
  rb_define_singleton_method(cgsl_complex, "cosh", rb_gsl_complex_cosh, -1);
  rb_define_singleton_method(cgsl_complex, "tanh", rb_gsl_complex_tanh, -1);
  rb_define_singleton_method(cgsl_complex, "sech", rb_gsl_complex_sech, -1);
  rb_define_singleton_method(cgsl_complex, "csch", rb_gsl_complex_csch, -1);
  rb_define_singleton_method(cgsl_complex, "coth", rb_gsl_complex_coth, -1);

  rb_define_singleton_method(cgsl_complex, "arcsinh", rb_gsl_complex_arcsinh, -1);
  rb_define_singleton_method(cgsl_complex, "arccosh", rb_gsl_complex_arccosh, -1);
  rb_define_singleton_method(cgsl_complex, "arccosh_real", rb_gsl_complex_arccosh_real, 1);
  rb_define_singleton_method(cgsl_complex, "arctanh", rb_gsl_complex_arctanh, -1);
  rb_define_singleton_method(cgsl_complex, "arctanh_real", rb_gsl_complex_arctanh_real, 1);
  rb_define_singleton_method(cgsl_complex, "arcsech", rb_gsl_complex_arcsech, -1);
  rb_define_singleton_method(cgsl_complex, "arccsch", rb_gsl_complex_arccsch, -1);
  rb_define_singleton_method(cgsl_complex, "arccoth", rb_gsl_complex_arccoth, -1);

  /***/
  rb_define_method(cgsl_complex, "sqrt", rb_gsl_complex_sqrt, -1);
  rb_define_method(cgsl_complex, "pow", rb_gsl_complex_pow, -1);
  rb_define_method(cgsl_complex, "pow_real", rb_gsl_complex_pow_real, -1);
  rb_define_method(cgsl_complex, "exp", rb_gsl_complex_exp, -1);

  rb_define_method(cgsl_complex, "exp", rb_gsl_complex_exp, -1);
  rb_define_method(cgsl_complex, "log", rb_gsl_complex_log, -1);
  rb_define_method(cgsl_complex, "log10", rb_gsl_complex_log10, -1);
  rb_define_method(cgsl_complex, "log_b", rb_gsl_complex_log_b, -1);

  rb_define_method(cgsl_complex, "sin", rb_gsl_complex_sin, -1);
  rb_define_method(cgsl_complex, "cos", rb_gsl_complex_cos, -1);
  rb_define_method(cgsl_complex, "tan", rb_gsl_complex_tan, -1);
  rb_define_method(cgsl_complex, "sec", rb_gsl_complex_sec, -1);
  rb_define_method(cgsl_complex, "csc", rb_gsl_complex_csc, -1);
  rb_define_method(cgsl_complex, "cot", rb_gsl_complex_cot, -1);

  rb_define_method(cgsl_complex, "arcsin", rb_gsl_complex_arcsin, -1);
  rb_define_method(cgsl_complex, "arccos", rb_gsl_complex_arccos, -1);
  rb_define_method(cgsl_complex, "arctan", rb_gsl_complex_arctan, -1);
  rb_define_method(cgsl_complex, "arcsec", rb_gsl_complex_arcsec, -1);
  rb_define_method(cgsl_complex, "arccsc", rb_gsl_complex_arccsc, -1);
  rb_define_method(cgsl_complex, "arccot", rb_gsl_complex_arccot, -1);

  rb_define_method(cgsl_complex, "sinh", rb_gsl_complex_sinh, -1);
  rb_define_method(cgsl_complex, "cosh", rb_gsl_complex_cosh, -1);
  rb_define_method(cgsl_complex, "tanh", rb_gsl_complex_tanh, -1);
  rb_define_method(cgsl_complex, "sech", rb_gsl_complex_sech, -1);
  rb_define_method(cgsl_complex, "csch", rb_gsl_complex_csch, -1);
  rb_define_method(cgsl_complex, "coth", rb_gsl_complex_coth, -1);

  rb_define_method(cgsl_complex, "arcsinh", rb_gsl_complex_arcsinh, -1);
  rb_define_method(cgsl_complex, "arccosh", rb_gsl_complex_arccosh, -1);
  rb_define_method(cgsl_complex, "arctanh", rb_gsl_complex_arctanh, -1);
  rb_define_method(cgsl_complex, "arcsech", rb_gsl_complex_arcsech, -1);
  rb_define_method(cgsl_complex, "arccsch", rb_gsl_complex_arccsch, -1);
  rb_define_method(cgsl_complex, "arccoth", rb_gsl_complex_arccoth, -1);
}
