/*
  histogram2d.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_common.h"
#include "rb_gsl_array.h"

static VALUE cgsl_histogram2d;

#ifndef HISTOGRAM2D_P
#define HISTOGRAM2D_P(x) (rb_obj_is_kind_of(x,cgsl_histogram2d)?1:0)
#endif

#ifndef CHECK_HISTOGRAM2D
#define CHECK_HISTOGRAM2D(x) if(!rb_obj_is_kind_of(x,cgsl_histogram2d))\
    rb_raise(rb_eTypeError, "wrong type (Histogram2d expected)");
#endif

static VALUE rb_gsl_histogram2d_alloc_uniform(int argc, VALUE *argv, VALUE klass);
static VALUE rb_gsl_histogram2d_alloc(int argc, VALUE *argv, VALUE klass)
{
  gsl_histogram2d *h = NULL;
  switch (argc) {
  case 2:
    CHECK_FIXNUM(argv[0]); CHECK_FIXNUM(argv[1]);
    h = gsl_histogram2d_alloc(FIX2INT(argv[0]), FIX2INT(argv[1]));
    return Data_Wrap_Struct(klass, 0, gsl_histogram2d_free, h);
    break;
  case 4:
  case 6:
    return rb_gsl_histogram2d_alloc_uniform(argc, argv, klass);
    break;
  default:
    rb_raise(rb_eArgError, "wrong number of arguments %d", argc);
    break;
  }
  return Qnil;  /* never reach here */
}

static VALUE rb_gsl_histogram2d_alloc_uniform(int argc, VALUE *argv, VALUE klass)
{
  gsl_histogram2d *h = NULL;
  double xmin, xmax, ymin, ymax;
  switch (argc) {
  case 4:
    CHECK_FIXNUM(argv[0]);     CHECK_FIXNUM(argv[2]); 
    Check_Type(argv[1], T_ARRAY); Check_Type(argv[3], T_ARRAY);
    if (RARRAY(argv[1])->len != 2 || RARRAY(argv[3])->len != 2)
      rb_raise(rb_eArgError, "array size must be 2");
    xmin = NUM2DBL(rb_ary_entry(argv[1], 0));
    xmax = NUM2DBL(rb_ary_entry(argv[1], 1));
    ymin = NUM2DBL(rb_ary_entry(argv[3], 0));
    ymax = NUM2DBL(rb_ary_entry(argv[3], 1));
    h = gsl_histogram2d_alloc(FIX2INT(argv[0]), FIX2INT(argv[2]));
    gsl_histogram2d_set_ranges_uniform(h, xmin, xmax, ymin, ymax);
    return Data_Wrap_Struct(klass, 0, gsl_histogram2d_free, h);
    break;
  case 6:
    CHECK_FIXNUM(argv[0]); 
    Need_Float(argv[1]); Need_Float(argv[2]);
    CHECK_FIXNUM(argv[3]);
    Need_Float(argv[4]); Need_Float(argv[5]);
    h = gsl_histogram2d_alloc(FIX2INT(argv[0]), FIX2INT(argv[3]));
    gsl_histogram2d_set_ranges_uniform(h, NUM2DBL(argv[1]), NUM2DBL(argv[2]),
				       NUM2DBL(argv[4]), NUM2DBL(argv[5]));
    return Data_Wrap_Struct(klass, 0, gsl_histogram2d_free, h);
    break;
  default:
    rb_raise(rb_eArgError, "wrong number of arguments %d", argc);
    break;
  }
  return Qnil;  /* never reach here */
}

static VALUE rb_gsl_histogram2d_set_ranges(VALUE obj, VALUE vvx, VALUE vvy)
{
  gsl_histogram2d *h = NULL;
  gsl_vector *vx, *vy;
  CHECK_VECTOR(vvx); CHECK_VECTOR(vvy);
  Data_Get_Struct(obj, gsl_histogram2d, h);
  Data_Get_Struct(vvx, gsl_vector, vx);
  Data_Get_Struct(vvy, gsl_vector, vy);
  gsl_histogram2d_set_ranges(h, vx->data, vx->size, vy->data, vy->size);
  return obj;
}

