/*
  linalg.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 <gsl/gsl_math.h>
#include "rb_gsl_array.h"
#include "rb_gsl_common.h"
#include "rb_gsl_linalg.h"

VALUE mgsl_linalg;
static VALUE cgsl_matrix_LU;
static VALUE cgsl_matrix_QR;
static VALUE cgsl_vector_tau;
static VALUE cgsl_matrix_Q;
static VALUE cgsl_matrix_R;
static VALUE cgsl_matrix_QR2;
static VALUE cgsl_matrix_Q2;
static VALUE cgsl_matrix_R2;
static VALUE cgsl_vector_tau2;
static VALUE cgsl_matrix_SV;
static VALUE cgsl_matrix_U;
static VALUE cgsl_matrix_V;
static VALUE cgsl_vector_S;
static VALUE cgsl_matrix_C;
static VALUE cgsl_matrix_Q3;
static VALUE cgsl_vector_tau3;

/* LU decomposition: original matrix is changed. */
static VALUE rb_gsl_linalg_LU_decomp(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *m = NULL;
  gsl_permutation *p = NULL;
  int signum, itmp;
  size_t size;
  VALUE obj2;

  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)",
			    argc);
    if (rb_obj_is_kind_of(argv[0], cgsl_matrix_complex))
      return rb_gsl_linalg_complex_LU_decomp(argc, argv, obj);
    
    CHECK_MATRIX(argv[0]);
    Data_Get_Struct(argv[0], gsl_matrix, m);
    itmp = 1;
    break;
  default:
    if (rb_obj_is_kind_of(obj, cgsl_matrix_complex))
      return rb_gsl_linalg_complex_LU_decomp(argc, argv, obj);

    CHECK_MATRIX(obj);
    Data_Get_Struct(obj, gsl_matrix, m);
    itmp = 0;
  }
  size = m->size1;
  switch (argc-itmp) {
  case 0:
    p = gsl_permutation_alloc(size);
    gsl_linalg_LU_decomp(m, p, &signum);
    RBASIC(obj)->klass = cgsl_matrix_LU;
    obj2 = Data_Wrap_Struct(cgsl_permutation, 0, gsl_permutation_free, p);
    return rb_ary_new3(2, obj2, INT2FIX(signum));
    break;
  case 1:  /* when a permutation object is given */
    CHECK_PERMUTATION(argv[itmp]);
    Data_Get_Struct(argv[itmp], gsl_permutation, p);
    gsl_linalg_LU_decomp(m, p, &signum);
    RBASIC(obj)->klass = cgsl_matrix_LU;
    return INT2FIX(signum);
    break;
  default:
    rb_raise(rb_eArgError, "Usage: LU_decomp!() or LU_decomp!(permutation)");
  }
}

/* LU decomposition: original matrix is not changed. */
static VALUE rb_gsl_linalg_LU_decomp2(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *mtmp = NULL, *m = NULL;
  gsl_permutation *p = NULL;
  int signum, itmp;
  size_t size;
  VALUE obj2, objm;
  
  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)",
			    argc);
    if (rb_obj_is_kind_of(argv[0], cgsl_matrix_complex))
      return rb_gsl_linalg_complex_LU_decomp2(argc, argv, obj);
  
    CHECK_MATRIX(argv[0]);
    Data_Get_Struct(argv[0], gsl_matrix, mtmp);
    itmp = 1;
    break;
  default:
    if (rb_obj_is_kind_of(obj, cgsl_matrix_complex))
      return rb_gsl_linalg_complex_LU_decomp2(argc, argv, obj);
  
    CHECK_MATRIX(obj);
    Data_Get_Struct(obj, gsl_matrix, mtmp);
    itmp = 0;
  }
  m = make_matrix_clone(mtmp);
  objm = Data_Wrap_Struct(cgsl_matrix_LU, 0, gsl_matrix_free, m);
  size = m->size1;
  switch (argc-itmp) {
  case 0:
    p = gsl_permutation_alloc(size);
    gsl_linalg_LU_decomp(m, p, &signum);
    obj2 = Data_Wrap_Struct(cgsl_permutation, 0, gsl_permutation_free, p);
    return rb_ary_new3(3, objm, obj2, INT2FIX(signum));
    break;
  case 1:
    CHECK_PERMUTATION(argv[itmp]);
    Data_Get_Struct(argv[itmp], gsl_permutation, p);
    gsl_linalg_LU_decomp(m, p, &signum);
    return rb_ary_new3(2, objm, INT2FIX(signum));
    break;
  default:
    rb_raise(rb_eArgError, "Usage: LU_decomp() or LU_decomp(permutation)");
  }
}

static gsl_matrix* get_matrix(VALUE obj, VALUE klass,int *flagm);
static gsl_permutation* get_permutation(VALUE obj,  size_t size, int *flagp);
static gsl_vector* get_vector2(VALUE obj,  int *flagv);

static gsl_matrix* get_matrix(VALUE obj, VALUE klass, int *flagm)
{
  gsl_matrix *mtmp = NULL, *m = NULL;
  CHECK_MATRIX(obj);
  if (CLASS_OF(obj) == klass) {
    Data_Get_Struct(obj, gsl_matrix, m);
    *flagm = 0;
  } else {
    Data_Get_Struct(obj, gsl_matrix, mtmp);
    m = make_matrix_clone(mtmp);
    *flagm = 1;
  }
  return m;
}

static gsl_permutation* get_permutation(VALUE obj, size_t size, int *flagp)
{
  gsl_permutation *p = NULL;
  if (CLASS_OF(obj) == cgsl_permutation) {
    Data_Get_Struct(obj, gsl_permutation, p);
    *flagp = 0;
  } else {
    p = gsl_permutation_alloc(size);
    *flagp = 1;
  }
  return p;
}

static gsl_vector* get_vector2(VALUE obj, int *flagv)
{
  gsl_vector *v = NULL;
  if (TYPE(obj) == T_ARRAY) {
    v = make_cvector_from_rarray(obj);
    *flagv = 1;
  } else {
    CHECK_VECTOR(obj);
    Data_Get_Struct(obj, gsl_vector, v);
    *flagv = 0;
  }
  return v;
}

/* singleton */
static VALUE rb_gsl_linalg_LU_solve(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *m = NULL;
  gsl_permutation *p = NULL;
  gsl_vector *b = NULL, *x = NULL;
  int signum, flagm = 0, flagp = 0, flagb = 0, flagx = 0, itmp;
  size_t size;

  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc < 2 || argc > 4) 
      rb_raise(rb_eArgError, "Usage: solve(m, b), solve(m, b, x), solve(lu, p, b), solve(lu, p, b, x)");
    m = get_matrix(argv[0], cgsl_matrix_LU, &flagm);
    itmp = 1;
    break;
  default:
    if (argc < 1 || argc > 3) 
      rb_raise(rb_eArgError, "Usage: LU_solve(b), LU_solve(p, b), LU_solve(b, x), solve(p, b, x)");
    
    m = get_matrix(obj, cgsl_matrix_LU, &flagm);
    itmp = 0;
  }
  size = m->size1;

  p = get_permutation(argv[itmp], size, &flagp);
  if (flagp == 1 && flagm == 0) rb_raise(rb_eArgError, "permutation must be given");
  if (flagp == 0) itmp++;

  b = get_vector2(argv[itmp], &flagb);
  itmp++;

  if (itmp == argc) {
    x = gsl_vector_alloc(size);
    flagx = 1;
  } else {
    CHECK_VECTOR(argv[itmp]);
    Data_Get_Struct(argv[itmp], gsl_vector, x);
  }
  if (flagm == 1) gsl_linalg_LU_decomp(m, p, &signum);
  gsl_linalg_LU_solve(m, p, b, x);
  if (flagm == 1) gsl_matrix_free(m);
  if (flagp == 1) gsl_permutation_free(p);
  if (flagb == 1) gsl_vector_free(b);
  if (flagx == 1) return Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, x);
  else return argv[argc-1];
}

/* bb must be Vector, it is replaced by the root of the system */
static VALUE rb_gsl_linalg_LU_svx(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *m = NULL;
  gsl_permutation *p = NULL;
  gsl_vector *b = NULL;
  int signum, flagm = 0, flagp = 0, flagb = 0, itmp;
  size_t size;

  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc < 2 || argc > 3) 
      rb_raise(rb_eArgError, "Usage: svx(m, b), svx(lu, p, b)");
    m = get_matrix(argv[0], cgsl_matrix_LU, &flagm);
    itmp = 1;
    break;
  default:
   if (argc < 1 || argc > 2) 
     rb_raise(rb_eArgError, "Usage: LU_svx(b), LU_svx(p, b)");
    m = get_matrix(obj, cgsl_matrix_LU, &flagm);
    itmp = 0;
  }

  size = m->size1;
  p = get_permutation(argv[itmp], size, &flagp);
  if (flagp == 1 && flagm == 0) rb_raise(rb_eArgError, "permutation must be given");
  if (flagp == 0) itmp++;

  CHECK_VECTOR(argv[itmp]);
  b = get_vector2(argv[itmp], &flagb);

  if (flagm == 1) gsl_linalg_LU_decomp(m, p, &signum);
  gsl_linalg_LU_svx(m, p, b);
  if (flagm == 1) gsl_matrix_free(m);
  if (flagp == 1) gsl_permutation_free(p);
  return argv[itmp];
}

/* singleton */
static VALUE rb_gsl_linalg_LU_refine(VALUE obj, VALUE vm,
				     VALUE lu, VALUE pp, VALUE bb,
				     VALUE xx)
{
  gsl_matrix *m = NULL, *mlu = NULL;
  gsl_permutation *p = NULL;
  gsl_vector *b = NULL, *x = NULL, *r = NULL;
  int flagb = 0;
  VALUE vr;

  CHECK_MATRIX(vm);
  CHECK_MATRIX(lu);
  CHECK_PERMUTATION(pp);
  CHECK_VECTOR(xx);
  Data_Get_Struct(vm, gsl_matrix, m);
  Data_Get_Struct(lu, gsl_matrix, mlu);
  Data_Get_Struct(pp, gsl_permutation, p);
  if (TYPE(bb) == T_ARRAY) {
    b = make_cvector_from_rarray(bb);
    flagb = 1;
  } else {
    CHECK_VECTOR(bb);
    Data_Get_Struct(bb, gsl_vector, b);
  }
  Data_Get_Struct(xx, gsl_vector, x);
  r = gsl_vector_alloc(m->size1);
  gsl_linalg_LU_refine(m, mlu, p, b, x, r);
  vr = Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, r);
  if (flagb == 1) gsl_vector_free(b);
  return rb_ary_new3(2, xx, vr);
}

static VALUE rb_gsl_linalg_LU_invert(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *m = NULL, *inverse = NULL;
  gsl_permutation *p = NULL;
  int signum, flagm = 0, flagp = 0, itmp;
  size_t size;
  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    /*    if (argc != 1) rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)",
	  argc);*/
    m = get_matrix(argv[0], cgsl_matrix_LU, &flagm);
    itmp = 1;
    break;
  default:
    m = get_matrix(obj, cgsl_matrix_LU, &flagm);
    itmp = 0;
  }
  size = m->size1;

  if (argc == itmp) {
    p = gsl_permutation_alloc(size);
    flagp = 1;
  } else {
    CHECK_PERMUTATION(argv[itmp]);
    p = get_permutation(argv[itmp], size, &flagp);
  }
  if (flagp == 1 && flagm == 0) rb_raise(rb_eArgError, "permutation must be given");
  if (flagp == 0) itmp++;

  if (flagm == 1 || flagp == 1) {
    /*  gsl_matrix_fprintf(stderr, m, "%g");*/
    gsl_linalg_LU_decomp(m, p, &signum);  
  }
  /*  gsl_matrix_fprintf(stderr, m, "%g");*/

  if (argc-1 == itmp) {
    CHECK_MATRIX(argv[itmp]);
    Data_Get_Struct(argv[itmp], gsl_matrix, inverse);
  } else {
    inverse = gsl_matrix_alloc(size, size);
  }
  /*  gsl_matrix_fprintf(stderr, m, "%g");*/
  gsl_linalg_LU_invert(m, p, inverse);
  /*  gsl_matrix_fprintf(stderr, inverse, "%g");*/
  if (flagm == 1) gsl_matrix_free(m);
  if (flagp == 1) gsl_permutation_free(p);
  if (argc-1 == itmp) return argv[itmp];
  else return Data_Wrap_Struct(cgsl_matrix, 0, gsl_matrix_free, inverse);

}

