

/*
 *                   COPYRIGHT (c) 1988-1994 BY                             *
 *        PARADIGM ASSOCIATES INCORPORATED, CAMBRIDGE, MASSACHUSETTS.       *
 *        See the source file SLIB.C for more information.                  *

 Array-hacking code moved to another source file.

 */

#include <stdio.h>
#include <string.h>
#include <setjmp.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>
#include <math.h>

#include "siod.h"
#include "siodp.h"

static void
init_sliba_version (void)
{
  setvar (cintern ("*sliba-version*"),
	  cintern ("$Id$"),
	  NIL);
}

static LISP sym_plists = NIL;
static LISP bashnum = NIL;
static LISP sym_e = NIL;
static LISP sym_f = NIL;

void
init_storage_a1 (long type)
{
  long j;
  struct user_type_hooks *p;
  set_gc_hooks (type,
		array_gc_relocate,
		array_gc_mark,
		array_gc_scan,
		array_gc_free,
		&j);
  set_print_hooks (type, array_prin1);
  p = get_user_type_hooks (type);
  p->fast_print = array_fast_print;
  p->fast_read = array_fast_read;
  p->equal = array_equal;
  p->c_sxhash = array_sxhash;
}

void
init_storage_a (void)
{
  gc_protect (&bashnum);
  bashnum = newcell (tc_flonum);
  init_storage_a1 (tc_string);
  init_storage_a1 (tc_double_array);
  init_storage_a1 (tc_long_array);
  init_storage_a1 (tc_lisp_array);
  init_storage_a1 (tc_byte_array);
}

LISP
array_gc_relocate (LISP ptr)
{
  LISP nw;
  if ((nw = heap) >= heap_end)
    gc_fatal_error ();
  heap = nw + 1;
  memcpy (nw, ptr, sizeof (struct obj));
  return (nw);
}

void
array_gc_scan (LISP ptr)
{
  long j;
  if TYPEP
    (ptr, tc_lisp_array)
      for (j = 0; j < ptr->storage_as.lisp_array.dim; ++j)
      ptr->storage_as.lisp_array.data[j] =
	gc_relocate (ptr->storage_as.lisp_array.data[j]);
}

LISP
array_gc_mark (LISP ptr)
{
  long j;
  if TYPEP
    (ptr, tc_lisp_array)
      for (j = 0; j < ptr->storage_as.lisp_array.dim; ++j)
      gc_mark (ptr->storage_as.lisp_array.data[j]);
  return (NIL);
}

void
array_gc_free (LISP ptr)
{
  switch (ptr->type)
    {
    case tc_string:
    case tc_byte_array:
      free (ptr->storage_as.string.data);
      break;
    case tc_double_array:
      free (ptr->storage_as.double_array.data);
      break;
    case tc_long_array:
      free (ptr->storage_as.long_array.data);
      break;
    case tc_lisp_array:
      free (ptr->storage_as.lisp_array.data);
      break;
    }
}

void
array_prin1 (LISP ptr, struct gen_printio *f)
{
  int j;
  switch (ptr->type)
    {
    case tc_string:
      gput_st (f, "\"");
      if (strcspn (ptr->storage_as.string.data, "\"\\\n\r\t") ==
	  strlen (ptr->storage_as.string.data))
	gput_st (f, ptr->storage_as.string.data);
      else
	{
	  int n, c;
	  char cbuff[3];
	  n = strlen (ptr->storage_as.string.data);
	  for (j = 0; j < n; ++j)
	    switch (c = ptr->storage_as.string.data[j])
	      {
	      case '\\':
	      case '"':
		cbuff[0] = '\\';
		cbuff[1] = c;
		cbuff[2] = 0;
		gput_st (f, cbuff);
		break;
	      case '\n':
		gput_st (f, "\\n");
		break;
	      case '\r':
		gput_st (f, "\\r");
		break;
	      case '\t':
		gput_st (f, "\\t");
		break;
	      default:
		cbuff[0] = c;
		cbuff[1] = 0;
		gput_st (f, cbuff);
		break;
	      }
	}
      gput_st (f, "\"");
      break;
    case tc_double_array:
      gput_st (f, "#(");
      for (j = 0; j < ptr->storage_as.double_array.dim; ++j)
	{
	  sprintf (tkbuffer, "%g", ptr->storage_as.double_array.data[j]);
	  gput_st (f, tkbuffer);
	  if ((j + 1) < ptr->storage_as.double_array.dim)
	    gput_st (f, " ");
	}
      gput_st (f, ")");
      break;
    case tc_long_array:
      gput_st (f, "#(");
      for (j = 0; j < ptr->storage_as.long_array.dim; ++j)
	{
	  sprintf (tkbuffer, "%ld", ptr->storage_as.long_array.data[j]);
	  gput_st (f, tkbuffer);
	  if ((j + 1) < ptr->storage_as.long_array.dim)
	    gput_st (f, " ");
	}
      gput_st (f, ")");
    case tc_byte_array:
      sprintf (tkbuffer, "#%ld\"", ptr->storage_as.string.dim);
      gput_st (f, tkbuffer);
      for (j = 0; j < ptr->storage_as.string.dim; ++j)
	{
	  sprintf (tkbuffer, "%02x", ptr->storage_as.string.data[j] & 0xFF);
	  gput_st (f, tkbuffer);
	}
      gput_st (f, "\"");
      break;
    case tc_lisp_array:
      gput_st (f, "#(");
      for (j = 0; j < ptr->storage_as.lisp_array.dim; ++j)
	{
	  lprin1g (ptr->storage_as.lisp_array.data[j], f);
	  if ((j + 1) < ptr->storage_as.lisp_array.dim)
	    gput_st (f, " ");
	}
      gput_st (f, ")");
      break;
    }
}

LISP
strcons (long length, char *data)
{
  long flag;
  LISP s;
  flag = no_interrupt (1);
  s = cons (NIL, NIL);
  s->type = tc_string;
  if (length == -1)
    length = strlen (data);
  s->storage_as.string.data = must_malloc (length + 1);
  s->storage_as.string.dim = length;
  if (data)
    memcpy (s->storage_as.string.data, data, length);
  s->storage_as.string.data[length] = 0;
  no_interrupt (flag);
  return (s);
}

int
rfs_getc (unsigned char **p)
{
  int i;
  i = **p;
  if (!i)
    return (EOF);
  *p = *p + 1;
  return (i);
}

void
rfs_ungetc (unsigned char c, unsigned char **p)
{
  *p = *p - 1;
}

LISP
read_from_string (LISP x)
{
  char *p;
  struct gen_readio s;
  p = get_c_string (x);
  s.getc_fcn = (int (*)(void *)) rfs_getc;
  s.ungetc_fcn = (void (*)(int, void *)) rfs_ungetc;
  s.cb_argument = (char *) &p;
  return (readtl (&s));
}

int
pts_puts (char *from, void *cb)
{
  LISP into;
  size_t fromlen, intolen, intosize, fitsize;
  into = (LISP) cb;
  fromlen = strlen (from);
  intolen = strlen (into->storage_as.string.data);
  intosize = into->storage_as.string.dim - intolen;
  fitsize = (fromlen < intosize) ? fromlen : intosize;
  memcpy (&into->storage_as.string.data[intolen], from, fitsize);
  into->storage_as.string.data[intolen + fitsize] = 0;
  if (fitsize < fromlen)
    err ("print to string overflow", NIL);
  return (1);
}

LISP
err_wta_str (LISP exp)
{
  return (err ("not a string", exp));
}

LISP
print_to_string (LISP exp, LISP str, LISP nostart)
{
  struct gen_printio s;
  if NTYPEP
    (str, tc_string) err_wta_str (str);
  s.putc_fcn = NULL;
  s.puts_fcn = pts_puts;
  s.cb_argument = str;
  if NULLP
    (nostart)
      str->storage_as.string.data[0] = 0;
  lprin1g (exp, &s);
  return (str);
}

LISP
aref1 (LISP a, LISP i)
{
  long k;
  if NFLONUMP
    (i) err ("bad index to aref", i);
  k = (long) FLONM (i);
  if (k < 0)
    err ("negative index to aref", i);
  switch TYPE
    (a)
    {
    case tc_string:
    case tc_byte_array:
      if (k >= a->storage_as.string.dim)
	err ("index too large", i);
      return (flocons ((double) a->storage_as.string.data[k]));
    case tc_double_array:
      if (k >= a->storage_as.double_array.dim)
	err ("index too large", i);
      return (flocons (a->storage_as.double_array.data[k]));
    case tc_long_array:
      if (k >= a->storage_as.long_array.dim)
	err ("index too large", i);
      return (flocons (a->storage_as.long_array.data[k]));
    case tc_lisp_array:
      if (k >= a->storage_as.lisp_array.dim)
	err ("index too large", i);
      return (a->storage_as.lisp_array.data[k]);
    default:
      return (err ("invalid argument to aref", a));
    }
}

void
err1_aset1 (LISP i)
{
  err ("index to aset too large", i);
}

void
err2_aset1 (LISP v)
{
  err ("bad value to store in array", v);
}

LISP
aset1 (LISP a, LISP i, LISP v)
{
  long k;
  if NFLONUMP
    (i) err ("bad index to aset", i);
  k = (long) FLONM (i);
  if (k < 0)
    err ("negative index to aset", i);
  switch TYPE
    (a)
    {
    case tc_string:
    case tc_byte_array:
      if NFLONUMP
	(v) err2_aset1 (v);
      if (k >= a->storage_as.string.dim)
	err1_aset1 (i);
      a->storage_as.string.data[k] = (char) FLONM (v);
      return (v);
    case tc_double_array:
      if NFLONUMP
	(v) err2_aset1 (v);
      if (k >= a->storage_as.double_array.dim)
	err1_aset1 (i);
      a->storage_as.double_array.data[k] = FLONM (v);
      return (v);
    case tc_long_array:
      if NFLONUMP
	(v) err2_aset1 (v);
      if (k >= a->storage_as.long_array.dim)
	err1_aset1 (i);
      a->storage_as.long_array.data[k] = (long) FLONM (v);
      return (v);
    case tc_lisp_array:
      if (k >= a->storage_as.lisp_array.dim)
	err1_aset1 (i);
      a->storage_as.lisp_array.data[k] = v;
      return (v);
    default:
      return (err ("invalid argument to aset", a));
    }
}

