/*
  fit.c
  Ruby/GSL: Ruby extension library for GSL (GNU Scientific Library)
    (C) Copyright 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 "include/rb_gsl_fit.h"

/* linear fit without weights: y = c0 + c1 x */
/* This returns 7 elements array */
static VALUE rb_gsl_fit_linear(int argc, VALUE *argv, VALUE obj)
{
  double *ptrx, *ptry;
  double c0, c1, cov00, cov01, cov11, sumsq;
  int status;
  size_t n, stridex, stridey;
  switch (argc) {
  case 2:
    ptrx = get_vector_ptr(argv[0], &stridex, &n);
    ptry = get_vector_ptr(argv[1], &stridey, &n);
    break;
  case 3:
    CHECK_FIXNUM(argv[2]);
    ptrx = get_vector_ptr(argv[0], &stridex, &n);
    ptry = get_vector_ptr(argv[1], &stridey, &n);
    n = FIX2INT(argv[2]);
    break;
  default:
    rb_raise(rb_eArgError, "wrong number of arguments (%d for 2 or 3)", argc);
    break;
  }
  status = gsl_fit_linear(ptrx, stridex, ptry, stridey, n, &c0, &c1, &cov00,
                          &cov01, &cov11, &sumsq);
  return rb_ary_new3(7, rb_float_new(c0), rb_float_new(c1), rb_float_new(cov00),
                     rb_float_new(cov01), rb_float_new(cov11), rb_float_new(sumsq),
                     INT2FIX(status));
}

/* linear fit with weights: y = c0 + c1 x */
static VALUE rb_gsl_fit_wlinear(int argc, VALUE *argv, VALUE obj)
{
  double *ptrx, *ptry, *ptrw;
  double c0, c1, cov00, cov01, cov11, sumsq;
  int status;
  size_t n, stridex, stridey, stridew;
  switch (argc) {
  case 3:
    ptrx = get_vector_ptr(argv[0], &stridex, &n);
    ptrw = get_vector_ptr(argv[1], &stridew, &n);
    ptry = get_vector_ptr(argv[2], &stridey, &n);
    break;
  case 4:
    CHECK_FIXNUM(argv[3]);
    ptrx = get_vector_ptr(argv[0], &stridex, &n);
    ptrw = get_vector_ptr(argv[1], &stridew, &n);
    ptry = get_vector_ptr(argv[2], &stridey, &n);
    n = FIX2INT(argv[3]);
    break;
  default:
    rb_raise(rb_eArgError, "wrong number of arguments (%d for 2 or 3)", argc);
    break;
  }
  status = gsl_fit_wlinear(ptrx, stridex, ptrw, stridew, ptry, stridey,
                           n,
                           &c0, &c1, &cov00, &cov01, &cov11, &sumsq);
  return rb_ary_new3(7, rb_float_new(c0), rb_float_new(c1), rb_float_new(cov00),
                     rb_float_new(cov01), rb_float_new(cov11), rb_float_new(sumsq),
                     INT2FIX(status));
}

static VALUE rb_gsl_fit_linear_est(int argc, VALUE *argv, VALUE obj)
{
  double y, yerr, x, c0, c1, c00, c01, c11;
  int status;
  size_t i;
  switch (argc) {
  case 2:
    x = NUM2DBL(argv[0]);
    if (TYPE(argv[1]) == T_ARRAY) {
      c0 = NUM2DBL(rb_ary_entry(argv[1], 0));
      c1 = NUM2DBL(rb_ary_entry(argv[1], 1));
      c00 = NUM2DBL(rb_ary_entry(argv[1], 2));
      c01 = NUM2DBL(rb_ary_entry(argv[1], 3));
      c11 = NUM2DBL(rb_ary_entry(argv[1], 4));
    } else {
      rb_raise(rb_eTypeError, "argv[1] Array expected");
    }
    break;
  case 6:
    for (i = 0; i < 6; i++) Need_Float(argv[i]);
    x = NUM2DBL(argv[0]);
    c0 = NUM2DBL(argv[1]);
    c1 = NUM2DBL(argv[2]);
    c00 = NUM2DBL(argv[3]);
    c01 = NUM2DBL(argv[4]);
    c11 = NUM2DBL(argv[5]);
    break;
  default:
    rb_raise(rb_eArgError, "wrong number of arguments (%d for 2 or 6)", argc);
  }
  status = gsl_fit_linear_est(x, c0, c1, c00, c01, c11, &y, &yerr);
  return rb_ary_new3(3, rb_float_new(y), rb_float_new(yerr), INT2FIX(status));
}