static VALUE rb_gsl_linalg_LU_det(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *m = NULL;
  gsl_permutation *p = NULL;
  int flagm = 0, flagp = 0, itmp, sign;
  size_t size;
  double det;

  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)",
			    argc);
    m = get_matrix(argv[0], cgsl_matrix_LU, &flagm);
    itmp = 1;
    break;
  default:
    m = get_matrix(obj, cgsl_matrix_LU, &flagm);
    itmp = 0;
  }
  size = m->size1;
  if (flagm == 0) {
    /*    if (argc+itmp != 2) 
      rb_raise(rb_eArgError, "permutation and sign must be given");
    p = get_permutation(argv[itmp], size, &flagp);
    itmp++;
    sign = FIX2INT(argv[itmp]);
    */
    if (argc+itmp == 1)  sign = FIX2INT(argv[itmp]);
    else sign = 1;
  } else {
    if (argc-itmp >= 2) {
      get_permutation(argv[itmp], size, &flagp);
    } else {
      p = gsl_permutation_alloc(size);
      flagp = 1;
    }
  } 
  if (flagm == 1) gsl_linalg_LU_decomp(m, p, &sign);  
  det = gsl_linalg_LU_det(m, sign);
  if (flagm == 1) gsl_matrix_free(m);
  if (flagp == 1) gsl_permutation_free(p);
  return rb_float_new(det);
}

static VALUE rb_gsl_linalg_LU_lndet(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *m = NULL;
  gsl_permutation *p = NULL;
  int flagm = 0, sign;
  double lndet;

  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of argument (%d for 1)",
			    argc);
    m = get_matrix(argv[0], cgsl_matrix_LU, &flagm);
    break;
  default:
    m = get_matrix(obj, cgsl_matrix_LU, &flagm);
  }
  if (flagm == 1) {
    p = gsl_permutation_alloc(m->size1);
    gsl_linalg_LU_decomp(m, p, &sign);  
  }
  lndet = gsl_linalg_LU_lndet(m);
  if (flagm == 1) {
    gsl_matrix_free(m);
    gsl_permutation_free(p);
  }
  return rb_float_new(lndet);
}

static VALUE rb_gsl_linalg_LU_sgndet(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *m = NULL;
  gsl_permutation *p = NULL;
  int flagm = 0, sign, signdet, itmp;
  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    /*    if (argc != 1) rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)",
	  argc);*/
    m = get_matrix(argv[0], cgsl_matrix_LU, &flagm);
    itmp = 1;
    break;
  default:
    m = get_matrix(obj, cgsl_matrix_LU, &flagm);
    itmp = 0;
  }
  if (flagm == 1) {
    p = gsl_permutation_alloc(m->size1);
    gsl_linalg_LU_decomp(m, p, &sign);  
  } else {
    if (argc-itmp != 1) rb_raise(rb_eArgError, "sign must be given");
    sign = FIX2INT(argv[itmp]);
  }
  signdet = gsl_linalg_LU_sgndet(m, sign);
  if (flagm == 1) {
    gsl_matrix_free(m);
    gsl_permutation_free(p);
  }
  return INT2FIX(signdet);
}

static VALUE rb_gsl_linalg_QR_decomp(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *m = NULL, *mtmp = NULL;
  gsl_vector *tau = NULL;
  int itmp;
  size_t size;
  VALUE vtau, vqr;
    
  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)",
			    argc);
    CHECK_MATRIX(argv[0]);
    Data_Get_Struct(argv[0], gsl_matrix, mtmp);
    itmp = 1;
    break;
  default:
    Data_Get_Struct(obj, gsl_matrix, mtmp);
    itmp = 0;
  }
  m = make_matrix_clone(mtmp);
  size = m->size1;
  if (argc == itmp) {
    tau = gsl_vector_alloc(GSL_MIN(mtmp->size1, mtmp->size2));
  } else if (argc-1 == itmp) {
    CHECK_VECTOR(argv[itmp]);
    Data_Get_Struct(argv[itmp], gsl_vector, tau);
  } else {
    rb_raise(rb_eArgError, "wrong number of arguments");
  }
  gsl_linalg_QR_decomp(m, tau);
  vqr = Data_Wrap_Struct(cgsl_matrix_QR, 0, gsl_matrix_free, m);
  if (argc-1 == itmp) {
    RBASIC(argv[itmp])->klass = cgsl_vector_tau;
    return vqr;
  } else {
    vtau = Data_Wrap_Struct(cgsl_vector_tau, 0, gsl_vector_free, tau);
    return rb_ary_new3(2, vqr, vtau);
  }
}

static VALUE rb_gsl_linalg_QR_solve(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *m = NULL;
  gsl_vector *b = NULL, *x = NULL, *tau = NULL;
  int flagm = 0, flagt = 0, flagb = 0, flagx = 0, itmp;
  size_t size;
  
  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc < 2 || argc > 4) {
      rb_raise(rb_eArgError, 
	       "Usage: solve(m, b), solve(m, b, x), "
	       "solve(qr, tau, b), solve(qr, tau, b, x)");
    }
    m = get_matrix(argv[0], cgsl_matrix_QR, &flagm);
    itmp = 1;
    break;
  default:
    if (argc < 1 || argc > 3) {
      rb_raise(rb_eArgError, 
	       "Usage: QR_solve(b), QR_solve(p, b), QR_solve(b, x), "
	       "QR_solve(p, b, x)");
    }
    m = get_matrix(obj, cgsl_matrix_QR, &flagm);
    itmp = 0;
  }
  size = m->size1;
  if (flagm == 0) {
    if (CLASS_OF(argv[itmp]) != cgsl_vector_tau)
      rb_raise(rb_eArgError, "tau vector must be given");
    Data_Get_Struct(argv[itmp], gsl_vector, tau);
    flagt = 0;
    itmp++;
  } else {
    if (CLASS_OF(argv[itmp]) == cgsl_vector_tau) {
      Data_Get_Struct(argv[itmp], gsl_vector, tau);
      flagt = 0;
      itmp++;
    } else {
      tau = gsl_vector_alloc(size);
      flagt = 1;
    }
  }

  b = get_vector2(argv[itmp], &flagb);
  itmp++;

  if (itmp == argc) {
    x = gsl_vector_alloc(m->size1);
    flagx = 1;
  } else {
    CHECK_VECTOR(argv[itmp]);
    Data_Get_Struct(argv[itmp], gsl_vector, x);
  }

  if (flagm == 1) gsl_linalg_QR_decomp(m, tau);

  gsl_linalg_QR_solve(m, tau, b, x);

  if (flagm == 1) gsl_matrix_free(m);
  if (flagt == 1) gsl_vector_free(tau);
  if (flagb == 1) gsl_vector_free(b);

  if (flagx == 1) return Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, x);
  else return argv[argc-1];
}


static VALUE rb_gsl_linalg_QR_svx(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *m = NULL;
  gsl_vector *b = NULL, *tau = NULL;
  int flagm = 0, flagt = 0, flagb = 0, itmp;
  size_t size;
  
  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc < 1 || argc > 3) {
      rb_raise(rb_eArgError, "Usage: svx(m, b), svx(qr, tau, b)");
    }
    m = get_matrix(argv[0], cgsl_matrix_QR, &flagm);
    itmp = 1;
    break;
  default:
    if (argc < 0 || argc > 2) {
      rb_raise(rb_eArgError, "Usage: QR_svx(b), QR_svx(p, b)");
    }
    m = get_matrix(obj, cgsl_matrix_QR, &flagm);
    itmp = 0;
  }
  size = m->size1;
  if (flagm == 0) {
    if (CLASS_OF(argv[itmp]) != cgsl_vector_tau)
      rb_raise(rb_eArgError, "tau vector must be given");
    Data_Get_Struct(argv[itmp], gsl_vector, tau);
    flagt = 0;
    itmp++;
  } else {
    if (CLASS_OF(argv[itmp]) == cgsl_vector_tau) {
      Data_Get_Struct(argv[itmp], gsl_vector, tau);
      flagt = 0;
      itmp++;
    } else {
      tau = gsl_vector_alloc(size);
      flagt = 1;
    }
  }

  b = get_vector2(argv[itmp], &flagb);
  if (flagm == 1) gsl_linalg_QR_decomp(m, tau);
  gsl_linalg_QR_svx(m, tau, b);

  if (flagm == 1) gsl_matrix_free(m);
  if (flagt == 1) gsl_vector_free(tau);
  return argv[itmp];
}

static VALUE rb_gsl_linalg_QR_lssolve(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *m = NULL;
  gsl_vector *b = NULL, *x = NULL, *tau = NULL, *residual = NULL;
  int flagm = 0, flagt = 0, flagb = 0, itmp;
  size_t size;
  VALUE vres, vx;

  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc < 2 || argc > 3) {
      rb_raise(rb_eArgError, 
	       "Usage: solve(m, b), solve(m, b), "
	       "solve(qr, tau, b), solve(qr, tau, b)");
    }
    m = get_matrix(argv[0], cgsl_matrix_QR, &flagm);
    itmp = 1;
    break;
  default:
    if (argc < 1 || argc > 2) {
      rb_raise(rb_eArgError, 
	       "Usage: QR_solve(b), QR_solve(p, b)");
    }
    m = get_matrix(obj, cgsl_matrix_QR, &flagm);
    itmp = 0;
  }
  size = m->size1;
  if (flagm == 0) {
    if (CLASS_OF(argv[itmp]) != cgsl_vector_tau)
      rb_raise(rb_eArgError, "tau vector must be given");
    Data_Get_Struct(argv[itmp], gsl_vector, tau);
    flagt = 0;
    itmp++;
  } else {
    if (CLASS_OF(argv[itmp]) == cgsl_vector_tau) {
      Data_Get_Struct(argv[itmp], gsl_vector, tau);
      flagt = 0;
      itmp++;
    } else {
      tau = gsl_vector_alloc(GSL_MIN(m->size1, m->size2));
      flagt = 1;
    }
  }

  b = get_vector2(argv[itmp], &flagb);
  itmp++;
  x = gsl_vector_alloc(tau->size);
  residual = gsl_vector_alloc(GSL_MAX(m->size1, m->size2));
  if (flagm == 1) {
    gsl_linalg_QR_decomp(m, tau);
  }
  gsl_linalg_QR_lssolve(m, tau, b, x, residual);
  if (flagm == 1) gsl_matrix_free(m);
  if (flagt == 1) gsl_vector_free(tau);
  if (flagb == 1) gsl_vector_free(b);
  vx = Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, x);
  vres = Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, residual);
  return rb_ary_new3(2, vx, vres);
}

static VALUE rb_gsl_linalg_QR_QTvec(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *QR = NULL;
  gsl_vector *tau = NULL, *v = NULL;
  int itmp;
  VALUE vtmp;

  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc < 2) rb_raise(rb_eArgError, "wrong number of arguments");
    vtmp = argv[0];
    itmp = 1;
    break;
  default:
    if (argc < 1) rb_raise(rb_eArgError, "wrong number of arguments");
    vtmp = obj;
    itmp = 0;
  }
  CHECK_MATRIX(vtmp);
  if (CLASS_OF(vtmp) != cgsl_matrix_QR) {
    rb_raise(rb_eTypeError, "not a QR matrix");
  }
  Data_Get_Struct(vtmp, gsl_matrix, QR);
  CHECK_VECTOR(argv[itmp]);
  Data_Get_Struct(argv[itmp], gsl_vector, tau);
  itmp++;
  if (argc-1 == itmp) {
    Data_Get_Struct(argv[itmp], gsl_vector, v);
    gsl_linalg_QR_QTvec(QR, tau, v);
    return argv[itmp];
  } else {
    v = gsl_vector_alloc(QR->size1);
    gsl_linalg_QR_QTvec(QR, tau, v);
    return Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, v);
  }
}

