/* xsbayes - Lisp interface to laplace approximation stuff             */
/* 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 <stdlib.h>
#include "xlisp.h"
#include "osdef.h"
#ifdef ANSI
#include "xlproto.h"
#include "xlsproto.h"
#else
#include "xlfun.h"
#include "xlsfun.h"
#endif ANSI
#include "xlsvar.h"

#ifdef ANSI
void callLminfun(LVAL,int,RVector,double *,RVector,RVector,int *),meminit(void),
     double2list(int,double *,LVAL),seq2double(int,LVAL,RVector),
     makespace(char **,int),seq2pchar(int,LVAL,LVAL *),setinternals(LVAL,LVAL),
     call_scaled_Lfun(RVector,double *,RVector,RVector);
LVAL newipars(MaxIPars),newdpars(MaxDPars),makecallarg(int),numderiv(int),
     getinternals(LVAL),newinternals(LVAL,LVAL,LVAL,double),scaled_eval(int);
MaxIPars getMaxIPars(LVAL);
MaxDPars getMaxDPars(LVAL);
#else
void callLminfun(),meminit(),double2list(),seq2double(),
     makespace(),seq2pchar(),setinternals(),call_scaled_Lfun();
LVAL newipars(),newdpars(),makecallarg(),numderiv(),
     getinternals(),newinternals(),scaled_eval();
MaxIPars getMaxIPars();
MaxDPars getMaxDPars();
#endif ANSI

/************************************************************************/
/**                                                                    **/
/**                      Definitions and Globals                       **/
/**                                                                    **/
/************************************************************************/

#define MAXALLOC 20

static char  *mem[MAXALLOC], memcount;
static LVAL arg;
/* in xlsdef.h JKL
typedef struct {
  int n, m, k, itnlimit, backtrack, verbose, vals_suppl, exptilt;
  int count, termcode;
} MaxIPars;

typedef struct {
  double typf, h, gradtol, steptol, maxstep, dflt, tilt, newtilt, hessadd;
} MaxDPars;

typedef struct {
  MaxIPars max;
  int full, covar;
} MomIPars;

typedef struct {
  MaxDPars max;
  double mgfdel;
} MomDPars;
*/
/************************************************************************/
/**                                                                    **/
/**                 Fake Replacements for S Interface                  **/
/**                                                                    **/
/************************************************************************/

static void meminit()
{
  static inited = FALSE;
  int i;

  if (! inited) {
    for (i = 0; i < MAXALLOC; i++) mem[i] = nil;
    inited = TRUE;
  }

  memcount = 0;
}

static void makespace(pptr, size)
     char **pptr;
     int size;
{
  if (size <= 0) return;
  if (*pptr == nil) *pptr = calloc(size, 1);
  else *pptr = realloc(*pptr, size);
  if (size > 0 && *pptr == nil) xlfail("memory allocation failed");
}

#ifdef SBAYES
char *S_alloc(n, m)
     int n, m;
{
  char *result;
  if (memcount >= MAXALLOC) result = nil;
  else {
    makespace(mem + memcount, n * m);
    result = mem[memcount];
    memcount++;
  }
  return(result);
}
#endif SBAYES

void call_S(fun, narg, args, mode, length, names, nvals, values)
     LVAL fun; /* type changed  JKL */
     char /* *fun,*/ **args, **mode, **names, **values;
     long narg, nvals, *length;
{
  int n = length[0], derivs;
  static double *fval = nil, *grad = nil, *hess = nil;

  makespace((char **)&fval, 1 * sizeof(double));  /* casts added  JKL */
  makespace((char **)&grad, n * sizeof(double));
  makespace((char **)&hess, n * n * sizeof(double));

  callLminfun(fun, n,(RVector)args[0], fval, grad, hess, &derivs);
  
  values[0] = (char *) fval;
  values[1] = (derivs > 0) ? (char *) grad : nil;
  values[2] = (derivs > 1) ? (char *) hess : nil;
}

void Recover(s, w)
     char *s, *w;
{
  xlfail(s);
}

