
#line 1 "../gen/tmpl/lib.c"
/*
  gsl_Multifit.c
  Ruby/Numo::GSL - GSL wrapper for Ruby/Numo::NArray

  created on: 2017-03-11
  Copyright (C) 2017 Masahiro Tanaka
*/

#include <ruby.h>
#include <assert.h>
#include "numo/narray.h"
#include "numo/template.h"
#include "../numo_gsl.h"
#line 15 "../gen/tmpl/lib.c"
#include <gsl/gsl_multifit.h>

#line 18 "../gen/tmpl/lib.c"
static VALUE mG;



#line 1 "../gen/tmpl/module.c"
/*
  module definition: Numo::GSL::Multifit
*/

#line 6 "../gen/tmpl/module.c"
static VALUE mMultifit;


#line 1 "../gen/tmpl/class.c"
/*
  class definition: Numo::GSL::Multifit::LinearWorkspace
*/

static VALUE cLinearWorkspace;

static void
multifit_linear_workspace_free(void *ptr)
{
    gsl_multifit_linear_free(ptr);
}

static size_t
multifit_linear_workspace_memsize(const void *ptr)
{
    return sizeof(gsl_multifit_linear_workspace);
}

static const rb_data_type_t multifit_linear_workspace_data_type = {
    "Numo::GSL::Multifit::LinearWorkspace",
    {NULL, multifit_linear_workspace_free, multifit_linear_workspace_memsize,},
    0, 0, RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED
};



#line 1 "../gen/tmpl/c_new_sizet_x2.c"

#line 5 "../gen/tmpl/c_new_sizet_x2.c"
/*
  @overload new(n,p)
  @param  [Integer]  n parameter
  @param  [Integer]  p parameter

  allocate instance of LinearWorkspace class.

This function allocates a workspace for fitting a model to a maximum of n
observations using a maximum of p parameters. The user may later supply
a smaller least squares system if desired. The size of the workspace is
O(np + p^2). */
static VALUE
multifit_linear_workspace_s_new(VALUE klass, VALUE v1, VALUE v2)
{
    gsl_multifit_linear_workspace *w;
    w = gsl_multifit_linear_alloc(NUM2SIZET(v1), NUM2SIZET(v2));
    if (!w) {
        rb_raise(rb_eNoMemError,"fail to allocate struct");
    }
    return TypedData_Wrap_Struct(cLinearWorkspace, &multifit_linear_workspace_data_type, (void*)w);
}


#line 1 "tmpl/multifit_linear.c"
static VALUE cLinearResult;

static void
iter_multifit_linear_workspace_s_linear(na_loop_t *const lp)
{
    gsl_matrix X;
    gsl_vector y;
    gsl_vector c;
    gsl_matrix cov;
    double *chisq;
    gsl_multifit_linear_workspace *ws = (gsl_multifit_linear_workspace*)(lp->opt_ptr);

    SET_GSL_MATRIX_FROM_LOOPT(lp,0,X);
    SET_GSL_VECTOR_FROM_LOOPT(lp,1,y);
    SET_GSL_VECTOR_FROM_LOOPT(lp,2,c);
    SET_GSL_MATRIX_FROM_LOOPT(lp,3,cov);
    chisq = (double*)NDL_PTR(lp,4);

    gsl_multifit_linear(&X,&y,&c,&cov,chisq,ws);
}

