/* mats1 - Elementary matrix operations                                */
/* 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 "linalg.h"

/* forward declarations */
LOCAL LVAL facelist P1H(int);
LOCAL LVAL xsbindfaces P1H(int);


/*************************************************************************/
/**                                                                     **/
/**           Matrix Construction and Decomposition Functions           **/
/**                                                                     **/
/*************************************************************************/

/* Built in DIAGONAL function */
LVAL xsdiagonal(V)
{
  LVAL arg, next, dim, val, data, result, result_data;
  int n, m, i;

  arg = xlgetarg();
  xllastarg();

  /* protect some pointers */
  xlstkcheck(3);
  xlsave(dim);
  xlsave(val);
  xlsave(result);
	
  if (matrixp(arg)) {

    /* extract diagonal from a matrix */
    n = (numrows(arg) < numcols(arg)) ? numrows(arg) : numcols(arg);
    m = numcols(arg);
    result = mklist(n, NIL);
    data = getdarraydata(arg);
    for (i = 0, next = result; i < n; i++, next = cdr(next))
      rplaca(next, gettvecelement(data, m * i + i));
  }
  else if (seqp(arg)) {

    /* construct a diagonal matrix */
    n = seqlen(arg);
    dim = cvfixnum((FIXTYPE) n);
    dim = list2(dim, dim);
    val = cvfixnum((FIXTYPE) 0);
    result = mkarray(dim, k_initelem, val, s_true);
    result_data = getdarraydata(result);
    for (i = 0; i < n; i++)
      setelement(result_data, n * i + i, getnextelement(&arg, i));
  }
  else xlbadtype(arg);
	
  /* restore the stack frame */
  xlpopn(3);

  return(result);
}

/* Return a list of rows or columns of a matrix read from the stack */
/***** this could be more efficient */
LOCAL LVAL facelist P1C(int, face)
{
  LVAL a, result, next, vect, data, type;
  int rows, cols, i, j;
	
  a = xlgamatrix();
  xllastarg();

  rows = numrows(a);
  cols = numcols(a);
	
  /* protect some pointers */
  xlsave1(result);

  data = getdarraydata(a);
  type = gettvecetype(data);
  switch(face) {
  case 0: /* rows */
    result = mklist(rows, NIL);
    for (next = result, i = 0; i < rows; i++, next = cdr(next)) {
      vect = mktvec(cols, type);
      rplaca(next, vect);
      for (j = 0; j < cols; j++) 
	settvecelement(vect, j, gettvecelement(data, cols * i + j));
    }
    break;
  case 1: /* columns */
    result = mklist(cols, NIL);
    for (next = result, j = 0; j < cols; j++, next = cdr(next)) {
      vect = mktvec(rows, type);
      rplaca(next, vect);
      for (i = 0; i < rows; i++) 
	settvecelement(vect, i, gettvecelement(data, cols * i + j));
    }
    break;
  default:
    xlfail(" bad face selector");
  }
    
  /* restore the stack frame */
  xlpop();

  return(result);
}

/* Built in ROW-LIST and COLUMN-LIST functions */
LVAL xsrowlist(V) { return(facelist(0)); }
LVAL xscolumnlist(V) { return(facelist(1)); }

/* Bind list of sequences or matrices along rows or columns */
LOCAL LVAL xsbindfaces P1C(int, face)
{
  LVAL next, data, dim, result, result_data;
  int totalsize, rows=0, cols=0, c=0, r=0, n, i, j;
  
  /* protect some pointers */
  xlstkcheck(3);
  xlsave(data);
  xlsave(dim);
  xlsave(result);
  
  /* Check the first argument and establish size of the binding face */
  next = peekarg(0);
  switch (face) {
  case 0:
    if (matrixp(next)) cols = numcols(next);
    else if (seqp(next)) cols = seqlen(next);
    else if (! compoundp(next)) cols = 1;
    else xlbadtype(next);
    break;
  case 1:
    if (matrixp(next)) rows = numrows(next);
    else if (seqp(next)) rows = seqlen(next);
    else if (! compoundp(next)) rows = 1;
    else xlbadtype(next);
    break;
  }

  /* Pass through the arguments on the stack to determine the result size */
  n = xlargc;
  for (i = 0, totalsize = 0; i < n; i++) {
    next = peekarg(i);
    if (matrixp(next)) {
      c = numcols(next);
      r = numrows(next); 
    }
    else if (seqp(next))
      switch (face) {
      case 0:  c = seqlen(next); r = 1; break;
      case 1:  c = 1; r = seqlen(next); break;
      }
    else if (! compoundp(next)) {
      c = 1;
      r = 1;
    }
    else xlbadtype(next);

    switch (face) {
    case 0:
      if (c != cols) xlfail("dimensions do not match");
      else totalsize += r;
      break;
    case 1:
      if (r != rows) xlfail("dimensions do not match");
      else totalsize += c;
    }
  }
  
  /* set up the result matrix */
  dim = newvector(2);
  switch (face) {
  case 0:
    setelement(dim, 0, cvfixnum((FIXTYPE) totalsize));
    setelement(dim, 1, cvfixnum((FIXTYPE) cols));
    break;
  case 1:
    setelement(dim, 0, cvfixnum((FIXTYPE) rows));
    setelement(dim, 1, cvfixnum((FIXTYPE) totalsize));
    break;
  }
  result = mkarray(dim, NIL, NIL, s_true);
  result_data = getdarraydata(result);

  /* compute the result */
  for (r = 0, c = 0; moreargs();) {
    next = xlgetarg();
    if (matrixp(next)) {
      rows = numrows(next);
      cols = numcols(next);
      data = getdarraydata(next);
    }
    else {
      switch (face) {
      case 0: rows = 1; break;
      case 1: cols = 1; break;
      }
      data = (vectorp(next) || tvecp(next)) ?
	next : coerce_to_tvec(next, s_true);
    }
    switch (face) {
    case 0:
      for (i = 0; i < rows; i++, r++) 
	for (j = 0; j < cols; j++)
	  setelement(result_data, cols * r + j,
		     gettvecelement(data, cols * i + j));
      break;
    case 1:
      for (j = 0; j < cols; j++, c++)
	for (i = 0; i < rows; i++) 
	  setelement(result_data, totalsize * i + c,
		     gettvecelement(data, cols * i + j));
      break;
    }
  }
  
  /* restore the stack frame */
  xlpopn(3);
  
  return(result);
}

/* Built in BIND-ROWS and BIND-COLUMNS functions */
LVAL xsbindrows(V) { return(xsbindfaces(0)); }
LVAL xsbindcols(V) { return(xsbindfaces(1)); }

/* Built in TRANSPOSE-LIST function */
LVAL xstransposelist(V)
{
  LVAL list, result, nextr, row, nextl;
  int m, n;
  
  list = xlgalist();
  xllastarg();
  
  xlstkcheck(2);
  xlsave(result);
  xlprotect(list);
  
  list = copylist(list);
  m = llength(list);
  if (! consp(car(list))) xlerror("not a list", car(list));
  n = llength(car(list));
  
  result = mklist(n, NIL);
  for (nextr = result; consp(nextr); nextr = cdr(nextr)) {
    row = mklist(m, NIL);
    rplaca(nextr, row);
    for (nextl = list; consp(nextl); nextl = cdr(nextl)) {
      if (!consp(car(nextl))) xlerror("not a list", car(nextl));
      rplaca(row, car(car(nextl)));
      row = cdr(row);
      rplaca(nextl, cdr(car(nextl)));
    }
  }
  
  xlpopn(2);
  return(result);
}


syntax highlighted by Code2HTML, v. 0.9.1