/************************************************************************/
/**                                                                    **/
/**                   Callback and Copying Function                    **/
/**                                                                    **/
/************************************************************************/

static void callLminfun(fun, n, x, fval, grad, hess, derivs)
     LVAL fun;
     int n, *derivs;
     RVector x, grad, hess;
     double *fval;
{
  LVAL result, seq;
  
  /* arg does not realy have to containt the function; this is a carryover */
  double2list(n, x, car(cdr(arg)));
  rplaca(arg, fun);
  result = xlapply(pushargs(car(arg), cdr(arg)));

  if (realp(result)) {
    *fval = makedouble(result);
    *derivs = 0;
  }
  else if (consp(result)) {
    *fval = makedouble(car(result));
    *derivs = 0;
    if (consp(cdr(result))) {
      seq = compounddataseq(car(cdr(result)));
      if (seqlen(seq) != n) xlerror("bad gradient", car(cdr(result)));
      seq2double(n, seq, grad);
      *derivs = 1;
      if (consp(cdr(cdr(result)))) {
	seq = compounddataseq(car(cdr(cdr(result))));
	if (seqlen(seq) != n * n)
	  xlerror("bad hessian", car(cdr(cdr(result))));
	seq2double(n * n, seq, hess);
	*derivs = 2;
      }
    }
  }
  else xlerror("bad function result", result);
}	   
  
static LVAL makecallarg(n)
     int n;
{
  LVAL carg;

  xlsave1(carg);
  carg = mklist(n, NIL);
  carg = consa(carg);
  carg = cons(NIL, carg);
  xlpop();
  return(carg);
}

static void seq2pchar(n, x, p)
     int n;
     LVAL x;
     /*char **/LVAL *p;/* changed JKL */
{
  int i;

  for (i = 0; i < n; i++) 
    p[i] = /*(char *)*/ getnextelement(&x, i);/* changed JKL */
}

static void seq2double(n, x, dx)
     int n;
     LVAL x;
     /*double **/RVector dx; /* changed JKL */
{
  int i;

  for (i = 0; i < n; i++) 
    dx[i] = makedouble(getnextelement(&x, i));
}

static void double2list(n, dx, x)
     int n;
     double *dx;
     LVAL x;
{
  int i;

  for (i = 0; i < n && consp(x) ; i++, x = cdr(x))
    rplaca(x, cvflonum((FLOTYPE) dx[i]));
}

/************************************************************************/
/**                                                                    **/
/**                      Numerical Derivatives                         **/
/**                                                                    **/
/************************************************************************/

static LVAL numderiv(order)
     int order;
{
  LVAL f, x, scale, result, data;
  double h, fval;
  static double *dx = nil, *grad = nil, *hess = nil, *typx = nil;
  int n, i, all;

  f = xlgetarg();
  x = xsgetsequence();
  scale = (moreargs()) ? xsgetsequence() : NIL;
  h = (moreargs()) ? makedouble(xlgetarg()) : -1.0;
  all = (moreargs() && xlgetarg() != NIL) ? TRUE : FALSE;

  n = seqlen(x);
  makespace((char **)&dx, n * sizeof(double));  /* casts added JKL */
  makespace((char **)&grad, n * sizeof(double));
  if (order > 1) makespace((char **)&hess, n * n * sizeof(double));
  makespace((char **)&typx, n * sizeof(double));

  seq2double(n, x, dx);
  if (scale != NIL) seq2double(n, scale, typx);
  else for (i = 0; i < n; i++) typx[i] = 1.0;

  xlstkcheck(2);
  xlsave(arg);
  xlsave(result);
  arg = makecallarg(n);

  evalfront(&f, &n, dx, &fval, grad, (order > 1) ? hess : nil, &h, typx);
  
  if (order == 1) {
    result = mklist(n, NIL);
    double2list(n, grad, result);
  }
  else {
    result = integer_list_2(n, n);
    result = newarray(result, NIL, NIL);
    data = arraydata(result);
    for (i = 0; i < n * n; i++)
      setelement(data, i, cvflonum((FLOTYPE) hess[i]));
	if (all) {
	  result = consa(result);
	  result = cons(mklist(n, NIL), result);
	  double2list(n, grad, car(result));
      result = cons(cvflonum((FLOTYPE) fval), result);
	}
  }
  xlpopn(2);

  return(result);
}

