/*
  histogram.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_histogram.h"
#include "rb_gsl_array.h"
VALUE cgsl_histogram;

static VALUE rb_gsl_histogram_alloc(int argc, VALUE *argv, VALUE klass)
{
  gsl_histogram *h = NULL;
  double min, max;
  size_t n;
  switch (argc) {
  case 1:
    CHECK_FIXNUM(argv[0]);
    n = FIX2INT(argv[0]);
    h = gsl_histogram_alloc(n);
    return Data_Wrap_Struct(klass, 0, gsl_histogram_free, h);
    break;
  case 3:
    CHECK_FIXNUM(argv[0]);
    Need_Float(argv[1]); Need_Float(argv[2]);
    n = FIX2INT(argv[0]);
    min = NUM2DBL(argv[1]);
    max = NUM2DBL(argv[2]);
    break;
  case 2:
    CHECK_FIXNUM(argv[0]);
    if (TYPE(argv[1]) != T_ARRAY) {
      rb_raise(rb_eTypeError, "wrong argument type %s (Array expected)", 
	       rb_class2name(CLASS_OF(argv[1])));
    }
    n = FIX2INT(argv[0]);
    min = NUM2DBL(rb_ary_entry(argv[1], 0));
    max = NUM2DBL(rb_ary_entry(argv[1], 1));
    break;
  default:
    rb_raise(rb_eArgError, "wrong number of arguments (%d for 1, 2, 3)", argc);
  }
  h = gsl_histogram_alloc(n);
  gsl_histogram_set_ranges_uniform(h, min, max);
  return Data_Wrap_Struct(klass, 0, gsl_histogram_free, h);
}

/* initialization + set uniform ranges (equal spacing from min to max) */
static VALUE rb_gsl_histogram_alloc_uniform(VALUE klass, VALUE nn, VALUE vmin, 
					    VALUE vmax)
{
  gsl_histogram *h = NULL;
  double min, max, tmp;
  CHECK_FIXNUM(nn);
  Need_Float(vmin); Need_Float(vmax);
  min = NUM2DBL(vmin);
  max = NUM2DBL(vmax);
  if (min > max) {
    tmp = min;
    min = max;
    max = tmp;
  }
  h = gsl_histogram_alloc(FIX2INT(nn));
  gsl_histogram_set_ranges_uniform(h, min, max);
  return Data_Wrap_Struct(klass, 0, gsl_histogram_free, h);
}

/* initialization + set ranges with a given spacing from min to max */
static VALUE rb_gsl_histogram_alloc_with_min_max_step(VALUE klass, VALUE vmin, 
					    VALUE vmax, VALUE ss)
{
  gsl_histogram *h = NULL;
  gsl_vector *v = NULL;
  double min, max, tmp, step;
  int i;
  size_t n;
  Need_Float(vmin); Need_Float(vmax); Need_Float(ss);
  min = NUM2DBL(vmin);
  max = NUM2DBL(vmax);
  step = NUM2DBL(ss);
  if (min > max) {
    tmp = min;
    min = max;
    max = tmp;
  }
  n = (int) ((max - min)/step);
  h = gsl_histogram_alloc(n);
  v = gsl_vector_alloc(n + 1);
  for (i = 0; i < n + 1; i++) gsl_vector_set(v, i, min + step*i);
  gsl_histogram_set_ranges(h, v->data, v->size);
  gsl_vector_free(v);
  return Data_Wrap_Struct(klass, 0, gsl_histogram_free, h);
}

static VALUE rb_gsl_histogram_bins(VALUE obj)
{
  gsl_histogram *h = NULL;
  Data_Get_Struct(obj, gsl_histogram, h);
  return INT2FIX(gsl_histogram_bins(h));
}