static VALUE rb_gsl_fit_mul(int argc, VALUE *argv, VALUE obj)
{
  double *ptrx, *ptry;
  double c1, cov11, sumsq;
  int status;
  size_t n, stridex, stridey;
  switch (argc) {
  case 2:
    ptrx = get_vector_ptr(argv[0], &stridex, &n);
    ptry = get_vector_ptr(argv[1], &stridey, &n);
    break;
  case 3:
    CHECK_FIXNUM(argv[2]);
    ptrx = get_vector_ptr(argv[0], &stridex, &n);
    ptry = get_vector_ptr(argv[1], &stridey, &n);
    n = FIX2INT(argv[2]);
    break;
  default:
    rb_raise(rb_eArgError, "wrong number of arguments (%d for 2 or 3)", argc);
    break;
  }
  status = gsl_fit_mul(ptrx, stridex, ptry, stridey, n, &c1, &cov11, &sumsq);
  return rb_ary_new3(4, rb_float_new(c1),
                     rb_float_new(cov11), rb_float_new(sumsq), INT2FIX(status));
}

static VALUE rb_gsl_fit_wmul(int argc, VALUE *argv, VALUE obj)
{
  double *ptrx, *ptry, *ptrw;
  double c1, cov11, sumsq;
  int status;
  size_t n, stridex, stridey, stridew;
  switch (argc) {
  case 3:
    ptrx = get_vector_ptr(argv[0], &stridex, &n);
    ptrw = get_vector_ptr(argv[1], &stridew, &n);
    ptry = get_vector_ptr(argv[2], &stridey, &n);
    break;
  case 4:
    CHECK_FIXNUM(argv[3]);
    ptrx = get_vector_ptr(argv[0], &stridex, &n);
    ptrw = get_vector_ptr(argv[1], &stridew, &n);
    ptry = get_vector_ptr(argv[2], &stridey, &n);
    n = FIX2INT(argv[3]);
    break;
  default:
    rb_raise(rb_eArgError, "wrong number of arguments (%d for 2 or 3)", argc);
    break;
  }
  status = gsl_fit_wmul(ptrx, stridex, ptrw, stridew, ptry, stridey,
                        n, &c1, &cov11, &sumsq);
  return rb_ary_new3(4, rb_float_new(c1),
                     rb_float_new(cov11), rb_float_new(sumsq), INT2FIX(status));
}

static VALUE rb_gsl_fit_mul_est(int argc, VALUE *argv, VALUE obj)
{
  double y, yerr, x, c1, c11;
  int status;
  switch (argc) {
  case 2:
    Need_Float(argv[0]);
    if (TYPE(argv[1]) == T_ARRAY) {
      c1 = NUM2DBL(rb_ary_entry(argv[1], 0));
      c11 = NUM2DBL(rb_ary_entry(argv[1], 1));
    } else {
      rb_raise(rb_eTypeError, "argv[1]: Array expected");
    }
    x = NUM2DBL(argv[0]);
    break;
  case 3:
    Need_Float(argv[0]); Need_Float(argv[1]); Need_Float(argv[2]);
    x = NUM2DBL(argv[0]);
    c1 = NUM2DBL(argv[1]);
    c11 = NUM2DBL(argv[2]);
    break;
  default:
    rb_raise(rb_eArgError, "wrong number of arguments (%d for 2 or 3)", argc);
    break;
  }
  status = gsl_fit_mul_est(x, c1, c11, &y, &yerr);
  return rb_ary_new3(3, rb_float_new(y), rb_float_new(yerr), INT2FIX(status));
}

void Init_gsl_fit(VALUE module)
{
  VALUE mgsl_fit;
  mgsl_fit = rb_define_module_under(module, "Fit");
  rb_define_module_function(mgsl_fit, "linear", rb_gsl_fit_linear, -1);
  rb_define_module_function(mgsl_fit, "wlinear", rb_gsl_fit_wlinear, -1);
  rb_define_module_function(mgsl_fit, "linear_est", rb_gsl_fit_linear_est, -1);
  rb_define_module_function(mgsl_fit, "mul", rb_gsl_fit_mul, -1);
  rb_define_module_function(mgsl_fit, "wmul", rb_gsl_fit_wmul, -1);
  rb_define_module_function(mgsl_fit, "mul_est", rb_gsl_fit_mul_est, -1);
}