static VALUE rb_gsl_histogram2d_set_ranges_uniform(VALUE obj, VALUE xmin, VALUE xmax, VALUE ymin, VALUE ymax)
{
  gsl_histogram2d *h = NULL;
  Need_Float(xmin); Need_Float(xmax);
  Need_Float(ymin); Need_Float(ymax);
  Data_Get_Struct(obj, gsl_histogram2d, h);
  gsl_histogram2d_set_ranges_uniform(h, NUM2DBL(xmin), NUM2DBL(xmax),
				     NUM2DBL(ymin), NUM2DBL(ymax));
  return obj;
}

static VALUE rb_gsl_histogram2d_clone(VALUE obj)
{
  gsl_histogram2d *h, *h2 = NULL;
  Data_Get_Struct(obj, gsl_histogram2d, h);
  h2 = gsl_histogram2d_clone(h);
  return Data_Wrap_Struct(CLASS_OF(obj), 0, gsl_histogram2d_free, h2);
}

/* singleton */
static VALUE rb_gsl_histogram2d_memcpy(VALUE obj, VALUE vhdest, VALUE vhsrc)
{
  gsl_histogram2d *hdest, *hsrc;
  CHECK_HISTOGRAM2D(vhdest);
  CHECK_HISTOGRAM2D(vhsrc);
  Data_Get_Struct(vhdest, gsl_histogram2d, hdest);
  Data_Get_Struct(vhsrc, gsl_histogram2d, hsrc);
  gsl_histogram2d_memcpy(hdest, hsrc);
  return vhdest;
}

static VALUE rb_gsl_histogram2d_increment(VALUE obj, VALUE x, VALUE y)
{
  gsl_histogram2d *h = NULL;
  Need_Float(x); Need_Float(y);
  Data_Get_Struct(obj, gsl_histogram2d, h);
  gsl_histogram2d_increment(h, NUM2DBL(x), NUM2DBL(y));
  return obj;
}

static VALUE rb_gsl_histogram2d_accumulate(VALUE obj, VALUE x, VALUE y, VALUE w)
{
  gsl_histogram2d *h = NULL;
  Need_Float(x); Need_Float(y); Need_Float(w);
  Data_Get_Struct(obj, gsl_histogram2d, h);
  gsl_histogram2d_accumulate(h, NUM2DBL(x), NUM2DBL(y), NUM2DBL(w));
  return obj;
}

static VALUE rb_gsl_histogram2d_get(VALUE obj, VALUE i, VALUE j)
{
  gsl_histogram2d *h = NULL;
  CHECK_FIXNUM(i); CHECK_FIXNUM(j);
  Data_Get_Struct(obj, gsl_histogram2d, h);
  return rb_float_new(gsl_histogram2d_get(h, FIX2INT(i), FIX2INT(j)));
}

static VALUE rb_gsl_histogram2d_get_xrange(VALUE obj, VALUE i)
{
  gsl_histogram2d *h = NULL;
  double x1, x2;
  CHECK_FIXNUM(i);
  Data_Get_Struct(obj, gsl_histogram2d, h);
  gsl_histogram2d_get_xrange(h, FIX2INT(i), &x1, &x2);
  return rb_ary_new3(2, rb_float_new(x1), rb_float_new(x2));
}

static VALUE rb_gsl_histogram2d_get_yrange(VALUE obj, VALUE j)
{
  gsl_histogram2d *h = NULL;
  double y1, y2;
  CHECK_FIXNUM(j);
  Data_Get_Struct(obj, gsl_histogram2d, h);
  gsl_histogram2d_get_yrange(h, FIX2INT(j), &y1, &y2);
  return rb_ary_new3(2, rb_float_new(y1), rb_float_new(y2));
}

static VALUE rb_gsl_histogram2d_xmax(VALUE obj)
{
  gsl_histogram2d *h = NULL;
  Data_Get_Struct(obj, gsl_histogram2d, h);
  return rb_float_new(gsl_histogram2d_xmax(h));
}

static VALUE rb_gsl_histogram2d_xmin(VALUE obj)
{
  gsl_histogram2d *h = NULL;
  Data_Get_Struct(obj, gsl_histogram2d, h);
  return rb_float_new(gsl_histogram2d_xmin(h));
}

static VALUE rb_gsl_histogram2d_nx(VALUE obj)
{
  gsl_histogram2d *h = NULL;
  Data_Get_Struct(obj, gsl_histogram2d, h);
  return INT2FIX(gsl_histogram2d_nx(h));
}