LISP
arcons (long typecode, long n, long initp)
{
  LISP a;
  long flag, j;
  flag = no_interrupt (1);
  a = cons (NIL, NIL);
  switch (typecode)
    {
    case tc_double_array:
      a->storage_as.double_array.dim = n;
      a->storage_as.double_array.data = (double *) must_malloc (n *
							   sizeof (double));
      if (initp)
	for (j = 0; j < n; ++j)
	  a->storage_as.double_array.data[j] = 0.0;
      break;
    case tc_long_array:
      a->storage_as.long_array.dim = n;
      a->storage_as.long_array.data = (long *) must_malloc (n * sizeof (long));
      if (initp)
	for (j = 0; j < n; ++j)
	  a->storage_as.long_array.data[j] = 0;
      break;
    case tc_string:
      a->storage_as.string.dim = n;
      a->storage_as.string.data = (char *) must_malloc (n + 1);
      a->storage_as.string.data[n] = 0;
      if (initp)
	for (j = 0; j < n; ++j)
	  a->storage_as.string.data[j] = ' ';
    case tc_byte_array:
      a->storage_as.string.dim = n;
      a->storage_as.string.data = (char *) must_malloc (n);
      if (initp)
	for (j = 0; j < n; ++j)
	  a->storage_as.string.data[j] = 0;
      break;
    case tc_lisp_array:
      a->storage_as.lisp_array.dim = n;
      a->storage_as.lisp_array.data = (LISP *) must_malloc (n * sizeof (LISP));
      for (j = 0; j < n; ++j)
	a->storage_as.lisp_array.data[j] = NIL;
      break;
    default:
      errswitch ();
    }
  a->type = typecode;
  no_interrupt (flag);
  return (a);
}

LISP
mallocl (void *place, long size)
{
  long n, r;
  LISP retval;
  n = size / sizeof (long);
  r = size % sizeof (long);
  if (r)
    ++n;
  retval = arcons (tc_long_array, n, 0);
  *(long **) place = retval->storage_as.long_array.data;
  return (retval);
}

LISP
cons_array (LISP dim, LISP kind)
{
  LISP a;
  long flag, n, j;
  if (NFLONUMP (dim) || (FLONM (dim) < 0))
    return (err ("bad dimension to cons-array", dim));
  else
    n = (long) FLONM (dim);
  flag = no_interrupt (1);
  a = cons (NIL, NIL);
  if EQ
    (cintern ("double"), kind)
    {
      a->type = tc_double_array;
      a->storage_as.double_array.dim = n;
      a->storage_as.double_array.data = (double *) must_malloc (n *
							   sizeof (double));
      for (j = 0; j < n; ++j)
	a->storage_as.double_array.data[j] = 0.0;
    }
  else if EQ
    (cintern ("long"), kind)
    {
      a->type = tc_long_array;
      a->storage_as.long_array.dim = n;
      a->storage_as.long_array.data = (long *) must_malloc (n * sizeof (long));
      for (j = 0; j < n; ++j)
	a->storage_as.long_array.data[j] = 0;
    }
  else if EQ
    (cintern ("string"), kind)
    {
      a->type = tc_string;
      a->storage_as.string.dim = n;
      a->storage_as.string.data = (char *) must_malloc (n + 1);
      a->storage_as.string.data[n] = 0;
      for (j = 0; j < n; ++j)
	a->storage_as.string.data[j] = ' ';
    }
  else if EQ
    (cintern ("byte"), kind)
    {
      a->type = tc_byte_array;
      a->storage_as.string.dim = n;
      a->storage_as.string.data = (char *) must_malloc (n);
      for (j = 0; j < n; ++j)
	a->storage_as.string.data[j] = 0;
    }
  else if (EQ (cintern ("lisp"), kind) || NULLP (kind))
    {
      a->type = tc_lisp_array;
      a->storage_as.lisp_array.dim = n;
      a->storage_as.lisp_array.data = (LISP *) must_malloc (n * sizeof (LISP));
      for (j = 0; j < n; ++j)
	a->storage_as.lisp_array.data[j] = NIL;
    }
  else
    err ("bad type of array", kind);
  no_interrupt (flag);
  return (a);
}

LISP
string_append (LISP args)
{
  long size;
  LISP l, s;
  char *data;
  size = 0;
  for (l = args; NNULLP (l); l = cdr (l))
    size += strlen (get_c_string (car (l)));
  s = strcons (size, NULL);
  data = s->storage_as.string.data;
  data[0] = 0;
  for (l = args; NNULLP (l); l = cdr (l))
    strcat (data, get_c_string (car (l)));
  return (s);
}

LISP
bytes_append (LISP args)
{
  long size, n, j;
  LISP l, s;
  char *data, *ptr;
  size = 0;
  for (l = args; NNULLP (l); l = cdr (l))
    {
      get_c_string_dim (car (l), &n);
      size += n;
    }
  s = arcons (tc_byte_array, size, 0);
  data = s->storage_as.string.data;
  for (j = 0, l = args; NNULLP (l); l = cdr (l))
    {
      ptr = get_c_string_dim (car (l), &n);
      memcpy (&data[j], ptr, n);
      j += n;
    }
  return (s);
}

LISP
substring (LISP str, LISP start, LISP end)
{
  long s, e, n;
  char *data;
  data = get_c_string_dim (str, &n);
  s = get_c_long (start);
  if NULLP
    (end)
      e = n;
  else
    e = get_c_long (end);
  if ((s < 0) || (s > e))
    err ("bad start index", start);
  if ((e < 0) || (e > n))
    err ("bad end index", end);
  return (strcons (e - s, &data[s]));
}

LISP
string_search (LISP token, LISP str)
{
  char *s1, *s2, *ptr;
  s1 = get_c_string (str);
  s2 = get_c_string (token);
  ptr = strstr (s1, s2);
  if (ptr)
    return (flocons (ptr - s1));
  else
    return (NIL);
}

#define IS_TRIM_SPACE(_x) (strchr(" \t\r\n",(_x)))

LISP
string_trim (LISP str)
{
  char *start, *end; /*, *sp = " \t\r\n";*/
  start = get_c_string (str);
  while (*start && IS_TRIM_SPACE (*start))
    ++start;
  end = &start[strlen (start)];
  while ((end > start) && IS_TRIM_SPACE (*(end - 1)))
    --end;
  return (strcons (end - start, start));
}

LISP
string_trim_left (LISP str)
{
  char *start, *end;
  start = get_c_string (str);
  while (*start && IS_TRIM_SPACE (*start))
    ++start;
  end = &start[strlen (start)];
  return (strcons (end - start, start));
}

LISP
string_trim_right (LISP str)
{
  char *start, *end;
  start = get_c_string (str);
  end = &start[strlen (start)];
  while ((end > start) && IS_TRIM_SPACE (*(end - 1)))
    --end;
  return (strcons (end - start, start));
}

LISP
string_upcase (LISP str)
{
  LISP result;
  char *s1, *s2;
  long j, n;
  s1 = get_c_string (str);
  n = strlen (s1);
  result = strcons (n, s1);
  s2 = get_c_string (result);
  for (j = 0; j < n; ++j)
    s2[j] = toupper (s2[j]);
  return (result);
}

LISP
string_downcase (LISP str)
{
  LISP result;
  char *s1, *s2;
  long j, n;
  s1 = get_c_string (str);
  n = strlen (s1);
  result = strcons (n, s1);
  s2 = get_c_string (result);
  for (j = 0; j < n; ++j)
    s2[j] = tolower (s2[j]);
  return (result);
}

LISP
lreadstring (struct gen_readio * f)
{
  int j, c, n;
  char *p;
  j = 0;
  p = tkbuffer;
  while (((c = GETC_FCN (f)) != '"') && (c != EOF))
    {
      if (c == '\\')
	{
	  c = GETC_FCN (f);
	  if (c == EOF)
	    err ("eof after \\", NIL);
	  switch (c)
	    {
	    case 'n':
	      c = '\n';
	      break;
	    case 't':
	      c = '\t';
	      break;
	    case 'r':
	      c = '\r';
	      break;
	    case 'd':
	      c = 0x04;
	      break;
	    case 'N':
	      c = 0;
	      break;
	    case 's':
	      c = ' ';
	      break;
	    case '0':
	      n = 0;
	      while (1)
		{
		  c = GETC_FCN (f);
		  if (c == EOF)
		    err ("eof after \\0", NIL);
		  if (isdigit (c))
		    n = n * 8 + c - '0';
		  else
		    {
		      UNGETC_FCN (c, f);
		      break;
		    }
		}
	      c = n;
	    }
	}
      if ((j + 1) >= TKBUFFERN)
	err ("read string overflow", NIL);
      ++j;
      *p++ = c;
    }
  *p = 0;
  return (strcons (j, tkbuffer));
}


LISP
lreadsharp (struct gen_readio * f)
{
  LISP obj, l, result;
  long j, n;
  int c;
  c = GETC_FCN (f);
  switch (c)
    {
    case '(':
      UNGETC_FCN (c, f);
      obj = lreadr (f);
      n = nlength (obj);
      result = arcons (tc_lisp_array, n, 1);
      for (l = obj, j = 0; j < n; l = cdr (l), ++j)
	result->storage_as.lisp_array.data[j] = car (l);
      return (result);
    case '.':
      obj = lreadr (f);
      return (leval (obj, NIL));
    case 'f':
      return (NIL);
    case 't':
      return (flocons (1));
    default:
      return (err ("readsharp syntax not handled", NIL));
    }
}