/*
  @overload linear(X,y)
  @param  [DFloat]    X (input matrix) predictor variables
  @param  [DFloat]    y (input vector) observation
  @return [GSL::Multifit::LinearResult] result Struct with members: c, cov, chisq.

  This function computes the best-fit parameters c of the model
y = X c for the observations y and the matrix of
predictor variables X, using the preallocated workspace provided
in work.  The p-by-p variance-covariance matrix of the model parameters
cov is set to \sigma^2 (X^T X)^{-1}, where \sigma is
the standard deviation of the fit residuals.
The sum of squares of the residuals from the best-fit,
\chi^2, is returned in chisq. If the coefficient of
determination is desired, it can be computed from the expression
R^2 = 1 - \chi^2 / TSS, where the total sum of squares (TSS) of
the observations y may be computed from gsl_stats_tss.

The best-fit is found by singular value decomposition of the matrix
X using the modified Golub-Reinsch SVD algorithm, with column
scaling to improve the accuracy of the singular values. Any components
which have zero singular value (to machine precision) are discarded
from the fit.

*/
static VALUE
multifit_linear_workspace_s_linear(VALUE mod, VALUE v1, VALUE v2)
{
    size_t c_shape[1], cov_shape[2];
    ndfunc_arg_in_t ain[2] = {{cDF,2},{cDF,1}};
    ndfunc_arg_out_t aout[3] = {{cDF,1,c_shape},{cDF,2,cov_shape},{cDF,0}};
    ndfunc_t ndf = { iter_multifit_linear_workspace_s_linear, NO_LOOP|NDF_EXTRACT,
                     2, 3, ain, aout };
    VALUE vws, r, result;
    narray_t *X, *y;
    size_t n, p;
    gsl_multifit_linear_workspace *ws;

    GetNArray(v1,X);
    GetNArray(v2,y);
    CHECK_GE_2D(X);
    CHECK_GE_1D(y);
    n = MAT_SIZE1(X);
    p = MAT_SIZE2(X);
    CHECK_SIZE_EQ(n,VEC_SIZE(y),"y size does not match X column size");

    c_shape[0] = cov_shape[0] = cov_shape[1] = p;

    ws = gsl_multifit_linear_alloc(n,p);
    if (!ws)
        rb_raise(rb_eNoMemError,"fail to allocate struct");
    vws = TypedData_Wrap_Struct(cLinearWorkspace, &multifit_linear_workspace_data_type, (void*)ws);

    r = na_ndloop3(&ndf, ws, 2, v1, v2);

    result = rb_class_new_instance(3, RARRAY_PTR(r), cLinearResult);

    RB_GC_GUARD(vws);
    RB_GC_GUARD(r);
    return result;
}


#line 1 "tmpl/multifit_wlinear.c"
static void
iter_multifit_linear_workspace_s_wlinear(na_loop_t *const lp)
{
    gsl_matrix X;
    gsl_vector y;
    gsl_vector w;
    gsl_vector c;
    gsl_matrix cov;
    double *chisq;
    gsl_multifit_linear_workspace *ws = (gsl_multifit_linear_workspace*)(lp->opt_ptr);

    SET_GSL_MATRIX_FROM_LOOPT(lp,0,X);
    SET_GSL_VECTOR_FROM_LOOPT(lp,1,y);
    SET_GSL_VECTOR_FROM_LOOPT(lp,2,w);
    SET_GSL_VECTOR_FROM_LOOPT(lp,3,c);
    SET_GSL_MATRIX_FROM_LOOPT(lp,4,cov);
    chisq = (double*)NDL_PTR(lp,5);

    gsl_multifit_wlinear(&X,&y,&w,&c,&cov,chisq,ws);
}