static VALUE rb_gsl_histogram_set_ranges(VALUE obj, VALUE range)
{
  gsl_histogram *h = NULL;
  gsl_vector *v = NULL;
  Data_Get_Struct(obj, gsl_histogram, h);
  if (TYPE(range) == T_ARRAY) {
    v = make_cvector_from_rarray(range);
    gsl_histogram_set_ranges(h, v->data, v->size);
    gsl_vector_free(v);
  } else {
    CHECK_VECTOR(range);
    Data_Get_Struct(range, gsl_vector, v);
    gsl_histogram_set_ranges(h, v->data, v->size);
  }
  return obj;
}

static VALUE rb_gsl_histogram_range(VALUE obj)
{
  gsl_histogram *h = NULL;
  gsl_vector_view *v = NULL;
  Data_Get_Struct(obj, gsl_histogram, h);
  v = gsl_vector_view_alloc();
  v->vector.data = h->range;
  v->vector.size = h->n;
  v->vector.stride = 1;
  return Data_Wrap_Struct(cgsl_vector_view_ro, 0, gsl_vector_view_free, v);
}

static VALUE rb_gsl_histogram_bin(VALUE obj)
{
  gsl_histogram *h = NULL;
  gsl_vector_view *v = NULL;
  Data_Get_Struct(obj, gsl_histogram, h);
  v = gsl_vector_view_alloc();
  v->vector.data = h->bin;
  v->vector.size = h->n;
  v->vector.stride = 1;
  return Data_Wrap_Struct(cgsl_vector_view_ro, 0, gsl_vector_view_free, v);
}

static VALUE rb_gsl_histogram_set_ranges_uniform(VALUE obj, VALUE xmin, VALUE xmax)
{
  gsl_histogram *h = NULL;
  Data_Get_Struct(obj, gsl_histogram, h);
  gsl_histogram_set_ranges_uniform(h, NUM2DBL(xmin), NUM2DBL(xmax));
  return obj;
}

/* singleton */
static VALUE rb_gsl_histogram_memcpy(VALUE obj, VALUE vhdest, VALUE vhsrc)
{
  gsl_histogram *hdest, *hsrc;
  CHECK_HISTOGRAM(vhdest);
  CHECK_HISTOGRAM(vhsrc);
  Data_Get_Struct(vhdest, gsl_histogram, hdest);
  Data_Get_Struct(vhsrc, gsl_histogram, hsrc);
  gsl_histogram_memcpy(hdest, hsrc);
  return vhdest;
}

static VALUE rb_gsl_histogram_clone(VALUE obj)
{
  gsl_histogram *hsrc, *hnew;
  Data_Get_Struct(obj, gsl_histogram, hsrc);
  hnew = gsl_histogram_clone(hsrc);
  return Data_Wrap_Struct(CLASS_OF(obj), 0, gsl_histogram_free, hnew);
}

static VALUE rb_gsl_histogram_increment(VALUE obj, VALUE x)
{
  gsl_histogram *h = NULL;
  Need_Float(x);
  Data_Get_Struct(obj, gsl_histogram, h);
  gsl_histogram_increment(h, NUM2DBL(x));
  return obj;
}

static VALUE rb_gsl_histogram_accumulate(VALUE obj, VALUE x, VALUE w)
{
  gsl_histogram *h = NULL;
  Need_Float(x); Need_Float(w);
  Data_Get_Struct(obj, gsl_histogram, h);
  gsl_histogram_accumulate(h, NUM2DBL(x), NUM2DBL(w));
  return obj;
}

static VALUE rb_gsl_histogram_get(VALUE obj, VALUE i)
{
  gsl_histogram *h = NULL;
  CHECK_FIXNUM(i);
  Data_Get_Struct(obj, gsl_histogram, h);
  gsl_histogram_get(h, FIX2INT(i));
  return obj;
}