LVAL xsnumgrad() { return(numderiv(1)); }
LVAL xsnumhess() { return(numderiv(2)); }

/************************************************************************/
/**                                                                    **/
/**                      Maximization Interface                        **/
/**                                                                    **/
/************************************************************************/

#define INTLEN 12
#define F_POS 0
#define G_POS 1
#define C_POS 2
#define X_POS 3
#define SCALE_POS 4
#define FVALS_POS 5
#define CVALS_POS 6
#define CTARG_POS 7
#define IPARS_POS 8
#define DPARS_POS 9
#define TSCAL_POS 10
#define MULT_POS 11

#define getffun(i) getelement(i, F_POS)
#define getgfuns(i) getelement(i, G_POS)
#define getcfuns(i) getelement(i, C_POS)
#define getx(i) getelement(i, X_POS)
#define getscale(i) getelement(i, SCALE_POS)
#define getfvals(i) getelement(i, FVALS_POS)
#define getcvals(i) getelement(i, CVALS_POS)
#define getctarget(i) getelement(i, CTARG_POS)
#define getipars(i) getelement(i, IPARS_POS)
#define getdpars(i) getelement(i, DPARS_POS)
#define gettscale(i) getelement(i, TSCAL_POS)
#define getmults(i) getelement(i, MULT_POS)
#define getderivstep(i) getflonum(getelement(getdpars(i), 1))

#define setx(i, v) setelement(i, X_POS, v)
#define setscale(i, v) setelement(i, SCALE_POS, v)
#define setmults(i, v) setelement(i, MULT_POS, v)
#define setfvals(i, v) setelement(i, FVALS_POS, v)
#define setcvals(i, v) setelement(i, CVALS_POS, v)
#define setipars(i, v) setelement(i, IPARS_POS, v)
#define setdpars(i, v) setelement(i, DPARS_POS, v)
#define setderivstep(i, v) setelement(getdpars(i), 1, cvflonum((FLOTYPE) (v)))

static LVAL getinternals(minfo)
     LVAL minfo;
{
  return(slot_value(minfo, xlenter("INTERNALS")));
}

static void setinternals(minfo, internals)
     LVAL minfo, internals;
{
  set_slot_value(minfo, xlenter("INTERNALS"), internals);
}

static LVAL newipars(ip)
     MaxIPars ip;
{
  LVAL result;

  xlsave1(result);
  result = newvector(10);
  setelement(result, 0, cvfixnum((FIXTYPE) ip.n));
  setelement(result, 1, cvfixnum((FIXTYPE) ip.m));
  setelement(result, 2, cvfixnum((FIXTYPE) ip.k));
  setelement(result, 3, cvfixnum((FIXTYPE) ip.itnlimit));
  setelement(result, 4, cvfixnum((FIXTYPE) ip.backtrack));
  setelement(result, 5, cvfixnum((FIXTYPE) ip.verbose));
  setelement(result, 6, cvfixnum((FIXTYPE) ip.vals_suppl));
  setelement(result, 7, cvfixnum((FIXTYPE) ip.exptilt));
  setelement(result, 8, cvfixnum((FIXTYPE) ip.count));
  setelement(result, 9, cvfixnum((FIXTYPE) ip.termcode));
  xlpop();
  return(result);
}

static LVAL newdpars(dp)
     MaxDPars dp;
{
  LVAL result;

  xlsave1(result);
  result = newvector(9);
  setelement(result, 0, cvflonum((FLOTYPE) dp.typf));
  setelement(result, 1, cvflonum((FLOTYPE) dp.h));
  setelement(result, 2, cvflonum((FLOTYPE) dp.gradtol));
  setelement(result, 3, cvflonum((FLOTYPE) dp.steptol));
  setelement(result, 4, cvflonum((FLOTYPE) dp.maxstep));
  setelement(result, 5, cvflonum((FLOTYPE) dp.dflt));
  setelement(result, 6, cvflonum((FLOTYPE) dp.tilt));
  setelement(result, 7, cvflonum((FLOTYPE) dp.newtilt));
  setelement(result, 8, cvflonum((FLOTYPE) dp.hessadd));
  xlpop();
  return(result);
}