/*
  @overload wlinear(X,w,y)
  @param  [DFloat]    X (input matrix) predictor variables
  @param  [DFloat]    w (input vector) observations
  @param  [DFloat]    y (input vector) weights
  @return [GSL::Multifit::LinearResult] result Struct with members: c, cov, chisq.

  This function computes the best-fit parameters c of the weighted
model y = X c for the observations y with weights w
and the matrix of predictor variables X, using the preallocated
workspace provided in work.  The p-by-p covariance matrix of the model
parameters cov is computed as (X^T W X)^{-1}. The weighted
sum of squares of the residuals from the best-fit, \chi^2, is
returned in chisq. If the coefficient of determination is
desired, it can be computed from the expression R^2 = 1 - \chi^2
/ WTSS, where the weighted total sum of squares (WTSS) of the
observations y may be computed from gsl_stats_wtss.

*/
static VALUE
multifit_linear_workspace_s_wlinear(VALUE mod, VALUE v1, VALUE v2, VALUE v3)
{
    size_t c_shape[1], cov_shape[2];
    ndfunc_arg_in_t ain[3] = {{cDF,2},{cDF,1},{cDF,1}};
    ndfunc_arg_out_t aout[3] = {{cDF,1,c_shape},{cDF,2,cov_shape},{cDF,0}};
    ndfunc_t ndf = { iter_multifit_linear_workspace_s_wlinear, NO_LOOP|NDF_EXTRACT,
                     3, 3, ain, aout };
    VALUE vws, r, result;
    narray_t *X, *y, *w;
    size_t n, p;
    gsl_multifit_linear_workspace *ws;

    GetNArray(v1,X);
    GetNArray(v2,y);
    GetNArray(v3,w);
    CHECK_GE_2D(X);
    CHECK_GE_1D(y);
    CHECK_GE_1D(w);
    n = MAT_SIZE1(X);
    p = MAT_SIZE2(X);
    CHECK_SIZE_EQ(n,VEC_SIZE(y),"y size does not match X column size");
    CHECK_SIZE_EQ(n,VEC_SIZE(w),"w size does not match X column size");

    c_shape[0] = cov_shape[0] = cov_shape[1] = p;

    ws = gsl_multifit_linear_alloc(n,p);
    if (!ws)
        rb_raise(rb_eNoMemError,"fail to allocate struct");
    vws = TypedData_Wrap_Struct(cLinearWorkspace, &multifit_linear_workspace_data_type, (void*)ws);

    r = na_ndloop3(&ndf, ws, 3, v1, v2, v3);

    result = rb_class_new_instance(3, RARRAY_PTR(r), cLinearResult);

    RB_GC_GUARD(vws);
    RB_GC_GUARD(r);
    return result;
}


#line 1 "tmpl/multifit_linear_est.c"
static void
iter_multifit_s_linear_est(na_loop_t *const lp)
{
    gsl_vector x;
    gsl_vector c;
    gsl_matrix cov;
    double   y, yerr;

    SET_GSL_VECTOR_FROM_LOOPT(lp,0,x);
    SET_GSL_VECTOR_FROM_LOOPT(lp,1,c);
    SET_GSL_MATRIX_FROM_LOOPT(lp,2,cov);

    gsl_multifit_linear_est(&x, &c, &cov, &y, &yerr);

    *(double*)NDL_PTR(lp,3) = y;
    *(double*)NDL_PTR(lp,4) = yerr;
}

/*
  @overload linear_est(x,c,cov)
  @param  [DFloat]    x (input vector) observations
  @param  [DFloat]    c (input vector) solusions
  @param  [DFloat]    cov (input matrix) covariance
  @return [[DFloat,DFloat]] array of (y, y_err).

  This function uses the best-fit multilinear regression coefficients
c and their covariance matrix
cov to compute the fitted function value
y and its standard deviation y_err for the model y = x.c 
at the point x.

*/
static VALUE
multifit_s_linear_est(VALUE mod, VALUE v1, VALUE v2, VALUE v3)
{
    ndfunc_arg_in_t ain[3] = {{cDF,1},{cDF,1},{cDF,2}};
    ndfunc_arg_out_t aout[2] = {{cDF,0},{cDF,0}};
    ndfunc_t ndf = { iter_multifit_s_linear_est, NO_LOOP|NDF_EXTRACT,
                     3, 2, ain, aout };
    narray_t *x, *c, *cov;

    GetNArray(v1,x);
    GetNArray(v2,c);
    GetNArray(v3,cov);
    CHECK_GE_1D(x);
    CHECK_GE_1D(c);
    CHECK_GE_2D(cov);
    CHECK_SIZE_EQ(VEC_SIZE(x),VEC_SIZE(c),"x size does not match c size");
    CHECK_SIZE_EQ(MAT_SIZE1(cov),MAT_SIZE2(cov),"cov is not square matrix");
    CHECK_SIZE_EQ(VEC_SIZE(c),MAT_SIZE1(cov),"c size does not match cov row size");
    return na_ndloop(&ndf, 3, v1, v2, v3);
}