#define HASH_COMBINE(_h1,_h2,_mod) ((((_h1) * 17 + 1) ^ (_h2)) % (_mod))

long
c_sxhash (LISP obj, long n)
{
  long hash;
  unsigned char *s;
  LISP tmp;
  struct user_type_hooks *p;
  STACK_CHECK (&obj);
  INTERRUPT_CHECK ();
  switch TYPE
    (obj)
    {
    case tc_nil:
      return (0);
    case tc_cons:
      hash = c_sxhash (CAR (obj), n);
      for (tmp = CDR (obj); CONSP (tmp); tmp = CDR (tmp))
	hash = HASH_COMBINE (hash, c_sxhash (CAR (tmp), n), n);
      hash = HASH_COMBINE (hash, c_sxhash (tmp, n), n);
      return (hash);
    case tc_symbol:
      for (hash = 0, s = (unsigned char *) PNAME (obj); *s; ++s)
	hash = HASH_COMBINE (hash, *s, n);
      return (hash);
    case tc_subr_0:
    case tc_subr_1:
    case tc_subr_2:
    case tc_subr_3:
    case tc_subr_4:
    case tc_subr_5:
    case tc_lsubr:
    case tc_fsubr:
    case tc_msubr:
      for (hash = 0, s = (unsigned char *) obj->storage_as.subr.name; *s; ++s)
	hash = HASH_COMBINE (hash, *s, n);
      return (hash);
    case tc_flonum:
      return (((unsigned long) FLONM (obj)) % n);
    default:
      p = get_user_type_hooks (TYPE (obj));
      if (p->c_sxhash)
	return ((*p->c_sxhash) (obj, n));
      else
	return (0);
    }
}

LISP
sxhash (LISP obj, LISP n)
{
  return (flocons (c_sxhash (obj, FLONUMP (n) ? (long) FLONM (n) : 10000)));
}

LISP
equal (LISP a, LISP b)
{
  struct user_type_hooks *p;
  long atype;
  STACK_CHECK (&a);
loop:
  INTERRUPT_CHECK ();
  if EQ
    (a, b) return (sym_t);
  atype = TYPE (a);
  if (atype != TYPE (b))
    return (NIL);
  switch (atype)
    {
    case tc_cons:
      if NULLP
	(equal (car (a), car (b))) return (NIL);
      a = cdr (a);
      b = cdr (b);
      goto loop;
    case tc_flonum:
      return ((FLONM (a) == FLONM (b)) ? sym_t : NIL);
    case tc_symbol:
      return (NIL);
    default:
      p = get_user_type_hooks (atype);
      if (p->equal)
	return ((*p->equal) (a, b));
      else
	return (NIL);
    }
}

LISP
array_equal (LISP a, LISP b)
{
  long j, len;
  switch (TYPE (a))
    {
    case tc_string:
    case tc_byte_array:
      len = a->storage_as.string.dim;
      if (len != b->storage_as.string.dim)
	return (NIL);
      if (memcmp (a->storage_as.string.data, b->storage_as.string.data, len) == 0)
	return (sym_t);
      else
	return (NIL);
    case tc_long_array:
      len = a->storage_as.long_array.dim;
      if (len != b->storage_as.long_array.dim)
	return (NIL);
      if (memcmp (a->storage_as.long_array.data,
		  b->storage_as.long_array.data,
		  len * sizeof (long)) == 0)
	  return (sym_t);
      else
	return (NIL);
    case tc_double_array:
      len = a->storage_as.double_array.dim;
      if (len != b->storage_as.double_array.dim)
	return (NIL);
      for (j = 0; j < len; ++j)
	if (a->storage_as.double_array.data[j] !=
	    b->storage_as.double_array.data[j])
	  return (NIL);
      return (sym_t);
    case tc_lisp_array:
      len = a->storage_as.lisp_array.dim;
      if (len != b->storage_as.lisp_array.dim)
	return (NIL);
      for (j = 0; j < len; ++j)
	if NULLP
	  (equal (a->storage_as.lisp_array.data[j],
		  b->storage_as.lisp_array.data[j]))
	    return (NIL);
      return (sym_t);
    default:
      return (errswitch ());
    }
}

long
array_sxhash (LISP a, long n)
{
  long j, len, hash;
  unsigned char *char_data;
  unsigned long *long_data;
  double *double_data;
  switch (TYPE (a))
    {
    case tc_string:
    case tc_byte_array:
      len = a->storage_as.string.dim;
      for (j = 0, hash = 0, char_data = (unsigned char *) a->storage_as.string.data;
	   j < len;
	   ++j, ++char_data)
	hash = HASH_COMBINE (hash, *char_data, n);
      return (hash);
    case tc_long_array:
      len = a->storage_as.long_array.dim;
      for (j = 0, hash = 0, long_data = (unsigned long *) a->storage_as.long_array.data;
	   j < len;
	   ++j, ++long_data)
	hash = HASH_COMBINE (hash, *long_data % n, n);
      return (hash);
    case tc_double_array:
      len = a->storage_as.double_array.dim;
      for (j = 0, hash = 0, double_data = a->storage_as.double_array.data;
	   j < len;
	   ++j, ++double_data)
	hash = HASH_COMBINE (hash, (unsigned long) *double_data % n, n);
      return (hash);
    case tc_lisp_array:
      len = a->storage_as.lisp_array.dim;
      for (j = 0, hash = 0; j < len; ++j)
	hash = HASH_COMBINE (hash,
			     c_sxhash (a->storage_as.lisp_array.data[j], n),
			     n);
      return (hash);
    default:
      errswitch ();
      return (0);
    }
}

long
href_index (LISP table, LISP key)
{
  long index;
  if NTYPEP
    (table, tc_lisp_array) err ("not a hash table", table);
  index = c_sxhash (key, table->storage_as.lisp_array.dim);
  if ((index < 0) || (index >= table->storage_as.lisp_array.dim))
    {
      err ("sxhash inconsistency", table);
      return (0);
    }
  else
    return (index);
}

LISP
href (LISP table, LISP key)
{
  return (cdr (assoc (key,
	      table->storage_as.lisp_array.data[href_index (table, key)])));
}

LISP
hset (LISP table, LISP key, LISP value)
{
  long index;
  LISP cell, l;
  index = href_index (table, key);
  l = table->storage_as.lisp_array.data[index];
  if NNULLP
    (cell = assoc (key, l))
      return (setcdr (cell, value));
  cell = cons (key, value);
  table->storage_as.lisp_array.data[index] = cons (cell, l);
  return (value);
}

LISP
assoc (LISP x, LISP alist)
{
  LISP l, tmp;
  for (l = alist; CONSP (l); l = CDR (l))
    {
      tmp = CAR (l);
      if (CONSP (tmp) && equal (CAR (tmp), x))
	return (tmp);
      INTERRUPT_CHECK ();
    }
  if EQ
    (l, NIL) return (NIL);
  return (err ("improper list to assoc", alist));
}

LISP
assv (LISP x, LISP alist)
{
  LISP l, tmp;
  for (l = alist; CONSP (l); l = CDR (l))
    {
      tmp = CAR (l);
      if (CONSP (tmp) && NNULLP (eql (CAR (tmp), x)))
	return (tmp);
      INTERRUPT_CHECK ();
    }
  if EQ
    (l, NIL) return (NIL);
  return (err ("improper list to assv", alist));
}

void
put_long (long i, FILE * f)
{
  fwrite (&i, sizeof (long), 1, f);
}

long
get_long (FILE * f)
{
  long i;
  fread (&i, sizeof (long), 1, f);
  return (i);
}

long
fast_print_table (LISP obj, LISP table)
{
  FILE *f;
  LISP ht, index;
  f = get_c_file (car (table), (FILE *) NULL);
  if NULLP
    (ht = car (cdr (table)))
      return (1);
  index = href (ht, obj);
  if NNULLP
    (index)
    {
      putc (FO_fetch, f);
      put_long (get_c_long (index), f);
      return (0);
    }
  if NULLP
    (index = car (cdr (cdr (table))))
      return (1);
  hset (ht, obj, index);
  FLONM (bashnum) = 1.0;
  setcar (cdr (cdr (table)), plus (index, bashnum));
  putc (FO_store, f);
  put_long (get_c_long (index), f);
  return (1);
}

LISP
fast_print (LISP obj, LISP table)
{
  FILE *f;
  long len;
  LISP tmp;
  struct user_type_hooks *p;
  STACK_CHECK (&obj);
  f = get_c_file (car (table), (FILE *) NULL);
  switch (TYPE (obj))
    {
    case tc_nil:
      putc (tc_nil, f);
      return (NIL);
    case tc_cons:
      for (len = 0, tmp = obj; CONSP (tmp); tmp = CDR (tmp))
	{
	  INTERRUPT_CHECK ();
	  ++len;
	}
      if (len == 1)
	{
	  putc (tc_cons, f);
	  fast_print (car (obj), table);
	  fast_print (cdr (obj), table);
	}
      else if NULLP
	(tmp)
	{
	  putc (FO_list, f);
	  put_long (len, f);
	  for (tmp = obj; CONSP (tmp); tmp = CDR (tmp))
	    fast_print (CAR (tmp), table);
	}
      else
	{
	  putc (FO_listd, f);
	  put_long (len, f);
	  for (tmp = obj; CONSP (tmp); tmp = CDR (tmp))
	    fast_print (CAR (tmp), table);
	  fast_print (tmp, table);
	}
      return (NIL);
    case tc_flonum:
      putc (tc_flonum, f);
      fwrite (&obj->storage_as.flonum.data,
	      sizeof (obj->storage_as.flonum.data),
	      1,
	      f);
      return (NIL);
    case tc_symbol:
      if (fast_print_table (obj, table))
	{
	  putc (tc_symbol, f);
	  len = strlen (PNAME (obj));
	  if (len >= TKBUFFERN)
	    err ("symbol name too long", obj);
	  put_long (len, f);
	  fwrite (PNAME (obj), len, 1, f);
	  return (sym_t);
	}
      else
	return (NIL);
    default:
      p = get_user_type_hooks (TYPE (obj));
      if (p->fast_print)
	return ((*p->fast_print) (obj, table));
      else
	return (err ("cannot fast-print", obj));
    }
}