static MaxIPars getMaxIPars(internals)
     LVAL internals;
{
  LVAL ipars = getipars(internals);
  MaxIPars ip;

  ip.n = getfixnum(getelement(ipars, 0));
  ip.m = getfixnum(getelement(ipars, 1));
  ip.k = getfixnum(getelement(ipars, 2));
  ip.itnlimit = getfixnum(getelement(ipars, 3));
  ip.backtrack = getfixnum(getelement(ipars, 4));
  ip.verbose = getfixnum(getelement(ipars, 5));
  ip.vals_suppl = getfixnum(getelement(ipars, 6));
  ip.exptilt = getfixnum(getelement(ipars, 7));
  ip.count = getfixnum(getelement(ipars, 8));
  ip.termcode = getfixnum(getelement(ipars, 9));

  return(ip);
}

static MaxDPars getMaxDPars(internals)
     LVAL internals;
{
  LVAL dpars = getdpars(internals);
  MaxDPars dp;

  dp.typf = getflonum(getelement(dpars, 0));
  dp.h = getflonum(getelement(dpars, 1));
  dp.gradtol = getflonum(getelement(dpars, 2));
  dp.steptol = getflonum(getelement(dpars, 3));
  dp.maxstep = getflonum(getelement(dpars, 4));
  dp.dflt = getflonum(getelement(dpars, 5));
  dp.tilt = getflonum(getelement(dpars, 6));
  dp.newtilt = getflonum(getelement(dpars, 7));
  dp.hessadd = getflonum(getelement(dpars, 8));

  return(dp);
}

static LVAL newinternals(f, x, scale, h)
     LVAL f, x, scale;
     double h;
{
  LVAL result;
  int n, i;
  MaxIPars ip;
  MaxDPars dp;

  n = seqlen(x);
  ip.n = n; ip.k = 0; ip.m = 0; ip.itnlimit = -1; ip.backtrack = TRUE;
  ip.verbose = 0; ip.vals_suppl = FALSE; ip.exptilt = TRUE;
  ip.count = 0; ip.termcode = 0;

  dp.typf = 1.0; dp.h = h; dp.gradtol = -1.0; dp.steptol = -1.0;
  dp.maxstep = -1.0; dp.dflt = 0.0; dp.tilt = 0.0; dp.newtilt = 0.0;
  dp.hessadd = 0.0;

  xlsave1(result);
  result = newvector(INTLEN);
  setelement(result, F_POS, f);
  setelement(result, X_POS, x);
  if (scale != NIL) setelement(result, SCALE_POS, scale);
  else {
    scale = newvector(n);
    setelement(result, SCALE_POS, scale);
    for (i = 0; i < n; i++) setelement(scale, i, cvflonum((FLOTYPE) 1.0));
  }
  setelement(result, IPARS_POS, newipars(ip));
  setelement(result, DPARS_POS, newdpars(dp));
  xlpop();
  
  return(result);
}

LVAL xsminfo_isnew()
{
  LVAL myinfo, f, x, scale, step;
  double h;

  myinfo = xlgaobject();
  f = xlgetarg();
  x = xsgetsequence();
  if (! xlgetkeyarg(xlenter(":SCALE"), &scale)) scale = NIL;
  if (xlgetkeyarg(xlenter(":DERIVSTEP"), &step)) h = makedouble(step);
  else h = -1.0;

  setinternals(myinfo, newinternals(f, x, scale, h));
  return(NIL);
}