static VALUE rb_gsl_linalg_QR_Qvec(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *QR = NULL;
  gsl_vector *tau = NULL, *v = NULL;
  int itmp;
  VALUE vtmp;

  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc < 2) rb_raise(rb_eArgError, "wrong number of arguments");
    vtmp = argv[0];
    itmp = 1;
    break;
  default:
    if (argc < 1) rb_raise(rb_eArgError, "wrong number of arguments");
    vtmp = obj;
    itmp = 0;
  }
  CHECK_MATRIX(vtmp);
  if (CLASS_OF(vtmp) != cgsl_matrix_QR) {
    rb_raise(rb_eTypeError, "not a QR matrix");
  }
  Data_Get_Struct(vtmp, gsl_matrix, QR);
  CHECK_VECTOR(argv[itmp]);
  Data_Get_Struct(argv[itmp], gsl_vector, tau);
  itmp++;
  if (argc-1 == itmp) {
    Data_Get_Struct(argv[itmp], gsl_vector, v);
    gsl_linalg_QR_Qvec(QR, tau, v);
    return argv[itmp];
  } else {
    v = gsl_vector_alloc(QR->size1);
    gsl_linalg_QR_Qvec(QR, tau, v);
    return Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, v);
  }
}

static VALUE rb_gsl_linalg_QR_Rsolve(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *QR = NULL;
  gsl_vector *b = NULL, *x = NULL;
  int flagb = 0, itmp;
  
  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)",
			    argc);
    Data_Get_Struct(argv[0], gsl_matrix, QR);
    itmp = 1;
    break;
  default:
    Data_Get_Struct(obj, gsl_matrix, QR);
    itmp = 0;
  }
  b = get_vector2(argv[itmp], &flagb);
  x = gsl_vector_alloc(b->size);
  gsl_linalg_QR_Rsolve(QR, b, x);
  if (flagb == 1) gsl_vector_free(b);
  return Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, x);
}

static VALUE rb_gsl_linalg_QR_Rsvx(VALUE obj, VALUE xx)
{
  gsl_matrix *QR = NULL;
  gsl_vector *x = NULL;
  Data_Get_Struct(obj, gsl_matrix, QR);
  Data_Get_Struct(xx, gsl_vector, x);
  gsl_linalg_QR_Rsvx(QR, x);
  return obj;
}

static VALUE rb_gsl_linalg_QR_unpack(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *QR = NULL, *Q = NULL, *R = NULL;
  gsl_vector *tau = NULL;
  int itmp;
  VALUE vtmp, vQ, vR;

  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);
    vtmp = argv[0];
    itmp = 1;
    break;
  default:
    if (argc != 1) rb_raise(rb_eArgError, 
			    "wrong number of arguments (%d for 1)", argc);
    vtmp = obj;
    itmp = 0;
  }
  CHECK_MATRIX(vtmp);
  if (CLASS_OF(vtmp) != cgsl_matrix_QR) {
    rb_raise(rb_eTypeError, "not a QR matrix");
  }
  Data_Get_Struct(vtmp, gsl_matrix, QR);
  CHECK_VECTOR(argv[itmp]);
  if (CLASS_OF(argv[itmp]) != cgsl_vector_tau)
    rb_raise(rb_eTypeError, "tau vector must be given");
  Data_Get_Struct(argv[itmp], gsl_vector, tau);
  Q = gsl_matrix_alloc(QR->size1, QR->size1);
  R = gsl_matrix_alloc(QR->size1, QR->size2);
  gsl_linalg_QR_unpack(QR, tau, Q, R);
  vQ = Data_Wrap_Struct(cgsl_matrix_Q, 0, gsl_matrix_free, Q);
  vR = Data_Wrap_Struct(cgsl_matrix_R, 0, gsl_matrix_free, R);
  return rb_ary_new3(2, vQ, vR);
}

/* singleton */
static VALUE rb_gsl_linalg_QR_QRsolve(VALUE obj, VALUE qq, VALUE rr, VALUE bb)
{
  gsl_matrix *Q = NULL, *R = NULL;
  gsl_vector *b = NULL, *x = NULL;
  int flagb = 0;

  CHECK_MATRIX(qq);
  if (CLASS_OF(qq) != cgsl_matrix_Q) rb_raise(rb_eTypeError, "not a Q matrix");
  CHECK_MATRIX(rr);
  if (CLASS_OF(rr) != cgsl_matrix_R) rb_raise(rb_eTypeError, "not a R matrix");
  if (TYPE(bb) == T_ARRAY) {
    b = make_cvector_from_rarray(bb);
    flagb = 1;
  } else {
    CHECK_VECTOR(bb);
    Data_Get_Struct(bb, gsl_vector, b);
  }
  Data_Get_Struct(qq, gsl_matrix, Q);
  Data_Get_Struct(rr, gsl_matrix, R);
  x = gsl_vector_alloc(b->size);
  gsl_linalg_QR_QRsolve(Q, R, b, x);
  if (flagb == 1) gsl_vector_free(b);
  return Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, x);
}

/* singleton */
static VALUE rb_gsl_linalg_QR_update(VALUE obj, VALUE qq, VALUE rr, VALUE ww,
				     VALUE vv)
{
  gsl_matrix *Q = NULL, *R = NULL;
  gsl_vector *w = NULL, *v = NULL;
  if (!rb_obj_is_kind_of(qq, cgsl_matrix_Q)) 
    rb_raise(rb_eTypeError, "not a Q matrix");
  if (!rb_obj_is_kind_of(rr, cgsl_matrix_R)) 
    rb_raise(rb_eTypeError, "not a R matrix");
  CHECK_VECTOR(ww);
  CHECK_VECTOR(vv);
  Data_Get_Struct(qq, gsl_matrix, Q);
  Data_Get_Struct(rr, gsl_matrix, R);
  Data_Get_Struct(ww, gsl_vector, w);
  Data_Get_Struct(vv, gsl_vector, v);
  gsl_linalg_QR_update(Q, R, w, v);
  return obj;
}

static VALUE rb_gsl_linalg_R_solve(VALUE obj, VALUE bb)
{
  gsl_matrix *R = NULL;
  gsl_vector *b = NULL, *x = NULL;
  int flagb = 0;
  if (CLASS_OF(obj) != cgsl_matrix_R) 
    rb_raise(rb_eTypeError, "not a R matrix");
  Data_Get_Struct(obj, gsl_matrix, R);
  if (TYPE(bb) == T_ARRAY) {
    b = make_cvector_from_rarray(bb);
    flagb = 1;
  } else {
    CHECK_VECTOR(bb);
    Data_Get_Struct(bb, gsl_vector, b);
  }
  x = gsl_vector_alloc(b->size);
  gsl_linalg_R_solve(R, b, x);
  if (flagb == 1) gsl_vector_free(b);
  return Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, x);
}

/*
static VALUE rb_gsl_linalg_R_svx(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *R = NULL;
  gsl_vector *x = NULL;
  int itmp;
  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
    Data_Get_Struct(argv[0], gsl_matrix, R);
    itmp = 1;
    break;
  default:
    Data_Get_Struct(obj, gsl_matrix, R);
    itmp = 0;
  }
  Data_Get_Struct(argv[itmp], gsl_vector, x);
  gsl_linalg_R_svx(R, x);
  return argv[itmp];
}
*/
static VALUE rb_gsl_linalg_QRPT_decomp(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *A = NULL, *QR = NULL;
  gsl_vector *tau = NULL, *norm = NULL;
  gsl_permutation *p = NULL;
  int signum;
  size_t size0;
  VALUE vtau, vp, vA, vQR;

  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)",
			    argc);
    vA = argv[0];
    break;
  default:
    vA = obj;
  }
  CHECK_MATRIX(vA);
  Data_Get_Struct(vA, gsl_matrix, A);
  QR = make_matrix_clone(A);
  size0 = GSL_MIN(A->size1, A->size2);
  tau = gsl_vector_alloc(size0);
  p = gsl_permutation_alloc(size0);
  norm = gsl_vector_alloc(size0);
  gsl_linalg_QRPT_decomp(QR, tau, p, &signum, norm);

  vQR = Data_Wrap_Struct(cgsl_matrix_QR2, 0, gsl_matrix_free, QR);
  vtau = Data_Wrap_Struct(cgsl_vector_tau2, 0, gsl_vector_free, tau);
  vp = Data_Wrap_Struct(cgsl_permutation, 0, gsl_permutation_free, p);
  gsl_vector_free(norm);
  return rb_ary_new3(4, vQR, vtau, vp, INT2FIX(signum));
}

static VALUE rb_gsl_linalg_QRPT_decomp3(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *A = NULL;
  gsl_vector *tau = NULL, *norm = NULL;
  gsl_permutation *p = NULL;
  int signum;
  size_t size0;
  VALUE vtau, vp, vA;

  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)",
			    argc);
    vA = argv[0];
    break;
  default:
    vA = obj;
  }
  CHECK_MATRIX(vA);
  Data_Get_Struct(vA, gsl_matrix, A);
  size0 = GSL_MIN(A->size1, A->size2);
  tau = gsl_vector_alloc(size0);
  p = gsl_permutation_alloc(size0);
  norm = gsl_vector_alloc(size0);
  gsl_linalg_QRPT_decomp(A, tau, p, &signum, norm);
  RBASIC(vA)->klass = cgsl_matrix_QR2;
  vtau = Data_Wrap_Struct(cgsl_vector_tau2, 0, gsl_vector_free, tau);
  vp = Data_Wrap_Struct(cgsl_permutation, 0, gsl_permutation_free, p);
  gsl_vector_free(norm);
  return rb_ary_new3(3, vtau, vp, INT2FIX(signum));
}

static VALUE rb_gsl_linalg_QRPT_decomp2(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *A = NULL, *Q = NULL, *R = NULL;
  gsl_vector *tau = NULL, *norm = NULL;
  gsl_permutation *p = NULL;
  int signum;
  size_t size0;
  VALUE vtau, vp, vA, vQ, vR;

  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of arguments");
    vA = argv[0];
    break;
  default:
    if (argc != 0) rb_raise(rb_eArgError, "wrong number of arguments");
    vA = obj;
  }
  CHECK_MATRIX(vA);
  Data_Get_Struct(vA, gsl_matrix, A);
  Q = gsl_matrix_alloc(A->size1, A->size2);
  R = gsl_matrix_alloc(A->size1, A->size2);
  size0 = GSL_MIN(A->size1, A->size2);
  tau = gsl_vector_alloc(size0);
  p = gsl_permutation_alloc(size0);
  norm = gsl_vector_alloc(size0);
  gsl_linalg_QRPT_decomp2(A, Q, R, tau, p, &signum, norm);

  vQ = Data_Wrap_Struct(cgsl_matrix_Q2, 0, gsl_matrix_free, Q);
  vR = Data_Wrap_Struct(cgsl_matrix_R2, 0, gsl_matrix_free, R);
  vtau = Data_Wrap_Struct(cgsl_vector_tau2, 0, gsl_vector_free, tau);
  vp = Data_Wrap_Struct(cgsl_permutation, 0, gsl_permutation_free, p);
  gsl_vector_free(norm);
  return rb_ary_new3(5, vQ, vR, vtau, vp, INT2FIX(signum));
}