LISP
fast_read (LISP table)
{
  FILE *f;
  LISP tmp, l;
  struct user_type_hooks *p;
  int c;
  long len;
  f = get_c_file (car (table), (FILE *) NULL);
  c = getc (f);
  if (c == EOF)
    return (table);
  switch (c)
    {
    case FO_comment:
      while ((c = getc (f)))
	switch (c)
	  {
	  case EOF:
	    return (table);
	  case '\n':
	    return (fast_read (table));
	  }
    case FO_fetch:
      len = get_long (f);
      FLONM (bashnum) = len;
      return (href (car (cdr (table)), bashnum));
    case FO_store:
      len = get_long (f);
      tmp = fast_read (table);
      hset (car (cdr (table)), flocons (len), tmp);
      return (tmp);
    case tc_nil:
      return (NIL);
    case tc_cons:
      tmp = fast_read (table);
      return (cons (tmp, fast_read (table)));
    case FO_list:
    case FO_listd:
      len = get_long (f);
      FLONM (bashnum) = len;
      l = make_list (bashnum, NIL);
      tmp = l;
      while (len > 1)
	{
	  CAR (tmp) = fast_read (table);
	  tmp = CDR (tmp);
	  --len;
	}
      CAR (tmp) = fast_read (table);
      if (c == FO_listd)
	CDR (tmp) = fast_read (table);
      return (l);
    case tc_flonum:
      tmp = newcell (tc_flonum);
      fread (&tmp->storage_as.flonum.data,
	     sizeof (tmp->storage_as.flonum.data),
	     1,
	     f);
      return (tmp);
    case tc_symbol:
      len = get_long (f);
      if (len >= TKBUFFERN)
	err ("symbol name too long", NIL);
      fread (tkbuffer, len, 1, f);
      tkbuffer[len] = 0;
      return (rintern (tkbuffer));
    default:
      p = get_user_type_hooks (c);
      if (p->fast_read)
	return (*p->fast_read) (c, table);
      else
	return (err ("unknown fast-read opcode", flocons (c)));
    }
}

LISP
array_fast_print (LISP ptr, LISP table)
{
  int j, len;
  FILE *f;
  f = get_c_file (car (table), (FILE *) NULL);
  switch (ptr->type)
    {
    case tc_string:
    case tc_byte_array:
      putc (ptr->type, f);
      len = ptr->storage_as.string.dim;
      put_long (len, f);
      fwrite (ptr->storage_as.string.data, len, 1, f);
      return (NIL);
    case tc_double_array:
      putc (tc_double_array, f);
      len = ptr->storage_as.double_array.dim * sizeof (double);
      put_long (len, f);
      fwrite (ptr->storage_as.double_array.data, len, 1, f);
      return (NIL);
    case tc_long_array:
      putc (tc_long_array, f);
      len = ptr->storage_as.long_array.dim * sizeof (long);
      put_long (len, f);
      fwrite (ptr->storage_as.long_array.data, len, 1, f);
      return (NIL);
    case tc_lisp_array:
      putc (tc_lisp_array, f);
      len = ptr->storage_as.lisp_array.dim;
      put_long (len, f);
      for (j = 0; j < len; ++j)
	fast_print (ptr->storage_as.lisp_array.data[j], table);
      return (NIL);
    default:
      return (errswitch ());
    }
}

LISP
array_fast_read (int code, LISP table)
{
  long j, len, iflag;
  FILE *f;
  LISP ptr;
  f = get_c_file (car (table), (FILE *) NULL);
  switch (code)
    {
    case tc_string:
      len = get_long (f);
      ptr = strcons (len, NULL);
      fread (ptr->storage_as.string.data, len, 1, f);
      ptr->storage_as.string.data[len] = 0;
      return (ptr);
    case tc_byte_array:
      len = get_long (f);
      iflag = no_interrupt (1);
      ptr = newcell (tc_byte_array);
      ptr->storage_as.string.dim = len;
      ptr->storage_as.string.data =
	(char *) must_malloc (len);
      fread (ptr->storage_as.string.data, len, 1, f);
      no_interrupt (iflag);
      return (ptr);
    case tc_double_array:
      len = get_long (f);
      iflag = no_interrupt (1);
      ptr = newcell (tc_double_array);
      ptr->storage_as.double_array.dim = len;
      ptr->storage_as.double_array.data =
	(double *) must_malloc (len * sizeof (double));
      fread (ptr->storage_as.double_array.data, sizeof (double), len, f);
      no_interrupt (iflag);
      return (ptr);
    case tc_long_array:
      len = get_long (f);
      iflag = no_interrupt (1);
      ptr = newcell (tc_long_array);
      ptr->storage_as.long_array.dim = len;
      ptr->storage_as.long_array.data =
	(long *) must_malloc (len * sizeof (long));
      fread (ptr->storage_as.long_array.data, sizeof (long), len, f);
      no_interrupt (iflag);
      return (ptr);
    case tc_lisp_array:
      len = get_long (f);
      FLONM (bashnum) = len;
      ptr = cons_array (bashnum, NIL);
      for (j = 0; j < len; ++j)
	ptr->storage_as.lisp_array.data[j] = fast_read (table);
      return (ptr);
    default:
      return (errswitch ());
    }
}

long
get_c_long (LISP x)
{
  if NFLONUMP
    (x) err ("not a number", x);
  return ((long) FLONM (x));
}

double
get_c_double (LISP x)
{
  if NFLONUMP
    (x) err ("not a number", x);
  return (FLONM (x));
}

LISP
make_list (LISP x, LISP v)
{
  long n;
  LISP l;
  n = get_c_long (x);
  l = NIL;
  while (n > 0)
    {
      l = cons (v, l);
      --n;
    }
  return (l);
}

LISP
lfread (LISP size, LISP file)
{
  long flag, n, ret, m;
  char *buffer;
  LISP s;
  FILE *f;
  f = get_c_file (file, stdin);
  flag = no_interrupt (1);
  switch (TYPE (size))
    {
    case tc_string:
    case tc_byte_array:
      s = size;
      buffer = s->storage_as.string.data;
      n = s->storage_as.string.dim;
      m = 0;
      break;
    default:
      n = get_c_long (size);
      buffer = (char *) must_malloc (n + 1);
      buffer[n] = 0;
      m = 1;
    }
  ret = fread (buffer, 1, n, f);
  if (ret == 0)
    {
      if (m)
	free (buffer);
      no_interrupt (flag);
      return (NIL);
    }
  if (m)
    {
      if (ret == n)
	{
	  s = cons (NIL, NIL);
	  s->type = tc_string;
	  s->storage_as.string.data = buffer;
	  s->storage_as.string.dim = n;
	}
      else
	{
	  s = strcons (ret, NULL);
	  memcpy (s->storage_as.string.data, buffer, ret);
	  free (buffer);
	}
      no_interrupt (flag);
      return (s);
    }
  no_interrupt (flag);
  return (flocons ((double) ret));
}

LISP
lfwrite (LISP string, LISP file)
{
  FILE *f;
  long flag;
  char *data;
  long dim, len;
  f = get_c_file (file, stdout);
  data = get_c_string_dim (CONSP (string) ? car (string) : string, &dim);
  len = CONSP (string) ? get_c_long (cadr (string)) : dim;
  if (len <= 0)
    return (NIL);
  if (len > dim)
    err ("write length too long", string);
  flag = no_interrupt (1);
  fwrite (data, 1, len, f);
  no_interrupt (flag);
  return (NIL);
}

LISP
lfflush (LISP file)
{
  FILE *f;
  long flag;
  f = get_c_file (file, stdout);
  flag = no_interrupt (1);
  fflush (f);
  no_interrupt (flag);
  return (NIL);
}

LISP
string_length (LISP string)
{
  if NTYPEP
    (string, tc_string) err_wta_str (string);
  return (flocons (strlen (string->storage_as.string.data)));
}

LISP
string_dim (LISP string)
{
  if NTYPEP
    (string, tc_string) err_wta_str (string);
  return (flocons ((double) string->storage_as.string.dim));
}

long
nlength (LISP obj)
{
  LISP l;
  long n;
  switch TYPE
    (obj)
    {
    case tc_string:
      return (strlen (obj->storage_as.string.data));
    case tc_byte_array:
      return (obj->storage_as.string.dim);
    case tc_double_array:
      return (obj->storage_as.double_array.dim);
    case tc_long_array:
      return (obj->storage_as.long_array.dim);
    case tc_lisp_array:
      return (obj->storage_as.lisp_array.dim);
    case tc_nil:
      return (0);
    case tc_cons:
      for (l = obj, n = 0; CONSP (l); l = CDR (l), ++n)
	INTERRUPT_CHECK ();
      if NNULLP
	(l) err ("improper list to length", obj);
      return (n);
    default:
      err ("wta to length", obj);
      return (0);
    }
}

LISP
llength (LISP obj)
{
  return (flocons (nlength (obj)));
}