LVAL xsminfo_maximize()
{
  LVAL minfo, internals, f, g, c, x, scale, verbose, tiltscale, result, mults; 
  MaxIPars ip;
  MaxDPars dp;
  int n, m, k;
  RVector dx = nil, typx = nil; /* changed JKL */
  static double /* *dx = nil, *typx = nil, */ *fvals = nil, *tscale = nil;
  static double *cvals = nil, *ctarget = nil;
/*  static char *gg = nil, *cc = nil;*/
  static LVAL gg = nil, cc = nil;/* changed JKL */
  char *msg;

  minfo = xlgaobject();
  verbose = (moreargs()) ? xlgafixnum() : NIL;

  internals = getinternals(minfo);
  f = getffun(internals);
  g = getgfuns(internals);
  c = getcfuns(internals);
  x = getx(internals);
  scale = getscale(internals);
  ip = getMaxIPars(internals);
  dp = getMaxDPars(internals);
  tiltscale = gettscale(internals);
  mults = getmults(internals);

  m = ip.m;
  k = ip.k;
  if (verbose != NIL) ip.verbose = getfixnum(verbose);
  n = seqlen(x);
  ip.n = n;
  makespace((char **)&gg, m * sizeof(char *));  /* casts added  JKL */
  makespace((char **)&cc, k * sizeof(char *));
  makespace((char **)&dx, (n + k) * sizeof(double));
  makespace((char **)&typx, n * sizeof(double));
  makespace((char **)&fvals, (1 + n + n * n) * sizeof(double));
  makespace((char **)&tscale, m * sizeof(double));
  makespace((char **)&cvals, (k + k * n) * sizeof(double));
  makespace((char **)&ctarget, k * sizeof(double));

  seq2pchar(m, g, &gg);  /* pointers added JKL */
  seq2pchar(k, c, &cc);
  seq2double(n, x, dx);
  seq2double(n, scale, typx);
  if (ip.vals_suppl) {
    seq2double(1 + n + n * n, getfvals(internals), fvals);
    seq2double(k + k * n, getcvals(internals), cvals);
  }
  seq2double(m, tiltscale, tscale);
  seq2double(k, getctarget(internals), ctarget);
  seq2double(k, mults, dx + n);

  xlsave1(arg);
  arg = makecallarg(n);

  meminit();  /* pointers added JKL */
  maxfront(&f, &gg, &cc, dx, typx, fvals, nil, cvals, ctarget, &ip, &dp, tscale, &msg);

  result = mklist(n, NIL);
  setx(internals, result);
  double2list(n, dx, result);
  result = mklist(1 + n + n * n, NIL);
  setfvals(internals, result);
  double2list(1 + n + n * n, fvals, result);
  if (k > 0) {
    result = mklist(k + k * n, NIL);
    setcvals(internals, result);
    double2list(k + k * n, cvals, result);
    result = mklist(k, NIL);
    setmults(internals, result);
    double2list(k, dx + n, result);
  }
  setipars(internals, newipars(ip));
  setdpars(internals, newdpars(dp));

  xlpop();
  
  return(make_string(msg));
}

/************************************************************************/
/**                                                                    **/
/**                        Laplace Interface                           **/
/**                                                                    **/
/************************************************************************/

LVAL xsminfo_loglap()
{
  LVAL minfo, internals;
  MaxIPars ip;
  MaxDPars dp;
  int n, k, detonly;
  double val;
  static double *fvals = nil, *cvals = nil;
  
  minfo = xlgaobject();
  detonly = (moreargs()) ? (xlgetarg() != NIL) : FALSE;

  internals = getinternals(minfo);
  ip = getMaxIPars(internals);
  dp = getMaxDPars(internals);

  n = ip.n;
  k = ip.k;
            /* casts added JKL */
  makespace((char **)&fvals, (1 + n + n * n) * sizeof(double));
  makespace((char **)&cvals, (k + k * n) * sizeof(double));
  seq2double(1 + n + n * n, getfvals(internals), fvals);
  seq2double(k + k * n, getcvals(internals), cvals);

  loglapdet(fvals, cvals, &ip, &dp, &val, &detonly);

  return(cvflonum((FLOTYPE) val));
}