static VALUE rb_gsl_linalg_QRPT_solve(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *QR = NULL, *A = NULL;
  gsl_vector *tau = NULL, *b = NULL, *x = NULL, *norm = NULL;
  gsl_permutation *p = NULL;
  int signum, itmp, flagb = 0, flagq = 0;
  VALUE vtmp;
  size_t size0;

  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    /*    if (argc != 1) rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)",
	  argc);*/
    vtmp = argv[0];
    itmp = 1;
    break;
  default:
    vtmp = obj;
    itmp = 0;
  }
  CHECK_MATRIX(vtmp);
  if (CLASS_OF(vtmp) == cgsl_matrix_QR2) {
    if (argc-itmp != 3) rb_raise(rb_eArgError, 
				 "wrong number of arguments (%d for %d)", 
				 argc, 4-itmp);
    CHECK_VECTOR(argv[itmp]);
    if (CLASS_OF(argv[itmp]) != cgsl_vector_tau2) 
      rb_raise(rb_eTypeError, "not a tau vector");
    CHECK_PERMUTATION(argv[itmp+1]);
    Data_Get_Struct(argv[itmp], gsl_vector, tau);
    Data_Get_Struct(argv[itmp+1], gsl_permutation, p);
    Data_Get_Struct(vtmp, gsl_matrix, QR);
    size0 = GSL_MIN(QR->size1, QR->size2);
    itmp += 2;
  } else {
    if (argc-itmp != 1) rb_raise(rb_eArgError, 
			    "wrong number of arguments (%d for %d)", argc, 2-itmp);
    Data_Get_Struct(vtmp, gsl_matrix, A);
    QR = make_matrix_clone(A);
    size0 = GSL_MIN(QR->size1, QR->size2);
    flagq = 1;
    p = gsl_permutation_alloc(size0);
    tau = gsl_vector_alloc(size0);
  }
  norm = gsl_vector_alloc(size0);
  if (TYPE(argv[itmp]) == T_ARRAY) {
    b = make_cvector_from_rarray(argv[itmp]);
    flagb = 1;
  } else {
    CHECK_VECTOR(argv[itmp]);
    Data_Get_Struct(argv[itmp], gsl_vector, b);
  }
  x = gsl_vector_alloc(b->size);
  if (flagq == 1) gsl_linalg_QRPT_decomp(QR, tau, p, &signum, norm);
  gsl_linalg_QRPT_solve(QR, tau, p, b, x);
  if (flagb == 1) gsl_vector_free(b);
  if (flagq == 1) {
    gsl_matrix_free(QR);
    gsl_permutation_free(p);
    gsl_vector_free(tau);
    gsl_vector_free(norm);
  }
  return Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, x);
}


static VALUE rb_gsl_linalg_QRPT_svx(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *QR = NULL, *A = NULL;
  gsl_vector *tau = NULL, *b = NULL, *norm = NULL;
  gsl_permutation *p = NULL;
  int signum, itmp, flagq = 0;
  VALUE vtmp;
  size_t size0;

  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)",
			    argc);
    vtmp = argv[0];
    itmp = 1;
    break;
  default:
    vtmp = obj;
    itmp = 0;
  }
  CHECK_MATRIX(vtmp);
  if (CLASS_OF(vtmp) == cgsl_matrix_QR2) {
    if (argc-itmp != 3) rb_raise(rb_eArgError, 
				 "wrong number of arguments (%d for %d)", 
				 argc, 3+itmp);
    CHECK_VECTOR(argv[itmp]);
    if (CLASS_OF(argv[itmp]) != cgsl_vector_tau2) 
      rb_raise(rb_eTypeError, "not a tau vector");
    CHECK_PERMUTATION(argv[itmp+1]);
    Data_Get_Struct(argv[itmp], gsl_vector, tau);
    Data_Get_Struct(argv[itmp+1], gsl_permutation, p);
    Data_Get_Struct(vtmp, gsl_matrix, QR);
    size0 = GSL_MIN(QR->size1, QR->size2);
    itmp += 2;
  } else {
    if (argc-itmp != 1) rb_raise(rb_eArgError, 
			    "wrong number of arguments (%d for %d)", argc, 2+itmp);
    Data_Get_Struct(vtmp, gsl_matrix, A);
    QR = make_matrix_clone(A);
    size0 = GSL_MIN(QR->size1, QR->size2);
    flagq = 1;
    p = gsl_permutation_alloc(size0);
    tau = gsl_vector_alloc(size0);
  }
  norm = gsl_vector_alloc(size0);
  CHECK_VECTOR(argv[itmp]);
  Data_Get_Struct(argv[itmp], gsl_vector, b);
  if (flagq == 1) gsl_linalg_QRPT_decomp(QR, tau, p, &signum, norm);
  gsl_linalg_QRPT_svx(QR, tau, p, b);
  if (flagq == 1) {
    gsl_matrix_free(QR);
    gsl_permutation_free(p);
    gsl_vector_free(tau);
    gsl_vector_free(norm);
  }
  return argv[itmp];
}

static VALUE rb_gsl_linalg_QRPT_QRsolve(VALUE obj, VALUE qq, VALUE rr, 
					VALUE pp, VALUE bb)
{
  gsl_matrix *Q = NULL, *R = NULL;
  gsl_vector *b = NULL, *x = NULL;
  gsl_permutation *p = NULL;
  int flagb = 0;

  if (CLASS_OF(qq) != cgsl_matrix_Q2) rb_raise(rb_eTypeError, "not a Q matrix");
  if (CLASS_OF(rr) != cgsl_matrix_R2) rb_raise(rb_eTypeError, "not a R matrix");
  if (TYPE(bb) == T_ARRAY) {
    b = make_cvector_from_rarray(bb);
    flagb = 1;
  } else {
    CHECK_VECTOR(bb);
    Data_Get_Struct(bb, gsl_vector, b);
  }
  CHECK_PERMUTATION(pp);
  Data_Get_Struct(qq, gsl_matrix, Q);
  Data_Get_Struct(rr, gsl_matrix, R);
  Data_Get_Struct(pp, gsl_permutation, p);
  x = gsl_vector_alloc(b->size);

  gsl_linalg_QRPT_QRsolve(Q, R, p, b, x);
  if (flagb == 1) gsl_vector_free(b);
  return Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, x);
}

static VALUE rb_gsl_linalg_QRPT_update(VALUE obj, VALUE qq, VALUE rr, 
				       VALUE pp, VALUE ww, VALUE vv)
{
  gsl_matrix *Q = NULL, *R = NULL;
  gsl_vector *w = NULL, *v = NULL;
  gsl_permutation *p = NULL;

  if (CLASS_OF(qq) != cgsl_matrix_Q2) rb_raise(rb_eTypeError, "not a Q matrix");
  if (CLASS_OF(rr) != cgsl_matrix_R2) rb_raise(rb_eTypeError, "not a R matrix");
  CHECK_PERMUTATION(pp);
  Data_Get_Struct(qq, gsl_matrix, Q);
  Data_Get_Struct(rr, gsl_matrix, R);
  Data_Get_Struct(pp, gsl_permutation, p);
  Data_Get_Struct(ww, gsl_vector, w);
  Data_Get_Struct(vv, gsl_vector, v);
  gsl_linalg_QRPT_update(Q, R, p, w, v);
  return obj;
}

static VALUE rb_gsl_linalg_QRPT_Rsolve(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *QR = NULL;
  gsl_vector *b = NULL, *x = NULL;
  gsl_permutation *p = NULL;
  int itmp, flagb = 0;
  VALUE vtmp;

  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)",
			    argc);
    vtmp = argv[0];
    itmp = 1;
    break;
  default:
    vtmp = obj;
    itmp = 0;
  }
  if (argc-itmp != 2)
    rb_raise(rb_eArgError, "wrong number of argument (%d for %d)", argc, 2+itmp);
  CHECK_MATRIX(vtmp);
  if (CLASS_OF(vtmp) != cgsl_matrix_QR2) {
    rb_raise(rb_eArgError, "not a QR matrix");
  }
  CHECK_PERMUTATION(argv[itmp]);
  Data_Get_Struct(argv[itmp], gsl_permutation, p);
  Data_Get_Struct(vtmp, gsl_matrix, QR);
  itmp++;
  if (TYPE(argv[itmp]) == T_ARRAY) {
    b = make_cvector_from_rarray(argv[itmp]);
    flagb = 1;
  } else {
    CHECK_VECTOR(argv[itmp]);
    Data_Get_Struct(argv[itmp], gsl_vector, b);
  }
  x = gsl_vector_alloc(b->size);
  gsl_linalg_QRPT_Rsolve(QR, p, b, x);
  if (flagb == 1) gsl_vector_free(b);
  return Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, x);
}


static VALUE rb_gsl_linalg_QRPT_Rsvx(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *QR = NULL;
  gsl_vector *b = NULL;
  gsl_permutation *p = NULL;
  int itmp, flagb = 0;
  VALUE vtmp;

  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)",
			    argc);
    vtmp = argv[0];
    itmp = 1;
    break;
  default:
    vtmp = obj;
    itmp = 0;
  }
  if (argc-itmp != 2)
    rb_raise(rb_eArgError, "wrong number of argument (%d for %d)", argc, 2+itmp);
  CHECK_MATRIX(vtmp);
  if (CLASS_OF(vtmp) != cgsl_matrix_QR2) {
    rb_raise(rb_eArgError, "not a QR matrix");
  }
  CHECK_PERMUTATION(argv[itmp]);
  Data_Get_Struct(argv[itmp], gsl_permutation, p);
  Data_Get_Struct(vtmp, gsl_matrix, QR);
  itmp++;
  if (TYPE(argv[itmp]) == T_ARRAY) {
    b = make_cvector_from_rarray(argv[itmp]);
    flagb = 1;
  } else {
    CHECK_VECTOR(argv[itmp]);
    Data_Get_Struct(argv[itmp], gsl_vector, b);
  }
  gsl_linalg_QRPT_Rsvx(QR, p, b);
  return argv[itmp];
}

static VALUE rb_gsl_linalg_SV_decomp(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *A = NULL, *V = NULL, *U = NULL;
  gsl_vector *w = NULL, *S = NULL;
  VALUE vs, vv, vu;
  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 1) rb_raise(rb_eArgError,
			    "wrong number of argument (%d for 1)", argc);
    CHECK_MATRIX(argv[0]);
    Data_Get_Struct(argv[0], gsl_matrix, A);
    break;
  default:
    Data_Get_Struct(obj, gsl_matrix, A);
  }
  U = make_matrix_clone(A);
  S = gsl_vector_alloc(A->size2);   /* see manual p 123 */
  V = gsl_matrix_alloc(A->size2, A->size2);
  w = gsl_vector_alloc(A->size2);
  gsl_linalg_SV_decomp(U, V, S, w);
  gsl_vector_free(w);
  vu = Data_Wrap_Struct(cgsl_matrix_U, 0, gsl_matrix_free, U);
  vv = Data_Wrap_Struct(cgsl_matrix_V, 0, gsl_matrix_free, V);
  vs = Data_Wrap_Struct(cgsl_vector_S, 0, gsl_vector_free, S);
  return rb_ary_new3(3, vu, vv, vs);
}

static VALUE rb_gsl_linalg_SV_decomp_mod(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *A = NULL, *V = NULL, *U = NULL, *X = NULL;
  gsl_vector *w = NULL, *S = NULL;
  VALUE vs, vv, vu;
  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 1) rb_raise(rb_eArgError,
			    "wrong number of argument (%d for 1)", argc);
    CHECK_MATRIX(argv[0]);
    Data_Get_Struct(argv[0], gsl_matrix, A);
    break;
  default:
    Data_Get_Struct(obj, gsl_matrix, A);
  }
  U = make_matrix_clone(A);
  S = gsl_vector_alloc(A->size2);   /* see manual p 123 */
  V = gsl_matrix_alloc(A->size2, A->size2);
  X = gsl_matrix_alloc(A->size2, A->size2);
  w = gsl_vector_alloc(A->size2);
  gsl_linalg_SV_decomp_mod(U, X, V, S, w);
  gsl_vector_free(w);
  gsl_matrix_free(X);
  vu = Data_Wrap_Struct(cgsl_matrix_U, 0, gsl_matrix_free, U);
  vv = Data_Wrap_Struct(cgsl_matrix_V, 0, gsl_matrix_free, V);
  vs = Data_Wrap_Struct(cgsl_vector_S, 0, gsl_vector_free, S);
  return rb_ary_new3(3, vu, vv, vs);
}

static VALUE rb_gsl_linalg_SV_decomp_jacobi(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *A = NULL, *V = NULL, *U = NULL;
  gsl_vector *S = NULL;
  VALUE vs, vv, vu;
  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 1) rb_raise(rb_eArgError,
			    "wrong number of argument (%d for 1)", argc);
    CHECK_MATRIX(argv[0]);
    Data_Get_Struct(argv[0], gsl_matrix, A);
    break;
  default:
    Data_Get_Struct(obj, gsl_matrix, A);
  }
  U = make_matrix_clone(A);
  S = gsl_vector_alloc(A->size2);   /* see manual p 123 */
  V = gsl_matrix_alloc(A->size2, A->size2);
  gsl_linalg_SV_decomp_jacobi(U, V, S);
  vu = Data_Wrap_Struct(cgsl_matrix_U, 0, gsl_matrix_free, U);
  vv = Data_Wrap_Struct(cgsl_matrix_V, 0, gsl_matrix_free, V);
  vs = Data_Wrap_Struct(cgsl_vector_S, 0, gsl_vector_free, S);
  return rb_ary_new3(3, vu, vv, vs);
}