#line 1 "tmpl/multifit_linear_residuals.c"
static void
iter_multifit_s_linear_residuals(na_loop_t *const lp)
{
    gsl_matrix x;
    gsl_vector y;
    gsl_vector c;
    gsl_vector r;

    SET_GSL_MATRIX_FROM_LOOPT(lp,0,x);
    SET_GSL_VECTOR_FROM_LOOPT(lp,1,y);
    SET_GSL_VECTOR_FROM_LOOPT(lp,2,c);
    SET_GSL_VECTOR_FROM_LOOPT(lp,3,r);

    gsl_multifit_linear_residuals(&x, &y, &c, &r);
}

/*
  @overload linear_residuals(X,y,c)
  @param  [DFloat]    X (input matrix) design matrix
  @param  [DFloat]    y (input vector) rhs vector
  @param  [DFloat]    c (input vector) fit coefficients
  @return [DFloat]    returns r

  This function computes the vector of residuals r = y - X c for
the observations y, coefficients c and matrix of predictor
variables X.

*/
static VALUE
multifit_s_linear_residuals(VALUE mod, VALUE v1, VALUE v2, VALUE v3)
{
    size_t shape[1];
    ndfunc_arg_in_t ain[3] = {{cDF,2},{cDF,1},{cDF,1}};
    ndfunc_arg_out_t aout[1] = {{cDF,1,shape}};
    ndfunc_t ndf = { iter_multifit_s_linear_residuals, NO_LOOP|NDF_EXTRACT,
                     3, 1, ain, aout };
    narray_t *x, *y, *c;

    GetNArray(v1,x);
    GetNArray(v2,y);
    GetNArray(v3,c);
    CHECK_GE_2D(x);
    CHECK_GE_1D(y);
    CHECK_GE_1D(c);
    CHECK_SIZE_EQ(MAT_SIZE1(x),VEC_SIZE(y),"y size does not match x row size");
    CHECK_SIZE_EQ(MAT_SIZE2(x),VEC_SIZE(c),"c size does not match x column size");
    shape[0] = VEC_SIZE(y);
    return na_ndloop(&ndf, 3, v1, v2, v3);
}


#line 28 "../gen/tmpl/lib.c"
void
Init_multifit(void)
{
    VALUE mN;
    mN = rb_define_module("Numo");
    mG = rb_define_module_under(mN, "GSL");

    


#line 1 "../gen/tmpl/init_module.c"

    /*
      Document-module: Numo::GSL::Multifit
      
    */
    {
    
    mMultifit = rb_define_module_under(mG, "Multifit");
    
    
    
#line 1 "../gen/tmpl/init_class.c"

    /*
      Document-class: Numo::GSL::Multifit::LinearWorkspace
      
    */
    {
    cLinearWorkspace = rb_define_class_under(mMultifit, "LinearWorkspace", rb_cObject);
    
    rb_undef_alloc_func(cLinearWorkspace);
    rb_define_singleton_method(cLinearWorkspace, "new", multifit_linear_workspace_s_new, 2);
    rb_define_module_function(mMultifit, "linear", multifit_linear_workspace_s_linear, 2);
    /*
      Document-class: Numo::GSL::Multifit::LinearResult
      */
    cLinearResult = rb_struct_define_under(mMultifit,"LinearResult","c","cov","chisq",NULL);
    rb_define_module_function(mMultifit, "wlinear", multifit_linear_workspace_s_wlinear, 3);
#line 10 "../gen/tmpl/init_class.c"
    }

    rb_define_module_function(mMultifit, "linear_est", multifit_s_linear_est, 3);
    rb_define_module_function(mMultifit, "linear_residuals", multifit_s_linear_residuals, 3);
#line 12 "../gen/tmpl/init_module.c"
    }
#line 41 "../gen/tmpl/lib.c"
}