/************************************************************************/
/**                                                                    **/
/**            Scaled Evaluation and Numerical Derivatives             **/
/**                                                                    **/
/************************************************************************/

#ifdef DODO
/**** function objects; exact derivatives; NIL values ****/

static struct scaled_Lfun_info {
  LVAL arglist, value;
  RVector scaling, z;
  int dim, in_range, num_derivs, cached;
  double cached_value;
} Lfun_info;

static LVAL scaled_eval(order)
	int order;
{
  struct scaled_Lfun_info old_info;
  LVAL Lfun, Lx, Lscaling, Lvalue, space, hspace, Ldata;
  RVector x, fsum, grad;
  RMatrix hess;
  double value, h;
  int k, i, j, all;
  
  old_info = Lfun_info;
  
  Lfun = xlgetarg();
  Lx = xsgetsequence();
  Lscaling = xsgetsequence();
  if (order > 0) h = makedouble(xlgetarg());
  if (order == 2) all = (moreargs() && xlgetarg() != NIL) ? TRUE : FALSE;
  xllastarg();
  
  xlstkcheck(5);
  xlsave(space);
  xlsave(hspace);
  xlsave(Lfun_info.arglist);
  xlsave(Lfun_info.value);
  xlsave(Lvalue);
  
  k = Lfun_info.dim = seqlen(Lx);
  space = newadata(sizeof(double), 2 + k * (2 * k + 5), FALSE);
  Lfun_info.scaling = (RVector) getadaddr(space);
  Lfun_info.z = Lfun_info.scaling + 2 + k * (k + 1);
  x = Lfun_info.scaling + 2 + k * (k + 2);
  fsum = Lfun_info.scaling + 2 + k * (k + 3);
  grad = Lfun_info.scaling + 2 + k * (k + 4);
  Lfun_info.arglist = mklist(2, NIL);
  rplaca(Lfun_info.arglist, Lfun);
  rplaca(cdr(Lfun_info.arglist), mklist(k, NIL));
  Lfun_info.in_range = TRUE;
  if (order == 2) {
    hspace = newadata(sizeof(double *), k, FALSE);
    hess = (RMatrix) getadaddr(hspace);
	for (i = 0; i < k; i++)
	  hess[i] = Lfun_info.scaling + 2 + k * (k + 5 + i);
  }
  else hess = nil;
  Lfun_info.cached = FALSE;
  Lfun_info.num_derivs = 0;
  
  seq2double(2 + k * (k + 1), Lscaling, Lfun_info.scaling);
  seq2double(k, Lx, x);

  call_scaled_Lfun(x, &value, grad, hess);
  switch (order) {
  case 0:
    if (Lfun_info.in_range) Lvalue = cvflonum((FLOTYPE) value);
    else Lvalue = Lfun_info.value;
    break;
  case 1:
    if (Lfun_info.num_derivs < 1)
	  numergrad(k, x, grad, fsum, call_scaled_Lfun, h, nil);
	if (Lfun_info.in_range) {
	  Lvalue = mklist(k, NIL);
	  double2list(k, grad, Lvalue);
	}
	else Lvalue = NIL;
    break;
  case 2:
    /* currently uses second differences even for analytic gradient */
    if (Lfun_info.num_derivs < 2) {
      numergrad(k, x, grad, fsum, call_scaled_Lfun, h, nil);
	  /* Lfun_info.cached_value = value;
	  Lfun_info.cached = TRUE;*/
      numerhess(k, x, hess, value, fsum, call_scaled_Lfun, h, nil);
	}
	if (Lfun_info.in_range) {
      Lvalue = integer_list_2(k, k);
      Lvalue = newarray(Lvalue, NIL, NIL);
      Ldata = arraydata(Lvalue);
      for (i = 0; i < k; i++)
        for (j = 0; j < k; j++)
          setelement(Ldata, i * k + j, cvflonum((FLOTYPE) hess[i][j]));
	  if (all) {
	    Lvalue = consa(Lvalue);
		Lvalue = cons(mklist(k, NIL), Lvalue);
		double2list(k, grad, car(Lvalue));
		Lvalue = cons(cvflonum((FLOTYPE) value), Lvalue);
	  }
	}
	else Lvalue = NIL;
    break;
  }
  
  xlpopn(5);
  Lfun_info = old_info;
  return(Lvalue);
}