static VALUE rb_gsl_linalg_SV_solve(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *A = NULL, *U = NULL, *V = NULL;
  gsl_vector *S = NULL, *b = NULL, *x = NULL;
  int flagb = 0, flagv = 0;
  
  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    CHECK_MATRIX(argv[0]);
    if (CLASS_OF(argv[0]) == cgsl_matrix_U) {
      if (argc != 4) rb_raise(rb_eArgError, 
			      "wrong number of arguments (%d for 4)", argc);
      Data_Get_Struct(argv[0], gsl_matrix, U);
      CHECK_MATRIX(argv[1]);
      if (CLASS_OF(argv[1]) != cgsl_matrix_V) 
	rb_raise(rb_eTypeError, "not a V matrix");
      Data_Get_Struct(argv[1], gsl_matrix, V);
      CHECK_VECTOR(argv[2]);
      if (CLASS_OF(argv[2]) != cgsl_vector_S) 
	rb_raise(rb_eTypeError, "not a S vector");
      Data_Get_Struct(argv[2], gsl_vector, S);
      if (TYPE(argv[3]) == T_ARRAY) {
	b = make_cvector_from_rarray(argv[3]);
	flagb = 1;
      } else {
	CHECK_VECTOR(argv[3]);
	Data_Get_Struct(argv[3], gsl_vector, b);
      }
    } else {
      if (argc != 2) rb_raise(rb_eArgError, 
			      "wrong number of arguments (%d for 2)", argc);
      Data_Get_Struct(argv[0], gsl_matrix, A);
      U = make_matrix_clone(A);
      if (TYPE(argv[1]) == T_ARRAY) {
	b = make_cvector_from_rarray(argv[1]);
	flagb = 1;
      } else {
	CHECK_VECTOR(argv[1]);
	Data_Get_Struct(argv[1], gsl_vector, b);
      }
      S = gsl_vector_alloc(A->size2);   /* see manual p 123 */
      V = gsl_matrix_alloc(A->size2, A->size2);
      gsl_linalg_SV_decomp_jacobi(U, V, S);
      flagv = 1;
    }
    break;
  default:
    if (argc != 1) rb_raise(rb_eArgError, 
			    "wrong number of arguments (%d for 1)", argc);
    Data_Get_Struct(obj, gsl_matrix, A);
    U = make_matrix_clone(A);
    if (TYPE(argv[0]) == T_ARRAY) {
      b = make_cvector_from_rarray(argv[0]);
      flagb = 1;
    } else {
      CHECK_VECTOR(argv[0]);
      Data_Get_Struct(argv[0], gsl_vector, b);
    }
    S = gsl_vector_alloc(A->size2);   /* see manual p 123 */
    V = gsl_matrix_alloc(A->size2, A->size2);
    gsl_linalg_SV_decomp_jacobi(U, V, S);
    flagv = 1;
  }
  x = gsl_vector_alloc(b->size);
  gsl_linalg_SV_solve(U, V, S, b, x);
  if (flagv == 1) {
    gsl_matrix_free(U);
    gsl_matrix_free(V);
    gsl_vector_free(S);
  }
  if (flagb == 1) gsl_vector_free(b);
  return Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, x);
}

/*****/

static VALUE rb_gsl_linalg_cholesky_decomp(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *A = NULL, *Atmp = NULL;
  switch(TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of argument (%d for 1)",
			    argc);
    CHECK_MATRIX(argv[0]);
    Data_Get_Struct(argv[0], gsl_matrix, Atmp);
    break;
  default:
    CHECK_MATRIX(obj);
    Data_Get_Struct(obj, gsl_matrix, Atmp);
  }
  A = make_matrix_clone(Atmp);
  gsl_linalg_cholesky_decomp(A);
  return Data_Wrap_Struct(cgsl_matrix_C, 0, gsl_matrix_free, A);
}

static VALUE rb_gsl_linalg_cholesky_solve(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *A = NULL, *Atmp = NULL;
  gsl_vector *b = NULL, *x = NULL;
  int flagb = 0, flaga = 0;
  VALUE vA, vb;
  switch(TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 2) rb_raise(rb_eArgError, "wrong number of argument (%d for 2)",
			    argc);
    vA = argv[0];
    vb = argv[1];
    break;
  default:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of argument (%d for 1)",
			    argc);
    vA = obj;
    vb = argv[0];
  }
  CHECK_MATRIX(vA);
  Data_Get_Struct(vA, gsl_matrix, Atmp);
  if (TYPE(vb) == T_ARRAY) {
    b = make_cvector_from_rarray(vb);
    flagb = 1;
  } else {
    CHECK_VECTOR(vb);
    Data_Get_Struct(vb, gsl_vector, b);
  }
  if (CLASS_OF(vA) == cgsl_matrix_C) {
    A = Atmp;
  } else {
    A = make_matrix_clone(Atmp);
    flaga = 1;
    gsl_linalg_cholesky_decomp(A);
  }
  x = gsl_vector_alloc(b->size);
  gsl_linalg_cholesky_solve(A, b, x);
  if (flaga == 1) gsl_matrix_free(A);
  if (flagb == 1) gsl_vector_free(b);
  return Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, x);
}


static VALUE rb_gsl_linalg_cholesky_svx(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *A = NULL, *Atmp = NULL;
  gsl_vector *b = NULL;
  int flaga = 0;
  VALUE vA, vb;
  switch(TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 2) rb_raise(rb_eArgError, "wrong number of argument (%d for 2)",
			    argc);
    vA = argv[0];
    vb = argv[1];
    break;
  default:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of argument (%d for 1)",
			    argc);
    vA = obj;
    vb = argv[0];
  }
  CHECK_MATRIX(vA);
  Data_Get_Struct(vA, gsl_matrix, Atmp);
  CHECK_VECTOR(vb);
  Data_Get_Struct(vb, gsl_vector, b);
  if (CLASS_OF(vA) == cgsl_matrix_C) {
    A = Atmp;
  } else {
    A = make_matrix_clone(Atmp);
    flaga = 1;
    gsl_linalg_cholesky_decomp(A);
  }
  gsl_linalg_cholesky_svx(A, b);
  if (flaga == 1) gsl_matrix_free(A);
  return vb;
}

static VALUE rb_gsl_linalg_symmtd_decomp(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *A = NULL, *Atmp = NULL;
  gsl_vector *tau = NULL;
  VALUE vQ, vtau;
  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of argument (%d for 1)",
			    argc);
    CHECK_MATRIX(argv[0]);
    Data_Get_Struct(argv[0], gsl_matrix, Atmp);
    break;
  default:
    CHECK_MATRIX(obj);
    Data_Get_Struct(obj, gsl_matrix, Atmp);
  }
  A = make_matrix_clone(Atmp);
  tau = gsl_vector_alloc(A->size1);
  gsl_linalg_symmtd_decomp(A, tau);
  vQ = Data_Wrap_Struct(cgsl_matrix_Q3, 0, gsl_matrix_free, A);
  vtau = Data_Wrap_Struct(cgsl_vector_tau3, 0, gsl_vector_free, tau);
  return rb_ary_new3(2, vQ, vtau);
}


static VALUE rb_gsl_linalg_symmtd_decomp2(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *A = NULL;
  gsl_vector *tau = NULL;
  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of argument (%d for 1)",
			    argc);
    CHECK_MATRIX(argv[0]);
    Data_Get_Struct(argv[0], gsl_matrix, A);
    break;
  default:
    CHECK_MATRIX(obj);
    Data_Get_Struct(obj, gsl_matrix, A);
  }
  tau = gsl_vector_alloc(A->size1);
  gsl_linalg_symmtd_decomp(A, tau);
  return Data_Wrap_Struct(cgsl_vector_tau3, 0, gsl_vector_free, tau);
}

static VALUE rb_gsl_linalg_symmtd_unpack(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *A = NULL, *Q = NULL;
  gsl_vector *tau = NULL, *d = NULL, *sd = NULL;
  VALUE vq, vd, vsd;
  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 2) rb_raise(rb_eArgError, "wrong number of argument (%d for 1)",
			    argc);
    CHECK_MATRIX(argv[0]);
    Data_Get_Struct(argv[0], gsl_matrix, A);
    Data_Get_Struct(argv[1], gsl_vector, tau);
    break;
  default:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of argument (%d for 1)",
			    argc);
    CHECK_MATRIX(obj);
    Data_Get_Struct(obj, gsl_matrix, A);
    Data_Get_Struct(argv[0], gsl_vector, tau);
  }
  Q = gsl_matrix_alloc(A->size1, A->size2);
  d = gsl_vector_alloc(tau->size);
  sd = gsl_vector_alloc(tau->size);
  gsl_linalg_symmtd_unpack(A, tau, Q, d, sd);

  vq = Data_Wrap_Struct(cgsl_matrix_Q3, 0, gsl_matrix_free, Q);
  vd = Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, d);
  vsd = Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, sd);

  return rb_ary_new3(3, vq, vd, vsd);
}

static VALUE rb_gsl_linalg_symmtd_unpack_T(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *A = NULL;
  gsl_vector *d = NULL, *sd = NULL;
  VALUE vd, vsd;
  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of argument (%d for 1)",
			    argc);
    CHECK_MATRIX(argv[0]);
    Data_Get_Struct(argv[0], gsl_matrix, A);
    break;
  default:
    Data_Get_Struct(obj, gsl_matrix, A);
  }
  d = gsl_vector_alloc(A->size1);
  sd = gsl_vector_alloc(A->size1);
  gsl_linalg_symmtd_unpack_T(A, d, sd);

  vd = Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, d);
  vsd = Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, sd);

  return rb_ary_new3(2, vd, vsd);
}

/*****/

static VALUE rb_gsl_linalg_hermtd_decomp(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix_complex *A = NULL, *Atmp = NULL;
  gsl_vector_complex *tau = NULL;
  VALUE vQ, vtau;
  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of argument (%d for 1)",
			    argc);
    CHECK_MATRIX_COMPLEX(argv[0]);
    Data_Get_Struct(argv[0], gsl_matrix_complex, Atmp);
    break;
  default:
    CHECK_MATRIX_COMPLEX(obj);
    Data_Get_Struct(obj, gsl_matrix_complex, Atmp);
  }
  A = make_matrix_complex_clone(Atmp);
  tau = gsl_vector_complex_alloc(A->size1);
  gsl_linalg_hermtd_decomp(A, tau);
  vQ = Data_Wrap_Struct(cgsl_matrix_complex, 0, gsl_matrix_complex_free, A);
  vtau = Data_Wrap_Struct(cgsl_vector_complex, 0, gsl_vector_complex_free, tau);
  return rb_ary_new3(2, vQ, vtau);
}

static VALUE rb_gsl_linalg_hermtd_decomp2(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix_complex *A = NULL;
  gsl_vector_complex *tau = NULL;
  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of argument (%d for 1)",
			    argc);
    CHECK_MATRIX_COMPLEX(argv[0]);
    Data_Get_Struct(argv[0], gsl_matrix_complex, A);
    break;
  default:
    CHECK_MATRIX_COMPLEX(obj);
    Data_Get_Struct(obj, gsl_matrix_complex, A);
  }
  tau = gsl_vector_complex_alloc(A->size1);
  gsl_linalg_hermtd_decomp(A, tau);
  return Data_Wrap_Struct(cgsl_vector_complex, 0, gsl_vector_complex_free, tau);
}