static VALUE rb_gsl_histogram_get_range(VALUE obj, VALUE i)
{
  gsl_histogram *h = NULL;
  double lower, upper;
  CHECK_FIXNUM(i);
  Data_Get_Struct(obj, gsl_histogram, h);
  gsl_histogram_get_range(h, FIX2INT(i), &lower, &upper);
  return rb_ary_new3(2, rb_float_new(lower), rb_float_new(upper));
}

static VALUE rb_gsl_histogram_max(VALUE obj)
{
  gsl_histogram *h = NULL;
  Data_Get_Struct(obj, gsl_histogram, h);
  return rb_float_new(gsl_histogram_max(h));
}

static VALUE rb_gsl_histogram_min(VALUE obj)
{
  gsl_histogram *h = NULL;
  Data_Get_Struct(obj, gsl_histogram, h);
  return rb_float_new(gsl_histogram_min(h));
}

static VALUE rb_gsl_histogram_reset(VALUE obj)
{
  gsl_histogram *h = NULL;
  Data_Get_Struct(obj, gsl_histogram, h);
  gsl_histogram_reset(h);
  return obj;
}

static VALUE rb_gsl_histogram_find(VALUE obj, VALUE x)
{
  gsl_histogram *h = NULL;
  size_t i;
  Need_Float(x);
  Data_Get_Struct(obj, gsl_histogram, h);
  gsl_histogram_find(h, NUM2DBL(x), &i);
  return INT2FIX(i);
}

static VALUE rb_gsl_histogram_max_val(VALUE obj)
{
  gsl_histogram *h = NULL;
  Data_Get_Struct(obj, gsl_histogram, h);
  return rb_float_new(gsl_histogram_max_val(h));
}

static VALUE rb_gsl_histogram_max_bin(VALUE obj)
{
  gsl_histogram *h = NULL;
  Data_Get_Struct(obj, gsl_histogram, h);
  return INT2FIX(gsl_histogram_max_bin(h));
}

static VALUE rb_gsl_histogram_min_val(VALUE obj)
{
  gsl_histogram *h = NULL;
  Data_Get_Struct(obj, gsl_histogram, h);
  return rb_float_new(gsl_histogram_min_val(h));
}

static VALUE rb_gsl_histogram_min_bin(VALUE obj)
{
  gsl_histogram *h = NULL;
  Data_Get_Struct(obj, gsl_histogram, h);
  return INT2FIX(gsl_histogram_min_bin(h));
}

static VALUE rb_gsl_histogram_mean(VALUE obj)
{
  gsl_histogram *h = NULL;
  Data_Get_Struct(obj, gsl_histogram, h);
  return rb_float_new(gsl_histogram_mean(h));
}

static VALUE rb_gsl_histogram_sigma(VALUE obj)
{
  gsl_histogram *h = NULL;
  Data_Get_Struct(obj, gsl_histogram, h);
  return rb_float_new(gsl_histogram_sigma(h));
}

static VALUE rb_gsl_histogram_sum(VALUE obj)
{
  gsl_histogram *h = NULL;
  Data_Get_Struct(obj, gsl_histogram, h);
  return rb_float_new(gsl_histogram_sum(h));
}

static VALUE rb_gsl_histogram_equal_bins_p(int argc, VALUE *argv, VALUE obj)
{
  gsl_histogram *h1, *h2;
  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_HISTOGRAM(argv[0]);
    CHECK_HISTOGRAM(argv[1]);
    Data_Get_Struct(argv[0], gsl_histogram, h1);
    Data_Get_Struct(argv[1], gsl_histogram, h2);
    break;
  default:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of arguments (%d for 2)",
			    argc);
    Data_Get_Struct(obj, gsl_histogram, h1);
    CHECK_HISTOGRAM(argv[0]);
    Data_Get_Struct(argv[0], gsl_histogram, h2);
  }
  return INT2FIX(gsl_histogram_equal_bins_p(h1, h2));
}