LISP
number2string (LISP x, LISP b, LISP w, LISP p)
{
  char buffer[1000];
  double y;
  long base, width, prec;
  if NFLONUMP
    (x) err ("wta", x);
  y = FLONM (x);
  width = NNULLP (w) ? get_c_long (w) : -1;
  if (width > 100)
    err ("width too long", w);
  prec = NNULLP (p) ? get_c_long (p) : -1;
  if (prec > 100)
    err ("precision too large", p);
  if (NULLP (b) || EQ (sym_e, b) || EQ (sym_f, b))
    {
      if ((width >= 0) && (prec >= 0))
	sprintf (buffer,
		 NULLP (b) ? "% *.*g" : EQ (sym_e, b) ? "% *.*e" : "% *.*f",
		 width,
		 prec,
		 y);
      else if (width >= 0)
	sprintf (buffer,
		 NULLP (b) ? "% *g" : EQ (sym_e, b) ? "% *e" : "% *f",
		 width,
		 y);
      else if (prec >= 0)
	sprintf (buffer,
		 NULLP (b) ? "%.*g" : EQ (sym_e, b) ? "%.*e" : "%.*f",
		 prec,
		 y);
      else
	sprintf (buffer,
		 NULLP (b) ? "%g" : EQ (sym_e, b) ? "%e" : "%f",
		 y);
    }
  else if (((base = get_c_long (b)) == 10) || (base == 8) || (base == 16))
    {
      if (width >= 0)
	sprintf (buffer,
		 (base == 10) ? "%0*ld" : (base == 8) ? "%0*lo" : "%0*lX",
		 width,
		 (long) y);
      else
	sprintf (buffer,
		 (base == 10) ? "%ld" : (base == 8) ? "%lo" : "%lX",
		 (long) y);
    }
  else
    err ("number base not handled", b);
  return (strcons (strlen (buffer), buffer));
}

LISP
string2number (LISP x, LISP b)
{
  char *str;
  long base, value = 0;
  double result = 0.0;
  str = get_c_string (x);
  if NULLP
    (b)
      result = atof (str);
  else if ((base = get_c_long (b)) == 10)
    {
      sscanf (str, "%ld", &value);
      result = (double) value;
    }
  else if (base == 8)
    {
      sscanf (str, "%lo", &value);
      result = (double) value;
    }
  else if (base == 16)
    {
      sscanf (str, "%lx", &value);
      result = (double) value;
    }
  else if ((base >= 1) && (base <= 16))
    {
      for (result = 0.0; *str; ++str)
	if (isdigit (*str))
	  result = result * base + *str - '0';
	else if (isxdigit (*str))
	  result = result * base + toupper (*str) - 'A' + 10;
    }
  else
    err ("number base not handled", b);
  return (flocons (result));
}

LISP
lstrcmp (LISP s1, LISP s2)
{
  return (flocons (strcmp (get_c_string (s1), get_c_string (s2))));
}

void
chk_string (LISP s, char **data, long *dim)
{
  if TYPEP
    (s, tc_string)
    {
      *data = s->storage_as.string.data;
      *dim = s->storage_as.string.dim;
    }
  else
    err_wta_str (s);
}

LISP
lstrcpy (LISP dest, LISP src)
{
  long ddim, slen;
  char *d, *s;
  chk_string (dest, &d, &ddim);
  s = get_c_string (src);
  slen = strlen (s);
  if (slen > ddim)
    err ("string too long", src);
  memcpy (d, s, slen);
  d[slen] = 0;
  return (NIL);
}

LISP
lstrcat (LISP dest, LISP src)
{
  long ddim, dlen, slen;
  char *d, *s;
  chk_string (dest, &d, &ddim);
  s = get_c_string (src);
  slen = strlen (s);
  dlen = strlen (d);
  if ((slen + dlen) > ddim)
    err ("string too long", src);
  memcpy (&d[dlen], s, slen);
  d[dlen + slen] = 0;
  return (NIL);
}

LISP
lstrbreakup (LISP str, LISP lmarker)
{
  char *start, *end, *marker;
  size_t k;
  LISP result = NIL;
  start = get_c_string (str);
  marker = get_c_string (lmarker);
  k = strlen (marker);
  while (*start)
    {
      if (!(end = strstr (start, marker)))
	end = &start[strlen (start)];
      result = cons (strcons (end - start, start), result);
      start = (*end) ? end + k : end;
    }
  return (nreverse (result));
}

LISP
lstrunbreakup (LISP elems, LISP lmarker)
{
  LISP result, l;
  for (l = elems, result = NIL; NNULLP (l); l = cdr (l))
    if EQ
      (l, elems)
	result = cons (car (l), result);
    else
      result = cons (car (l), cons (lmarker, result));
  return (string_append (nreverse (result)));
}

LISP
stringp (LISP x)
{
  return (TYPEP (x, tc_string) ? sym_t : NIL);
}

static char *base64_encode_table = "\
ABCDEFGHIJKLMNOPQRSTUVWXYZ\
abcdefghijklmnopqrstuvwxyz\
0123456789+/=";

static char *base64_decode_table = NULL;

static void
init_base64_table (void)
{
  int j;
  base64_decode_table = (char *) malloc (256);
  memset (base64_decode_table, -1, 256);
  for (j = 0; j < 65; ++j)
    base64_decode_table[(unsigned char) base64_encode_table[j]] = j;
}

#define BITMSK(N) ((1 << (N)) - 1)

#define ITEM1(X)   (X >> 2) & BITMSK(6)
#define ITEM2(X,Y) ((X & BITMSK(2)) << 4) | ((Y >> 4) & BITMSK(4))
#define ITEM3(X,Y) ((X & BITMSK(4)) << 2) | ((Y >> 6) & BITMSK(2))
#define ITEM4(X)   X & BITMSK(6)

LISP
base64encode (LISP in)
{
  char *s, *t = base64_encode_table;
  unsigned char *p1, *p2;
  LISP out;
  long j, m, n, chunks, leftover;
  s = get_c_string_dim (in, &n);
  chunks = n / 3;
  leftover = n % 3;
  m = (chunks + ((leftover) ? 1 : 0)) * 4;
  out = strcons (m, NULL);
  p2 = (unsigned char *) get_c_string (out);
  for (j = 0, p1 = (unsigned char *) s; j < chunks; ++j, p1 += 3)
    {
      *p2++ = t[ITEM1 (p1[0])];
      *p2++ = t[ITEM2 (p1[0], p1[1])];
      *p2++ = t[ITEM3 (p1[1], p1[2])];
      *p2++ = t[ITEM4 (p1[2])];
    }
  switch (leftover)
    {
    case 0:
      break;
    case 1:
      *p2++ = t[ITEM1 (p1[0])];
      *p2++ = t[ITEM2 (p1[0], 0)];
      *p2++ = base64_encode_table[64];
      *p2++ = base64_encode_table[64];
      break;
    case 2:
      *p2++ = t[ITEM1 (p1[0])];
      *p2++ = t[ITEM2 (p1[0], p1[1])];
      *p2++ = t[ITEM3 (p1[1], 0)];
      *p2++ = base64_encode_table[64];
      break;
    default:
      errswitch ();
    }
  return (out);
}

LISP
base64decode (LISP in)
{
  char *s, *t = base64_decode_table;
  LISP out;
  unsigned char *p1, *p2;
  long j, m, n, chunks, leftover, item1, item2, item3, item4;
  s = get_c_string (in);
  n = strlen (s);
  if (n == 0)
    return (strcons (0, NULL));
  if (n % 4)
    err ("illegal base64 data length", in);
  if (s[n - 1] == base64_encode_table[64])
    {
      if (s[n - 2] == base64_encode_table[64])
	leftover = 1;
      else
	leftover = 2;
    }
  else
    leftover = 0;
  chunks = (n / 4) - ((leftover) ? 1 : 0);
  m = (chunks * 3) + leftover;
  out = strcons (m, NULL);
  p2 = (unsigned char *) get_c_string (out);
  for (j = 0, p1 = (unsigned char *) s; j < chunks; ++j, p1 += 4)
    {
      if ((item1 = t[p1[0]]) & ~BITMSK (6))
	return (NIL);
      if ((item2 = t[p1[1]]) & ~BITMSK (6))
	return (NIL);
      if ((item3 = t[p1[2]]) & ~BITMSK (6))
	return (NIL);
      if ((item4 = t[p1[3]]) & ~BITMSK (6))
	return (NIL);
      *p2++ = (item1 << 2) | (item2 >> 4);
      *p2++ = (item2 << 4) | (item3 >> 2);
      *p2++ = (item3 << 6) | item4;
    }
  switch (leftover)
    {
    case 0:
      break;
    case 1:
      if ((item1 = t[p1[0]]) & ~BITMSK (6))
	return (NIL);
      if ((item2 = t[p1[1]]) & ~BITMSK (6))
	return (NIL);
      *p2++ = (item1 << 2) | (item2 >> 4);
      break;
    case 2:
      if ((item1 = t[p1[0]]) & ~BITMSK (6))
	return (NIL);
      if ((item2 = t[p1[1]]) & ~BITMSK (6))
	return (NIL);
      if ((item3 = t[p1[2]]) & ~BITMSK (6))
	return (NIL);
      *p2++ = (item1 << 2) | (item2 >> 4);
      *p2++ = (item2 << 4) | (item3 >> 2);
      break;
    default:
      errswitch ();
    }
  return (out);
}

LISP
memq (LISP x, LISP il)
{
  LISP l, tmp;
  for (l = il; CONSP (l); l = CDR (l))
    {
      tmp = CAR (l);
      if EQ
	(x, tmp) return (l);
      INTERRUPT_CHECK ();
    }
  if EQ
    (l, NIL) return (NIL);
  return (err ("improper list to memq", il));
}

LISP
member (LISP x, LISP il)
{
  LISP l, tmp;
  for (l = il; CONSP (l); l = CDR (l))
    {
      tmp = CAR (l);
      if NNULLP
	(equal (x, tmp)) return (l);
      INTERRUPT_CHECK ();
    }
  if EQ
    (l, NIL) return (NIL);
  return (err ("improper list to member", il));
}