static VALUE rb_gsl_linalg_hermtd_unpack(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix_complex *A = NULL, *Q = NULL;
  gsl_vector_complex *tau = NULL;
  gsl_vector *d = NULL, *sd = NULL;
  VALUE vq, vd, vsd;
  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 2) rb_raise(rb_eArgError, "wrong number of argument (%d for 1)",
			    argc);
    CHECK_MATRIX_COMPLEX(argv[0]);
    Data_Get_Struct(argv[0], gsl_matrix_complex, A);
    Data_Get_Struct(argv[1], gsl_vector_complex, tau);
    break;
  default:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of argument (%d for 1)",
			    argc);
    CHECK_MATRIX_COMPLEX(obj);
    Data_Get_Struct(obj, gsl_matrix_complex, A);
    Data_Get_Struct(argv[0], gsl_vector_complex, tau);
  }
  Q = gsl_matrix_complex_alloc(A->size1, A->size2);
  d = gsl_vector_alloc(tau->size);
  sd = gsl_vector_alloc(tau->size);
  gsl_linalg_hermtd_unpack(A, tau, Q, d, sd);

  vq = Data_Wrap_Struct(cgsl_matrix_complex, 0, gsl_matrix_complex_free, Q);
  vd = Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, d);
  vsd = Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, sd);

  return rb_ary_new3(3, vq, vd, vsd);
}

static VALUE rb_gsl_linalg_hermtd_unpack_T(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix_complex *A = NULL;
  gsl_vector *d = NULL, *sd = NULL;
  VALUE vd, vsd;
  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of argument (%d for 1)",
			    argc);
    CHECK_MATRIX_COMPLEX(argv[0]);
    Data_Get_Struct(argv[0], gsl_matrix_complex, A);
    break;
  default:
    Data_Get_Struct(obj, gsl_matrix_complex, A);
  }
  d = gsl_vector_alloc(A->size1);
  sd = gsl_vector_alloc(A->size1);
  gsl_linalg_hermtd_unpack_T(A, d, sd);

  vd = Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, d);
  vsd = Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, sd);

  return rb_ary_new3(2, vd, vsd);
}

/******/

static VALUE rb_gsl_linalg_bidiag_decomp(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *A = NULL, *Atmp = NULL;
  gsl_vector *tau_U = NULL, *tau_V = NULL;
  size_t size0;
  int status;
  VALUE vu, vv, vA;

  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)",
			    argc);
    Data_Get_Struct(argv[0], gsl_matrix, Atmp);
    break;
  default:
    Data_Get_Struct(obj, gsl_matrix, Atmp);
  }
  A = make_matrix_clone(Atmp);
  size0 = GSL_MIN(A->size1, A->size2);
  tau_U = gsl_vector_alloc(size0);
  tau_V = gsl_vector_alloc(size0-1);
  status = gsl_linalg_bidiag_decomp(A, tau_U, tau_V);
  vA = Data_Wrap_Struct(cgsl_matrix, 0, gsl_matrix_free, A);
  vu = Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, tau_U);
  vv = Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, tau_V);
  return rb_ary_new3(3, vA, vu, vv);
}

static VALUE rb_gsl_linalg_bidiag_decomp2(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *A = NULL;
  gsl_vector *tau_U = NULL, *tau_V = NULL;
  size_t size0;
  VALUE vu, vv;

  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)",
			    argc);
    Data_Get_Struct(argv[0], gsl_matrix, A);
    break;
  default:
    Data_Get_Struct(obj, gsl_matrix, A);
  }
  size0 = GSL_MIN(A->size1, A->size2);
  tau_U = gsl_vector_alloc(size0);
  tau_V = gsl_vector_alloc(size0-1);
  gsl_linalg_bidiag_decomp(A, tau_U, tau_V);
  vu = Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, tau_U);
  vv = Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, tau_V);
  return rb_ary_new3(2, vu, vv);
}

static VALUE rb_gsl_linalg_bidiag_unpack(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *A = NULL, *U = NULL, *V = NULL;
  gsl_vector *tau_U = NULL, *tau_V = NULL, *d = NULL, *s = NULL;
  size_t size0;
  VALUE vu, vv, vd, vs;

  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 3) rb_raise(rb_eArgError, "wrong number of arguments (%d for 3)",
			    argc);
    CHECK_MATRIX(argv[0]);
    CHECK_VECTOR(argv[1]);
    CHECK_VECTOR(argv[2]);
    Data_Get_Struct(argv[0], gsl_matrix, A);
    Data_Get_Struct(argv[1], gsl_vector, tau_U);
    Data_Get_Struct(argv[2], gsl_vector, tau_V);
    break;
  default:
    if (argc != 2) rb_raise(rb_eArgError, "wrong number of arguments (%d for 2)",
			    argc);
    CHECK_MATRIX(obj);
    CHECK_VECTOR(argv[0]);
    CHECK_VECTOR(argv[1]);
    Data_Get_Struct(obj, gsl_matrix, A);
    Data_Get_Struct(argv[0], gsl_vector, tau_U);
    Data_Get_Struct(argv[1], gsl_vector, tau_V);
  }
  U = gsl_matrix_alloc(A->size1, A->size2);
  V = gsl_matrix_alloc(A->size2, A->size2);
  size0 = GSL_MIN(A->size1, A->size2);
  d = gsl_vector_alloc(size0);
  s = gsl_vector_alloc(size0);
  gsl_linalg_bidiag_unpack(A, tau_U, U, tau_V, V, d, s);
  vu = Data_Wrap_Struct(cgsl_matrix, 0, gsl_matrix_free, U);
  vv = Data_Wrap_Struct(cgsl_matrix, 0, gsl_matrix_free, V);
  vd = Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, d);
  vs = Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, s);
  return rb_ary_new3(4, vu, vv, vd, vs);
}

static VALUE rb_gsl_linalg_bidiag_unpack2(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *A = NULL, *V = NULL;
  gsl_vector *tau_V = NULL, *tau_U = NULL;
  VALUE vv;

  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
       if (argc != 3) rb_raise(rb_eArgError, "wrong number of arguments (%d for 3)",
			    argc);
    CHECK_MATRIX(argv[0]);
    CHECK_VECTOR(argv[1]);
    CHECK_VECTOR(argv[2]);
    Data_Get_Struct(argv[0], gsl_matrix, A);
    Data_Get_Struct(argv[1], gsl_vector, tau_U);
    Data_Get_Struct(argv[2], gsl_vector, tau_V);
    break;
  default:
    if (argc != 2) rb_raise(rb_eArgError, "wrong number of arguments (%d for 2)",
			    argc);
    CHECK_MATRIX(obj);
    CHECK_VECTOR(argv[0]);
    CHECK_VECTOR(argv[1]);
    Data_Get_Struct(obj, gsl_matrix, A);
    Data_Get_Struct(argv[0], gsl_vector, tau_U);
    Data_Get_Struct(argv[1], gsl_vector, tau_V);
  } 
  V = gsl_matrix_alloc(A->size2, A->size2);
  gsl_linalg_bidiag_unpack2(A, tau_U, tau_V, V);
  vv = Data_Wrap_Struct(cgsl_matrix, 0, gsl_matrix_free, V);
  return vv;
}

static VALUE rb_gsl_linalg_bidiag_unpack_B(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *A = NULL;
  gsl_vector *d = NULL, *s = NULL;
  size_t size0;
  VALUE vd, vs;

  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of arguments (%d for 3)",
			    argc);
    CHECK_MATRIX(argv[0]);
    Data_Get_Struct(argv[0], gsl_matrix, A);
    break;
  default:
    CHECK_MATRIX(obj);
    Data_Get_Struct(obj, gsl_matrix, A);
  } 
  size0 = GSL_MIN(A->size1, A->size2);
  d = gsl_vector_alloc(size0);
  s = gsl_vector_alloc(size0);
  gsl_linalg_bidiag_unpack_B(A, d, s);
  vd = Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, d);
  vs = Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, s);
  return rb_ary_new3(2, vd, vs);
}

/* Householder Transformations 11.Jul.2004 */
static VALUE rb_gsl_linalg_householder_transform(int argc, VALUE *argv, VALUE obj)
{
  gsl_vector *v = NULL;
  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    CHECK_VECTOR(argv[0]);
    Data_Get_Struct(argv[0], gsl_vector, v);
    break;
  default:
    Data_Get_Struct(obj, gsl_vector, v);
  }
  return rb_float_new(gsl_linalg_householder_transform(v));
}

/* singleton */
static VALUE rb_gsl_linalg_householder_hm(VALUE obj, VALUE t, VALUE vv, VALUE aa)
{
  gsl_vector *v = NULL;
  double tau;
  gsl_matrix *A = NULL;
  CHECK_VECTOR(vv);
  CHECK_MATRIX(aa);
  tau = NUM2DBL(t);
  Data_Get_Struct(vv, gsl_vector, v);
  Data_Get_Struct(aa, gsl_matrix, A);
  gsl_linalg_householder_hm(tau, v, A);
  return aa;
}

static VALUE rb_gsl_linalg_householder_mh(VALUE obj, VALUE t, VALUE vv, VALUE aa)
{
  gsl_vector *v = NULL;
  double tau;
  gsl_matrix *A = NULL;
  CHECK_VECTOR(vv);
  CHECK_MATRIX(aa);
  tau = NUM2DBL(t);
  Data_Get_Struct(vv, gsl_vector, v);
  Data_Get_Struct(aa, gsl_matrix, A);
  gsl_linalg_householder_mh(tau, v, A);
  return aa;
}

static VALUE rb_gsl_linalg_householder_hv(VALUE obj, VALUE t, VALUE vv, VALUE ww)
{
  gsl_vector *v = NULL, *w = NULL;
  double tau;
  CHECK_VECTOR(vv);
  CHECK_VECTOR(ww);
  tau = NUM2DBL(t);
  Data_Get_Struct(vv, gsl_vector, v);
  Data_Get_Struct(ww, gsl_vector, w);
  gsl_linalg_householder_hv(tau, v, w);
  return ww;
}

/* 17.Apr.2004 */
static VALUE rb_gsl_linalg_HH_solve(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *A = NULL, *Atmp = NULL;
  gsl_vector *b = NULL, *x = NULL;
  int flagb = 0;
  VALUE vA, vb;
  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 2) rb_raise(rb_eArgError, "wrong number of argument (%d for 2)",
			    argc);
    vA = argv[0];
    vb = argv[1];
    break;
  default:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of argument (%d for 1)",
			    argc);
    vA = obj;
    vb = argv[0];
  }
  CHECK_MATRIX(vA);
  Data_Get_Struct(vA, gsl_matrix, Atmp);
  if (TYPE(vb) == T_ARRAY) {
    b = make_cvector_from_rarray(vb);
    flagb = 1;
  } else {
    CHECK_VECTOR(vb);
    Data_Get_Struct(vb, gsl_vector, b);
  }
  A = make_matrix_clone(Atmp);
  x = gsl_vector_alloc(b->size);
  gsl_linalg_HH_solve(A, b, x);
  gsl_matrix_free(A);
  if (flagb == 1) gsl_vector_free(b);
  return Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, x);
}

static VALUE rb_gsl_linalg_HH_svx(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *A = NULL, *Atmp = NULL;
  gsl_vector *b = NULL;
  VALUE vA, vb;
  switch (TYPE(obj)) {
  case T_MODULE:
  case T_CLASS:
  case T_OBJECT:
    if (argc != 2) rb_raise(rb_eArgError, "wrong number of argument (%d for 2)",
			    argc);
    vA = argv[0];
    vb = argv[1];
    break;
  default:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of argument (%d for 1)",
			    argc);
    vA = obj;
    vb = argv[0];
  }
  CHECK_MATRIX(vA);
  Data_Get_Struct(vA, gsl_matrix, Atmp);
  CHECK_VECTOR(vb);
  Data_Get_Struct(vb, gsl_vector, b);
  A = make_matrix_clone(Atmp);
  gsl_linalg_HH_svx(A, b);
  gsl_matrix_free(A);
  return vb;
}

static VALUE rb_gsl_linalg_solve_symm_tridiag(VALUE obj, VALUE dd, VALUE ee, VALUE bb)
{
  gsl_vector *b = NULL, *x = NULL, *d = NULL, *e = NULL;

  Data_Get_Struct(dd, gsl_vector, d);
  Data_Get_Struct(ee, gsl_vector, e);
  Data_Get_Struct(bb, gsl_vector, b);
  x = gsl_vector_alloc(b->size);

  gsl_linalg_solve_symm_tridiag(d, e, b, x);
  return Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, x);
}