static VALUE rb_gsl_histogram_equal_bins_p2(int argc, VALUE *argv, VALUE obj)
{
  gsl_histogram *h1, *h2;
  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_HISTOGRAM(argv[0]);
    CHECK_HISTOGRAM(argv[1]);
    Data_Get_Struct(argv[0], gsl_histogram, h1);
    Data_Get_Struct(argv[1], gsl_histogram, h2);
    break;
  default:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of arguments (%d for 2)",
			    argc);
    Data_Get_Struct(obj, gsl_histogram, h1);
    CHECK_HISTOGRAM(argv[0]);
    Data_Get_Struct(argv[0], gsl_histogram, h2);
  }
  if (gsl_histogram_equal_bins_p(h1, h2)) return Qtrue;
  else return Qfalse;
}

static VALUE rb_gsl_histogram_add(VALUE obj, VALUE hh2)
{
  gsl_histogram *h1, *h2;
  CHECK_HISTOGRAM(hh2);
  Data_Get_Struct(obj, gsl_histogram, h1);
  Data_Get_Struct(hh2, gsl_histogram, h2);
  gsl_histogram_add(h1, h2);
  return obj;
}

static VALUE rb_gsl_histogram_add2(VALUE obj, VALUE hh2)
{
  gsl_histogram *h1, *h2, *hnew;
  CHECK_HISTOGRAM(hh2);
  Data_Get_Struct(obj, gsl_histogram, h1);
  Data_Get_Struct(hh2, gsl_histogram, h2);
  hnew = gsl_histogram_clone(h1);
  gsl_histogram_add(hnew, h2);
  return Data_Wrap_Struct(cgsl_histogram, 0, gsl_histogram_free, hnew);
}

static VALUE rb_gsl_histogram_sub(VALUE obj, VALUE hh2)
{
  gsl_histogram *h1, *h2;
  CHECK_HISTOGRAM(hh2);
  Data_Get_Struct(obj, gsl_histogram, h1);
  Data_Get_Struct(hh2, gsl_histogram, h2);
  gsl_histogram_sub(h1, h2);
  return obj;
}

static VALUE rb_gsl_histogram_sub2(VALUE obj, VALUE hh2)
{
  gsl_histogram *h1, *h2, *hnew;
  CHECK_HISTOGRAM(hh2);
  Data_Get_Struct(obj, gsl_histogram, h1);
  Data_Get_Struct(hh2, gsl_histogram, h2);
  hnew = gsl_histogram_clone(h1);
  gsl_histogram_sub(hnew, h2);
  return Data_Wrap_Struct(cgsl_histogram, 0, gsl_histogram_free, hnew);
}

static VALUE rb_gsl_histogram_mul(VALUE obj, VALUE hh2)
{
  gsl_histogram *h1, *h2;
  CHECK_HISTOGRAM(hh2);
  Data_Get_Struct(obj, gsl_histogram, h1);
  Data_Get_Struct(hh2, gsl_histogram, h2);
  gsl_histogram_mul(h1, h2);
  return obj;
}

static VALUE rb_gsl_histogram_mul2(VALUE obj, VALUE hh2)
{
  gsl_histogram *h1, *h2, *hnew;
  CHECK_HISTOGRAM(hh2);
  Data_Get_Struct(obj, gsl_histogram, h1);
  Data_Get_Struct(hh2, gsl_histogram, h2);
  hnew = gsl_histogram_clone(h1);
  gsl_histogram_mul(hnew, h2);
  return Data_Wrap_Struct(cgsl_histogram, 0, gsl_histogram_free, hnew);
}

static VALUE rb_gsl_histogram_div(VALUE obj, VALUE hh2)
{
  gsl_histogram *h1, *h2;
  CHECK_HISTOGRAM(hh2);
  Data_Get_Struct(obj, gsl_histogram, h1);
  Data_Get_Struct(hh2, gsl_histogram, h2);
  gsl_histogram_div(h1, h2);
  return obj;
}

