/* xltvec.c - typed vectors */
/* 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"

LOCAL int typecode P1H(LVAL);
LOCAL int typesize P1H(int);

LOCAL FIXTYPE getirealpart P1C(LVAL, x)
{
  switch (ntype(x)) {
  case FIXNUM:
    return getfixnum(x);
  case COMPLEX:
    if (fixp(getreal(x)))
      return getfixnum(getreal(x));
    else
      xlbadtype(x);
    break;
  default:
    xlbadtype(x);
  }
  return 0; /* not reached */
}

LOCAL FIXTYPE getiimagpart P1C(LVAL, x)
{
  switch (ntype(x)) {
  case FIXNUM:
    return 0;
  case COMPLEX:
    if (fixp(getimag(x)))
      return getfixnum(getimag(x));
    else
      xlbadtype(x);
    break;
  default:
    xlbadtype(x);
  }
  return 0; /* not reached */
}

LOCAL FLOTYPE getdrealpart P1C(LVAL, x)
{
  switch (ntype(x)) {
  case FIXNUM:
    return (FLOTYPE) getfixnum(x);
  case FLONUM:
    return getflonum(x);
  case COMPLEX:
    return makefloat(getreal(x));
  default:
    xlbadtype(x);
  }
  return 0; /* not reached */
}

LOCAL FLOTYPE getdimagpart P1C(LVAL, x)
{
  switch (ntype(x)) {
  case FIXNUM:
  case FLONUM:
    return 0.0;
  case COMPLEX:
    return makefloat(getimag(x));
  default:
    xlbadtype(x);
  }
  return 0; /* not reached */
}

LOCAL int typecode P1C(LVAL, x)
{
  x = xlparsetype(x);
  if (x == a_char) return(CD_CHARACTER);
  else if (x == a_fixnum) return(CD_FIXTYPE);
  else if (x == a_flonum) return(CD_FLOTYPE);
  else if (consp(x) && consp(cdr(x)) && car(x) == a_complex) {
    x = xlparsetype(car(cdr(x)));
    if (x == a_fixnum) return(CD_CXFIXTYPE);
    else if (x == a_flonum) return(CD_CXFLOTYPE);
    else return(CD_TRUE);
  }
  else if (x == s_c_char) return(CD_CHAR);
  else if (x == s_c_uchar) return(CD_UCHAR);
  else if (x == s_c_short) return(CD_SHORT);
  else if (x == s_c_int) return(CD_INT);
  else if (x == s_c_long) return(CD_LONG);
  else if (x == s_c_float) return(CD_FLOAT);
  else if (x == s_c_double) return(CD_DOUBLE);
  else if (x == s_c_complex) return(CD_COMPLEX);
  else if (x == s_c_dcomplex) return(CD_DCOMPLEX);
  else return(CD_TRUE);
}

LOCAL int typesize P1C(int, code)
{
  switch(code) {
  case CD_CHARACTER: return(sizeof(char));
  case CD_FIXTYPE:   return(sizeof(FIXTYPE));
  case CD_FLOTYPE:   return(sizeof(FLOTYPE));
  case CD_CXFIXTYPE: return(2 * sizeof(FIXTYPE));
  case CD_CXFLOTYPE: return(2 * sizeof(FLOTYPE));
  case CD_CHAR:      return(sizeof(char));
  case CD_SHORT:     return(sizeof(short));
  case CD_INT:       return(sizeof(int));
  case CD_LONG:      return(sizeof(long));
  case CD_FLOAT:     return(sizeof(float));
  case CD_DOUBLE:    return(sizeof(double));
  case CD_COMPLEX:   return(2 * sizeof(float));
  case CD_DCOMPLEX:  return(2 * sizeof(double));
  default:           return(1);
  }
}

LVAL mktvec P2C(int, n, LVAL, etype)
{
  LVAL val;
  int type;

  type = typecode(etype);
  if (type == CD_TRUE)
    val = newvector(n);
  else if (type == CD_CHARACTER) {
    int i;
    val = newstring(n);
    for (i = 0; i < n; i++)
      setstringch(val, i, ' ');
  }
  else {
    val = newtvec(n, typesize(type));
    settvectype(val, type);
  }
  return(val);
}

#define settvecdataelt(c, t, i, v) (((t *) (c))[i] = ((t) (v)))
#define gettvecdataelt(c, t, i) (((t *) (c))[i])

#define CVFIX(x) cvfixnum((FIXTYPE) (x))
#define CVFLO(x) cvflonum((FLOTYPE) (x))

#define u_char unsigned char
#define u_short unsigned short
#define u_int unsigned int
#define u_long unsigned long

int gettvecsize P1C(LVAL, x)
{
  switch(ntype(x)) {
  case VECTOR: return(getsize(x));
  case STRING: return(getslength(x));
  case TVEC:   return(gettlength(x) / typesize(gettvectype(x)));
  default: xlbadtype(x);
  }
  /* not reacched */
  return 0;
}