static VALUE rb_gsl_linalg_solve_tridiag(VALUE obj, VALUE dd, VALUE ee, VALUE ff,
					 VALUE bb)
{
  gsl_vector *b = NULL, *x = NULL, *d = NULL, *e = NULL, *f = NULL;

  Data_Get_Struct(dd, gsl_vector, d);
  Data_Get_Struct(ee, gsl_vector, e);
  Data_Get_Struct(ff, gsl_vector, f);
  Data_Get_Struct(bb, gsl_vector, b);
  x = gsl_vector_alloc(b->size);

  gsl_linalg_solve_tridiag(d, e, f, b, x);
  return Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, x);
}

static VALUE rb_gsl_linalg_solve_symm_cyc_tridiag(VALUE obj, VALUE dd, VALUE ee, VALUE bb)
{
  gsl_vector *b = NULL, *x = NULL, *d = NULL, *e = NULL;

  Data_Get_Struct(dd, gsl_vector, d);
  Data_Get_Struct(ee, gsl_vector, e);
  Data_Get_Struct(bb, gsl_vector, b);
  x = gsl_vector_alloc(b->size);

  gsl_linalg_solve_symm_cyc_tridiag(d, e, b, x);
  return Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, x);
}

static VALUE rb_gsl_linalg_solve_cyc_tridiag(VALUE obj, VALUE dd, VALUE ee, 
					     VALUE ff, VALUE bb)
{
  gsl_vector *b = NULL, *x = NULL, *d = NULL, *e = NULL, *f = NULL;

  Data_Get_Struct(dd, gsl_vector, d);
  Data_Get_Struct(ee, gsl_vector, e);
  Data_Get_Struct(ff, gsl_vector, f);
  Data_Get_Struct(bb, gsl_vector, b);
  x = gsl_vector_alloc(b->size);
  gsl_linalg_solve_cyc_tridiag(d, e, f, b, x);
  return Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, x);
}

static VALUE rb_gsl_linalg_balance_columns(int argc, VALUE *argv, VALUE obj)
{
  gsl_matrix *A = NULL;
  gsl_vector *D = NULL;

  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_MATRIX(argv[0]);
    CHECK_VECTOR(argv[1]);
    Data_Get_Struct(argv[0], gsl_matrix, A);
    Data_Get_Struct(argv[1], gsl_vector, D);
    break;
  default:
    if (argc != 1) rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)",
			    argc);
    CHECK_MATRIX(obj);
    CHECK_VECTOR(argv[0]);
    Data_Get_Struct(obj, gsl_matrix, A);
    Data_Get_Struct(argv[0], gsl_vector, D);
  }
  return INT2FIX(gsl_linalg_balance_columns(A, D));
}