static VALUE rb_gsl_histogram_div2(VALUE obj, VALUE hh2)
{
  gsl_histogram *h1, *h2, *hnew;
  CHECK_HISTOGRAM(hh2);
  Data_Get_Struct(obj, gsl_histogram, h1);
  Data_Get_Struct(hh2, gsl_histogram, h2);
  hnew = gsl_histogram_clone(h1);
  gsl_histogram_div(hnew, h2);
  return Data_Wrap_Struct(cgsl_histogram, 0, gsl_histogram_free, hnew);
}

static VALUE rb_gsl_histogram_scale(VALUE obj, VALUE scale)
{
  gsl_histogram *h = NULL;
  Need_Float(scale);
  Data_Get_Struct(obj, gsl_histogram, h);
  gsl_histogram_scale(h, NUM2DBL(scale));
  return obj;
}

static VALUE rb_gsl_histogram_scale2(VALUE obj, VALUE scale)
{
  gsl_histogram *h, *hnew;
  Need_Float(scale);
  Data_Get_Struct(obj, gsl_histogram, h);
  hnew = gsl_histogram_clone(h);
  gsl_histogram_scale(hnew, NUM2DBL(scale));
  return Data_Wrap_Struct(cgsl_histogram, 0, gsl_histogram_free, hnew);
}

static VALUE rb_gsl_histogram_shift(VALUE obj, VALUE shift)
{
  gsl_histogram *h = NULL;
  Need_Float(shift);
  Data_Get_Struct(obj, gsl_histogram, h);
  gsl_histogram_shift(h, NUM2DBL(shift));
  return obj;
}

static VALUE rb_gsl_histogram_shift2(VALUE obj, VALUE shift)
{
  gsl_histogram *h, *hnew;
  Need_Float(shift);
  Data_Get_Struct(obj, gsl_histogram, h);
  hnew = gsl_histogram_clone(h);
  gsl_histogram_shift(hnew, NUM2DBL(shift));
  return Data_Wrap_Struct(cgsl_histogram, 0, gsl_histogram_free, hnew);
}

static VALUE rb_gsl_histogram_fwrite(VALUE obj, VALUE io)
{
  gsl_histogram *h = NULL;
  FILE *f;
  int status, flag = 0;
  Data_Get_Struct(obj, gsl_histogram, h);
  f = rb_gsl_open_writefile(io, &flag);
  status = gsl_histogram_fwrite(f, h);
  if (flag == 1) fclose(f);
  return INT2FIX(status);
}

static VALUE rb_gsl_histogram_fread(VALUE obj, VALUE io)
{
  gsl_histogram *h = NULL;
  FILE *f;
  int status, flag = 0;
  Data_Get_Struct(obj, gsl_histogram, h);
  f = rb_gsl_open_readfile(io, &flag);
  status = gsl_histogram_fread(f, h);
  if (flag == 1) fclose(f);
  return INT2FIX(status);
}

/*static VALUE rb_gsl_histogram_fprintf(VALUE obj, VALUE io, 
  VALUE rformat, VALUE binformat)*/
static VALUE rb_gsl_histogram_fprintf(int argc, VALUE *argv, VALUE obj)
{
  gsl_histogram *h = NULL;
  FILE *fp;
  int status, flag = 0;

  if (argc != 1 && argc != 3) {
    rb_raise(rb_eArgError, "wrong number of arguments (%d for 1 or 3)", argc);
  }
  Data_Get_Struct(obj, gsl_histogram, h);
  fp = rb_gsl_open_writefile(argv[0], &flag);
  if (argc == 3) {
    Check_Type(argv[1], T_STRING);
    Check_Type(argv[2], T_STRING);
    status = gsl_histogram_fprintf(fp, h, STR2CSTR(argv[1]), STR2CSTR(argv[2]));
  } else {
    status = gsl_histogram_fprintf(fp, h, "%g", "%g");
  }
  if (flag == 1) fclose(fp);
  return INT2FIX(status);
}