static VALUE rb_gsl_histogram2d_ymax(VALUE obj)
{
  gsl_histogram2d *h = NULL;
  Data_Get_Struct(obj, gsl_histogram2d, h);
  return rb_float_new(gsl_histogram2d_ymax(h));
}

static VALUE rb_gsl_histogram2d_ymin(VALUE obj)
{
  gsl_histogram2d *h = NULL;
  Data_Get_Struct(obj, gsl_histogram2d, h);
  return rb_float_new(gsl_histogram2d_ymin(h));
}

static VALUE rb_gsl_histogram2d_ny(VALUE obj)
{
  gsl_histogram2d *h = NULL;
  Data_Get_Struct(obj, gsl_histogram2d, h);
  return INT2FIX(gsl_histogram2d_ny(h));
}

static VALUE rb_gsl_histogram2d_find(VALUE obj, VALUE x, VALUE y)
{
  gsl_histogram2d *h = NULL;
  size_t i, j;
  Need_Float(x);Need_Float(y);
  Data_Get_Struct(obj, gsl_histogram2d, h);
  gsl_histogram2d_find(h, NUM2DBL(x), NUM2DBL(y), &i, &j);
  return rb_ary_new3(2, INT2FIX(i), INT2FIX(j));
}

static VALUE rb_gsl_histogram2d_max_val(VALUE obj)
{
  gsl_histogram2d *h = NULL;
  Data_Get_Struct(obj, gsl_histogram2d, h);
  return rb_float_new(gsl_histogram2d_max_val(h));
}

static VALUE rb_gsl_histogram2d_max_bin(VALUE obj)
{
  gsl_histogram2d *h = NULL;
  size_t i, j;
  Data_Get_Struct(obj, gsl_histogram2d, h);
  gsl_histogram2d_max_bin(h, &i, &j);
  return rb_ary_new3(2, INT2FIX(i), INT2FIX(j));
}

static VALUE rb_gsl_histogram2d_min_val(VALUE obj)
{
  gsl_histogram2d *h = NULL;
  Data_Get_Struct(obj, gsl_histogram2d, h);
  return rb_float_new(gsl_histogram2d_min_val(h));
}

static VALUE rb_gsl_histogram2d_min_bin(VALUE obj)
{
  gsl_histogram2d *h = NULL;
  size_t i, j;
  Data_Get_Struct(obj, gsl_histogram2d, h);
  gsl_histogram2d_min_bin(h, &i, &j);
  return rb_ary_new3(2, INT2FIX(i), INT2FIX(j));
}

static VALUE rb_gsl_histogram2d_xmean(VALUE obj)
{
  gsl_histogram2d *h = NULL;
  Data_Get_Struct(obj, gsl_histogram2d, h);
  return rb_float_new(gsl_histogram2d_xmean(h));
}

static VALUE rb_gsl_histogram2d_ymean(VALUE obj)
{
  gsl_histogram2d *h = NULL;
  Data_Get_Struct(obj, gsl_histogram2d, h);
  return rb_float_new(gsl_histogram2d_ymean(h));
}

static VALUE rb_gsl_histogram2d_xsigma(VALUE obj)
{
  gsl_histogram2d *h = NULL;
  Data_Get_Struct(obj, gsl_histogram2d, h);
  return rb_float_new(gsl_histogram2d_xsigma(h));
}

static VALUE rb_gsl_histogram2d_ysigma(VALUE obj)
{
  gsl_histogram2d *h = NULL;
  Data_Get_Struct(obj, gsl_histogram2d, h);
  return rb_float_new(gsl_histogram2d_ysigma(h));
}

static VALUE rb_gsl_histogram2d_cov(VALUE obj)
{
  gsl_histogram2d *h = NULL;
  Data_Get_Struct(obj, gsl_histogram2d, h);
  return rb_float_new(gsl_histogram2d_cov(h));
}

static VALUE rb_gsl_histogram2d_sum(VALUE obj)
{
  gsl_histogram2d *h = NULL;
  Data_Get_Struct(obj, gsl_histogram2d, h);
  return rb_float_new(gsl_histogram2d_sum(h));
}

/* singleton */
static VALUE rb_gsl_histogram2d_equal_bins_p(VALUE obj, VALUE hh1, VALUE hh2)
{
  gsl_histogram2d *h1 = NULL, *h2 = NULL;
  CHECK_HISTOGRAM2D(hh1); CHECK_HISTOGRAM2D(hh2);
  Data_Get_Struct(hh1, gsl_histogram2d, h1);
  Data_Get_Struct(hh2, gsl_histogram2d, h2);
  if (gsl_histogram2d_equal_bins_p(h1, h2)) return Qtrue;
  else return Qfalse;
}