void Init_gsl_linalg()
{
  VALUE mgsl_linalg_LU;
  VALUE mgsl_linalg_QR;
  VALUE mgsl_linalg_QRPT;
  VALUE mgsl_linalg_SV;
  VALUE mgsl_linalg_cholesky;
  VALUE mgsl_linalg_symmtd;
  VALUE mgsl_linalg_hermtd;
  VALUE mgsl_linalg_bidiag;
  VALUE mgsl_linalg_tridiag;
  VALUE mgsl_linalg_HH;
  VALUE mgsl_linalg_Householder;

  mgsl_linalg = rb_define_module_under(mgsl, "Linalg");
  mgsl_linalg_LU = rb_define_module_under(mgsl_linalg, "LU");
  cgsl_matrix_LU = rb_define_class_under(mgsl_linalg_LU, "LUMatrix", cgsl_matrix);
  mgsl_linalg_QR = rb_define_module_under(mgsl_linalg, "QR");
  cgsl_matrix_QR = rb_define_class_under(mgsl_linalg_QR, "QRMatrix", cgsl_matrix);
  cgsl_vector_tau = rb_define_class_under(mgsl_linalg_QR, "TauVector", cgsl_vector);
  cgsl_matrix_Q = rb_define_class_under(mgsl_linalg_QR, "QMatrix", cgsl_matrix);
  cgsl_matrix_R = rb_define_class_under(mgsl_linalg_QR, "RMatrix", cgsl_matrix);
  mgsl_linalg_QRPT = rb_define_module_under(mgsl_linalg, "QRPT");
  cgsl_matrix_QR2 = rb_define_class_under(mgsl_linalg_QRPT, "QRMatrix", cgsl_matrix);
  cgsl_matrix_Q2 = rb_define_class_under(mgsl_linalg_QRPT, "QMatrix", cgsl_matrix);
  cgsl_matrix_R2 = rb_define_class_under(mgsl_linalg_QRPT, "RMatrix", cgsl_matrix);
  cgsl_vector_tau2 = rb_define_class_under(mgsl_linalg_QRPT, "TauVector", cgsl_vector);
  mgsl_linalg_SV = rb_define_module_under(mgsl_linalg, "SV");
  cgsl_matrix_SV = rb_define_class_under(mgsl_linalg_SV, "SVMatrix", cgsl_matrix);
  cgsl_matrix_U = rb_define_class_under(mgsl_linalg_SV, "UMatrix", cgsl_matrix);
  cgsl_matrix_V = rb_define_class_under(mgsl_linalg_SV, "VMatrix", cgsl_matrix);
  cgsl_vector_S = rb_define_class_under(mgsl_linalg_SV, "SVector", cgsl_vector);
  mgsl_linalg_cholesky = rb_define_module_under(mgsl_linalg, "Cholesky");
  cgsl_matrix_C = rb_define_class_under(mgsl_linalg_cholesky, "CholeskyMatrix", cgsl_matrix);
  mgsl_linalg_symmtd = rb_define_module_under(mgsl_linalg, "Symmtd");
  cgsl_matrix_Q3 = rb_define_class_under(mgsl_linalg_symmtd, "QMatrix", cgsl_matrix);
  cgsl_vector_tau3 = rb_define_class_under(mgsl_linalg_symmtd, "TauVector", cgsl_vector);

  mgsl_linalg_hermtd = rb_define_module_under(mgsl_linalg, "Hermtd");
  mgsl_linalg_bidiag = rb_define_module_under(mgsl_linalg, "Bidiag");
  mgsl_linalg_tridiag = rb_define_module_under(mgsl_linalg, "Tridiag");

  mgsl_linalg_HH = rb_define_module_under(mgsl_linalg, "HH");
  mgsl_linalg_Householder = rb_define_module_under(mgsl_linalg, "Householder");

  /*****/
  rb_define_singleton_method(mgsl_linalg_LU, "decomp!", rb_gsl_linalg_LU_decomp, -1);
  rb_define_singleton_method(mgsl_linalg_LU, "decomp", rb_gsl_linalg_LU_decomp2, -1);
  rb_define_method(cgsl_matrix, "LU_decomp!", rb_gsl_linalg_LU_decomp, -1);
  rb_define_method(cgsl_matrix, "LU_decomp", rb_gsl_linalg_LU_decomp2, -1);

  rb_define_singleton_method(mgsl_linalg_LU, "solve", rb_gsl_linalg_LU_solve, -1);
  rb_define_method(cgsl_matrix, "LU_solve", rb_gsl_linalg_LU_solve, -1);
  rb_define_method(cgsl_matrix_LU, "solve", rb_gsl_linalg_LU_solve, -1);

  rb_define_singleton_method(mgsl_linalg_LU, "svx", rb_gsl_linalg_LU_svx, -1);
  rb_define_method(cgsl_matrix, "LU_svx", rb_gsl_linalg_LU_svx, -1);
  rb_define_method(cgsl_matrix_LU, "svx", rb_gsl_linalg_LU_svx, -1);

  rb_define_singleton_method(mgsl_linalg_LU, "invert", rb_gsl_linalg_LU_invert, -1);
  rb_define_singleton_method(mgsl_linalg_LU, "inv", rb_gsl_linalg_LU_invert, -1);
  rb_define_singleton_method(mgsl_linalg_LU, "refine", rb_gsl_linalg_LU_refine, 5);

  rb_define_method(cgsl_matrix, "invert", rb_gsl_linalg_LU_invert, -1);
  rb_define_alias(cgsl_matrix, "inv", "invert");

  rb_define_singleton_method(mgsl_linalg_LU, "det", rb_gsl_linalg_LU_det, -1);
  rb_define_method(cgsl_matrix, "LU_det", rb_gsl_linalg_LU_det, -1);
  rb_define_alias(cgsl_matrix, "det", "LU_det");

  rb_define_singleton_method(mgsl_linalg_LU, "lndet", rb_gsl_linalg_LU_lndet, -1);
  rb_define_method(cgsl_matrix, "LU_lndet", rb_gsl_linalg_LU_lndet, -1);
  rb_define_alias(cgsl_matrix, "lndet", "LU_lndet");

  rb_define_singleton_method(mgsl_linalg_LU, "sgndet", rb_gsl_linalg_LU_sgndet, -1);
  rb_define_method(cgsl_matrix, "LU_sgndet", rb_gsl_linalg_LU_sgndet, -1);
  rb_define_alias(cgsl_matrix, "sgndet", "LU_sgndet");

  /*****/
  rb_define_singleton_method(mgsl_linalg_QR, "decomp", rb_gsl_linalg_QR_decomp, -1);
  rb_define_method(cgsl_matrix, "QR_decomp", rb_gsl_linalg_QR_decomp, -1);

  rb_define_singleton_method(mgsl_linalg_QR, "solve", rb_gsl_linalg_QR_solve, -1);
  rb_define_singleton_method(mgsl_linalg_QR, "svx", rb_gsl_linalg_QR_svx, -1);
  rb_define_method(cgsl_matrix, "QR_solve", rb_gsl_linalg_QR_solve, -1);
  rb_define_method(cgsl_matrix_QR, "solve", rb_gsl_linalg_QR_solve, -1);
  rb_define_method(cgsl_matrix, "QR_svx", rb_gsl_linalg_QR_svx, -1);
  rb_define_method(cgsl_matrix_QR, "svx", rb_gsl_linalg_QR_svx, -1);

  rb_define_singleton_method(mgsl_linalg_QR, "lssolve", rb_gsl_linalg_QR_lssolve, -1);
  rb_define_method(cgsl_matrix, "QR_lssolve", rb_gsl_linalg_QR_lssolve, -1);
  rb_define_method(cgsl_matrix_QR, "lssolve", rb_gsl_linalg_QR_lssolve, -1);

  rb_define_singleton_method(mgsl_linalg_QR, "QTvec", rb_gsl_linalg_QR_QTvec, -1);
  rb_define_method(cgsl_matrix_QR, "QTvec", rb_gsl_linalg_QR_QTvec, -1);
 rb_define_singleton_method(mgsl_linalg_QR, "Qvec", rb_gsl_linalg_QR_Qvec, -1);
  rb_define_method(cgsl_matrix_QR, "Qvec", rb_gsl_linalg_QR_Qvec, -1);


  rb_define_method(cgsl_matrix, "QR_Rsolve", rb_gsl_linalg_QR_Rsolve, -1);
  rb_define_method(cgsl_matrix_QR, "Rsolve", rb_gsl_linalg_QR_Rsolve, -1);

  rb_define_method(cgsl_matrix_QR, "Rsvx", rb_gsl_linalg_QR_Rsvx, 1);

  rb_define_singleton_method(mgsl_linalg_QR, "unpack", rb_gsl_linalg_QR_unpack, -1);
  rb_define_method(cgsl_matrix_QR, "unpack", rb_gsl_linalg_QR_unpack, -1);

  rb_define_singleton_method(mgsl_linalg_QR, "QRsolve", rb_gsl_linalg_QR_QRsolve, 3);
  rb_define_singleton_method(mgsl_linalg_QR, "update", rb_gsl_linalg_QR_update, 4);

  rb_define_method(cgsl_matrix_R, "solve", rb_gsl_linalg_R_solve, 1);
  /*
  rb_define_method(cgsl_matrix_R, "svx", rb_gsl_linalg_R_svx, -1);
  */
  rb_define_singleton_method(mgsl_linalg_QRPT, "decomp", rb_gsl_linalg_QRPT_decomp, -1);
  rb_define_method(cgsl_matrix, "QRPT_decomp", rb_gsl_linalg_QRPT_decomp, -1);
  rb_define_singleton_method(mgsl_linalg_QRPT, "decomp!", rb_gsl_linalg_QRPT_decomp3, -1);
  rb_define_method(cgsl_matrix, "QRPT_decomp!", rb_gsl_linalg_QRPT_decomp3, -1);
 rb_define_singleton_method(mgsl_linalg_QRPT, "decomp2", rb_gsl_linalg_QRPT_decomp2, -1);
  rb_define_method(cgsl_matrix, "QRPT_decomp2", rb_gsl_linalg_QRPT_decomp2, -1);

  rb_define_singleton_method(mgsl_linalg_QRPT, "solve", rb_gsl_linalg_QRPT_solve, -1);
  rb_define_method(cgsl_matrix, "QRPT_solve", rb_gsl_linalg_QRPT_solve, -1);
  rb_define_method(cgsl_matrix_QR2, "solve", rb_gsl_linalg_QRPT_solve, -1);

  rb_define_singleton_method(mgsl_linalg_QRPT, "svx", rb_gsl_linalg_QRPT_svx, -1);
  rb_define_method(cgsl_matrix, "QRPT_svx", rb_gsl_linalg_QRPT_svx, -1);
  rb_define_method(cgsl_matrix_QR2, "svx", rb_gsl_linalg_QRPT_svx, -1);

  rb_define_singleton_method(mgsl_linalg_QRPT, "QRsolve", rb_gsl_linalg_QRPT_QRsolve, 4);
  rb_define_singleton_method(mgsl_linalg_QRPT, "update", rb_gsl_linalg_QRPT_update, 5);

  rb_define_singleton_method(mgsl_linalg_QRPT, "Rsolve", rb_gsl_linalg_QRPT_Rsolve, -1);
  rb_define_method(cgsl_matrix_QR2, "Rsolve", rb_gsl_linalg_QRPT_Rsolve, -1);
  rb_define_singleton_method(mgsl_linalg_QRPT, "Rsvx", rb_gsl_linalg_QRPT_Rsvx, -1);
  rb_define_method(cgsl_matrix_QR2, "Rsvx", rb_gsl_linalg_QRPT_Rsvx, -1);

  /*****/
  rb_define_singleton_method(mgsl_linalg_SV, "decomp", rb_gsl_linalg_SV_decomp, -1);
  rb_define_method(cgsl_matrix, "SV_decomp", rb_gsl_linalg_SV_decomp, -1);
  rb_define_singleton_method(mgsl_linalg_SV, "decomp_mod", rb_gsl_linalg_SV_decomp_mod, -1);
  rb_define_method(cgsl_matrix, "SV_decomp_mod", rb_gsl_linalg_SV_decomp_mod, -1);
  rb_define_singleton_method(mgsl_linalg_SV, "decomp_jacobi", rb_gsl_linalg_SV_decomp_jacobi, -1);
  rb_define_method(cgsl_matrix, "SV_decomp_jacobi", rb_gsl_linalg_SV_decomp_jacobi, -1);

  rb_define_singleton_method(mgsl_linalg_SV, "solve", rb_gsl_linalg_SV_solve, -1);

  rb_define_method(cgsl_matrix, "SV_solve", rb_gsl_linalg_SV_solve, -1);

  /*****/
  rb_define_singleton_method(mgsl_linalg_cholesky, "decomp", rb_gsl_linalg_cholesky_decomp, -1);
  rb_define_method(cgsl_matrix, "cholesky_decomp", rb_gsl_linalg_cholesky_decomp, -1);

  rb_define_singleton_method(mgsl_linalg_cholesky, "solve", rb_gsl_linalg_cholesky_solve, -1);
  rb_define_method(cgsl_matrix, "cholesky_solve", rb_gsl_linalg_cholesky_solve, -1);
  rb_define_method(cgsl_matrix_C, "solve", rb_gsl_linalg_cholesky_solve, -1);
  rb_define_singleton_method(mgsl_linalg_cholesky, "svx", rb_gsl_linalg_cholesky_svx, -1);
  rb_define_method(cgsl_matrix, "cholesky_svx", rb_gsl_linalg_cholesky_svx, -1);
  rb_define_method(cgsl_matrix_C, "svx", rb_gsl_linalg_cholesky_svx, -1);

  /*****/

  rb_define_singleton_method(mgsl_linalg_symmtd, "decomp", rb_gsl_linalg_symmtd_decomp, -1);
  rb_define_method(cgsl_matrix, "symmtd_decomp", rb_gsl_linalg_symmtd_decomp, -1);
  rb_define_singleton_method(mgsl_linalg_symmtd, "decomp!", rb_gsl_linalg_symmtd_decomp2, -1);
  rb_define_method(cgsl_matrix, "symmtd_decomp!", rb_gsl_linalg_symmtd_decomp2, -1);

  rb_define_method(cgsl_matrix, "symmtd_unpack", rb_gsl_linalg_symmtd_unpack, -1);
  rb_define_method(cgsl_matrix, "symmtd_unpack_T", rb_gsl_linalg_symmtd_unpack_T, -1);

  rb_define_singleton_method(mgsl_linalg_symmtd, "unpack", rb_gsl_linalg_symmtd_unpack, -1);
  rb_define_singleton_method(mgsl_linalg_symmtd, "unpack_T", rb_gsl_linalg_symmtd_unpack_T, -1);
  /*****/
  rb_define_singleton_method(mgsl_linalg_hermtd, "decomp", rb_gsl_linalg_hermtd_decomp, -1);
  rb_define_method(cgsl_matrix, "hermtd_decomp", rb_gsl_linalg_hermtd_decomp, -1);
   rb_define_singleton_method(mgsl_linalg_hermtd, "decomp!", rb_gsl_linalg_hermtd_decomp2, -1);
  rb_define_method(cgsl_matrix, "hermtd_decomp!", rb_gsl_linalg_hermtd_decomp2, -1);
 
  rb_define_method(cgsl_matrix_complex, "hermtd_unpack", rb_gsl_linalg_symmtd_unpack, -1);
  rb_define_singleton_method(mgsl_linalg_hermtd, "unpack", rb_gsl_linalg_hermtd_unpack, -1);
  rb_define_method(cgsl_matrix_complex, "hermtd_unpack_T", rb_gsl_linalg_symmtd_unpack_T, -1);
  rb_define_singleton_method(mgsl_linalg_hermtd, "unpack_T", rb_gsl_linalg_hermtd_unpack_T, -1);

  /*****/
  rb_define_method(cgsl_matrix, "bidiag_decomp", rb_gsl_linalg_bidiag_decomp, -1);
  rb_define_method(cgsl_matrix, "bidiag_decomp!", rb_gsl_linalg_bidiag_decomp2, -1);

  rb_define_singleton_method(mgsl_linalg, "bidiag_decomp", rb_gsl_linalg_bidiag_decomp, -1);
  rb_define_singleton_method(mgsl_linalg, "bidiag_decomp!", rb_gsl_linalg_bidiag_decomp2, -1);
  rb_define_singleton_method(mgsl_linalg_bidiag, "decomp", rb_gsl_linalg_bidiag_decomp, -1);
  rb_define_singleton_method(mgsl_linalg_bidiag, "decomp!", rb_gsl_linalg_bidiag_decomp2, -1);

  rb_define_method(cgsl_matrix, "bidiag_unpack", rb_gsl_linalg_bidiag_unpack, -1);
  rb_define_method(cgsl_matrix, "bidiag_unpack2", rb_gsl_linalg_bidiag_unpack2, -1);
  rb_define_singleton_method(mgsl_linalg, "bidiag_unpack", rb_gsl_linalg_bidiag_unpack, -1);
  rb_define_singleton_method(mgsl_linalg, "bidiag_unpack2", rb_gsl_linalg_bidiag_unpack2, -1);
  rb_define_singleton_method(mgsl_linalg_bidiag, "unpack", rb_gsl_linalg_bidiag_unpack, -1);
  rb_define_singleton_method(mgsl_linalg_bidiag, "unpack2", rb_gsl_linalg_bidiag_unpack2, -1);

  rb_define_method(cgsl_matrix, "bidiag_unpack_B", rb_gsl_linalg_bidiag_unpack_B, -1);
  rb_define_singleton_method(mgsl_linalg, "bidiag_unpack_B", rb_gsl_linalg_bidiag_unpack_B, -1);
  rb_define_singleton_method(mgsl_linalg_bidiag, "unpack_B", rb_gsl_linalg_bidiag_unpack_B, -1);
  /*****/
  rb_define_singleton_method(mgsl_linalg, "householder_transform", 
			     rb_gsl_linalg_householder_transform, -1);
  rb_define_singleton_method(mgsl_linalg_Householder, "transform", 
			     rb_gsl_linalg_householder_transform, -1);
  rb_define_singleton_method(mgsl_linalg_HH, "transform", 
			     rb_gsl_linalg_householder_transform, -1);
  rb_define_method(cgsl_vector, "householder_transform", 
		   rb_gsl_linalg_householder_transform, -1);

  rb_define_singleton_method(mgsl_linalg, "householder_hm", 
			     rb_gsl_linalg_householder_hm, 3);
  rb_define_singleton_method(mgsl_linalg_Householder, "hm", 
			     rb_gsl_linalg_householder_hm, 3);
  rb_define_singleton_method(mgsl_linalg_HH, "hm", 
			     rb_gsl_linalg_householder_hm, 3);

  rb_define_singleton_method(mgsl_linalg, "householder_mh", 
			     rb_gsl_linalg_householder_mh, 3);
  rb_define_singleton_method(mgsl_linalg_Householder, "mh", 
			     rb_gsl_linalg_householder_mh, 3);
  rb_define_singleton_method(mgsl_linalg_HH, "mh", 
			     rb_gsl_linalg_householder_mh, 3);

  rb_define_singleton_method(mgsl_linalg, "householder_hv", 
			     rb_gsl_linalg_householder_hv, 3);
  rb_define_singleton_method(mgsl_linalg_Householder, "hv", 
			     rb_gsl_linalg_householder_hv, 3);
  rb_define_singleton_method(mgsl_linalg_HH, "hv", 
			     rb_gsl_linalg_householder_hv, 3);

  rb_define_singleton_method(mgsl_linalg_HH, "solve", rb_gsl_linalg_HH_solve, -1);
  rb_define_method(cgsl_matrix, "HH_solve", rb_gsl_linalg_HH_solve, -1);
  rb_define_singleton_method(mgsl_linalg_HH, "svx", rb_gsl_linalg_HH_svx, -1);
  rb_define_method(cgsl_matrix, "HH_svx", rb_gsl_linalg_HH_svx, -1);

  /*****/

  rb_define_singleton_method(mgsl_linalg, "solve_symm_tridiag", rb_gsl_linalg_solve_symm_tridiag, 3);
  rb_define_singleton_method(mgsl_linalg, "solve_symm_cyc_tridiag", rb_gsl_linalg_solve_symm_cyc_tridiag, 3);

  rb_define_singleton_method(mgsl_linalg, "solve_tridiag", rb_gsl_linalg_solve_tridiag, 4);
  rb_define_singleton_method(mgsl_linalg, "solve_cyc_tridiag", rb_gsl_linalg_solve_cyc_tridiag, 4);

  rb_define_singleton_method(mgsl_linalg_tridiag, "solve_symm", rb_gsl_linalg_solve_symm_tridiag, 3);
  rb_define_singleton_method(mgsl_linalg_tridiag, "solve_symm_cyc", rb_gsl_linalg_solve_symm_cyc_tridiag, 3);

  rb_define_singleton_method(mgsl_linalg_tridiag, "solve", rb_gsl_linalg_solve_tridiag, 4);
  rb_define_singleton_method(mgsl_linalg_tridiag, "solve_cyc", rb_gsl_linalg_solve_cyc_tridiag, 4);

  /*****/
  rb_define_singleton_method(mgsl_linalg, "balance_columns", 
			     rb_gsl_linalg_balance_columns, -1);
  rb_define_method(cgsl_matrix, "balance_columns", 
			     rb_gsl_linalg_balance_columns, -1);
			     
}