LISP
memv (LISP x, LISP il)
{
  LISP l, tmp;
  for (l = il; CONSP (l); l = CDR (l))
    {
      tmp = CAR (l);
      if NNULLP
	(eql (x, tmp)) return (l);
      INTERRUPT_CHECK ();
    }
  if EQ
    (l, NIL) return (NIL);
  return (err ("improper list to memv", il));
}


LISP
nth (LISP x, LISP li)
{
  LISP l;
  long j, n = get_c_long (x);
  for (j = 0, l = li; (j < n) && CONSP (l); ++j)
    l = CDR (l);
  if CONSP
    (l)
      return (CAR (l));
  else
    return (err ("bad arg to nth", x));
}

/* these lxxx_default functions are convenient for manipulating
   command-line argument lists */

LISP
lref_default (LISP li, LISP x, LISP fcn)
{
  LISP l;
  long j, n = get_c_long (x);
  for (j = 0, l = li; (j < n) && CONSP (l); ++j)
    l = CDR (l);
  if CONSP
    (l)
      return (CAR (l));
  else if NNULLP
    (fcn)
      return (lapply (fcn, NIL));
  else
    return (NIL);
}

LISP
larg_default (LISP li, LISP x, LISP dval)
{
  LISP l = li, elem;
  long j = 0, n = get_c_long (x);
  while NNULLP
    (l)
    {
      elem = car (l);
      if (TYPEP (elem, tc_string) && strchr ("-:", *get_c_string (elem)))
	l = cdr (l);
      else if (j == n)
	return (elem);
      else
	{
	  l = cdr (l);
	  ++j;
	}
    }
  return (dval);
}

LISP
lkey_default (LISP li, LISP key, LISP dval)
{
  LISP l = li, elem;
  char *ckey, *celem;
  long n;
  ckey = get_c_string (key);
  n = strlen (ckey);
  while NNULLP
    (l)
    {
      elem = car (l);
      if (TYPEP (elem, tc_string) && (*(celem = get_c_string (elem)) == ':') &&
	  (strncmp (&celem[1], ckey, n) == 0) && (celem[n + 1] == '='))
	return (strcons (strlen (&celem[n + 2]), &celem[n + 2]));
      l = cdr (l);
    }
  return (dval);
}


LISP
llist (LISP l)
{
  return (l);
}

LISP
writes1 (FILE * f, LISP l)
{
  LISP v;
  STACK_CHECK (&v);
  INTERRUPT_CHECK ();
  for (v = l; CONSP (v); v = CDR (v))
    writes1 (f, CAR (v));
  switch TYPE
    (v)
    {
    case tc_nil:
      break;
    case tc_symbol:
    case tc_string:
      fput_st (f, get_c_string (v));
      break;
    default:
      lprin1f (v, f);
      break;
    }
  return (NIL);
}

LISP
writes (LISP args)
{
  return (writes1 (get_c_file (car (args), stdout), cdr (args)));
}

LISP
last (LISP l)
{
  LISP v1, v2;
  v1 = l;
  v2 = CONSP (v1) ? CDR (v1) : err ("bad arg to last", l);
  while (CONSP (v2))
    {
      INTERRUPT_CHECK ();
      v1 = v2;
      v2 = CDR (v2);
    }
  return (v1);
}

LISP
butlast (LISP l)
{
  INTERRUPT_CHECK ();
  STACK_CHECK (&l);
  if NULLP
    (l) err ("list is empty", l);
  if CONSP (l)
    {
      if NULLP (CDR (l))
	return (NIL);
      else
	return (cons (CAR (l), butlast (CDR (l))));
    }
  return (err ("not a list", l));
}

LISP
nconc (LISP a, LISP b)
{
  if NULLP
    (a)
      return (b);
  setcdr (last (a), b);
  return (a);
}

LISP
funcall1 (LISP fcn, LISP a1)
{
  switch TYPE
    (fcn)
    {
    case tc_subr_1:
      STACK_CHECK (&fcn);
      INTERRUPT_CHECK ();
      return (SUBR1 (fcn) (a1));
    case tc_closure:
      if TYPEP
	(fcn->storage_as.closure.code, tc_subr_2)
	{
	  STACK_CHECK (&fcn);
	  INTERRUPT_CHECK ();
	  return (SUBR2 (fcn->storage_as.closure.code)
		  (fcn->storage_as.closure.env, a1));
	}
    default:
      return (lapply (fcn, cons (a1, NIL)));
    }
}

LISP
funcall2 (LISP fcn, LISP a1, LISP a2)
{
  switch TYPE
    (fcn)
    {
    case tc_subr_2:
    case tc_subr_2n:
      STACK_CHECK (&fcn);
      INTERRUPT_CHECK ();
      return (SUBR2 (fcn) (a1, a2));
    default:
      return (lapply (fcn, cons (a1, cons (a2, NIL))));
    }
}

LISP
lqsort (LISP l, LISP f, LISP g)
     /* this is a stupid recursive qsort */
{
  int j, n;
  LISP v, mark, less, notless;
  for (v = l, n = 0; CONSP (v); v = CDR (v), ++n)
    INTERRUPT_CHECK ();
  if NNULLP
    (v) err ("bad list to qsort", l);
  if (n == 0)
    return (NIL);
  j = rand () % n;
  for (v = l, n = 0; n < j; ++n)
    v = CDR (v);
  mark = CAR (v);
  for (less = NIL, notless = NIL, v = l, n = 0; NNULLP (v); v = CDR (v), ++n)
    if (j != n)
      {
	if NNULLP
	  (funcall2 (f,
		     NULLP (g) ? CAR (v) : funcall1 (g, CAR (v)),
		     NULLP (g) ? mark : funcall1 (g, mark)))
	    less = cons (CAR (v), less);
	else
	  notless = cons (CAR (v), notless);
      }
  return (nconc (lqsort (less, f, g),
		 cons (mark,
		       lqsort (notless, f, g))));
}

LISP
string_lessp (LISP s1, LISP s2)
{
  if (strcmp (get_c_string (s1), get_c_string (s2)) < 0)
    return (sym_t);
  else
    return (NIL);
}

LISP
benchmark_funcall1 (LISP ln, LISP f, LISP a1)
{
  long j, n;
  LISP value = NIL;
  n = get_c_long (ln);
  for (j = 0; j < n; ++j)
    value = funcall1 (f, a1);
  return (value);
}

LISP
benchmark_funcall2 (LISP l)
{
  long j, n;
  LISP ln = car (l);
  LISP f = car (cdr (l));
  LISP a1 = car (cdr (cdr (l)));
  LISP a2 = car (cdr (cdr (cdr (l))));
  LISP value = NULL;
  n = get_c_long (ln);
  for (j = 0; j < n; ++j)
    value = funcall2 (f, a1, a2);
  return (value);
}

LISP
benchmark_eval (LISP ln, LISP exp, LISP env)
{
  long j, n;
  LISP value = NIL;
  n = get_c_long (ln);
  for (j = 0; j < n; ++j)
    value = leval (exp, env);
  return (value);
}

LISP
mapcar1 (LISP fcn, LISP in)
{
  LISP res, ptr, l;
  if NULLP
    (in) return (NIL);
  res = ptr = cons (funcall1 (fcn, car (in)), NIL);
  for (l = cdr (in); CONSP (l); l = CDR (l))
    ptr = CDR (ptr) = cons (funcall1 (fcn, CAR (l)), CDR (ptr));
  return (res);
}

LISP
mapcar2 (LISP fcn, LISP in1, LISP in2)
{
  LISP res, ptr, l1, l2;
  if (NULLP (in1) || NULLP (in2))
    return (NIL);
  res = ptr = cons (funcall2 (fcn, car (in1), car (in2)), NIL);
  for (l1 = cdr (in1), l2 = cdr (in2); CONSP (l1) && CONSP (l2); l1 = CDR (l1), l2 = CDR (l2))
    ptr = CDR (ptr) = cons (funcall2 (fcn, CAR (l1), CAR (l2)), CDR (ptr));
  return (res);
}

LISP
mapcar (LISP l)
{
  LISP fcn = car (l);
  switch (get_c_long (llength (l)))
    {
    case 2:
      return (mapcar1 (fcn, car (cdr (l))));
    case 3:
      return (mapcar2 (fcn, car (cdr (l)), car (cdr (cdr (l)))));
    default:
      return (err ("mapcar case not handled", l));
    }
}

LISP
lfmod (LISP x, LISP y)
{
  if NFLONUMP
    (x) err ("wta(1st) to fmod", x);
  if NFLONUMP
    (y) err ("wta(2nd) to fmod", y);
  return (flocons (fmod (FLONM (x), FLONM (y))));
}

LISP
lsubset (LISP fcn, LISP l)
{
  LISP result = NIL, v;
  for (v = l; CONSP (v); v = CDR (v))
    if NNULLP
      (funcall1 (fcn, CAR (v)))
	result = cons (CAR (v), result);
  return (nreverse (result));
}

LISP
ass (LISP x, LISP alist, LISP fcn)
{
  LISP l, tmp;
  for (l = alist; CONSP (l); l = CDR (l))
    {
      tmp = CAR (l);
      if (CONSP (tmp) && NNULLP (funcall2 (fcn, CAR (tmp), x)))
	return (tmp);
      INTERRUPT_CHECK ();
    }
  if EQ
    (l, NIL) return (NIL);
  return (err ("improper list to ass", alist));
}

LISP
append2 (LISP l1, LISP l2)
{
  long n;
  LISP result = NIL, p1, p2;
  n = nlength (l1) + nlength (l2);
  while (n > 0)
    {
      result = cons (NIL, result);
      --n;
    }
  for (p1 = result, p2 = l1; NNULLP (p2); p1 = cdr (p1), p2 = cdr (p2))
    setcar (p1, car (p2));
  for (p2 = l2; NNULLP (p2); p1 = cdr (p1), p2 = cdr (p2))
    setcar (p1, car (p2));
  return (result);
}

