/*  
 *                   COPYRIGHT (c) 1988-1996 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: sliba.c,v 1.2 2000/03/25 23:17:20 johns Exp $"),
	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,const 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:
     if (k >= a->storage_as.string.dim) err("index too large",i);
     return(flocons((double) a->storage_as.u_string.data[k]));
    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 = (short) 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;
 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;
 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
   return(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 unsigned char *base64_encode_table = "\
ABCDEFGHIJKLMNOPQRSTUVWXYZ\
abcdefghijklmnopqrstuvwxyz\
0123456789+/=";

static unsigned 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[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++ = (unsigned char) ((item1 << 2) | (item2 >> 4));
    *p2++ = (unsigned char) ((item2 << 4) | (item3 >> 2));
    *p2++ = (unsigned char) ((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++ = (unsigned char) ((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++ = (unsigned char) ((item1 << 2) | (item2 >> 4));
      *p2++ = (unsigned char) ((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 = NIL;
 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,LISP fmode)
{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,NNULLP(fmode) ? get_c_string(fmode) : "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;}}

static LISP swrite2(LISP name,LISP table)
{LISP value,key;
 if (SYMBOLP(name) && (PNAME(name)[0] == '.'))
   key = rintern(&PNAME(name)[1]);
 else
   key = name;
 value = href(table,key);
 if (CONSP(value))
   {if (CONSP(CDR(value)) && EQ(name,key))
     hset(table,key,CDR(value));
    return(CAR(value));}
 else if (NULLP(value))
   return(name);
 else
   return(value);}

LISP swrite(LISP stream,LISP table,LISP data)
{long j,k,m,n;
 switch(TYPE(data))
   {case tc_symbol:
      swrite1(stream,swrite2(data,table));
      break;
    case tc_lisp_array:
      n = data->storage_as.lisp_array.dim;
      if (n < 1)
	err("no object repeat count",data);
      m = get_c_long(swrite2(data->storage_as.lisp_array.data[0],
			     table));
      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 */
      break;
    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) ? len1 : 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);}

#ifdef vms
int strncasecmp(const char *s1, const char *s2, int n)
{int j,c1,c2;
 for(j=0;j<n;++j)
     {c1 = toupper(s1[j]);
    c2 = toupper(s2[j]);
    if ((c1 == 0) && (c2 == 0)) return(0);
    if (c1 == 0) return(-1);
    if (c2 == 0) return(1);
    if (c1 < c2) return(-1);
    if (c2 > c1) return(1);}
 return(0);}
#endif

LISP substring_equalcase(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) ? len1 : get_c_long(end);
 if ((s < 0) || (s > e) || (e < 0) || (e > n) || ((e - s) != len1))
   return(NIL);
 return((strncasecmp(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_5("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_4("substring-equalcase?",substring_equalcase);
 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();}