static VALUE rb_gsl_histogram2d_add(VALUE obj, VALUE hh2)
{
  gsl_histogram2d *h1 = NULL, *h2 = NULL;
  CHECK_HISTOGRAM2D(hh2);
  Data_Get_Struct(obj, gsl_histogram2d, h1);
  Data_Get_Struct(hh2, gsl_histogram2d, h2);
  gsl_histogram2d_add(h1, h2);
  return obj;
}
static VALUE rb_gsl_histogram2d_sub(VALUE obj, VALUE hh2)
{
  gsl_histogram2d *h1 = NULL, *h2 = NULL;
  CHECK_HISTOGRAM2D(hh2);
  Data_Get_Struct(obj, gsl_histogram2d, h1);
  Data_Get_Struct(hh2, gsl_histogram2d, h2);
  gsl_histogram2d_sub(h1, h2);
  return obj;
}
static VALUE rb_gsl_histogram2d_mul(VALUE obj, VALUE hh2)
{
  gsl_histogram2d *h1 = NULL, *h2 = NULL;
  CHECK_HISTOGRAM2D(hh2);
  Data_Get_Struct(obj, gsl_histogram2d, h1);
  Data_Get_Struct(hh2, gsl_histogram2d, h2);
  gsl_histogram2d_mul(h1, h2);
  return obj;
}
static VALUE rb_gsl_histogram2d_div(VALUE obj, VALUE hh2)
{
  gsl_histogram2d *h1 = NULL, *h2 = NULL;
  CHECK_HISTOGRAM2D(hh2);
  Data_Get_Struct(obj, gsl_histogram2d, h1);
  Data_Get_Struct(hh2, gsl_histogram2d, h2);
  gsl_histogram2d_div(h1, h2);
  return obj;
}

static VALUE rb_gsl_histogram2d_scale(VALUE obj, VALUE s)
{
  gsl_histogram2d *h = NULL;
  Need_Float(s);
  Data_Get_Struct(obj, gsl_histogram2d, h);
  gsl_histogram2d_scale(h, NUM2DBL(s));
  return obj;
}

static VALUE rb_gsl_histogram2d_shift(VALUE obj, VALUE s)
{
  gsl_histogram2d *h = NULL;
  Need_Float(s);
  Data_Get_Struct(obj, gsl_histogram2d, h);
  gsl_histogram2d_shift(h, NUM2DBL(s));
  return obj;
}

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

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

static VALUE rb_gsl_histogram2d_fprintf(int argc, VALUE *argv, VALUE obj)
{
  gsl_histogram2d *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_histogram2d, 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_histogram2d_fprintf(fp, h, STR2CSTR(argv[1]), STR2CSTR(argv[2]));
  } else {
    status = gsl_histogram2d_fprintf(fp, h, "%g", "%g");
  }
  if (flag == 1) fclose(fp);
  return INT2FIX(status);
}

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

static VALUE rb_gsl_histogram2d_reset(VALUE obj)
{
  gsl_histogram2d *h = NULL;
  Data_Get_Struct(obj, gsl_histogram2d, h);
  gsl_histogram2d_reset(h);
  return obj;
}

static VALUE rb_gsl_histogram2d_pdf_alloc(VALUE klass, VALUE nx, VALUE ny)
{
  gsl_histogram2d_pdf *h = NULL;
  CHECK_FIXNUM(nx); CHECK_FIXNUM(ny);
  h = gsl_histogram2d_pdf_alloc(FIX2INT(nx), FIX2INT(ny));
  return Data_Wrap_Struct(klass, 0, gsl_histogram2d_pdf_free, h);
}

static VALUE rb_gsl_histogram2d_pdf_init(VALUE obj, VALUE hh)
{
  gsl_histogram2d_pdf *pdf = NULL;
  gsl_histogram2d *h = NULL;
  CHECK_HISTOGRAM2D(hh);
  Data_Get_Struct(obj, gsl_histogram2d_pdf, pdf);
  Data_Get_Struct(hh, gsl_histogram2d, h);
  gsl_histogram2d_pdf_init(pdf, h);
  return obj;
}