LISP
append (LISP l)
{
  STACK_CHECK (&l);
  INTERRUPT_CHECK ();
  if NULLP
    (l)
      return (NIL);
  else if NULLP
    (cdr (l))
      return (car (l));
  else if NULLP
    (cddr (l))
      return (append2 (car (l), cadr (l)));
  else
    return (append2 (car (l), append (cdr (l))));
}

LISP
listn (long n,...)
{
  LISP result, ptr;
  long j;
  va_list args;
  for (j = 0, result = NIL; j < n; ++j)
    result = cons (NIL, result);
  va_start (args, n);
  for (j = 0, ptr = result; j < n; ptr = cdr (ptr), ++j)
    setcar (ptr, va_arg (args, LISP));
  va_end (args);
  return (result);
}


LISP
fast_load (LISP lfname, LISP noeval)
{
  char *fname;
  LISP stream;
  LISP result = NIL, form;
  fname = get_c_string (lfname);
  if (siod_verbose_level >= 3)
    {
      put_st ("fast loading ");
      put_st (fname);
      put_st ("\n");
    }
  stream = listn (3,
		  fopen_c (fname, "rb"),
		  cons_array (flocons (100), NIL),
		  flocons (0));
  while (NEQ (stream, form = fast_read (stream)))
    {
      if (siod_verbose_level >= 5)
	lprint (form, NIL);
      if NULLP
	(noeval)
	  leval (form, NIL);
      else
	result = cons (form, result);
    }
  fclose_l (car (stream));
  if (siod_verbose_level >= 3)
    put_st ("done.\n");
  return (nreverse (result));
}

static void
shexstr (char *outstr, void *buff, size_t len)
{
  unsigned char *data = buff;
  size_t j;
  for (j = 0; j < len; ++j)
    sprintf (&outstr[j * 2], "%02X", data[j]);
}

LISP
fast_save (LISP fname, LISP forms, LISP nohash, LISP comment)
{
  char *cname, msgbuff[100], databuff[50];
  LISP stream, l;
  FILE *f;
  long l_one = 1;
  double d_one = 1.0;
  cname = get_c_string (fname);
  if (siod_verbose_level >= 3)
    {
      put_st ("fast saving forms to ");
      put_st (cname);
      put_st ("\n");
    }
  stream = listn (3,
		  fopen_c (cname, "wb"),
		  NNULLP (nohash) ? NIL : cons_array (flocons (100), NIL),
		  flocons (0));
  f = get_c_file (car (stream), NULL);
  if NNULLP
    (comment)
      fput_st (f, get_c_string (comment));
  sprintf (msgbuff, "# Siod Binary Object Save File\n");
  fput_st (f, msgbuff);
  sprintf (msgbuff, "# sizeof(long) = %d\n# sizeof(double) = %d\n",
	   sizeof (long), sizeof (double));
  fput_st (f, msgbuff);
  shexstr (databuff, &l_one, sizeof (l_one));
  sprintf (msgbuff, "# 1 = %s\n", databuff);
  fput_st (f, msgbuff);
  shexstr (databuff, &d_one, sizeof (d_one));
  sprintf (msgbuff, "# 1.0 = %s\n", databuff);
  fput_st (f, msgbuff);
  for (l = forms; NNULLP (l); l = cdr (l))
    fast_print (car (l), stream);
  fclose_l (car (stream));
  if (siod_verbose_level >= 3)
    put_st ("done.\n");
  return (NIL);
}

void
swrite1 (LISP stream, LISP data)
{
  FILE *f = get_c_file (stream, stdout);
  switch TYPE
    (data)
    {
    case tc_symbol:
    case tc_string:
      fput_st (f, get_c_string (data));
      break;
    default:
      lprin1f (data, f);
      break;
    }
}

LISP
swrite (LISP stream, LISP table, LISP data)
{
  LISP value, key;
  long j, k, m, n;
  switch (TYPE (data))
    {
    case tc_symbol:
      value = href (table, data);
      if CONSP
	(value)
	{
	  swrite1 (stream, CAR (value));
	  if NNULLP
	    (CDR (value))
	      hset (table, data, CDR (value));
	}
      else
	swrite1 (stream, value);
      break;
    case tc_lisp_array:
      n = data->storage_as.lisp_array.dim;
      if (n < 1)
	err ("no object repeat count", data);
      key = data->storage_as.lisp_array.data[0];
      if NULLP
	(value = href (table, key))
	  value = key;
      else if CONSP
	(value)
	{
	  if NNULLP
	    (CDR (value))
	      hset (table, key, CDR (value));
	  value = CAR (value);
	}
      m = get_c_long (value);
      for (k = 0; k < m; ++k)
	for (j = 1; j < n; ++j)
	  swrite (stream, table, data->storage_as.lisp_array.data[j]);
      break;
    case tc_cons:
      /* this should be handled similar to the array case */
    default:
      swrite1 (stream, data);
    }
  return (NIL);
}

LISP
lpow (LISP x, LISP y)
{
  if NFLONUMP
    (x) err ("wta(1st) to pow", x);
  if NFLONUMP
    (y) err ("wta(2nd) to pow", y);
  return (flocons (pow (FLONM (x), FLONM (y))));
}

LISP
lexp (LISP x)
{
  return (flocons (exp (get_c_double (x))));
}

LISP
llog (LISP x)
{
  return (flocons (log (get_c_double (x))));
}

LISP
lsin (LISP x)
{
  return (flocons (sin (get_c_double (x))));
}

LISP
lcos (LISP x)
{
  return (flocons (cos (get_c_double (x))));
}

LISP
ltan (LISP x)
{
  return (flocons (tan (get_c_double (x))));
}

LISP
lasin (LISP x)
{
  return (flocons (asin (get_c_double (x))));
}

LISP
lacos (LISP x)
{
  return (flocons (acos (get_c_double (x))));
}

LISP
latan (LISP x)
{
  return (flocons (atan (get_c_double (x))));
}

LISP
latan2 (LISP x, LISP y)
{
  return (flocons (atan2 (get_c_double (x), get_c_double (y))));
}

LISP
hexstr (LISP a)
{
  unsigned char *in;
  char *out;
  LISP result;
  long j, dim;
  in = (unsigned char *) get_c_string_dim (a, &dim);
  result = strcons (dim * 2, NULL);
  for (out = get_c_string (result), j = 0; j < dim; ++j, out += 2)
    sprintf (out, "%02x", in[j]);
  return (result);
}

static int
xdigitvalue (int c)
{
  if (isdigit (c))
      return (c - '0');
  if (isxdigit (c))
      return (toupper (c) - 'A' + 10);
  return (0);
}

LISP
hexstr2bytes (LISP a)
{
  char *in;
  unsigned char *out;
  LISP result;
  long j, dim;
  in = get_c_string (a);
  dim = strlen (in) / 2;
  result = arcons (tc_byte_array, dim, 0);
  out = (unsigned char *) result->storage_as.string.data;
  for (j = 0; j < dim; ++j)
    out[j] = xdigitvalue (in[j * 2]) * 16 + xdigitvalue (in[j * 2 + 1]);
  return (result);
}

LISP
getprop (LISP plist, LISP key)
{
  LISP l;
  for (l = cdr (plist); NNULLP (l); l = cddr (l))
    if EQ
      (car (l), key)
	return (cadr (l));
    else
      INTERRUPT_CHECK ();
  return (NIL);
}

LISP
setprop (LISP plist, LISP key, LISP value)
{
  err ("not implemented", NIL);
  return (NIL);
}

LISP
putprop (LISP plist, LISP value, LISP key)
{
  return (setprop (plist, key, value));
}

LISP
ltypeof (LISP obj)
{
  long x;
  x = TYPE (obj);
  switch (x)
    {
    case tc_nil:
      return (cintern ("tc_nil"));
    case tc_cons:
      return (cintern ("tc_cons"));
    case tc_flonum:
      return (cintern ("tc_flonum"));
    case tc_symbol:
      return (cintern ("tc_symbol"));
    case tc_subr_0:
      return (cintern ("tc_subr_0"));
    case tc_subr_1:
      return (cintern ("tc_subr_1"));
    case tc_subr_2:
      return (cintern ("tc_subr_2"));
    case tc_subr_2n:
      return (cintern ("tc_subr_2n"));
    case tc_subr_3:
      return (cintern ("tc_subr_3"));
    case tc_subr_4:
      return (cintern ("tc_subr_4"));
    case tc_subr_5:
      return (cintern ("tc_subr_5"));
    case tc_lsubr:
      return (cintern ("tc_lsubr"));
    case tc_fsubr:
      return (cintern ("tc_fsubr"));
    case tc_msubr:
      return (cintern ("tc_msubr"));
    case tc_closure:
      return (cintern ("tc_closure"));
    case tc_free_cell:
      return (cintern ("tc_free_cell"));
    case tc_string:
      return (cintern ("tc_string"));
    case tc_byte_array:
      return (cintern ("tc_byte_array"));
    case tc_double_array:
      return (cintern ("tc_double_array"));
    case tc_long_array:
      return (cintern ("tc_long_array"));
    case tc_lisp_array:
      return (cintern ("tc_lisp_array"));
    case tc_c_file:
      return (cintern ("tc_c_file"));
    default:
      return (flocons (x));
    }
}

LISP
caaar (LISP x)
{
  return (car (car (car (x))));
}

LISP
caadr (LISP x)
{
  return (car (car (cdr (x))));
}

LISP
cadar (LISP x)
{
  return (car (cdr (car (x))));
}

LISP
caddr (LISP x)
{
  return (car (cdr (cdr (x))));
}

LISP
cdaar (LISP x)
{
  return (cdr (car (car (x))));
}

LISP
cdadr (LISP x)
{
  return (cdr (car (cdr (x))));
}

LISP
cddar (LISP x)
{
  return (cdr (cdr (car (x))));
}