static void call_scaled_Lfun(x, value, grad, hess)
	RVector x, grad, hess;
	double *value;
{
  LVAL temp, Lvalue, Lgrad, Lhess;
  double center, scale;
  RVector mean, sigma, z;
  int i, j, k;
  
  /* cheat to avoid recalculating function value in Hessian evaluation */
  if (Lfun_info.cached && Lfun_info.num_derivs == 0) {
    Lfun_info.cached = FALSE;
	*value = Lfun_info.cached_value;
	return;
  }
  
  k = Lfun_info.dim;
  center = Lfun_info.scaling[0];
  scale = Lfun_info.scaling[1];
  if (scale == 0.0) xlfail("division by zero");
  mean = Lfun_info.scaling + 2;
  sigma = Lfun_info.scaling + 2 + k;
  z = Lfun_info.z;
  
  for (i = 0; i < k; i++) {
	z[i] = mean[i];
    for (j = 0; j <= i; j++)
	  z[i] += sigma[i * k + j] * x[j];
  }
  
  double2list(k, z, car(cdr(Lfun_info.arglist)));

  temp = xlapply(pushargs(car(Lfun_info.arglist), cdr(Lfun_info.arglist)));
  
  Lfun_info.num_derivs = (consp(temp)) ? seqlen(temp) - 1 : 0;
  
  Lvalue = (consp(temp)) ? car(Lvalue) : temp;
  Lgrad = (consp(temp) && consp(cdr(temp))) ? car(cdr(temp)) : NIL;
  Lhess = (consp(temp) && consp(cdr(temp)) && consp(cdr(cdr(temp)))) 
        ? car(cdr(cdr(temp))) : NIL;
  
  if (fixp(Lvalue) || floatp(Lvalue)) {
  	*value = (makedouble(Lvalue) - center) / scale;
	if (grad != nil && Lgrad != NIL) {
	  seq2double(k, Lgrad, grad);
	  for (i = 0; i < k; i++) {
	    z[i] = 0.0;
	    for (j = i; j < k; j++) z[i] += sigma[j * k + i] * grad[j];
	    grad[i] = z[i] / scale;
	  }
	}
	if (hess != nil && Lhess != NIL) {
	  if (! matrixp(Lhess)) xlerror("not an a matrix", Lhess);
	  seq2double(k * k, arraydata(Lhess), hess);
	  /* rescale */
	}
  }
  else {
    *value = 0.0;
	Lfun_info.in_range = FALSE;
    Lfun_info.value = Lvalue;
  }
}

LVAL xsscaled_c2_eval() { return(scaled_eval(0)); }
LVAL xsscaled_c2_grad() { return(scaled_eval(1)); }
LVAL xsscaled_c2_hess() { return(scaled_eval(2)); }
#endif DODO

LVAL xsaxpy()
{
  LVAL result, next, tx, a, x, y;
  int i, j, n, start, end, lower;
  double val;
  
  a = arraydata(xsgetmatrix());
  x = xsgetsequence();
  y = xsgetsequence();
  lower = (moreargs() && xlgetarg() != NIL) ? TRUE : FALSE;
  
  n = seqlen(x);
  if (n * n != seqlen(a) || n != seqlen(y)) xlfail("dimensions do not match");
  
  xlsave(result);
  result = mklist(n, NIL);
  for (i = 0, start = 0, next = result; i < n; i++, start += n, next = cdr(next)) {
  	val = makedouble(getnextelement(&y, i));
	end = (lower) ? i : n - 1;
    for (j = 0, tx = x; j <= end; j++) {
	  val += makedouble(getnextelement(&tx, j)) 
           * makedouble(getelement(a, start + j));
    }
	rplaca(next, cvflonum((FLOTYPE) val));
  }
  xlpop();
  return(result);
}