static VALUE rb_gsl_histogram2d_pdf_sample(VALUE obj, VALUE r1, VALUE r2)
{
  gsl_histogram2d_pdf *pdf = NULL;
  double x, y;
  Need_Float(r1); Need_Float(r2);
  Data_Get_Struct(obj, gsl_histogram2d_pdf, pdf);
  gsl_histogram2d_pdf_sample(pdf, NUM2DBL(r1), NUM2DBL(r2), &x, &y);
  return rb_ary_new3(2, rb_float_new(x), rb_float_new(y));
}

static VALUE rb_gsl_histogram2d_xrange(VALUE obj)
{
  gsl_histogram2d *h = NULL;
  gsl_vector_view *v = NULL;
  Data_Get_Struct(obj, gsl_histogram2d, h);
  v = gsl_vector_view_alloc(h->nx);
  v->vector.data = h->xrange;
  v->vector.size = h->nx;
  v->vector.stride = 1;
  return Data_Wrap_Struct(cgsl_vector_view_ro, 0, gsl_vector_view_free, v);
}

static VALUE rb_gsl_histogram2d_yrange(VALUE obj)
{
  gsl_histogram2d *h = NULL;
  gsl_vector_view *v = NULL;
  Data_Get_Struct(obj, gsl_histogram2d, h);
  v = gsl_vector_view_alloc(h->ny);
  v->vector.data = h->yrange;
  v->vector.size = h->ny;
  v->vector.stride = 1;
  return Data_Wrap_Struct(cgsl_vector_view_ro, 0, gsl_vector_view_free, v);
}

static VALUE rb_gsl_histogram2d_bin(VALUE obj)
{
  gsl_histogram2d *h = NULL;
  gsl_matrix_view *v = NULL;
  Data_Get_Struct(obj, gsl_histogram2d, h);
  v = gsl_matrix_view_alloc(h->nx, h->ny);
  v->matrix.data = h->bin;
  return Data_Wrap_Struct(cgsl_matrix_view_ro, 0, gsl_matrix_view_free, v);
}

