/* adapted from DQRDC of LINPACK */
/* XLISP-STAT 2.1 Copyright (c) 1990, by Luke Tierney                  */
/* Additions to Xlisp 2.1, Copyright (c) 1989 by David Michael Betz    */
/* You may give out copies of this software; for conditions see the    */
/* file COPYING included with this distribution.                       */
 

#include "xlisp.h"
#include "osdef.h"
#ifdef ANSI
#include "xlsproto.h"
#else
#include "xlsfun.h"
#endif ANSI

#ifdef ANSI
double NORM2(int,int,int,double **),DOT(int,int,int,int,double **);
void AXPY(int,int,int,int,double,double **),
     SCALE(int,int,int,double,double **),SWAP(int,int,int,double **);
#else
double NORM2(),DOT();
void AXPY(),SCALE(),SWAP();
#endif ANSI

#define SIGN(a, b) ((b) >= 0.0 ? fabs(a) : -fabs(a))

static double NORM2(i, j, n, x)
     int i, j, n;
     double **x;
{
  int k;
  double maxx, sum, temp;

  for (k = i, maxx = 0.0; k < n; k++) {
    temp = fabs(x[k][j]);
    if (maxx < temp) maxx = temp;
  }
  if (maxx == 0.0) return(0.0);
  else {
    for (k = i, sum = 0.0; k < n; k++) {
      temp = x[k][j] / maxx;
      sum += temp * temp;
    }
    return(maxx * sqrt(sum));
  }
}

static double DOT(i, j, k, n, x)
     int i, j, k;
     double **x;
{
  int l;
  double sum;

  for (l = i, sum = 0.0; l < n; l++) sum += x[l][j] * x[l][k];
  return(sum);
}

static void AXPY(i, j, k, n, a, x)
     int i, j, k, n;
     double a, **x;
{
  int l;
  for (l = i; l < n; l++) x[l][k] = a * x[l][j] + x[l][k];
}

static void SCALE(i, j, n, a, x)
     int i, j, n;
     double a, **x;
{
  int k;
  for (k = i; k < n; k++) x[k][j] *= a;
}

static void SWAP(i, j, n, a)
  int i, j, n;
  double **a;
{
  int k;
  double temp;
  for (k = 0; k < n; k++) {
    temp = a[k][i];
    a[k][i] = a[k][j];
    a[k][j] = temp;
  }
}

void qrdecomp(x,n,p,v,jpvt,pivot)
     int n, p, pivot;
     /* int * */ IVector jpvt; /* changed JKL */
     /* double ** */ RMatrix x, v;
{
  int i,j,k,jp,l,lp1,lup,maxj;
  double maxnrm,tt,*qraux,*work;
  double nrmxl,t;

  if (n < 0) return;
  work = v[0];
  qraux = rvector(p);

  /*
   * compute the norms of the free columns.
   */
  if (pivot)
    for (j = 0; j < p; j++) {
      jpvt[j] = j;
      qraux[j] = NORM2(0, j, n, x);
      work[j] = qraux[j];
    }
  /*
   * perform the householder reduction of x.
   */
  lup = (n < p) ? n : p;
  for (l = 0; l < lup; l++) {
    if (pivot) {
      /*
       * locate the column of largest norm and bring it
       * into the pivot position.
       */
      maxnrm = 0.0;
      maxj = l;
      for (j = l; j < p; j++) 
	if (qraux[j]>maxnrm) {
	  maxnrm = qraux[j];
	  maxj = j;
	}
      if (maxj!=l) {
	SWAP(l, maxj, n, x);
	qraux[maxj] = qraux[l];
	work[maxj] = work[l];
	jp = jpvt[maxj];
	jpvt[maxj] = jpvt[l];
	jpvt[l] = jp;
      }
    }
    qraux[l] = 0.0;
    if (l != n-1) {
      /*
       * compute the householder transformation for column l.
       */
      nrmxl = NORM2(l, l, n, x);
      if (nrmxl != 0.0) {
	if (x[l][l] != 0.0)
	  nrmxl = SIGN(nrmxl, x[l][l]);
	SCALE(l, l, n, 1.0 / nrmxl, x);
	x[l][l] = 1.0+x[l][l];
	/*
	 * apply the transformation to the remaining columns,
	 * updating the norms.
	 */
	lp1 = l+1;
	for (j = lp1; j < p; j++) {
	  t = -DOT(l, l, j, n, x) / x[l][l];
	  AXPY(l, l, j, n, t, x);
	  if (pivot)
	    if (qraux[j]!=0.0) {
	      tt = 1.0-(fabs(x[l][j])/qraux[j])*(fabs(x[l][j])/qraux[j]);
	      if (tt < 0.0) tt = 0.0;
	      t = tt;
	      tt = 1.0+0.05*tt*(qraux[j]/work[j])*(qraux[j]/work[j]);
	      if (tt!=1.0)
		qraux[j] = qraux[j]*sqrt(t);
	      else {
		qraux[j] = NORM2(l+1, j, n, x);
		work[j] = qraux[j];
	      }
	    }
	}
	/*
	 * save the transformation.
	 */
	qraux[l] = x[l][l];
	x[l][l] = -nrmxl;
      }
    }
  }

  /* copy over the upper triangle of a */
  for (i = 0; i < p; i++) {
    for (j = 0; j < i; j++) v[i][j] = 0.0;
    for (j = i; j < p; j++) {
      v[i][j] = x[i][j];
    }
  }
    
  /* accumulate the Q transformation -- assumes p <= n */
  for (i = 0; i < p; i++) {
    x[i][i] = qraux[i];
    for (k = 0; k < i; k++) x[k][i] = 0.0;
  }
  for (i = p - 1; i >= 0; i--) {
    if (i == n - 1) x[i][i] = 1.0;
    else {
      for (k = i; k < n; k++) x[k][i] = -x[k][i];
      x[i][i] += 1.0;
    }
    for (j = i - 1; j >= 0; j--) {
      if (x[j][j] != 0.0) {
	t = -DOT(j, j, i, n, x) / x[j][j];
	AXPY(j, j, i, n, t, x);
      }
    }
  }

  free_vector((Vector)qraux); /* cast added  JKL */
}