/*static VALUE rb_gsl_histogram_printf(VALUE obj,
  VALUE rformat, VALUE binformat)*/
static VALUE rb_gsl_histogram_printf(int argc, VALUE *argv, VALUE obj)
{
  gsl_histogram *h = NULL;
  int status;

  Data_Get_Struct(obj, gsl_histogram, h);
  if (argc == 2) {
    Check_Type(argv[0], T_STRING);
    Check_Type(argv[1], T_STRING);
    status = gsl_histogram_fprintf(stdout, h, STR2CSTR(argv[0]), STR2CSTR(argv[1]));
  } else {
    status = gsl_histogram_fprintf(stdout, h, "%g", "%g");
  }
  return INT2FIX(status);
}

static VALUE rb_gsl_histogram_fscanf(VALUE obj, VALUE io)
{
  gsl_histogram *h = NULL;
  FILE *fp;
  int status, flag = 0;
  Data_Get_Struct(obj, gsl_histogram, h);
  fp = rb_gsl_open_readfile(io, &flag);
  status = gsl_histogram_fscanf(fp, h);
  if (flag == 1) fclose(fp);
  return INT2FIX(status);
}

static VALUE rb_gsl_histogram_print(VALUE obj)
{
  gsl_histogram *h = NULL;
  int status;
  Data_Get_Struct(obj, gsl_histogram, h);
  status = gsl_histogram_fprintf(stdout, h, "%g", "%g");
  return INT2FIX(status);
}

static VALUE rb_gsl_histogram_pdf_alloc(VALUE klass, VALUE nn)
{
  gsl_histogram_pdf *h = NULL;
  CHECK_FIXNUM(nn);
  h = gsl_histogram_pdf_alloc(FIX2INT(nn));
  return Data_Wrap_Struct(klass, 0, gsl_histogram_pdf_free, h);
}

static VALUE rb_gsl_histogram_pdf_init(VALUE obj, VALUE hh)
{
  gsl_histogram_pdf *p = NULL;
  gsl_histogram *h = NULL;
  CHECK_HISTOGRAM(hh);
  Data_Get_Struct(obj, gsl_histogram_pdf, p);
  Data_Get_Struct(hh, gsl_histogram, h);
  gsl_histogram_pdf_init(p, h);
  return obj;
}

static VALUE rb_gsl_histogram_pdf_sample(VALUE obj, VALUE r)
{
  gsl_histogram_pdf *p = NULL;
  Need_Float(r);
  Data_Get_Struct(obj, gsl_histogram_pdf, p);
  gsl_histogram_pdf_sample(p, NUM2DBL(r));
  return obj;
}

static VALUE rb_gsl_histogram_pdf_range(VALUE obj)
{
  gsl_histogram_pdf *h = NULL;
  gsl_vector_view *v = NULL;
  Data_Get_Struct(obj, gsl_histogram_pdf, h);
  v = gsl_vector_view_alloc(h->n);
  v->vector.data = h->range;
  v->vector.size = h->n;
  v->vector.stride = 1;
  return Data_Wrap_Struct(cgsl_vector_view_ro, 0, gsl_vector_view_free, v);
}

static VALUE rb_gsl_histogram_pdf_sum(VALUE obj)
{
  gsl_histogram_pdf *h = NULL;
  gsl_vector_view *v = NULL;
  Data_Get_Struct(obj, gsl_histogram_pdf, h);
  v = gsl_vector_view_alloc(h->n);
  v->vector.data = h->sum;
  v->vector.size = h->n;
  v->vector.stride = 1;
  return Data_Wrap_Struct(cgsl_vector_view_ro, 0, gsl_vector_view_free, v);
}

