/* statistics - Basic statistical functions for XLISP-STAT.            */
/* 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 "xlstat.h"

typedef LVAL (*subrfun)(V);

LOCAL LVAL datareduce1 P4H(subrfun, subrfun, LVAL, int);


LVAL xssum(V)  { return(datareduce1(xssum, xadd, cvfixnum((FIXTYPE) 0), FALSE)); }
LVAL xsprod(V) { return(datareduce1(xsprod, xmul, cvfixnum((FIXTYPE) 1), FALSE)); }
LVAL xsmin(V) { return(datareduce1(xsmin, xmin, NIL, FALSE)); }
LVAL xsmax(V) { return(datareduce1(xsmax, xmax, NIL, FALSE)); }
LVAL xscount(V)  { return(datareduce1(xscount, xadd, cvfixnum((FIXTYPE) 0), TRUE)); }

LOCAL LVAL datareduce1 P4C(subrfun, f, subrfun, bf, LVAL, nullval, int, count)
{
  LVAL fcn, x, result;
  
  switch (xlargc) {
  case 0: result = nullval; break;
  case 1: 
    if (compoundp(peekarg(0))) {
      xlstkcheck(2);
      xlsave(x);
      xlsave(fcn);
      fcn = cvsubr(bf, SUBR, 0);
      x = subr_map_elements(f);
      x = compounddataseq(x);
      result = reduce(fcn, x, FALSE, NIL);
      xlpopn(2);
    }
    else result = (count) ? cvfixnum((FIXTYPE) 1) : xlgetarg();
    break;
  default:
    xlsave1(x);
    x = makearglist(xlargc, xlargv);
    result = xlcallsubr1(f, x);
    xlpop();
  }
  return(result);
}

LOCAL int all_simple P1C(LVAL, x)
{
  int i, n;

  switch (ntype(x)) {
  case CONS:
    for (; consp(x); x = cdr(x)) 
      if (compoundp(car(x))) return(FALSE);
    break;
  case VECTOR:
    n = getsize(x);
    for (i = 0; i < n; i++) 
      if (compoundp(getelement(x, i))) return(FALSE);
    break;
  case TVEC:
    break;
  case SYMBOL:
    if (null(x)) break;
    /* else fall through */
  default:
    xlerror("not a sequence", x);
  }
  return(TRUE);
}

static LVAL lastcdr P1C(LVAL, x)
{
  LVAL last = NIL;
  
  for (; consp(x); x = cdr(x)) last = x;
  
  return(last);
}

static LVAL elementlist P1C(LVAL, x)
{
  LVAL next, last, result;
  
  if (!compoundp(x)) result = consa(x);
  else {
    xlprot1(x);
    x = compounddataseq(x);
    x = (listp(x)) ? copylist(x) : coerce_to_list(x);
    if (all_simple(x)) result = x;
    else {
      for (next = x; consp(next); next = cdr(next))
        rplaca(next, elementlist(car(next)));
      result = car(x);
      last = lastcdr(car(x));
      for (next = cdr(x); consp(next); next = cdr(next)) {
        rplacd(last, car(next));
        last = lastcdr(car(next));
      }
    }
    xlpop();
  }
  return(result);
}
      
LVAL elementseq P1C(LVAL, x)
{
  if (! compoundp(x)) xlerror("not a compound data item", x);
  x = compounddataseq(x);
  if (all_simple(x)) return(x);
  else return(elementlist(x));
}

LVAL xselement_seq(V) { return(elementseq(xlgetarg())); }

static LVAL base_ifelse(V)
{
  LVAL a, b, c;
  
  a = xlgetarg();
  b = xlgetarg();
  c = xlgetarg();
  xllastarg();
  
  return((a != NIL) ? b : c);
}

LVAL xsifelse(V) { return(subr_map_elements(base_ifelse)); }

typedef struct {
  double real, imag;
  int complex;
} Number;

static VOID make_number P2C(Number *, num, LVAL, x)
{
  if (realp(x)) {
    num->real = makefloat(x);
    num->imag = 0.0;
    num->complex = FALSE;
  }
  else if (complexp(x)) {
    num->real = makefloat(getreal(x));
    num->imag = makefloat(getimag(x));
    num->complex = TRUE;
  }
  else xlerror("not a number", x);
}

static VOID base_mean P3C(int *, count, Number *, mean, LVAL, x)
{
  LVAL y;
  Number num;
  double c, p, q;
  int i, n;

  if (! compoundp(x)) {
    c = *count; p = c / (c + 1.0); q = 1.0 - p;
    make_number(&num, x);
    mean->real = p * mean->real + q * num.real;
    mean->complex = mean->complex || num.complex;
    if (mean->complex) mean->imag = p * mean->imag + q * num.imag;
    (*count)++;
  }
  else {
    x = compounddataseq(x);
    n = seqlen(x);
    for (i = 0; i < n; i++) {
      y = getnextelement(&x, i);
      base_mean(count, mean, y);
    }
  }
}

LVAL xsmean(V)
{
  Number mean;
  int count;
  LVAL x;

  x = xlgetarg();
  xllastarg();

  mean.real = 0.0; mean.imag = 0.0; mean.complex = FALSE;
  count = 0;
  base_mean(&count, &mean, x);
  if (mean.complex) return(newdcomplex(mean.real,mean.imag));
  else return(cvflonum((FLOTYPE) mean.real));
}

LVAL xssample(V)
{
  LVAL x, result, temp, elem;
  int n, N, replace, i, j;
  
  x = xlgaseq();
  n = getfixnum(xlgafixnum());
  N = seqlen(x);
  replace = (moreargs()) ? (xlgetarg() != NIL) : FALSE;
  xllastarg();

  if (! replace && n > N) n = N;

  xlstkcheck(4);
  xlprotect(x);
  xlsave(result);
  xlsave(elem);
  xlsave(temp);
  x = (listp(x)) ? coerce_to_tvec(x, s_true) : copyvector(x);
  result = NIL;
  if (N > 0 && n > 0) {
    for (i = 0; i < n; i++) {
      j = (replace) ? osrand(N) : i + osrand(N - i);
      elem = gettvecelement(x, j);
      result = cons(elem, result);
      if (! replace) {           /* swap elements i and j */
        temp = gettvecelement(x, i);
        settvecelement(x, i, elem);
        settvecelement(x, j, temp);
      }
    }
  }
  xlpopn(4);
  return(result);
}


syntax highlighted by Code2HTML, v. 0.9.1