void Init_gsl_histogram2d()
{
  VALUE cgsl_histogram2d_pdf;

  cgsl_histogram2d = rb_define_class_under(mgsl, "Histogram2d", rb_cObject);

  rb_define_singleton_method(cgsl_histogram2d, "new", rb_gsl_histogram2d_alloc, -1);
  rb_define_singleton_method(cgsl_histogram2d, "alloc", rb_gsl_histogram2d_alloc, -1);
  rb_define_singleton_method(cgsl_histogram2d, "new_uniform", rb_gsl_histogram2d_alloc_uniform, -1);
  rb_define_singleton_method(cgsl_histogram2d, "alloc_uniform", rb_gsl_histogram2d_alloc_uniform, -1);
  rb_define_singleton_method(cgsl_histogram2d, "equal_bins_p", rb_gsl_histogram2d_equal_bins_p, 2);

  rb_define_method(cgsl_histogram2d, "set_ranges",  rb_gsl_histogram2d_set_ranges, 2);
  rb_define_method(cgsl_histogram2d, "set_ranges_uniform",  rb_gsl_histogram2d_set_ranges_uniform, 4);
  rb_define_singleton_method(cgsl_histogram2d, "memcpy",  rb_gsl_histogram2d_memcpy, 2);
  rb_define_method(cgsl_histogram2d, "clone",  rb_gsl_histogram2d_clone, 0);
  rb_define_method(cgsl_histogram2d, "increment",  rb_gsl_histogram2d_increment, 2);
  rb_define_alias(cgsl_histogram2d, "fill", "increment");
  rb_define_method(cgsl_histogram2d, "accumulate",  rb_gsl_histogram2d_accumulate, 3);
  rb_define_method(cgsl_histogram2d, "get",  rb_gsl_histogram2d_get, 2);
  rb_define_method(cgsl_histogram2d, "get_xrange",  rb_gsl_histogram2d_get_xrange, 1);
  rb_define_method(cgsl_histogram2d, "get_yrange",  rb_gsl_histogram2d_get_yrange, 1);
  rb_define_method(cgsl_histogram2d, "xmax",  rb_gsl_histogram2d_xmax, 0);
  rb_define_method(cgsl_histogram2d, "xmin",  rb_gsl_histogram2d_xmin, 0);
  rb_define_method(cgsl_histogram2d, "ymax",  rb_gsl_histogram2d_ymax, 0);
  rb_define_method(cgsl_histogram2d, "ymin",  rb_gsl_histogram2d_ymin, 0);
  rb_define_method(cgsl_histogram2d, "nx",  rb_gsl_histogram2d_nx, 0);
  rb_define_method(cgsl_histogram2d, "ny",  rb_gsl_histogram2d_ny, 0);

  rb_define_method(cgsl_histogram2d, "find",  rb_gsl_histogram2d_find, 2);

  rb_define_method(cgsl_histogram2d, "max_val",  rb_gsl_histogram2d_max_val, 0);
  rb_define_method(cgsl_histogram2d, "max_bin",  rb_gsl_histogram2d_max_bin, 0);
  rb_define_method(cgsl_histogram2d, "min_val",  rb_gsl_histogram2d_min_val, 0);
  rb_define_method(cgsl_histogram2d, "min_bin",  rb_gsl_histogram2d_min_bin, 0);
  rb_define_method(cgsl_histogram2d, "xmean",  rb_gsl_histogram2d_xmean, 0);
  rb_define_method(cgsl_histogram2d, "ymean",  rb_gsl_histogram2d_ymean, 0);
  rb_define_method(cgsl_histogram2d, "xsigma",  rb_gsl_histogram2d_xsigma, 0);
  rb_define_method(cgsl_histogram2d, "ysigma",  rb_gsl_histogram2d_ysigma, 0);
  rb_define_method(cgsl_histogram2d, "cov",  rb_gsl_histogram2d_cov, 0);
  rb_define_method(cgsl_histogram2d, "sum",  rb_gsl_histogram2d_sum, 0);

  rb_define_method(cgsl_histogram2d, "add",  rb_gsl_histogram2d_add, 1);
  rb_define_alias(cgsl_histogram2d, "+=", "add");
  rb_define_method(cgsl_histogram2d, "sub",  rb_gsl_histogram2d_sub, 1);
  rb_define_alias(cgsl_histogram2d, "-=", "sub");
  rb_define_method(cgsl_histogram2d, "mul",  rb_gsl_histogram2d_mul, 1);
  rb_define_alias(cgsl_histogram2d, "*=", "mul");
  rb_define_method(cgsl_histogram2d, "div",  rb_gsl_histogram2d_div, 1);
  rb_define_alias(cgsl_histogram2d, "/=", "div");
  rb_define_method(cgsl_histogram2d, "scale",  rb_gsl_histogram2d_scale, 1);
  rb_define_method(cgsl_histogram2d, "shift",  rb_gsl_histogram2d_shift, 1);

  rb_define_method(cgsl_histogram2d, "fwrite",  rb_gsl_histogram2d_fwrite, 1);
  rb_define_method(cgsl_histogram2d, "fread",  rb_gsl_histogram2d_fread, 1);
  rb_define_method(cgsl_histogram2d, "fprintf",  rb_gsl_histogram2d_fprintf, -1);
  rb_define_method(cgsl_histogram2d, "fscanf",  rb_gsl_histogram2d_fscanf, 3);

  cgsl_histogram2d_pdf = rb_define_class_under(mgsl, "Histogram2d_pdf", rb_cObject);
  rb_define_singleton_method(cgsl_histogram2d_pdf, "new", rb_gsl_histogram2d_pdf_alloc, 2);
  rb_define_singleton_method(cgsl_histogram2d_pdf, "alloc", rb_gsl_histogram2d_pdf_alloc, 2);

  rb_define_method(cgsl_histogram2d_pdf, "init",  rb_gsl_histogram2d_pdf_init, 1);
  rb_define_method(cgsl_histogram2d_pdf, "sample",  rb_gsl_histogram2d_pdf_sample, 2);

  rb_define_method(cgsl_histogram2d, "xrange",  rb_gsl_histogram2d_xrange, 0);
  rb_define_method(cgsl_histogram2d, "yrange",  rb_gsl_histogram2d_yrange, 0);
  rb_define_method(cgsl_histogram2d, "bin",  rb_gsl_histogram2d_bin, 0);

  rb_define_method(cgsl_histogram2d, "reset",  rb_gsl_histogram2d_reset, 0);
}

#ifdef HISTOGRAM2D_P
#undef HISTOGRAM2D_P
#endif
#ifdef CHECK_HISTOGRAM2D
#undef CHECK_HISTOGRAM2D
#endif