void Init_gsl_histogram()
{
  VALUE cgsl_histogram_pdf;

  cgsl_histogram = rb_define_class_under(mgsl, "Histogram", rb_cObject);
  rb_define_singleton_method(cgsl_histogram, "alloc", rb_gsl_histogram_alloc, -1);
  rb_define_singleton_method(cgsl_histogram, "new", rb_gsl_histogram_alloc, -1);
  rb_define_singleton_method(cgsl_histogram, "alloc_uniform", 
			     rb_gsl_histogram_alloc_uniform, -1);
  rb_define_singleton_method(cgsl_histogram, "new_uniform", 
			     rb_gsl_histogram_alloc_uniform, -1);

  rb_define_singleton_method(cgsl_histogram, "alloc_with_min_max_step", 
			     rb_gsl_histogram_alloc_with_min_max_step, 3);
  rb_define_singleton_method(cgsl_histogram, "new_with_min_max_step", 
			     rb_gsl_histogram_alloc_with_min_max_step, 3);

  rb_define_method(cgsl_histogram, "bins", rb_gsl_histogram_bins, 0);
  rb_define_alias(cgsl_histogram, "n", "bins");
  rb_define_alias(cgsl_histogram, "size", "bins");
  rb_define_method(cgsl_histogram, "set_ranges", rb_gsl_histogram_set_ranges, 1);
  rb_define_method(cgsl_histogram, "range", rb_gsl_histogram_range, 0);
  rb_define_method(cgsl_histogram, "bin", rb_gsl_histogram_bin, 0);
  rb_define_method(cgsl_histogram, "set_ranges_uniform", 
		   rb_gsl_histogram_set_ranges_uniform, 2);
  rb_define_singleton_method(cgsl_histogram, "memcpy", rb_gsl_histogram_memcpy, 2);
  rb_define_method(cgsl_histogram, "clone", rb_gsl_histogram_clone, 0);
  rb_define_method(cgsl_histogram, "increment", rb_gsl_histogram_increment, 1);
  rb_define_alias(cgsl_histogram, "fill", "increment");
  rb_define_method(cgsl_histogram, "accumulate", rb_gsl_histogram_accumulate, 2);
  rb_define_method(cgsl_histogram, "get", rb_gsl_histogram_get, 1);
  rb_define_alias(cgsl_histogram, "[]", "get");
  rb_define_method(cgsl_histogram, "get_range", rb_gsl_histogram_get_range, 1);
  rb_define_method(cgsl_histogram, "max", rb_gsl_histogram_max, 0);
  rb_define_method(cgsl_histogram, "min", rb_gsl_histogram_min, 0);
  rb_define_method(cgsl_histogram, "reset", rb_gsl_histogram_reset, 0);
  rb_define_method(cgsl_histogram, "find", rb_gsl_histogram_find, 1);
  rb_define_method(cgsl_histogram, "max_val", rb_gsl_histogram_max_val, 0);
  rb_define_method(cgsl_histogram, "max_bin", rb_gsl_histogram_max_bin, 0);
  rb_define_method(cgsl_histogram, "min_val", rb_gsl_histogram_min_val, 0);
  rb_define_method(cgsl_histogram, "min_bin", rb_gsl_histogram_min_bin, 0);
  rb_define_method(cgsl_histogram, "mean", rb_gsl_histogram_mean, 0);
  rb_define_method(cgsl_histogram, "sigma", rb_gsl_histogram_sigma, 0);
  rb_define_method(cgsl_histogram, "sum", rb_gsl_histogram_sum, 0);
  rb_define_alias(cgsl_histogram, "integrate", "sum");

  rb_define_method(cgsl_histogram, "equal_bins_p", rb_gsl_histogram_equal_bins_p, -1);
  rb_define_singleton_method(cgsl_histogram, "equal_bins_p", rb_gsl_histogram_equal_bins_p, -1);
  rb_define_method(cgsl_histogram, "equal_bins_p?", rb_gsl_histogram_equal_bins_p2, -1);
  rb_define_singleton_method(cgsl_histogram, "equal_bins_p?", rb_gsl_histogram_equal_bins_p2, -1);

  rb_define_method(cgsl_histogram, "add!", rb_gsl_histogram_add, 1);
  rb_define_alias(cgsl_histogram, "+=", "add!");
  rb_define_method(cgsl_histogram, "add", rb_gsl_histogram_add2, 1);
  rb_define_alias(cgsl_histogram, "+", "add");
  rb_define_method(cgsl_histogram, "sub!", rb_gsl_histogram_sub, 1);
  rb_define_alias(cgsl_histogram, "-=", "sub!");
  rb_define_method(cgsl_histogram, "sub", rb_gsl_histogram_sub2, 1);
  rb_define_alias(cgsl_histogram, "-", "sub");
  rb_define_method(cgsl_histogram, "mul!", rb_gsl_histogram_mul, 1);
  rb_define_alias(cgsl_histogram, "*=", "mul!");
  rb_define_method(cgsl_histogram, "mul", rb_gsl_histogram_mul2, 1);
  rb_define_alias(cgsl_histogram, "*", "mul");
  rb_define_method(cgsl_histogram, "div!", rb_gsl_histogram_div, 1);
  rb_define_alias(cgsl_histogram, "/=", "div!");
  rb_define_method(cgsl_histogram, "div", rb_gsl_histogram_div2, 1);
  rb_define_alias(cgsl_histogram, "/", "div");
  rb_define_method(cgsl_histogram, "scale!", rb_gsl_histogram_scale, 1);
  rb_define_method(cgsl_histogram, "scale", rb_gsl_histogram_scale2, 1);
  rb_define_method(cgsl_histogram, "shift!", rb_gsl_histogram_shift, 1);
  rb_define_method(cgsl_histogram, "shift", rb_gsl_histogram_shift2, 1);

  /*
  rb_define_method(cgsl_histogram, "add", rb_gsl_histogram_add, 1);
  rb_define_method(cgsl_histogram, "sub", rb_gsl_histogram_sub, 1);
  rb_define_method(cgsl_histogram, "mul", rb_gsl_histogram_mul, 1);
  rb_define_method(cgsl_histogram, "div", rb_gsl_histogram_div, 1);
  rb_define_method(cgsl_histogram, "scale", rb_gsl_histogram_scale, 1);
  rb_define_method(cgsl_histogram, "shift", rb_gsl_histogram_shift, 1);
  */

  rb_define_method(cgsl_histogram, "fwrite", rb_gsl_histogram_fwrite, 1);
  rb_define_method(cgsl_histogram, "fread", rb_gsl_histogram_fread, 1);
  rb_define_method(cgsl_histogram, "fprintf", rb_gsl_histogram_fprintf, -1);
  rb_define_method(cgsl_histogram, "printf", rb_gsl_histogram_printf, -1);
  rb_define_method(cgsl_histogram, "fscanf", rb_gsl_histogram_fscanf, 1);
  rb_define_method(cgsl_histogram, "print", rb_gsl_histogram_print, 0);

  cgsl_histogram_pdf = rb_define_class_under(cgsl_histogram, "Pdf", rb_cObject);
  rb_define_singleton_method(cgsl_histogram_pdf, "alloc", rb_gsl_histogram_pdf_alloc, 1);
  rb_define_singleton_method(cgsl_histogram_pdf, "new", rb_gsl_histogram_pdf_alloc, 1);
  rb_define_method(cgsl_histogram_pdf, "init", rb_gsl_histogram_pdf_init, 1);
  rb_define_method(cgsl_histogram_pdf, "sample", rb_gsl_histogram_pdf_sample, 1);

  rb_define_method(cgsl_histogram_pdf, "range", rb_gsl_histogram_pdf_range, 0);
  rb_define_method(cgsl_histogram_pdf, "sum", rb_gsl_histogram_pdf_sum, 0);
}