LVAL gettvecelement P2C(LVAL, x, int, i)
{
  double rval, ival;
  FIXTYPE irval, iival;
  ALLOCTYPE *v;
  int type;

  switch (ntype(x)) {
  case VECTOR: return(getelement(x, i));
  case STRING: return(cvchar(getstringch(x, i)));
  case TVEC:
    type = gettvectype(x);
    v = gettvecdata(x);
  
    switch (type) {
    case CD_CHARACTER:  return(cvchar(gettvecdataelt(v, char, i)));
    case CD_FIXTYPE:    return(CVFIX(gettvecdataelt(v, FIXTYPE, i)));
    case CD_FLOTYPE:    return(CVFLO(gettvecdataelt(v, FLOTYPE, i)));
    case CD_CXFIXTYPE:
      irval = gettvecdataelt(v, FIXTYPE, 2 * i);
      iival = gettvecdataelt(v, FIXTYPE, 2 * i + 1);
      return(newicomplex(irval, iival));
    case CD_CXFLOTYPE:
      rval = gettvecdataelt(v, FLOTYPE, 2 * i);
      ival = gettvecdataelt(v, FLOTYPE, 2 * i + 1);
      return(newdcomplex(rval, ival));
    case CD_CHAR:       return(CVFIX(gettvecdataelt(v, char, i)));
    case CD_UCHAR:      return(CVFIX(gettvecdataelt(v, unsigned char, i)));
    case CD_SHORT:      return(CVFIX(gettvecdataelt(v, short, i)));
    case CD_INT:        return(CVFIX(gettvecdataelt(v, int, i)));
    case CD_LONG:       return(CVFIX(gettvecdataelt(v, long, i)));
    case CD_FLOAT:      return(CVFLO(gettvecdataelt(v, float, i)));
    case CD_DOUBLE:     return(CVFLO(gettvecdataelt(v, double, i)));
    case CD_COMPLEX:
      rval = gettvecdataelt(v, float, 2 * i);
      ival = gettvecdataelt(v, float, 2 * i + 1);
      return(newdcomplex(rval, ival));
    case CD_DCOMPLEX:
      rval = gettvecdataelt(v, double, 2 * i);
      ival = gettvecdataelt(v, double, 2 * i + 1);
      return(newdcomplex(rval, ival));
    default:
      xlbadtype(x);
    }
  default:
    xlbadtype(x);
  }
  /* not reached */
  return(NIL);
}

VOID settvecelement P3C(LVAL, x, int, i, LVAL, item)
{
  ALLOCTYPE *v;
  int type;

  switch (ntype(x)) {
  case VECTOR: setelement(x, i, item); break;
  case STRING:
    if (! charp(item)) xlbadtype(item);
    setstringch(x, i, getchcode(item));
    break;
  case TVEC:
    type = gettvectype(x);
    v = gettvecdata(x);

    switch (type) {
    case CD_CHARACTER:
      if (! charp(item)) xlbadtype(item);
      settvecdataelt(v, char, i, getchcode(item));
      break;
    case CD_FIXTYPE:
      if (! fixp(item)) xlbadtype(item);
      settvecdataelt(v, FIXTYPE, i, getfixnum(item));
      break;
    case CD_FLOTYPE:
      settvecdataelt(v, FLOTYPE, i, MAKEFLOAT(item));
      break;
    case CD_CXFIXTYPE:
      settvecdataelt(v, FIXTYPE, 2 * i, getirealpart(item));
      settvecdataelt(v, FIXTYPE, 2 * i + 1, getiimagpart(item));
      break;
    case CD_CXFLOTYPE:
      settvecdataelt(v, FLOTYPE, 2 * i, getdrealpart(item));
      settvecdataelt(v, FLOTYPE, 2 * i + 1, getdimagpart(item));
      break;
    case CD_CHAR:
      if (! fixp(item)) xlbadtype(item);
      settvecdataelt(v, char, i, getfixnum(item));
      break;
    case CD_UCHAR:
      if (! fixp(item)) xlbadtype(item);
      settvecdataelt(v, unsigned char, i, getfixnum(item));
      break;
    case CD_SHORT: 
      if (! fixp(item)) xlbadtype(item);
      settvecdataelt(v, short, i, getfixnum(item));
      break;
    case CD_INT:
      if (! fixp(item)) xlbadtype(item);
      settvecdataelt(v, int, i, getfixnum(item));
      break;
    case CD_LONG:
      if (! fixp(item)) xlbadtype(item);
      settvecdataelt(v, long, i, getfixnum(item));
      break;
    case CD_FLOAT:
      settvecdataelt(v, float, i, MAKEFLOAT(item));
      break;
    case CD_DOUBLE:
      settvecdataelt(v, double, i, MAKEFLOAT(item));
      break;
    case CD_COMPLEX:
      settvecdataelt(v, float, 2 * i, getdrealpart(item));
      settvecdataelt(v, float, 2 * i + 1, getdimagpart(item));
      break;
    case CD_DCOMPLEX:
      settvecdataelt(v, double, 2 * i, getdrealpart(item));
      settvecdataelt(v, double, 2 * i + 1, getdimagpart(item));
      break;
    default:
      xlbadtype(x);
    }
    break;
  default:
    xlbadtype(x);
  }
}