LISP
cdddr (LISP x)
{
  return (cdr (cdr (cdr (x))));
}

LISP
ash (LISP value, LISP n)
{
  long m, k;
  m = get_c_long (value);
  k = get_c_long (n);
  if (k > 0)
    m = m << k;
  else
    m = m >> (-k);
  return (flocons (m));
}

LISP
bitand (LISP a, LISP b)
{
  return (flocons (get_c_long (a) & get_c_long (b)));
}

LISP
bitor (LISP a, LISP b)
{
  return (flocons (get_c_long (a) | get_c_long (b)));
}

LISP
bitxor (LISP a, LISP b)
{
  return (flocons (get_c_long (a) ^ get_c_long (b)));
}

LISP
bitnot (LISP a)
{
  return (flocons (~get_c_long (a)));
}

LISP
leval_prog1 (LISP args, LISP env)
{
  LISP retval, l;
  retval = leval (car (args), env);
  for (l = cdr (args); NNULLP (l); l = cdr (l))
    leval (car (l), env);
  return (retval);
}

LISP
leval_cond (LISP * pform, LISP * penv)
{
  LISP args, env, clause, value, next;
  args = cdr (*pform);
  env = *penv;
  if NULLP
    (args)
    {
      *pform = NIL;
      return (NIL);
    }
  next = cdr (args);
  while NNULLP
    (next)
    {
      clause = car (args);
      value = leval (car (clause), env);
      if NNULLP
	(value)
	{
	  clause = cdr (clause);
	  if NULLP
	    (clause)
	    {
	      *pform = value;
	      return (NIL);
	    }
	  else
	    {
	      next = cdr (clause);
	      while (NNULLP (next))
		{
		  leval (car (clause), env);
		  clause = next;
		  next = cdr (next);
		}
	      *pform = car (clause);
	      return (sym_t);
	    }
	}
      args = next;
      next = cdr (next);
    }
  clause = car (args);
  next = cdr (clause);
  if NULLP
    (next)
    {
      *pform = car (clause);
      return (sym_t);
    }
  value = leval (car (clause), env);
  if NULLP
    (value)
    {
      *pform = NIL;
      return (NIL);
    }
  clause = next;
  next = cdr (next);
  while (NNULLP (next))
    {
      leval (car (clause), env);
      clause = next;
      next = cdr (next);
    }
  *pform = car (clause);
  return (sym_t);
}

LISP
lstrspn (LISP str1, LISP str2)
{
  return (flocons (strspn (get_c_string (str1), get_c_string (str2))));
}

LISP
lstrcspn (LISP str1, LISP str2)
{
  return (flocons (strcspn (get_c_string (str1), get_c_string (str2))));
}

LISP
substring_equal (LISP str1, LISP str2, LISP start, LISP end)
{
  char *cstr1, *cstr2;
  long len1, n, s, e;
  cstr1 = get_c_string_dim (str1, &len1);
  cstr2 = get_c_string_dim (str2, &n);
  s = NULLP (start) ? 0 : get_c_long (start);
  e = NULLP (end) ? n : get_c_long (end);
  if ((s < 0) || (s > e) || (e < 0) || (e > n) || ((e - s) != len1))
    return (NIL);
  return ((memcmp (cstr1, &cstr2[s], e - s) == 0) ? a_true_value () : NIL);
}

LISP
set_eval_history (LISP len, LISP circ)
{
  LISP data;
  data = NULLP (len) ? len : make_list (len, NIL);
  if NNULLP
    (circ)
      data = nconc (data, data);
  setvar (cintern ("*eval-history-ptr*"), data, NIL);
  setvar (cintern ("*eval-history*"), data, NIL);
  return (len);
}

static LISP
parser_fasl (LISP ignore)
{
  return (closure (listn (3,
			  NIL,
			  cons_array (flocons (100), NIL),
			  flocons (0)),
		   leval (cintern ("parser_fasl_hook"), NIL)));
}

static LISP
parser_fasl_hook (LISP env, LISP f)
{
  LISP result;
  setcar (env, f);
  result = fast_read (env);
  if EQ
    (result, env)
      return (get_eof_val ());
  else
    return (result);
}

void
init_subrs_a (void)
{
  init_subr_2 ("aref", aref1);
  init_subr_3 ("aset", aset1);
  init_lsubr ("string-append", string_append);
  init_lsubr ("bytes-append", bytes_append);
  init_subr_1 ("string-length", string_length);
  init_subr_1 ("string-dimension", string_dim);
  init_subr_1 ("read-from-string", read_from_string);
  init_subr_3 ("print-to-string", print_to_string);
  init_subr_2 ("cons-array", cons_array);
  init_subr_2 ("sxhash", sxhash);
  init_subr_2 ("equal?", equal);
  init_subr_2 ("href", href);
  init_subr_3 ("hset", hset);
  init_subr_2 ("assoc", assoc);
  init_subr_2 ("assv", assv);
  init_subr_1 ("fast-read", fast_read);
  init_subr_2 ("fast-print", fast_print);
  init_subr_2 ("make-list", make_list);
  init_subr_2 ("fread", lfread);
  init_subr_2 ("fwrite", lfwrite);
  init_subr_1 ("fflush", lfflush);
  init_subr_1 ("length", llength);
  init_subr_4 ("number->string", number2string);
  init_subr_2 ("string->number", string2number);
  init_subr_3 ("substring", substring);
  init_subr_2 ("string-search", string_search);
  init_subr_1 ("string-trim", string_trim);
  init_subr_1 ("string-trim-left", string_trim_left);
  init_subr_1 ("string-trim-right", string_trim_right);
  init_subr_1 ("string-upcase", string_upcase);
  init_subr_1 ("string-downcase", string_downcase);
  init_subr_2 ("strcmp", lstrcmp);
  init_subr_2 ("strcat", lstrcat);
  init_subr_2 ("strcpy", lstrcpy);
  init_subr_2 ("strbreakup", lstrbreakup);
  init_subr_2 ("unbreakupstr", lstrunbreakup);
  init_subr_1 ("string?", stringp);
  gc_protect_sym (&sym_e, "e");
  gc_protect_sym (&sym_f, "f");
  gc_protect_sym (&sym_plists, "*plists*");
  setvar (sym_plists, arcons (tc_lisp_array, 100, 1), NIL);
  init_subr_3 ("lref-default", lref_default);
  init_subr_3 ("larg-default", larg_default);
  init_subr_3 ("lkey-default", lkey_default);
  init_lsubr ("list", llist);
  init_lsubr ("writes", writes);
  init_subr_3 ("qsort", lqsort);
  init_subr_2 ("string-lessp", string_lessp);
  init_lsubr ("mapcar", mapcar);
  init_subr_3 ("mapcar2", mapcar2);
  init_subr_2 ("mapcar1", mapcar1);
  init_subr_3 ("benchmark-funcall1", benchmark_funcall1);
  init_lsubr ("benchmark-funcall2", benchmark_funcall2);
  init_subr_3 ("benchmark-eval", benchmark_eval);
  init_subr_2 ("fmod", lfmod);
  init_subr_2 ("subset", lsubset);
  init_subr_1 ("base64encode", base64encode);
  init_subr_1 ("base64decode", base64decode);
  init_subr_3 ("ass", ass);
  init_subr_2 ("append2", append2);
  init_lsubr ("append", append);
  init_subr_4 ("fast-save", fast_save);
  init_subr_2 ("fast-load", fast_load);
  init_subr_3 ("swrite", swrite);
  init_subr_2 ("pow", lpow);
  init_subr_1 ("exp", lexp);
  init_subr_1 ("log", llog);
  init_subr_1 ("sin", lsin);
  init_subr_1 ("cos", lcos);
  init_subr_1 ("tan", ltan);
  init_subr_1 ("asin", lasin);
  init_subr_1 ("acos", lacos);
  init_subr_1 ("atan", latan);
  init_subr_2 ("atan2", latan2);
  init_subr_1 ("typeof", ltypeof);
  init_subr_1 ("caaar", caaar);
  init_subr_1 ("caadr", caadr);
  init_subr_1 ("cadar", cadar);
  init_subr_1 ("caddr", caddr);
  init_subr_1 ("cdaar", cdaar);
  init_subr_1 ("cdadr", cdadr);
  init_subr_1 ("cddar", cddar);
  init_subr_1 ("cdddr", cdddr);
  setvar (cintern ("*pi*"), flocons (atan (1.0) * 4), NIL);
  init_base64_table ();
  init_subr_1 ("array->hexstr", hexstr);
  init_subr_1 ("hexstr->bytes", hexstr2bytes);
  init_subr_3 ("ass", ass);
  init_subr_2 ("bit-and", bitand);
  init_subr_2 ("bit-or", bitor);
  init_subr_2 ("bit-xor", bitxor);
  init_subr_1 ("bit-not", bitnot);
  init_msubr ("cond", leval_cond);
  init_fsubr ("prog1", leval_prog1);
  init_subr_2 ("strspn", lstrspn);
  init_subr_2 ("strcspn", lstrcspn);
  init_subr_4 ("substring-equal?", substring_equal);
  init_subr_1 ("butlast", butlast);
  init_subr_2 ("ash", ash);
  init_subr_2 ("get", getprop);
  init_subr_3 ("setprop", setprop);
  init_subr_3 ("putprop", putprop);
  init_subr_1 ("last", last);
  init_subr_2 ("memq", memq);
  init_subr_2 ("memv", memv);
  init_subr_2 ("member", member);
  init_subr_2 ("nth", nth);
  init_subr_2 ("nconc", nconc);
  init_subr_2 ("set-eval-history", set_eval_history);
  init_subr_1 ("parser_fasl", parser_fasl);
  setvar (cintern ("*parser_fasl.scm-loaded*"), a_true_value (), NIL);
  init_subr_2 ("parser_fasl_hook", parser_fasl_hook);
  init_sliba_version ();
}