LVAL gettvecetype P1C(LVAL, x)
{
  switch (ntype(x)) {
  case VECTOR: return(s_true);
  case STRING: return(a_char);
  case TVEC:
    switch (gettvectype(x)) {
    case CD_CHARACTER: return(a_char);
    case CD_FIXTYPE:   return(a_fixnum);
    case CD_FLOTYPE:   return(a_flonum);
    case CD_CXFIXTYPE: return(cons(a_complex, consa(a_fixnum)));
    case CD_CXFLOTYPE: return(cons(a_complex, consa(a_flonum)));
    case CD_CHAR:      return(s_c_char);
    case CD_UCHAR:     return(s_c_uchar);
    case CD_SHORT:     return(s_c_short);
    case CD_INT:       return(s_c_int);
    case CD_LONG:      return(s_c_long);
    case CD_FLOAT:     return(s_c_float);
    case CD_DOUBLE:    return(s_c_double);
    case CD_COMPLEX:   return(s_c_complex);
    case CD_DCOMPLEX:  return(s_c_dcomplex);
    default: xlbadtype(x);
    }
  default: xlbadtype(x);
  }
  /* not reached */
  return(NIL);
}

int gettveceltsize P1C(LVAL, x)
{
  switch (ntype(x)) {
  case VECTOR: return(sizeof(LVAL));
  case STRING: return(1);
  case TVEC:   return(typesize(gettvectype(x)));
  default:     xlbadtype(x);
  }
  /* not reached */
  return(0);
}

VOID xlreplace P6C(LVAL, x, LVAL, y,
		   int, start1, int, end1,
		   int, start2, int, end2)
{
  int n1, n2, i, j;

  n1 = listp(x) ? llength(x) : gettvecsize(x);
  n2 = listp(y) ? llength(y) : gettvecsize(y);
  if (! (0 <= start1 && start1 <= end1 && end1 <= n1) ||
      ! (0 <= start2 && start2 <= end2 && end2 <= n2))
    xlfail("range error");

  /* adjust list arguments */
  if (consp(x))
    while (start1 > 0) { start1--; end1--; x = cdr(x); }
  if (consp(y))
    while (start2 > 0) { start2--; end2--; y = cdr(y); }

  /* quick return for no copying */
  if (start1 >= end1 || start2 >= end2)
    return;

  /* do the replacement */
  switch (ntype(x)) {
  case CONS:
    switch (ntype(y)) {
    case CONS:
      for (; end1 > 0 && end2 > 0; x = cdr(x), y = cdr(y), end1--, end2--)
	rplaca(x, car(y));
      break;
    case VECTOR:
      for (i = start2; end1 > 0 && i < end2; x = cdr(x), end1--, i++)
	rplaca(x, getelement(y, i));
      break;
    case STRING:
    case TVEC:
      for (i = start2; end1 > 0 && i < end2; x = cdr(x), end1--, i++)
	rplaca(x, gettvecelement(y, i));
      break;
    default:
      xlbadtype(y);
    }
    break;
  case VECTOR:
    switch (ntype(y)) {
    case CONS:
      for (i = start1; i < end1 && end2 > 0; i++, y = cdr(y), end2--)
	setelement(x, i, car(y));
      break;
    case VECTOR:
      for (i = start1, j = start2; i < end1 && j < end2; i++, j++)
	setelement(x, i, getelement(y, j));
      break;
    case STRING:
    case TVEC:
      for (i = start1, j = start2; i < end1 && j < end2; i++, j++)
	setelement(x, i, gettvecelement(y, j));
      break;
    default:
      xlbadtype(y);
    }
    break;
  case STRING:
  case TVEC:
    switch (ntype(y)) {
    case CONS:
      for (i = start1; i < end1 && end2 > 0; i++, y = cdr(y), end2--)
	settvecelement(x, i, car(y));
      break;
    case VECTOR:
      for (i = start1, j = start2; i < end1 && j < end2; i++, j++)
	settvecelement(x, i, getelement(y, j));
      break;
    case STRING:
    case TVEC:
      if (gettvectype(x) == gettvectype(y)) {
	char *dx, *dy;
	int nbytes;
	dx = ((char *) gettvecdata(x)) + start1;
	dy = ((char *) gettvecdata(y)) + start2;
	end1 -= start1;
	end2 -= start2;
	nbytes = ((end1 < end2) ? end1 : end2) * typesize(gettvectype(x));
	MEMCPY(dx, dy, nbytes);
      }
      else
	for (i = start1, j = start2; i < end1 && j < end2; i++, j++)
	  settvecelement(x, i, gettvecelement(y, j));
      break;
    default:
      xlbadtype(y);
    }
    break;
  default:
    xlbadtype(x);
  }

  return;
}

LVAL xtveceltsize(V)
{
  LVAL x = xlgetarg();
  xllastarg();
  return cvfixnum(gettveceltsize(x));
}


syntax highlighted by Code2HTML, v. 0.9.1