/* s7, a Scheme interpreter
 *
 *    derived from:
 *
 * --------------------------------------------------------------------------------
 * T I N Y S C H E M E    1 . 3 9
 *   Dimitrios Souflis (dsouflis@acm.org)
 *   Based on MiniScheme (original credits follow)
 * (MINISCM)               coded by Atsushi Moriwaki (11/5/1989)
 * (MINISCM)           E-MAIL :  moriwaki@kurims.kurims.kyoto-u.ac.jp
 * (MINISCM) This version has been modified by R.C. Secrist.
 * (MINISCM)
 * (MINISCM) Mini-Scheme is now maintained by Akira KIDA.
 * (MINISCM)
 * (MINISCM) This is a revised and modified version by Akira KIDA.
 * (MINISCM)	current version is 0.85k4 (15 May 1994)
 * --------------------------------------------------------------------------------
 *
 * apparently tinyScheme is under the BSD license, so I guess s7 is too. 
 * Here is Snd's verbiage which can apply here:
 *
 *     The authors hereby grant permission to use, copy, modify, distribute,
 *     and license this software and its documentation for any purpose.  No
 *     written agreement, license, or royalty fee is required.  Modifications
 *     to this software may be copyrighted by their authors and need not
 *     follow the licensing terms described here.
 *
 * followed by the usual all-caps shouting about liability.
 *
 * --------------------------------------------------------------------------------
 *
 * s7, Bill Schottstaedt, Aug-08
 *
 * Mike Scholz provided the FreeBSD support (complex trig funcs, etc)
 * Rick Taube and Andrew Burnson provided the MS Visual C++ support
 *
 * Documentation is in s7.h and s7.html.  
 * s7test.scm is a regression test.
 * glistener.c is a gtk-based listener.
 * cload.scm and lib*.scm tie in various C libraries.
 * lint.scm checks Scheme code for infelicities.
 * r7rs.scm implements some of r7rs (small).
 * write.scm currrenly has pretty-print.
 * mockery.scm has the mock-data definitions.
 * stuff.scm has some stuff.
 *
 *
 * ---------------- compile time switches ---------------- 
 */

#include <mus-config.h>

/* 
 * Your config file goes here, or just replace that #include line with the defines you need.
 * The compile-time switches involve booleans, complex numbers, and multiprecision arithmetic.
 * Currently we assume we have setjmp.h (used by the error handlers).
 *
 * Complex number support which is problematic in C++, Solaris, and netBSD
 *   is on the HAVE_COMPLEX_NUMBERS switch. On a Mac, or in Linux, if you're not using C++,
 *   I use:
 *
 *   #define HAVE_COMPLEX_NUMBERS 1
 *   #define HAVE_COMPLEX_TRIG 1
 *
 *   In C++ I use:
 *
 *   #define HAVE_COMPLEX_NUMBERS 1
 *   #define HAVE_COMPLEX_TRIG 0
 *
 *   In windows, both are 0.
 *
 *   Some systems (FreeBSD) have complex.h, but some random subset of the trig funcs, so
 *   HAVE_COMPLEX_NUMBERS means we can find
 *
 *      cimag creal cabs csqrt carg conj
 *
 *   and HAVE_COMPLEX_TRIG means we have
 *
 *      cacos cacosh casin casinh catan catanh ccos ccosh cexp clog cpow csin csinh ctan ctanh
 * 
 * When HAVE_COMPLEX_NUMBERS is 0 or undefined, the complex functions are stubs that simply return their
 *   argument -- this will be very confusing for the s7 user because, for example, (sqrt -2)
 *   will return something bogus (it will not signal an error).
 *
 *
 * To get multiprecision arithmetic, set WITH_GMP to 1.
 *   You'll also need libgmp, libmpfr, and libmpc (version 0.8.0 or later)
 *
 * if WITH_SYSTEM_EXTRAS is 1 (default is 1 unless _MSC_VER), various OS and file related functions are included.
 *
 * so the incoming (non-s7-specific) compile-time switches are
 *     HAVE_COMPLEX_NUMBERS, HAVE_COMPLEX_TRIG, SIZEOF_VOID_P
 *
 * and we use these predefined macros: __cplusplus, _MSC_VER, __GNUC__, __clang__, __ANDROID__
 *
 * if SIZEOF_VOID_P is not defined, we look for __SIZEOF_POINTER__ instead
 *   the default is to assume that we're running on a 64-bit machine.
 *
 * in openBSD I think you need to include -ftrampolines in CFLAGS.
 *
 * if you want this file to compile into a stand-alone interpreter, define WITH_MAIN (this assumes access to readline)
 *   then load it with -lreadline -ldl -lm, (and perhaps -ltinfo) etc.
 *
 * -O3 does not seem to gain us anything.
 */


/* ---------------- initial sizes ---------------- */

#ifndef INITIAL_HEAP_SIZE
#define INITIAL_HEAP_SIZE 128000
/* the heap grows as needed, this is its initial size. 
 * If the initial heap is small, s7 can run in less than 2 Mbytes of memory. There are cases where a bigger heap is faster.
 * As of 5-May-2011, the heap size must be a multiple of 32.  Each object takes about 60 bytes (48 + 8?) so this represents 6 Mbytes?
 */
#endif

#define SYMBOL_TABLE_SIZE 13567 
/* names are hashed into the symbol table (a vector) and collisions are chained as lists. 
 */

#define INITIAL_STACK_SIZE 512
/* the stack grows as needed, each frame takes 4 entries, this is its initial size.
 *   this needs to be big enough to handle the eval_c_string's at startup (ca 100) 
 *   In s7test.scm, the maximum stack size is ca 440.  In snd-test.scm, it's ca 200.
 *   This number matters only because call/cc copies the stack, which requires filling
 *   the unused portion of the new stack, which requires memcpy of #<unspecified>'s.
 */

#define INITIAL_PROTECTED_OBJECTS_SIZE 16  
/* a vector of objects that are (semi-permanently) protected from the GC, grows as needed */

#define GC_TEMPS_SIZE 256
/* the number of recent objects that are temporarily gc-protected; 8 works for s7test and snd-test. 
 *    For the FFI, this sets the lag between a call on s7_cons and the first moment when its result
 *    might be vulnerable to the GC. 
 */

#define WITH_GCC (defined(__GNUC__) || defined(__clang__))


/* ---------------- scheme choices ---------------- */

#ifndef WITH_GMP
  #define WITH_GMP 0
  /* this includes multiprecision arithmetic for all numeric types and functions, using gmp, mpfr, and mpc
   * WITH_GMP adds the following functions: bignum, bignum?, bignum-precision
   * using gmp with precision=128 is about 3 times slower than using C doubles and long long ints.
   */
#endif

#if WITH_GMP
  #define DEFAULT_BIGNUM_PRECISION 128
#endif

#ifndef WITH_PURE_S7
  #define WITH_PURE_S7 0
#endif
#if WITH_PURE_S7
  #define WITH_EXTRA_EXPONENT_MARKERS 0
  #define WITH_AT_SIGN_AS_EXPONENT 1
  #define WITH_IMMUTABLE_UNQUOTE 1
  #define WITH_QUASIQUOTE_VECTOR 0
  #define WITH_MAKE_COMPLEX 1
  /* also omitted: *-ci* functions, char-ready? */
#endif

#ifndef WITH_EXTRA_EXPONENT_MARKERS
#define WITH_EXTRA_EXPONENT_MARKERS 1
  /* if 1, s7 recognizes "d", "f", "l", and "s" as exponent markers, in addition to "e" (also "D", "F", "L", "S")
   */
#endif

#ifndef WITH_AT_SIGN_AS_EXPONENT
  #define WITH_AT_SIGN_AS_EXPONENT 1
  /* for bases 15 and 16, 'e' is a digit, so to get exponents in those bases (and actually
   *    any greater than 10), we need a different exponent marker.  This makes '@' an exponent marker.
   */
#endif

#ifndef WITH_SYSTEM_EXTRAS
  #define WITH_SYSTEM_EXTRAS (!_MSC_VER)
  /* this adds several functions that access file info, directories, times, etc
   *    this may be replaced by the cload business below
   */
#endif

#ifndef WITH_C_LOADER
  #define WITH_C_LOADER WITH_GCC
  /* (load file.so [e]) looks for (e 'init_func) and if found, calls it
   *   as the shared object init function.  If WITH_SYSTEM_EXTRAS is 0, the caller
   *   needs to supply system and delete-file so that cload.scm works.
   */
#endif

#ifndef WITH_IMMUTABLE_UNQUOTE
  #define WITH_IMMUTABLE_UNQUOTE 0
  /* this removes the name "unquote" */
#endif

#ifndef WITH_QUASIQUOTE_VECTOR
  #define WITH_QUASIQUOTE_VECTOR 1
  /* this determines whether we include support for quasiquoted vector constants `#(...) */
#endif

#ifndef WITH_MAKE_COMPLEX 
  #define WITH_MAKE_COMPLEX 0
  /* this removes magnitude (use abs for all numbers), make-polar, and make-rectangular, renaming the latter make-complex */
#endif

/* other similar choices: exact/inexact including #i/#e, call-with-values etc, eof-object?
 */

#ifndef DEBUGGING
  #define DEBUGGING 0
#endif

#define BOLD_TEXT "\033[1m"
#define UNBOLD_TEXT "\033[22m"
#define RED_TEXT "\033[31m"
#define NORMAL_TEXT "\033[0m"


/* -------------------------------------------------------------------------------- */

/* s7.c is organized as follows:
 *
 *    structs and type flags
 *    constants
 *    GC
 *    stacks
 *    symbols
 *    environments
 *    keywords
 *    continuations
 *    numbers
 *    characters
 *    strings
 *    ports
 *    format
 *    system extras
 *    lists
 *    vectors and hash-tables
 *    c-objects and functions
 *    eq?
 *    generic length, copy, reverse, fill!
 *    error handlers
 *    sundry leftovers
 *    multiple-values, quasiquote
 *    eval
 *    multiprecision arithmetic
 *    *s7* environment
 *    initialization
 *    repl
 *
 * naming conventions: s7_* usually are C accessible (s7.h), g_* are scheme accessible (FFI), H_* are documentation strings,
 *   *_1 are auxilliary functions, big_* refer to gmp and friends, scheme "?" corresponds to C "_is_", scheme "->" to C "_to_".
 */

/* in case mus-config.h forgets these */
#ifndef HAVE_COMPLEX_NUMBERS
  #define HAVE_COMPLEX_NUMBERS 1
#endif
#if __cplusplus
  #ifndef HAVE_COMPLEX_TRIG
    #define HAVE_COMPLEX_TRIG 0
  #endif
#else
  #ifndef HAVE_COMPLEX_TRIG
    #define HAVE_COMPLEX_TRIG 1
  #endif
#endif

#ifndef _MSC_VER
  #include <unistd.h>
  #include <sys/param.h> 
  #include <strings.h>
  #include <errno.h>
#else
  #include <io.h>
  #pragma warning(disable: 4244)
#endif
#include <limits.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <time.h>
#include <stdarg.h>
#include <stddef.h>

#if __cplusplus
  #include <cmath>
#else
  #include <math.h>
#endif

#if HAVE_COMPLEX_NUMBERS
  #if __cplusplus
    #include <complex>
  #else
    #include <complex.h>
    #if defined(__sun) && defined(__SVR4)
      #undef _Complex_I
      #define _Complex_I 1.0fi
    #endif
  #endif
#ifndef CMPLX
  /* c11 addition? */
  #define CMPLX(r, i) ((r) + ((i) * _Complex_I))
#endif
#endif

#include <setjmp.h>
/* currently longjmps in s7_call, s7_error, g_throw, eval at OP_ERROR_HOOK_QUIT
 */

#include "s7.h"

#ifndef M_PI
  #define M_PI 3.1415926535897932384626433832795029L
#endif

#ifndef INFINITY
  #define INFINITY (-log(0.0))
  /* 1.0 / 0.0 is also used, there is sometimes a function, infinity(), MSC apparently uses HUGE_VALF
   */
#endif

#ifndef NAN
  #define NAN (INFINITY / INFINITY)
#endif

#define WRITE_REAL_PRECISION 16

#if ((!__NetBSD__) && ((_MSC_VER) || (!defined(__STC__)) || (defined(__STDC_VERSION__) && (__STDC_VERSION__ < 199901L))))
  #define __func__ __FUNCTION__
#endif

#define DISPLAY(Obj) s7_object_to_c_string(sc, Obj)
#define DISPLAY_80(Obj) object_to_truncated_string(sc, Obj, 80)

enum {OP_NO_OP, 
      OP_READ_INTERNAL, OP_EVAL, 
      OP_EVAL_ARGS, OP_EVAL_ARGS1, OP_EVAL_ARGS2, OP_EVAL_ARGS3, OP_EVAL_ARGS4, OP_EVAL_ARGS5,
      OP_APPLY, OP_EVAL_MACRO, OP_LAMBDA, OP_QUOTE, 
      OP_DEFINE, OP_DEFINE1, OP_BEGIN, OP_BEGIN_UNCHECKED, OP_BEGIN1,
      OP_IF, OP_IF1, OP_WHEN, OP_WHEN1, OP_UNLESS, OP_UNLESS1, OP_SET, OP_SET1, OP_SET2,
      OP_LET, OP_LET1, OP_LET_STAR, OP_LET_STAR1, OP_LET_STAR2,
      OP_LETREC, OP_LETREC1, OP_LETREC_STAR, OP_LETREC_STAR1, OP_COND, OP_COND1, OP_COND_SIMPLE, OP_COND1_SIMPLE,
      OP_AND, OP_AND1, OP_OR, OP_OR1, 
      OP_DEFINE_MACRO, OP_DEFINE_MACRO_STAR, OP_DEFINE_EXPANSION,
      OP_CASE, OP_CASE1, OP_READ_LIST, OP_READ_NEXT, OP_READ_DOT, OP_READ_QUOTE, 
      OP_READ_QUASIQUOTE, OP_READ_QUASIQUOTE_VECTOR, OP_READ_UNQUOTE, OP_READ_APPLY_VALUES,
      OP_READ_VECTOR, OP_READ_BYTEVECTOR, OP_READ_DONE, 
      OP_LOAD_RETURN_IF_EOF, OP_LOAD_CLOSE_AND_POP_IF_EOF, OP_EVAL_STRING, OP_EVAL_DONE,
      OP_CATCH, OP_DYNAMIC_WIND, OP_DEFINE_CONSTANT, OP_DEFINE_CONSTANT1, 
      OP_DO, OP_DO_END, OP_DO_END1, OP_DO_STEP, OP_DO_STEP2, OP_DO_INIT,
      OP_DEFINE_STAR, OP_LAMBDA_STAR, OP_LAMBDA_STAR_DEFAULT, OP_ERROR_QUIT, OP_UNWIND_INPUT, OP_UNWIND_OUTPUT, 
      OP_ERROR_HOOK_QUIT, 
      OP_WITH_LET, OP_WITH_LET1, OP_WITH_LET_UNCHECKED, OP_WITH_LET_S, 
      OP_WITH_BAFFLE, OP_WITH_BAFFLE_UNCHECKED, OP_EXPANSION,
      OP_FOR_EACH, OP_FOR_EACH_SIMPLE, OP_FOR_EACH_SIMPLER, OP_FOR_EACH_SIMPLEST,
      OP_MAP, OP_MAP_SIMPLE, OP_BARRIER, OP_DEACTIVATE_GOTO,

      OP_DEFINE_BACRO, OP_DEFINE_BACRO_STAR, 
      OP_GET_OUTPUT_STRING, OP_GET_OUTPUT_STRING_1, OP_SORT, OP_SORT1, OP_SORT2, OP_SORT3, OP_SORT_PAIR_END, OP_SORT_VECTOR_END, OP_SORT_STRING_END, 
      OP_EVAL_STRING_1, OP_EVAL_STRING_2, 
      OP_MEMBER_IF, OP_ASSOC_IF, OP_MEMBER_IF1, OP_ASSOC_IF1,
      
      OP_QUOTE_UNCHECKED, OP_LAMBDA_UNCHECKED, OP_LET_UNCHECKED, OP_CASE_UNCHECKED, OP_WHEN_UNCHECKED, OP_UNLESS_UNCHECKED,
      OP_SET_UNCHECKED, OP_SET_SYMBOL_C, OP_SET_SYMBOL_S, OP_SET_SYMBOL_Q, OP_SET_SYMBOL_P, OP_SET_SYMBOL_Z, OP_SET_SYMBOL_A,
      OP_SET_SYMBOL_SAFE_S, OP_SET_SYMBOL_SAFE_C, 
      OP_SET_SYMBOL_SAFE_SS, OP_SET_SYMBOL_SAFE_SSS, 
      OP_SET_SYMBOL_UNKNOWN_G,
      OP_SET_NORMAL, OP_SET_PAIR, OP_SET_PAIR_Z, OP_SET_PAIR_A, OP_SET_PAIR_P, OP_SET_PAIR_ZA, OP_SET_PAIR_UNKNOWN_G,
      OP_SET_PAIR_P_1, OP_SET_WITH_ACCESSOR, OP_SET_PWS, OP_SET_ENV_S, OP_SET_ENV_ALL_X,
      OP_SET_PAIR_C, OP_SET_PAIR_C_P, OP_SET_PAIR_C_P_1, OP_SET_SAFE, OP_SET_FV_SCALED,
      OP_LET_STAR_UNCHECKED, OP_LETREC_UNCHECKED, OP_LETREC_STAR_UNCHECKED, OP_COND_UNCHECKED,
      OP_LAMBDA_STAR_UNCHECKED, OP_DO_UNCHECKED, OP_DEFINE_UNCHECKED, OP_DEFINE_STAR_UNCHECKED, OP_DEFINE_FUNCHECKED,
      OP_DEFINE_WITH_ACCESSOR, OP_DEFINE_MACRO_WITH_ACCESSOR,
      OP_LET_NO_VARS, OP_NAMED_LET, OP_NAMED_LET_NO_VARS, OP_NAMED_LET_STAR,
      OP_CASE_SIMPLE, OP_CASE_SIMPLER, OP_CASE_SIMPLER_1, OP_CASE_SIMPLER_SS,
      OP_CASE_SIMPLEST, OP_CASE_SIMPLEST_SS, OP_CASE_SIMPLEST_ELSE, OP_CASE_SIMPLEST_ELSE_C,
      OP_LET_C, OP_LET_S, OP_LET_Q, OP_LET_ALL_C, OP_LET_ALL_S, OP_LET_ALL_X,
      OP_LET_STAR_ALL_X, OP_LET_opCq, OP_LET_opSSq,
      OP_IF_P_P_P, OP_IF_P_P, OP_IF_P_P_X, OP_IF_A_P_X, OP_IF_P_X_P, 
      OP_IF_B_P, OP_IF_ANDP_P, OP_IF_ANDP_P_P, OP_IF_ORP_P, OP_IF_ORP_P_P, 
      OP_IF_PPP, OP_IF_PP, OP_IF_PPX, OP_IF_PXP, 
      OP_IF_S_P_P, OP_IF_S_P, OP_IF_S_P_X, OP_IF_S_X_P, OP_IF_P_FEED, OP_IF_P_FEED_1, OP_WHEN_S, OP_UNLESS_S,
      OP_IF_UNCHECKED, OP_AND_UNCHECKED, OP_AND_P, OP_AND_P1, OP_OR_UNCHECKED, OP_OR_P, OP_OR_P1,
      
      OP_SAFE_IF_Z_Z, OP_SAFE_IF_A_Z, 
      OP_CATCH_1, OP_CATCH_2, OP_CATCH_ALL, OP_COND_ALL_X, OP_COND_S,
      OP_SIMPLE_DO, OP_SIMPLE_DO_STEP, OP_SAFE_DOTIMES, OP_SAFE_DOTIMES_STEP, OP_SAFE_DOTIMES_STEP_P, OP_SAFE_DOTIMES_STEP_O, OP_SAFE_DOTIMES_STEP_A,
      OP_SIMPLE_SAFE_DOTIMES, OP_SAFE_DO, OP_SAFE_DO_STEP, OP_SAFE_DO_STEP_1, OP_SAFE_DOTIMES_C_C,
      OP_SIMPLE_DO_P, OP_SIMPLE_DO_STEP_P, OP_DOX, OP_DOX_STEP, OP_DOX_STEP_P, OP_SIMPLE_DO_FOREVER,
      OP_DOTIMES_P, OP_DOTIMES_STEP_P, OP_SAFE_DOTIMES_C_A,
      OP_SIMPLE_DO_A, OP_SIMPLE_DO_STEP_A, OP_DO_ALL_X, OP_DO_ALL_X_STEP,
      OP_SAFE_IF_Z_Z_1, OP_SAFE_IF_CC_X_P, OP_SAFE_IF_CC_P_P, 
      OP_SAFE_IF_CS_P_P, OP_SAFE_IF_CS_X_P, OP_SAFE_IF_IS_NULL_S_P, OP_SAFE_IF_CC_P, OP_SAFE_IF_CS_P, OP_SAFE_IF_CS_P_X, OP_SAFE_IF_IS_NULL_Q_P,
      OP_SAFE_IF_CSS_X_P, OP_SAFE_IF_CSC_X_P, OP_SAFE_IF_CSC_X_O_A,
      OP_SAFE_IF_CSQ_P, OP_SAFE_IF_CSQ_P_P, OP_SAFE_IF_CSS_P, OP_SAFE_IF_CSS_P_P, OP_SAFE_IF_CSC_P, OP_SAFE_IF_CSC_P_P, 
      OP_SAFE_IF_IS_PAIR_P, OP_SAFE_IF_IS_PAIR_P_X, OP_SAFE_IF_IS_PAIR_P_P, OP_SAFE_IF_C_SS_P,
      OP_SAFE_IF_IS_SYMBOL_P, OP_SAFE_IF_IS_SYMBOL_P_P, OP_SAFE_IF_NOT_S_P,
      OP_IF_Z_P, OP_IF_Z_P_P, OP_IF_A_P, OP_IF_A_P_P, 
      OP_SAFE_C_P_1, OP_SAFE_C_PP_1, OP_SAFE_C_PP_2, OP_SAFE_C_PP_3, OP_SAFE_C_PP_4, OP_SAFE_C_PP_5, OP_SAFE_C_PP_6, 
      OP_EVAL_ARGS_P_1, OP_EVAL_ARGS_P_1_MV, OP_EVAL_ARGS_P_2, OP_EVAL_ARGS_P_2_MV, 
      OP_EVAL_ARGS_P_3, OP_EVAL_ARGS_P_4, OP_EVAL_ARGS_P_3_MV, OP_EVAL_ARGS_P_4_MV, 
      OP_EVAL_ARGS_SSP_1, OP_EVAL_ARGS_SSP_MV, OP_EVAL_MACRO_MV,
      OP_INCREMENT_1, OP_DECREMENT_1, OP_SET_CONS, 
      OP_INCREMENT_SS, OP_INCREMENT_SSS, OP_INCREMENT_SZ, OP_INCREMENT_C_TEMP, OP_INCREMENT_SA, OP_INCREMENT_SAA,
      OP_LET_R, OP_LET_ALL_R, OP_LET_C_D, OP_LET_R_P, OP_LET_CAR_P, OP_LET_ONE, OP_LET_ONE_1, OP_LET_Z, OP_LET_Z_1,

      OP_SAFE_C_ZZ_1, OP_SAFE_C_ZZ_2, OP_SAFE_C_SZ_1, OP_SAFE_C_ZS_1, OP_SAFE_C_ZA_1, OP_INCREMENT_SZ_1, OP_SAFE_C_SZ_SZ,
      OP_SAFE_C_ZAA_1, OP_SAFE_C_AZA_1, OP_SAFE_C_AAZ_1, OP_SAFE_C_SSZ_1, 
      OP_SAFE_C_ZZA_1, OP_SAFE_C_ZZA_2, OP_SAFE_C_ZAZ_1, OP_SAFE_C_ZAZ_2, OP_SAFE_C_AZZ_1, OP_SAFE_C_AZZ_2, 
      OP_SAFE_C_ZZZ_1, OP_SAFE_C_ZZZ_2, OP_SAFE_C_ZZZ_3, 
      OP_SAFE_C_ZZZZ_1, OP_SAFE_C_ZZZZ_2, OP_SAFE_C_ZZZZ_3, OP_SAFE_C_ZZZZ_4, 
      /* the zzzz cases exist only to trigger later optimizations (canter in clm-ins.scm and many in animals.scm) 
       *   we need the top of the do-loop outa expr to be optimized
       *   so that the direct_looped function is called which then calls find_gf.  Perhaps add possible_direct_loop?
       */

      OP_SAFE_C_opSq_P_1, OP_SAFE_C_opSq_P_MV, OP_C_P_1, OP_C_P_2, OP_C_SP_1, OP_C_SP_2,
      OP_CLOSURE_P_1, OP_CLOSURE_P_2, OP_SAFE_CLOSURE_P_1,
      OP_MAX_DEFINED_1};

#define OP_MAX_DEFINED (OP_MAX_DEFINED_1 + 1)

#if (((defined(SIZEOF_VOID_P)) && (SIZEOF_VOID_P == 4)) || ((defined(__SIZEOF_POINTER__)) && (__SIZEOF_POINTER__ == 4)))
  #define opcode_t unsigned int
  #define PRINT_NAME_PADDING 8
  #define PRINT_NAME_SIZE (20 - PRINT_NAME_PADDING - 2)
#else
  #define opcode_t unsigned long long int
  #define PRINT_NAME_PADDING 16
  #define PRINT_NAME_SIZE (40 - PRINT_NAME_PADDING - 2)
#endif
/* ideally we'd use (2 * max(sizeof(s7_Int), sizeof(s7_Double))) for the padding,
 *   but I can't think of a way to do this short of a configuration script.
 *   If someone defines s7_Double to be long double, there's much wasted memory (and other more serious problems...)
 */

/* these names are intended for error messages (eval_error_with_name) and debugging.  
 */
static const char *op_names[OP_MAX_DEFINED + 1] = 
  {"no-op", 
   "read-internal", "eval", 
   "eval-args", "eval-args1", "eval-args2", "eval-args3", "eval-args4", "eval-args5",
   "apply", "eval-macro", "lambda", "quote", 
   "define", "define", "begin", "begin", "begin",
   "if", "if", "when", "when", "unless", "unless", "set!", "set!", "set!", 
   "let", "let", "let*", "let*", "let*",
   "letrec", "letrec", "letrec*", "letrec*", "cond", "cond", "cond", "cond",
   "and", "and", "or", "or", 
   "define-macro", "define-macro*", "define-expansion",
   "case", "case", "read-list", "read-list", "read-dot", "read-quote", 
   "read-quasiquote", "read-quasiquote-vector", "read-unquote", "read-apply-values",
   "read-vector", "read-bytevector", "read-done", 
   "load", "load", "eval-string", "eval",
   "catch", "dynamic-wind", "define-constant", "define-constant", 
   "do", "do", "do", "do", "do", "do",
   "define*", "lambda*", "lambda*", "error-quit", "unwind-input", "unwind-output", 
   "error-hook-quit", "with-let", "with-let", "with-let", "with-let", 
   "with-baffle", "with-baffle", "define-expansion",
   "for-each", "for-each", "for-each", "for-each",
   "map", "map", "barrier", "deactivate-goto",
   "define-bacro", "define-bacro*", 
   "get-output-string", "get-output-string", "sort!", "sort!", "sort!", "sort!", "sort!", "sort!", "sort!",
   "eval-string", "eval-string", 
   "member", "assoc", "member", "assoc",
   
   "quote", "lambda", "let", "case", "when", "unless",
   "set!", "set!", "set!", "set!", "set!", "set!", "set!", "set!", "set!",
   "set!", "set!", "set!", "set!", "set!", "set!", "set!", "set!", "set!",
   "set!", "set!", "set!", "set!", "set!", "set!", "set!", "set!", "set!", "set!", "set!",
   "let*", "letrec", "letrec*", "cond",
   "lambda*", "do", "define", "define*", "define",
   "define", "define-macro",
   "let", "let", "let", "let*",
   "case", "case", "case", "case",
   "case", "case", "case", "case", 
   "let", "let", "let", "let", "let", "let",
   "let*", "let", "let",

   "if", "if", "if", "if", "if", "if",
   "if", "if", "if", "if", "if", 
   "if", "if", "if", "if", "if", 
   "if", "if", "if", "if", "if", "when", "unless",
   "and", "and", "and", "or", "or", "or", 

   "if", "if",
   "catch", "catch", "catch", "cond", "cond",
   "do", "do", "do", "do", "do", 
   "do", "do", "do", "do", "do", "do",
   "do", "do", "do", "do", "do",
   "do", "do", "do", "do", "do",
   "do", "do", "do", "do", "do",
   "if", "if", "if", "if",
   "if", "if", "if", "if", "if", 
   "if", "if", "if",
   "if", "if", "if", "if", "if", "if", "if",
   "if", "if", "if", "if",
   "if", "if", "if", "if", "if", "if",
   "c_p_1", "c_pp_1", "c_pp_2", "c_pp_3", "c_pp_4", "c_pp_5", "c_pp_6", 
   "eval_args_p_1", "eval_args_p_1_mv", "eval_args_p_2", "eval_args_p_2_mv", 
   "eval_args_p_3", "eval_args_p_4", "eval_args_p_3_mv", "eval_args_p_4_mv", 
   "eval_args_ssp_1", "eval_args_ssp_mv", "eval_macro_mv",
   "increment_1", "decrement_1", "set_cons", 
   "increment_ss", "increment_sss", "increment_sz", "increment_c_temp", "increment_sa", "increment_saa",
   "let", "let", "let", "let", "let", "let", "let", "let", "let", 

   "c_zz_1", "c_zz_2", "c_sz_1", "c_zs_1", "c_za_1", "increment_sz_1", "c_sz_sz",
   "c_zaa_1", "c_aza_1", "c_aaz_1", "c_ssz_1", 
   "c_zza_1", "c_zza_2", "c_zaz_1", "c_zaz_2", "c_azz_1", "c_azz_2", 
   "c_zzz_1", "c_zzz_2", "c_zzz_3", "zzzz_1", "zzzz_2", "zzzz_3", "zzzz_4",

   "c_opsq_p_1", "c_opsq_p_mv", "c_p_1", "c_p_2", "c_sp_1", "c_sp_2",
   "closure_p_1", "closure_p_2", "safe_closure_p_1",
   "op-max"
  };


/* these are just for debugging but it's annoying to keep setting the DEBUGGING switch */
static const char *real_op_names[OP_MAX_DEFINED + 1] = {
  "no_op", 
  "read_internal", "eval", 
  "eval_args", "eval_args1", "eval_args2", "eval_args3", "eval_args4", "eval_args5",
  "apply", "eval_macro", "lambda", "quote", 
  "define", "define1", "begin", "begin-unchecked", "begin1",
  "if", "if1", "when", "when1", "unless", "unless1", "set", "set1", "set2",
  "let", "let1", "let_star", "let_star1", "let_star2", 
  "letrec", "letrec1", "letrec_star", "letrec_star1", "cond", "cond1", "cond_simple", "cond1_simple",
  "and", "and1", "or", "or1", 
  "define_macro", "define_macro_star", "define_expansion",
  "case", "case1", "read_list", "read_next", "read_dot", "read_quote", 
  "read_quasiquote", "read_quasiquote_vector", "read_unquote", "read_apply_values",
  "read_vector", "read_bytevector", "read_done", 
  "load_return_if_eof", "load_close_and_pop_if_eof", "eval_string", "eval_done",
  "catch", "dynamic_wind", "define_constant", "define_constant1", 
  "do", "do_end", "do_end1", "do_step", "do_step2", "do_init",
  "define_star", "lambda_star", "lambda_star_default", "error_quit", "unwind_input", "unwind_output", 
  "error_hook_quit", 
  "with_let", "with_let1", "with_let_unchecked", "with_let_s", 
  "with_baffle", "with_baffle_unchecked", "expansion",
  "for_each", "for_each_simple", "for_each_simpler", "for_each_simplest",
  "map", "map_simple", "barrier", "deactivate_goto",
  
  "define_bacro", "define_bacro_star", 
  "get_output_string", "get-output-string-1", "sort", "sort1", "sort2", "sort3", "sort_pair_end", "sort_vector_end", "sort_string_end", 
  "eval_string_1", "eval_string_2", 
  "member_if", "assoc_if", "member_if1", "assoc_if1",
  
  "quote_unchecked", "lambda_unchecked", "let_unchecked", "case_unchecked", "when_unchecked", "unless_unchecked",
  "set_unchecked", "set_symbol_c", "set_symbol_s", "set_symbol_q", "set_symbol_p", "set_symbol_z", "set_symbol_a",
  "set_symbol_safe_s", "set_symbol_safe_c", 
  "set_symbol_safe_ss", "set_symbol_safe_sss",
  "set_symbol_unknown_g",
  "set_normal", "set_pair", "set_pair_z", "set_pair_a", "set_pair_p", "set_pair_za", "set_pair_unknown_g",
  "set_pair_p_1", "set_with_accessor", "set_pws", "set_env_s", "set_env_all_x",
  "set_pair_c", "set_pair_c_p", "set_pair_c_p_1", "set_safe", "set_fv_scaled",
  "let_star_unchecked", "letrec_unchecked", "letrec_star_unchecked", "cond_unchecked",
  "lambda_star_unchecked", "do_unchecked", "define_unchecked", "define_star_unchecked", "define_funchecked",
  "define_with_accessor", "define_macro_with_accessor",
  "let_no_vars", "named_let", "named_let_no_vars", "named_let_star",
  "case_simple", "case_simpler", "case_simpler_1", "case_simpler_ss",
  "case_simplest", "case_simplest_ss", "case_simplest_else", "case_simplest_else_c",
  "let_c", "let_s", "let_q", "let_all_c", "let_all_s", "let_all_x", 
  "let_star_all_x", "let_opcq", "let_opssq", 
  "if_p_p_p", "if_p_p", "if_p_p_x", "if_a_p_x", "if_p_x_p", 
  "if_b_p", "if_andp_p", "if_andp_p_p", "if_orp_p", "if_orp_p_p", 
  "if_ppp", "if_pp", "if_ppx", "if_pxp", 
  "if_s_p_p", "if_s_p", "if_s_p_x", "if_s_x_p", "if_p_feed", "if_p_feed_1", "when_s", "unless_s",
  "if_unchecked", "and_unchecked", "and_p", "and_p1", "or_unchecked", "or_p", "or_p1",
  
  "safe_if_z_z", "safe_if_a_z", 
  "catch_1", "catch_2", "catch_all", "cond_all_x", "cond_s",
  "simple_do", "simple_do_step", "safe_dotimes", "safe_dotimes_step", "safe_dotimes_step_p", "safe_dotimes_step_o", "safe_dotimes_step_a",
  "simple_safe_dotimes", "safe_do", "safe_do_step", "safe_do_step_1", "safe_dotimes_c_c",
  "simple_do_p", "simple_do_step_p", "dox", "dox_step", "dox_step_p", "simple_do_forever",
  "dotimes_p", "dotimes_step_p", "safe_dotimes_c_a",
  "simple_do_a", "simple_do_step_a", "do_all_x", "do_all_x_step",
  "safe_if_z_z_1", "safe_if_cc_x_p", "safe_if_cc_p_p", 
  "safe_if_cs_p_p", "safe_if_cs_x_p", "safe_if_is_null_s_p", "safe_if_cc_p", "safe_if_cs_p", "safe_if_cs_p_x", "safe_if_is_null_q_p",
  "safe_if_css_x_p", "safe_if_csc_x_p", "safe_if_csc_x_o_a",
  "safe_if_csq_p", "safe_if_csq_p_p", "safe_if_css_p", "safe_if_css_p_p", "safe_if_csc_p", "safe_if_csc_p_p", 
  "safe_if_is_pair_p", "safe_if_is_pair_p_x", "safe_if_is_pair_p_p", "safe_if_c_ss_p", 
  "safe_if_is_symbol_p", "safe_if_is_symbol_p_p", "safe_if_not_s_p",
  "if_z_p", "if_z_p_p", "if_a_p", "if_a_p_p", 
  "safe_c_p_1", "safe_c_pp_1", "safe_c_pp_2", "safe_c_pp_3", "safe_c_pp_4", "safe_c_pp_5", "safe_c_pp_6", 
  "eval_args_p_1", "eval_args_p_1_mv", "eval_args_p_2", "eval_args_p_2_mv", 
  "eval_args_p_3", "eval_args_p_4", "eval_args_p_3_mv", "eval_args_p_4_mv", 
  "eval_args_ssp_1", "eval_args_ssp_mv", "eval_macro_mv",
  "increment_1", "decrement_1", "set_cons", 
  "increment_ss", "increment_sss", "increment_sz", "increment_c_temp", "increment_sa", "increment_saa",
  "let_r", "let_all_r", "let_c_d", "let_r_p", "let_car_p", "let_one", "let_one_1", "let_z", "let_z_1",

  "safe_c_zz_1", "safe_c_zz_2", "safe_c_sz_1", "safe_c_zs_1", "safe_c_za_1", "increment_sz_1", "safe_c_sz_sz",
  "safe_c_zaa_1", "safe_c_aza_1", "safe_c_aaz_1", "safe_c_ssz_1", 
  "safe_c_zza_1", "safe_c_zza_2", "safe_c_zaz_1", "safe_c_zaz_2", "safe_c_azz_1", "safe_c_azz_2", 
  "safe_c_zzz_1", "safe_c_zzz_2", "safe_c_zzz_3", 
  "safe_c_zzzz_1", "safe_c_zzzz_2", "safe_c_zzzz_3", "safe_c_zzzz_4", 

  "safe_c_opsq_p_1", "safe_c_opsq_p_mv", "c_p_1", "c_p_2", "c_sp_1", "c_sp_2",
  "closure_p_1", "closure_p_2", "safe_closure_p_2",
  "max_defined_1"};

typedef enum{E_C_P, E_C_PP, E_C_CP, E_C_SP, E_C_PC, E_C_PS} combine_op_t;

enum {OP_SAFE_C_C, HOP_SAFE_C_C, OP_SAFE_C_S, HOP_SAFE_C_S, 
      OP_SAFE_C_SS, HOP_SAFE_C_SS, OP_SAFE_C_SC, HOP_SAFE_C_SC, OP_SAFE_C_CS, HOP_SAFE_C_CS, 
      OP_SAFE_C_Q, HOP_SAFE_C_Q, OP_SAFE_C_SQ, HOP_SAFE_C_SQ, OP_SAFE_C_QS, HOP_SAFE_C_QS, OP_SAFE_C_QQ, HOP_SAFE_C_QQ, 
      OP_SAFE_C_CQ, HOP_SAFE_C_CQ, OP_SAFE_C_QC, HOP_SAFE_C_QC, 
      OP_SAFE_C_SSS, HOP_SAFE_C_SSS, OP_SAFE_C_SCS, HOP_SAFE_C_SCS, OP_SAFE_C_SSC, HOP_SAFE_C_SSC, OP_SAFE_C_CSS, HOP_SAFE_C_CSS, 
      OP_SAFE_C_SCC, HOP_SAFE_C_SCC, OP_SAFE_C_CSC, HOP_SAFE_C_CSC,
      OP_SAFE_C_ALL_S, HOP_SAFE_C_ALL_S, OP_SAFE_C_ALL_X, HOP_SAFE_C_ALL_X, OP_SAFE_C_SSA, HOP_SAFE_C_SSA, 
      OP_SAFE_C_CSA, HOP_SAFE_C_CSA, OP_SAFE_C_SCA, HOP_SAFE_C_SCA, OP_SAFE_C_CAS, HOP_SAFE_C_CAS,
      OP_SAFE_C_A, HOP_SAFE_C_A, OP_SAFE_C_AA, HOP_SAFE_C_AA, OP_SAFE_C_AAA, HOP_SAFE_C_AAA, OP_SAFE_C_AAAA, HOP_SAFE_C_AAAA, 
      OP_SAFE_C_SQS, HOP_SAFE_C_SQS, OP_SAFE_C_opAq, HOP_SAFE_C_opAq, OP_SAFE_C_opAAq, HOP_SAFE_C_opAAq, OP_SAFE_C_opAAAq, HOP_SAFE_C_opAAAq,
      OP_SAFE_C_S_opAAq, HOP_SAFE_C_S_opAAq, OP_SAFE_C_S_opAAAq, HOP_SAFE_C_S_opAAAq, 

      OP_SAFE_C_opCq, HOP_SAFE_C_opCq, OP_SAFE_C_opSq, HOP_SAFE_C_opSq, 
      OP_SAFE_C_opSSq, HOP_SAFE_C_opSSq, OP_SAFE_C_opSCq, HOP_SAFE_C_opSCq, OP_SAFE_C_opSQq, HOP_SAFE_C_opSQq, 
      OP_SAFE_C_opCSq, HOP_SAFE_C_opCSq, OP_SAFE_C_S_opSq, HOP_SAFE_C_S_opSq,
      OP_SAFE_C_C_opSCq, HOP_SAFE_C_C_opSCq, 
      OP_SAFE_C_S_opSCq, HOP_SAFE_C_S_opSCq, OP_SAFE_C_S_opCSq, HOP_SAFE_C_S_opCSq, 
      OP_SAFE_C_opSq_S, HOP_SAFE_C_opSq_S, OP_SAFE_C_opSq_C, HOP_SAFE_C_opSq_C, 
      OP_SAFE_C_opSq_opSq, HOP_SAFE_C_opSq_opSq, OP_SAFE_C_S_opSSq, HOP_SAFE_C_S_opSSq, OP_SAFE_C_C_opSq, HOP_SAFE_C_C_opSq, 
      OP_SAFE_C_C_opCSq, HOP_SAFE_C_C_opCSq, OP_SAFE_C_opCSq_C, HOP_SAFE_C_opCSq_C, 
      OP_SAFE_C_S_opCq, HOP_SAFE_C_S_opCq, OP_SAFE_C_opSSq_C, HOP_SAFE_C_opSSq_C, OP_SAFE_C_C_opSSq, HOP_SAFE_C_C_opSSq, 
      OP_SAFE_C_C_opCq, HOP_SAFE_C_C_opCq, OP_SAFE_C_opCq_S, HOP_SAFE_C_opCq_S, 
      OP_SAFE_C_opCq_opCq, HOP_SAFE_C_opCq_opCq, OP_SAFE_C_opCq_C, HOP_SAFE_C_opCq_C, 
      OP_SAFE_C_opSCq_opSCq, HOP_SAFE_C_opSCq_opSCq, OP_SAFE_C_opSSq_opSSq, HOP_SAFE_C_opSSq_opSSq, 
      OP_SAFE_C_opSSq_opCq, HOP_SAFE_C_opSSq_opCq, OP_SAFE_C_opSSq_opSq, HOP_SAFE_C_opSSq_opSq, OP_SAFE_C_opSq_opSSq, HOP_SAFE_C_opSq_opSSq,
      OP_SAFE_C_opSSq_S, HOP_SAFE_C_opSSq_S, OP_SAFE_C_opSCq_S, HOP_SAFE_C_opSCq_S, OP_SAFE_C_opCSq_S, HOP_SAFE_C_opCSq_S,
      OP_SAFE_C_opSCq_C, HOP_SAFE_C_opSCq_C, OP_SAFE_C_opCq_opSSq, HOP_SAFE_C_opCq_opSSq, 
      OP_SAFE_C_S_op_opSSq_Sq, HOP_SAFE_C_S_op_opSSq_Sq, OP_SAFE_C_S_op_S_opSSqq, HOP_SAFE_C_S_op_S_opSSqq, 
      OP_SAFE_C_op_opSSq_q_C, HOP_SAFE_C_op_opSSq_q_C, OP_SAFE_C_S_op_opSSq_opSSqq, HOP_SAFE_C_S_op_opSSq_opSSqq, 
      OP_SAFE_C_opSSq_op_opSSq_q, HOP_SAFE_C_opSSq_op_opSSq_q, OP_SAFE_C_op_opSSq_Sq_opSSq, HOP_SAFE_C_op_opSSq_Sq_opSSq,
      OP_SAFE_C_op_opSq_q, HOP_SAFE_C_op_opSq_q, OP_SAFE_C_C_op_S_opCqq, HOP_SAFE_C_C_op_S_opCqq, 
      OP_SAFE_C_op_S_opSq_q, HOP_SAFE_C_op_S_opSq_q, 

      OP_SAFE_C_STAR_CD, HOP_SAFE_C_STAR_CD, 

      OP_SAFE_C_Z, HOP_SAFE_C_Z, OP_SAFE_C_ZZ, HOP_SAFE_C_ZZ, OP_SAFE_C_SZ, HOP_SAFE_C_SZ, OP_SAFE_C_ZS, HOP_SAFE_C_ZS, 
      OP_SAFE_C_CZ, HOP_SAFE_C_CZ, OP_SAFE_C_ZC, HOP_SAFE_C_ZC, 
      OP_SAFE_C_opCq_Z, HOP_SAFE_C_opCq_Z, OP_SAFE_C_S_opSZq, HOP_SAFE_C_S_opSZq, 
      OP_SAFE_C_AZ, HOP_SAFE_C_AZ, OP_SAFE_C_ZA, HOP_SAFE_C_ZA, 
      OP_SAFE_C_ZAA, HOP_SAFE_C_ZAA, OP_SAFE_C_AZA, HOP_SAFE_C_AZA, OP_SAFE_C_AAZ, HOP_SAFE_C_AAZ, OP_SAFE_C_SSZ, HOP_SAFE_C_SSZ,
      OP_SAFE_C_ZZA, HOP_SAFE_C_ZZA, OP_SAFE_C_ZAZ, HOP_SAFE_C_ZAZ, OP_SAFE_C_AZZ, HOP_SAFE_C_AZZ, 
      OP_SAFE_C_ZZZ, HOP_SAFE_C_ZZZ, OP_SAFE_C_ZZZZ, HOP_SAFE_C_ZZZZ,
      
      OP_THUNK, HOP_THUNK, 
      OP_CLOSURE_S, HOP_CLOSURE_S, OP_CLOSURE_C, HOP_CLOSURE_C, OP_CLOSURE_Q, HOP_CLOSURE_Q, 
      OP_CLOSURE_SS, HOP_CLOSURE_SS, OP_CLOSURE_SSb, HOP_CLOSURE_SSb, 
      OP_CLOSURE_SC, HOP_CLOSURE_SC, OP_CLOSURE_CS, HOP_CLOSURE_CS, 
      OP_CLOSURE_A, HOP_CLOSURE_A, OP_CLOSURE_AA, HOP_CLOSURE_AA, 
      OP_CLOSURE_opSq_S, HOP_CLOSURE_opSq_S, OP_CLOSURE_opSq_opSq, HOP_CLOSURE_opSq_opSq, 
      OP_CLOSURE_S_opSq, HOP_CLOSURE_S_opSq, OP_CLOSURE_S_opSSq, HOP_CLOSURE_S_opSSq, OP_CLOSURE_opSSq_S, HOP_CLOSURE_opSSq_S, 
      OP_CLOSURE_ALL_X, HOP_CLOSURE_ALL_X, OP_CLOSURE_ALL_S, HOP_CLOSURE_ALL_S, 
      
      OP_GLOSURE_A, HOP_GLOSURE_A, OP_GLOSURE_S, HOP_GLOSURE_S, OP_GLOSURE_P, HOP_GLOSURE_P, 

      OP_CLOSURE_STAR_S, HOP_CLOSURE_STAR_S, OP_CLOSURE_STAR_SX, HOP_CLOSURE_STAR_SX,
      OP_CLOSURE_STAR, HOP_CLOSURE_STAR, OP_CLOSURE_STAR_ALL_X, HOP_CLOSURE_STAR_ALL_X, 

      OP_SAFE_THUNK, HOP_SAFE_THUNK, 
      OP_SAFE_CLOSURE_S, HOP_SAFE_CLOSURE_S, OP_SAFE_CLOSURE_C, HOP_SAFE_CLOSURE_C, OP_SAFE_CLOSURE_Q, HOP_SAFE_CLOSURE_Q, 
      OP_SAFE_CLOSURE_SS, HOP_SAFE_CLOSURE_SS, OP_SAFE_CLOSURE_SC, HOP_SAFE_CLOSURE_SC, OP_SAFE_CLOSURE_CS, HOP_SAFE_CLOSURE_CS, 
      OP_SAFE_CLOSURE_A, HOP_SAFE_CLOSURE_A, OP_SAFE_CLOSURE_SA, HOP_SAFE_CLOSURE_SA, 
      OP_SAFE_CLOSURE_opSq_S, HOP_SAFE_CLOSURE_opSq_S, OP_SAFE_CLOSURE_opSq_opSq, HOP_SAFE_CLOSURE_opSq_opSq, 
      OP_SAFE_CLOSURE_S_opSq, HOP_SAFE_CLOSURE_S_opSq, OP_SAFE_CLOSURE_S_opSSq, HOP_SAFE_CLOSURE_S_opSSq, OP_SAFE_CLOSURE_opSSq_S, HOP_SAFE_CLOSURE_opSSq_S, 
      OP_SAFE_CLOSURE_SAA, HOP_SAFE_CLOSURE_SAA, 
      OP_SAFE_CLOSURE_ALL_X, HOP_SAFE_CLOSURE_ALL_X, OP_SAFE_CLOSURE_AA, HOP_SAFE_CLOSURE_AA, 
      
      OP_SAFE_GLOSURE_A, HOP_SAFE_GLOSURE_A, OP_SAFE_GLOSURE_S, HOP_SAFE_GLOSURE_S, OP_SAFE_GLOSURE_P, HOP_SAFE_GLOSURE_P, 

      OP_SAFE_CLOSURE_STAR_S, HOP_SAFE_CLOSURE_STAR_S, OP_SAFE_CLOSURE_STAR_SS, HOP_SAFE_CLOSURE_STAR_SS, 
      OP_SAFE_CLOSURE_STAR_SC, HOP_SAFE_CLOSURE_STAR_SC, OP_SAFE_CLOSURE_STAR_SA, HOP_SAFE_CLOSURE_STAR_SA, OP_SAFE_CLOSURE_STAR_S0, HOP_SAFE_CLOSURE_STAR_S0,
      OP_SAFE_CLOSURE_STAR, HOP_SAFE_CLOSURE_STAR, OP_SAFE_CLOSURE_STAR_ALL_X, HOP_SAFE_CLOSURE_STAR_ALL_X, 

      /* these can't be embedded, and have to be the last thing called */
      OP_APPLY_SS, HOP_APPLY_SS,
      OP_C_ALL_X, HOP_C_ALL_X, OP_CALL_WITH_EXIT, HOP_CALL_WITH_EXIT, OP_C_CATCH, HOP_C_CATCH, OP_C_CATCH_ALL, HOP_C_CATCH_ALL,
      OP_C_S_opSq, HOP_C_S_opSq, OP_C_S_opCq, HOP_C_S_opCq, 
      OP_C_FOR_EACH_LS, HOP_C_FOR_EACH_LS, OP_C_FOR_EACH_LS_2, HOP_C_FOR_EACH_LS_2, OP_C_FOR_EACH_L_opSq, HOP_C_FOR_EACH_L_opSq, 
      OP_C_S, HOP_C_S, OP_READ_S, HOP_READ_S, OP_C_P, HOP_C_P, OP_C_SP, HOP_C_SP, OP_C_A, HOP_C_A,

      OP_GOTO, HOP_GOTO, OP_GOTO_C, HOP_GOTO_C, OP_GOTO_S, HOP_GOTO_S, OP_GOTO_A, HOP_GOTO_A, 
      
      OP_VECTOR_C, HOP_VECTOR_C, OP_VECTOR_S, HOP_VECTOR_S, OP_VECTOR_A, HOP_VECTOR_A, OP_VECTOR_CC, HOP_VECTOR_CC, 
      OP_STRING_C, HOP_STRING_C, OP_STRING_S, HOP_STRING_S, OP_STRING_A, HOP_STRING_A, 
      OP_C_OBJECT, HOP_C_OBJECT, OP_C_OBJECT_C, HOP_C_OBJECT_C, OP_C_OBJECT_S, HOP_C_OBJECT_S, 
      OP_C_OBJECT_A, HOP_C_OBJECT_A, OP_C_OBJECT_SS, HOP_C_OBJECT_SS, OP_C_OBJECT_CC, HOP_C_OBJECT_CC, 
      OP_PAIR_C, HOP_PAIR_C, OP_PAIR_S, HOP_PAIR_S, OP_PAIR_A, HOP_PAIR_A, 
      OP_HASH_TABLE_C, HOP_HASH_TABLE_C, OP_HASH_TABLE_S, HOP_HASH_TABLE_S, OP_HASH_TABLE_A, HOP_HASH_TABLE_A, 
      OP_ENVIRONMENT_S, HOP_ENVIRONMENT_S, OP_ENVIRONMENT_Q, HOP_ENVIRONMENT_Q, OP_ENVIRONMENT_A, HOP_ENVIRONMENT_A, OP_ENVIRONMENT_C, HOP_ENVIRONMENT_C, 
      
      OP_UNKNOWN, HOP_UNKNOWN, OP_UNKNOWN_ALL_S, HOP_UNKNOWN_ALL_S, OP_UNKNOWN_ALL_X, HOP_UNKNOWN_ALL_X,
      OP_UNKNOWN_G, HOP_UNKNOWN_G, OP_UNKNOWN_GG, HOP_UNKNOWN_GG, OP_UNKNOWN_A, HOP_UNKNOWN_A, OP_UNKNOWN_AA, HOP_UNKNOWN_AA, 

      OP_SAFE_C_opVSq_S, HOP_SAFE_C_opVSq_S, OP_SAFE_C_S_opVSq, HOP_SAFE_C_S_opVSq, OP_SAFE_C_opVSq_opVSq, HOP_SAFE_C_opVSq_opVSq, 
      
      OP_SAFE_C_P, HOP_SAFE_C_P, OP_SAFE_C_PP, HOP_SAFE_C_PP,
      OP_SAFE_C_opSq_P, HOP_SAFE_C_opSq_P, 
      OP_SAFE_C_SP, HOP_SAFE_C_SP, OP_SAFE_C_CP, HOP_SAFE_C_CP, OP_SAFE_C_QP, HOP_SAFE_C_QP, OP_SAFE_C_AP, HOP_SAFE_C_AP, 
      OP_SAFE_C_PS, HOP_SAFE_C_PS, OP_SAFE_C_PC, HOP_SAFE_C_PC, OP_SAFE_C_PQ, HOP_SAFE_C_PQ,
      OP_SAFE_C_SSP, HOP_SAFE_C_SSP, 
      OPT_MAX_DEFINED
};

#define is_safe_c_op(op) ((op < OP_THUNK) && (op >= OP_SAFE_C_C)) /* used only in safe_stepper */
                                                                  /* for an unsigned int op, op is always >= OP_SAFE_C_C (0) */
#define is_unknown_op(op) ((op >= OP_UNKNOWN) && (op < OP_SAFE_C_P))
#define is_callable_c_op(op) (((op < OP_THUNK) && (op >= OP_SAFE_C_C)) || (op >= OP_SAFE_C_opVSq_S)) /* used only in check_set */

/* also for debugging */
static const char *opt_names[OPT_MAX_DEFINED + 1] =
  {   "safe_c_c", "h_safe_c_c", "safe_c_s", "h_safe_c_s", 
      "safe_c_ss", "h_safe_c_ss", "safe_c_sc", "h_safe_c_sc", "safe_c_cs", "h_safe_c_cs", 
      "safe_c_q", "h_safe_c_q", "safe_c_sq", "h_safe_c_sq", "safe_c_qs", "h_safe_c_qs", "safe_c_qq", "h_safe_c_qq", 
      "safe_c_cq", "h_safe_c_cq", "safe_c_qc", "h_safe_c_qc", 
      "safe_c_sss", "h_safe_c_sss", "safe_c_scs", "h_safe_c_scs", "safe_c_ssc", "h_safe_c_ssc", "safe_c_css", "h_safe_c_css", 
      "safe_c_scc", "h_safe_c_scc", "safe_c_csc", "h_safe_c_csc",
      "safe_c_all_s", "h_safe_c_all_s", "safe_c_all_x", "h_safe_c_all_x", "safe_c_ssa", "h_safe_c_ssa", 
      "safe_c_csa", "h_safe_c_csa", "safe_c_sca", "h_safe_c_sca", "safe_c_cas", "h_safe_c_cas",
      "safe_c_a", "h_safe_c_a", "safe_c_aa", "h_safe_c_aa", "safe_c_aaa", "h_safe_c_aaa", "safe_c_aaaa", "h_safe_c_aaaa", 
      "safe_c_sqs", "h_safe_c_sqs", "safe_c_opaq", "h_safe_c_opaq", "safe_c_opaaq", "h_safe_c_opaaq", "safe_c_opaaaq", "h_safe_c_opaaaq", 
      "safe_c_s_opaaq", "h_safe_c_s_opaaq", "safe_c_s_opaaaq", "h_safe_c_s_opaaaq", 
      
      "safe_c_opcq", "h_safe_c_opcq", "safe_c_opsq", "h_safe_c_opsq", 
      "safe_c_opssq", "h_safe_c_opssq", "safe_c_opscq", "h_safe_c_opscq", "safe_c_opsqq", "h_safe_c_opsqq", 
      "safe_c_opcsq", "h_safe_c_opcsq", "safe_c_s_opsq", "h_safe_c_s_opsq",
      "safe_c_c_opscq", "h_safe_c_c_opscq", 
      "safe_c_s_opscq", "h_safe_c_s_opscq", "safe_c_s_opcsq", "h_safe_c_s_opcsq", 
      "safe_c_opsq_s", "h_safe_c_opsq_s", "safe_c_opsq_c", "h_safe_c_opsq_c", 
      "safe_c_opsq_opsq", "h_safe_c_opsq_opsq", "safe_c_s_opssq", "h_safe_c_s_opssq", "safe_c_c_opsq", "h_safe_c_c_opsq", 
      "safe_c_c_opcsq", "h_safe_c_c_opcsq", "safe_c_opcsq_c", "h_safe_c_opcsq_c", 
      "safe_c_s_opcq", "h_safe_c_s_opcq", "safe_c_opssq_c", "h_safe_c_opssq_c", "safe_c_c_opssq", "h_safe_c_c_opssq", 
      "safe_c_c_opcq", "h_safe_c_c_opcq", "safe_c_opcq_s", "h_safe_c_opcq_s", 
      "safe_c_opcq_opcq", "h_safe_c_opcq_opcq", "safe_c_opcq_c", "h_safe_c_opcq_c", 
      "safe_c_opscq_opscq", "h_safe_c_opscq_opscq", "safe_c_opssq_opssq", "h_safe_c_opssq_opssq", 
      "safe_c_opssq_opcq", "h_safe_c_opssq_opcq", "safe_c_opssq_opsq", "h_safe_c_opssq_opsq", "safe_c_opsq_opssq", "h_safe_c_opsq_opssq",
      "safe_c_opssq_s", "h_safe_c_opssq_s", "safe_c_opscq_s", "h_safe_c_opscq_s", "safe_c_opcsq_s", "h_safe_c_opcsq_s",
      "safe_c_opscq_c", "h_safe_c_opscq_c", "safe_c_opcq_opssq", "h_safe_c_opcq_opssq",
      "safe_c_s_op_opssq_sq", "h_safe_c_s_op_opssq_sq", "safe_c_s_op_s_opssqq", "h_safe_c_s_op_s_opssqq", 
      "safe_c_op_opssq_q_c", "h_safe_c_op_opssq_q_c", "safe_c_s_op_opssq_opssqq", "h_safe_c_s_op_opssq_opssqq", 
      "safe_c_opssq_op_opssq_q", "h_safe_c_opssq_op_opssq_q", "safe_c_op_opssq_sq_opssq", "h_safe_c_op_opssq_sq_opssq",
      "safe_c_op_opsq_q", "h_safe_c_op_opsq_q", "safe_c_c_op_s_opcqq", "h_safe_c_c_op_s_opcqq",
      "safe_c_op_s_opsq_q", "h_safe_c_op_s_opsq_q",

      "safe_c*_cd", "h_safe_c*_cd",

      "safe_c_z", "h_safe_c_z", "safe_c_zz", "h_safe_c_zz", "safe_c_sz", "h_safe_c_sz", "safe_c_zs", "h_safe_c_zs", 
      "safe_c_cz", "h_safe_c_cz", "safe_c_zc", "h_safe_c_zc", 
      "safe_c_opcq_z", "h_safe_c_opcq_z", "safe_c_s_opszq", "h_safe_c_s_opszq", 
      "safe_c_az", "h_safe_c_az", "safe_c_za", "h_safe_c_za", 
      "safe_c_zaa", "h_safe_c_zaa", "safe_c_aza", "h_safe_c_aza", "safe_c_aaz", "h_safe_c_aaz", "safe_c_ssz", "h_safe_c_ssz",
      "safe_c_zza", "h_safe_c_zza", "safe_c_zaz", "h_safe_c_zaz", "safe_c_azz", "h_safe_c_azz", 
      "safe_c_zzz", "h_safe_c_zzz", "safe_c_zzzz", "h_safe_c_zzzz",

      "thunk", "h_thunk", 
      "closure_s", "h_closure_s", "closure_c", "h_closure_c", "closure_q", "h_closure_q", 
      "closure_ss", "h_closure_ss", "closure_ssb", "h_closure_ssb",
      "closure_sc", "h_closure_sc", "closure_cs", "h_closure_cs", 
      "closure_a", "h_closure_a", "closure_aa", "h_closure_aa", 
      "closure_opsq_s", "h_closure_opsq_s", "closure_opsq_opsq", "h_closure_opsq_opsq", 
      "closure_s_opsq", "h_closure_s_opsq", "closure_s_opssq", "h_closure_s_opssq", "closure_opssq_s", "h_closure_opssq_s", 
      "closure_all_x", "h_closure_all_x", "closure_all_s", "h_closure_all_s", 
      
      "glosure_a", "h_glosure_a", "glosure_s", "h_glosure_s", "closure_p", "h_closure_p", 

      "closure_star_s", "h_closure_star_s", "closure_star_sx", "h_closure_star_sx", 
      "closure_star", "h_closure_star", "closure_star_all_x", "h_closure_star_all_x", 

      "safe_thunk", "h_safe_thunk", 
      "safe_closure_s", "h_safe_closure_s", "safe_closure_c", "h_safe_closure_c", "safe_closure_q", "h_safe_closure_q", 
      "safe_closure_ss", "h_safe_closure_ss", "safe_closure_sc", "h_safe_closure_sc", "safe_closure_cs", "h_safe_closure_cs", 
      "safe_closure_a", "h_safe_closure_a", "safe_closure_sa", "h_safe_closure_sa", 
      "safe_closure_opsq_s", "h_safe_closure_opsq_s", "safe_closure_opsq_opsq", "h_safe_closure_opsq_opsq", 
      "safe_closure_s_opsq", "h_safe_closure_s_opsq", "safe_closure_s_opssq", "h_safe_closure_s_opssq", "safe_closure_opssq_s", "h_safe_closure_opssq_s", 
      "safe_closure_saa", "h_safe_closure_saa", 
      "safe_closure_all_x", "h_safe_closure_all_x", "safe_closure_aa", "h_safe_closure_aa", 
      
      "safe_glosure_a", "h_safe_glosure_a", "safe_glosure_s", "h_safe_glosure_s", "safe_closure_p", "h_safe_closure_p", 

      "safe_closure_star_s", "h_safe_closure_star_s", "safe_closure_star_ss", "h_safe_closure_star_ss", 
      "safe_closure_star_sc", "h_safe_closure_star_sc", "safe_closure_star_sa", "h_safe_closure_star_sa", "safe_closure_star_s0", "h_safe_closure_star_s0",
      "safe_closure_star", "h_safe_closure_star", "safe_closure_star_all_x", "h_safe_closure_star_all_x", 
      
      "apply_ss", "h_apply_ss",
      "c_all_x", "h_c_all_x", "call_with_exit", "h_call_with_exit", "c_catch", "h_c_catch", "c_catch_all", "h_c_catch_all",
      "c_s_opsq", "h_c_s_opsq", "c_s_opcq", "h_c_s_opcq", 
      "c_for_each_ls", "h_c_for_each_ls", "c_for_each_ls_2", "h_c_for_each_ls_2", "c_for_each_l_opsq", "h_c_for_each_l_opsq", 
      "c_s", "h_c_s", "read_s", "h_read_s", "c_p", "h_c_p", "c_sp", "h_c_sp", "c_a", "h_c_a",

      "goto", "h_goto", "goto_c", "h_goto_c", "goto_s", "h_goto_s", "goto_a", "h_goto_a", 
      
      "vector_c", "h_vector_c", "vector_s", "h_vector_s", "vector_a", "h_vector_a", "vector_cc", "h_vector_cc", 
      "string_c", "h_string_c", "string_s", "h_string_s", "string_a", "h_string_a", 
      "c_object", "h_c_object", "c_object_c", "h_c_object_c", "c_object_s", "h_c_object_s", 
      "c_object_a", "h_c_object_a", 
      "c_object_ss", "h_c_object_ss", "c_object_cc", "h_c_object_cc", 
      "pair_c", "h_pair_c", "pair_s", "h_pair_s", "pair_a", "h_pair_a", 
      "hash_table_c", "h_hash_table_c", "hash_table_s", "h_hash_table_s", "hash_table_a", "h_hash_table_a", 
      "environment_s", "h_environment_s", "environment_q", "h_environment_q", "environment_a", "h_environment_a", "environment_c", "h_environment_c", 
      
      "unknown", "h_unknown", "unknown_all_s", "h_unknown_all_s", "unknown_all_x", "h_unknown_all_x",
      "unknown_g", "h_unknown_g", "unknown_gg", "h_unknown_gg", "unknown_a", "h_unknown_a", "unknown_aa", "h_unknown_aa", 

      "safe_c_opvsq_s", "h_safe_c_opvsq_s", "safe_c_s_opvsq", "h_safe_c_s_opvsq", "safe_c_opvsq_opvsq", "h_safe_c_opvsq_opvsq", 
      
      "safe_c_p", "h_safe_c_p", "safe_c_pp", "h_safe_c_pp",
      "safe_c_opsq_p", "h_safe_c_opsq_p", 
      "safe_c_sp", "h_safe_c_sp", "safe_c_cp", "h_safe_c_cp", "safe_c_qp", "h_safe_c_qp", "safe_c_ap", "h_safe_c_ap", 
      "safe_c_ps", "h_safe_c_ps", "safe_c_pc", "h_safe_c_pc", "safe_c_pq", "h_safe_c_pq",
      "safe_c_ssp", "h_safe_c_ssp", 
      "opt_max_defined"
  };

#define opt_name(E) ((is_optimized(E)) ? opt_names[optimize_data(E)] : "unopt")

#define NUM_CHARS 256

typedef enum {TOKEN_EOF, TOKEN_LEFT_PAREN, TOKEN_RIGHT_PAREN, TOKEN_DOT, TOKEN_ATOM, TOKEN_QUOTE, TOKEN_DOUBLE_QUOTE, 
	      TOKEN_BACK_QUOTE, TOKEN_COMMA, TOKEN_AT_MARK, TOKEN_SHARP_CONST, 
	      TOKEN_VECTOR, TOKEN_BYTEVECTOR} token_t;

typedef enum {FILE_PORT, STRING_PORT, FUNCTION_PORT} port_type_t;

typedef struct port_t {
  bool needs_free;
  FILE *file;
  char *filename;
  int filename_length, gc_loc;
  void *next;
  s7_pointer (*input_function)(s7_scheme *sc, s7_read_t read_choice, s7_pointer port);
  void (*output_function)(s7_scheme *sc, unsigned char c, s7_pointer port);
  /* a version of string ports using a pointer to the current location and a pointer to the end
   *   (rather than an integer for both, indexing from the base string) was not faster.
   */
  s7_pointer orig_str;                                                   /* GC protection for string port string */
  int (*read_character)(s7_scheme *sc, s7_pointer port);                 /* function to read a character */
  void (*write_character)(s7_scheme *sc, int c, s7_pointer port);        /* function to write a character */
  void (*write_string)(s7_scheme *sc, const char *str, int len, s7_pointer port); /* function to write a string of known length */
  token_t (*read_semicolon)(s7_scheme *sc, s7_pointer port);             /* internal skip-to-semicolon reader */
  int (*read_white_space)(s7_scheme *sc, s7_pointer port);               /* internal skip white space reader */
  s7_pointer (*read_name)(s7_scheme *sc, s7_pointer pt);                 /* internal get-next-name reader */
  s7_pointer (*read_sharp)(s7_scheme *sc, s7_pointer pt);                /* internal get-next-sharp-constant reader */
  s7_pointer (*read_line)(s7_scheme *sc, s7_pointer pt, bool eol_case);  /* function to display or write a string up to \n */
  void (*display)(s7_scheme *sc, const char *s, s7_pointer pt);
} port_t;


typedef struct c_proc_ext_t {
  s7_pointer looped_ff, let_looped_ff;
  s7_ex *(*exf)(s7_scheme *sc, s7_pointer expr);
  void *chooser_data;
  s7_pointer *arg_defaults, *arg_names;
  s7_pointer call_args;
  int keyed_args;
  bool simple_defaults;
} c_proc_ext_t;

typedef struct c_proc_t {
  const char *name;
  int name_length;
  unsigned int id;
  char *doc;
  s7_pointer generic_ff;
  s7_pointer (*chooser)(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr);
  c_proc_ext_t *ext;
} c_proc_t;


typedef struct {               /* call/cc */
  unsigned int stack_size, op_stack_loc, op_stack_size;
  int local_key;
} continuation_t;


typedef struct vdims_t {
  unsigned int ndims;
  bool elements_allocated, dimensions_allocated; /* these are allocated as bytes, not ints, so the struct size is 32 */
  s7_Int *dims, *offsets;
  s7_pointer original;
} vdims_t;


typedef struct c_object_t {
  int type;
  unsigned int outer_type;
  const char *name;
  s7_pointer scheme_name;
  char *(*print)(s7_scheme *sc, void *value);
  void (*free)(void *value);
  bool (*equal)(void *val1, void *val2);
  void (*gc_mark)(void *val);
  s7_pointer (*ref)(s7_scheme *sc, s7_pointer obj, s7_pointer args);
  s7_pointer (*set)(s7_scheme *sc, s7_pointer obj, s7_pointer args);
  s7_pointer (*length)(s7_scheme *sc, s7_pointer obj);
  s7_pointer (*copy)(s7_scheme *sc, s7_pointer obj);
  s7_pointer (*reverse)(s7_scheme *sc, s7_pointer obj);
  s7_pointer (*fill)(s7_scheme *sc, s7_pointer obj, s7_pointer val);
  char *(*print_readably)(s7_scheme *sc, void *value);
} c_object_t;


/* cell structure */
typedef struct s7_cell {
  union {
    unsigned int flag;
    unsigned char type_field;
    unsigned short sflag;
  } tf;
  int hloc;
  union {
    
    union {
      s7_Int integer_value;
      s7_Double real_value;

      struct {
	char padding[PRINT_NAME_PADDING];
	char name[PRINT_NAME_SIZE + 2];	
      } pval;

      struct {
	s7_Int numerator;
	s7_Int denominator;
      } fraction_value;

      struct {
	s7_Double rl;
	s7_Double im;
      } complex_value;

      unsigned long ul_value;           /* these two are not used by s7 in any way */
      unsigned long long ull_value;

#if WITH_GMP
      mpz_t big_integer;
      mpq_t big_ratio;
      mpfr_t big_real;
      mpc_t big_complex;
#endif
    } number;

    struct {
      port_t *port;
      unsigned char *data;
      unsigned int size, point;        /* these limit the in-core portion of a string-port to 2^31 bytes */
      unsigned int line_number, file_number;
      bool is_closed;
      port_type_t ptype;
    } prt;
    
    struct{
      unsigned char c;
      unsigned char up_c, down_c;
      bool alpha_c, digit_c, space_c, upper_c, lower_c;
      const char *c_name;              /* actually we have room here for the name */
      int length;
    } chr;

    void *c_pointer;

    int baffle_key;

    struct {
      s7_Int length;
      union {
	s7_pointer *objects;
	s7_Int *ints;
	s7_Double *floats;
      } elements;
      vdims_t *dim_info;
      s7_pointer (*vget)(s7_scheme *sc, s7_pointer vec, s7_Int loc);
      s7_pointer (*vset)(s7_scheme *sc, s7_pointer vec, s7_Int loc, s7_pointer val);
    } vector;
    
    struct {
      s7_Int length;
      s7_pointer *elements;
      unsigned int entries;          
      s7_pointer (*hash_func)(s7_scheme *sc, s7_pointer table, s7_pointer key); 
      s7_pointer eq_func;
    } hasher;
    
    struct {
      c_proc_t *c_proc;                /* C functions, macros */
      s7_function ff;
      s7_pointer setter;
      unsigned int required_args, optional_args, all_args;
      bool rest_arg;
    } fnc;
      
    struct {                           /* pairs */ 
      s7_pointer car, cdr, ecdr, fcdr;
      unsigned int line;
      union {
	struct {
	  unsigned short data;
	  unsigned short data_index;
	} d;
	unsigned int op;
      } dat;
    } cons;

    struct {
      s7_pointer car, cdr, ecdr, fcdr, gcdr;
    } ext_cons;

    struct {
      s7_pointer args, body, env, setter;
      int arity;
    } func;

    struct { 
      unsigned int length;
      unsigned int hash;  
      char *svalue;
      union {
	bool needs_free; 
	int accessor;
      } str_ext;
      /* extra data for symbols which always have a string name field (hash field is also used specially by symbols) */
      unsigned int tag;
      s7_pointer initial_slot;
      union {
	char *documentation;
	s7_pointer ksym;
      } doc;
    } string;
    
    struct {                       /* symbols */
      s7_pointer name, global_slot, local_slot;
      long long int id;
      int op;  
    } sym;

    struct {                       /* syntax */
      s7_pointer symbol;
      int op;
    } syn;

    struct {                       /* slots (bindings) */
      s7_pointer sym, val, nxt, pending_value, expr;
    } slt;

    struct {                       /* environments (frames) */
      s7_pointer slots, nxt;
      long long int id;          /* id of rootlet is -1 */
      union {
	struct {
	  s7_pointer function;     /* __func__ (code) if this is a funclet */
	  unsigned int line, file; /* __func__ location if it is known */
	} efnc;
	struct {
	  s7_pointer dox1, dox2;   /* do loop variables */
	} dox;
	struct {                   /* (catch #t ...) opts */
	  s7_pointer result;
	  unsigned int op_stack_loc, goto_loc;
	} ctall;
      } edat;
    } envr;

    struct {
      /* these 3 are just place-holders */
      s7_pointer unused_slots, unused_nxt;
      long long int unused_id;
      /* these two fields are for some special case objects like #<unspecified> */
      const char *name;
      int len;
    } unq;

    struct {               /* counter (internal) */
      s7_pointer result, list, env; /* env = counter_let (curlet after map/for-each frame created) */
      int val, len;        /* val=count */
      s7_pointer args;
    } ctr;

    struct {               /* additional object types (C) */
      int type;
      void *value;         /*  the value the caller associates with the object */
      s7_pointer e;        /*   the method list, if any (openlet) */
    } c_obj;
    
    struct {
      continuation_t *continuation;
      s7_pointer stack;
      s7_pointer *stack_start, *stack_end, *op_stack;
    } cwcc;

    struct {               /* call-with-exit */
      unsigned int goto_loc, op_stack_loc;
      bool active;
    } rexit;
    
    struct {               /* catch */
      unsigned int goto_loc, op_stack_loc;
      s7_pointer tag;
      s7_pointer handler;
    } rcatch; /* C++ reserves "catch" I guess */
    
    struct {               /* dynamic-wind */
      s7_pointer in, out, body;
      unsigned int state;
    } winder;
  } object;

#if DEBUGGING
  int current_alloc_line, previous_alloc_line, current_alloc_type, previous_alloc_type, debugger_bits, gc_line, uses;
  const char *current_alloc_func, *previous_alloc_func, *gc_func;
#endif

} s7_cell;


typedef struct {
  s7_pointer *objs;
  int size, top, ref;
  bool has_hits;
  int *refs;
} shared_info;


typedef struct {
  int loc, gc_loc, curly_len, ctr;
  char *curly_str;
  s7_pointer args;
} format_data;


typedef struct {
  unsigned long long int ran_seed, ran_carry;
} s7_rng_t;


typedef struct gc_obj {
  s7_pointer p;
  struct gc_obj *nxt;
} gc_obj;


static s7_pointer *small_ints, *chars;
static s7_pointer real_zero, real_NaN, real_pi, real_one, arity_not_set, max_arity, real_infinity, real_minus_infinity; 


struct s7_scheme {  
  opcode_t op;                        /* making this global is much slower! */
  s7_pointer value;
  s7_pointer args;                    /* arguments of current function */
  s7_pointer code, cur_code;          /* current code */
  s7_pointer envir;                   /* curlet */
  token_t tok;

  s7_pointer stack;                   /* stack is a vector */
  unsigned int stack_size;
  s7_pointer *stack_start, *stack_end, *stack_resize_trigger;
  
  s7_pointer *op_stack, *op_stack_now, *op_stack_end;
  unsigned int op_stack_size, maximum_stack_size;
  s7_pointer stacktrace_env;

  s7_cell **heap, **free_heap, **free_heap_top, **free_heap_trigger, **previous_free_heap_top;
  unsigned int heap_size;
  int gc_freed;

  /* "int" or "unsigned int" seems safe here:
   *      sizeof(s7_cell) = 48 bytes
   *      so to get more than 2^32 actual objects would require ca 206 GBytes RAM
   *      vectors might be full of the same object (sc->NIL for example), so there
   *      we need ca 38 GBytes RAM (8 bytes per pointer).  
   */
  
  gc_obj *permanent_objects;

  s7_pointer protected_objects;       /* a vector of gc-protected objects */
  unsigned int protected_objects_size, protected_objects_loc;

  s7_pointer NIL;                     /* empty list */
  s7_pointer T;                       /* #t */
  s7_pointer F;                       /* #f */
  s7_pointer EOF_OBJECT;              /* #<eof> */
  s7_pointer UNDEFINED;               /* #<undefined> */
  s7_pointer UNSPECIFIED;             /* #<unspecified> */
  s7_pointer NO_VALUE;                /* the (values) value */
  s7_pointer ELSE;                    /* else */  
  s7_pointer GC_NIL;                  /* a marker for an unoccupied slot in sc->protected_objects (and other similar stuff) */
  
  s7_pointer symbol_table;            /* symbol table */
  s7_pointer rootlet, shadow_rootlet; /* rootlet */
  s7_Int rootlet_entries;
  s7_pointer unlet;                   /* original bindings of predefined functions */
  
  s7_pointer input_port;              /* current-input-port */
  s7_pointer input_port_stack;        /*   input port stack (load and read internally) */
  s7_pointer output_port;             /* current-output-port */
  s7_pointer error_port;              /* current-error-port */
  s7_pointer owlet;                   /* owlet */
  s7_pointer error_type, error_data, error_code, error_line, error_file; /* owlet slots */
  s7_pointer standard_input, standard_output, standard_error;

  s7_pointer sharp_readers;           /* the binding pair for the global *#readers* list */
  s7_pointer vector_print_length;     /* same for *vector-print-length* */
  s7_pointer load_hook;               /* *load-hook* hook object */
  s7_pointer unbound_variable_hook;   /* *unbound-variable-hook* hook object */
  s7_pointer error_hook;              /* *error-hook* hook object */
  s7_pointer direct_str;

  bool gc_off, gc_stats;              /* gc_off: if true, the GC won't run, gc_stats: if true, print stats during GC */
  unsigned int gensym_counter, cycle_counter, f_class, add_class, equal_class;
  int format_column;
  int capture_env_counter;
  bool symbol_table_is_locked;  
  unsigned long long int let_number;
  double default_rationalize_error, morally_equal_float_epsilon, hash_table_float_epsilon;
  s7_Int default_hash_table_length, initial_string_port_length;
  s7_pointer *op_names_saved;
  vdims_t *wrap_only;

  char *typnam;
  int typnam_len;
  s7_pointer tmp_str;
  int tmp_str_size;
  char *tmp_str_chars;
  char *help_arglist;

  #define INITIAL_FILE_NAMES_SIZE 8
  s7_pointer *file_names;
  int file_names_size, file_names_top;

  #define INITIAL_STRBUF_SIZE 1024
  unsigned int strbuf_size;
  char *strbuf;
  int print_width;
  s7_pointer *singletons;
  
  char *read_line_buf;
  unsigned int read_line_buf_size;

  s7_pointer v, w, x, z;         /* evaluator local vars */
  s7_pointer temp1, temp2, temp3;
  s7_pointer temp_cell, temp_cell_1, temp_cell_2, temp_cell_3, temp_cell_4;
  s7_pointer T1_1, T2_1, T2_2, T3_1, T3_2, T3_3, Z2_1, Z2_2;
  s7_pointer A1_1, A2_1, A2_2, A3_1, A3_2, A3_3, A4_1, A4_2, A4_3, A4_4;

  jmp_buf goto_start;
  bool longjmp_ok;
  void (*begin_hook)(s7_scheme *sc, bool *val);
  
  int no_values, current_line, s7_call_line, safety;
  const char *current_file, *s7_call_file, *s7_call_name;

  shared_info *circle_info;
  format_data **fdats;
  int num_fdats;

  s7_pointer *strings, *vectors, *input_ports, *output_ports, *continuations, *c_objects, *hash_tables, *gensyms;
  int strings_size, vectors_size, input_ports_size, output_ports_size, continuations_size, c_objects_size, hash_tables_size, gensyms_size;
  int strings_loc, vectors_loc, input_ports_loc, output_ports_loc, continuations_loc, c_objects_loc, hash_tables_loc, gensyms_loc;

  unsigned int syms_tag;
  int ht_iter_tag, rng_tag, baffle_ctr;
  s7_rng_t *default_rng;

#if WITH_GMP
  void *default_big_rng;
  int big_rng_tag;

  s7_pointer *bigints, *bigratios, *bigreals, *bignumbers;
  int bigints_size, bigratios_size, bigreals_size, bignumbers_size;
  int bigints_loc, bigratios_loc, bigreals_loc, bignumbers_loc;
#endif

  /* these are symbols, primarily for the generic function search.  
   */
  s7_pointer MINUS, MULTIPLY, ADD, DIVIDE, LT, LEQ, EQ, GT, GEQ, ABS, ACOS, ACOSH;
  s7_pointer ANGLE, APPEND, APPLY, IS_ARITABLE, ARITY, ASH, ASIN, ASINH, ASSOC, ASSQ, ASSV, ATAN, ATANH;
  s7_pointer SUBLET, VARLET, CUTLET, AUTOLOAD, AUTOLOADER, IS_BOOLEAN, BYTEVECTOR, IS_BYTEVECTOR, CAAAAR, CAAADR, CAAAR, CAADAR, CAADDR;
  s7_pointer CAADR, CAAR, CADAAR, CADADR, CADAR, CADDAR, CADDDR, CADDR, CADR, CALL_CC, CALL_WITH_CURRENT_CONTINUATION, CALL_WITH_EXIT, COVERLET;
  s7_pointer CALL_WITH_INPUT_FILE, CALL_WITH_INPUT_STRING, CALL_WITH_OUTPUT_FILE, CALL_WITH_OUTPUT_STRING, CAR, CATCH, CDAAAR;
  s7_pointer CDAADR, CDAAR, CDADAR, CDADDR, CDADR, CDAR, CDDAAR, CDDADR, CDDAR, CDDDAR, CDDDDR, CDDDR, CDDR, CDR, CEILING;
  s7_pointer CHAR_LEQ, CHAR_LT, CHAR_EQ, CHAR_GEQ, CHAR_GT, IS_CHAR, CHAR_POSITION, CHAR_TO_INTEGER, IS_CHAR_ALPHABETIC, CHAR_CI_LEQ, CHAR_CI_LT, CHAR_CI_EQ;
  s7_pointer CHAR_CI_GEQ, CHAR_CI_GT, CHAR_DOWNCASE, IS_CHAR_LOWER_CASE, IS_CHAR_NUMERIC, IS_CHAR_READY, CHAR_UPCASE, IS_CHAR_UPPER_CASE;
  s7_pointer IS_CHAR_WHITESPACE, CLOSE_INPUT_PORT, CLOSE_OUTPUT_PORT, IS_COMPLEX, CONS, IS_CONSTANT, IS_CONTINUATION, COPY, COS, COSH, C_POINTER, IS_C_POINTER;
  s7_pointer IS_DEFINED, DENOMINATOR, DISPLAY, DYNAMIC_WIND, IS_LET, INLET, LET_REF, LET_REF_FALLBACK, LET_SET, LET_SET_FALLBACK, LET_TO_LIST;
  s7_pointer IS_EOF_OBJECT, IS_EQ, IS_EQUAL, IS_EQV, ERROR, EVAL, EVAL_STRING, IS_EVEN, IS_EXACT;
  s7_pointer EXACT_TO_INEXACT, EXP, EXPT, FILL, FLOAT_VECTOR, IS_FLOAT_VECTOR, FLOAT_VECTOR_REF, FLOAT_VECTOR_SET;
  s7_pointer FLOOR, FLUSH_OUTPUT_PORT, FORMAT, FOR_EACH, GC, GCD, GENSYM, IS_GENSYM, GET_OUTPUT_STRING, HASH_TABLE, HASH_TABLE_STAR;
  s7_pointer IS_HASH_TABLE, IS_HASH_TABLE_ITERATOR, HASH_TABLE_REF, HASH_TABLE_SET, HASH_TABLE_SIZE, HASH_TABLE_ENTRIES, HELP, IMAG_PART, IS_INEXACT, INEXACT_TO_EXACT;
  s7_pointer IS_INFINITE, IS_INPUT_PORT, IS_INTEGER, INTEGER_TO_CHAR, INTEGER_DECODE_FLOAT, INTEGER_LENGTH, IS_KEYWORD, KEYWORD_TO_SYMBOL, LCM, LENGTH;
  s7_pointer LIST, IS_LIST, LIST_TO_STRING, LIST_TO_VECTOR, LIST_REF, LIST_SET, LIST_TAIL, LOAD, LOG, LOGAND, LOGBIT, LOGIOR, LOGNOT, LOGXOR;
  s7_pointer IS_MACRO, MAGNITUDE, MAKE_BYTEVECTOR, MAKE_FLOAT_VECTOR, MAKE_HASH_TABLE, MAKE_HASH_TABLE_ITERATOR, MAKE_KEYWORD, MAKE_LIST, MAKE_POLAR, MAKE_RANDOM_STATE;
  s7_pointer MAKE_RECTANGULAR, MAKE_STRING, MAKE_SHARED_VECTOR, MAKE_VECTOR, MAP, MAX, MEMBER, MEMQ, MEMV, MIN, MODULO, IS_MORALLY_EQUAL, IS_NAN, IS_NEGATIVE, NEWLINE;
  s7_pointer NOT, IS_NULL, IS_NUMBER, NUMBER_TO_STRING, NUMERATOR, OBJECT_TO_STRING, IS_ODD, OPENLET, IS_OPENLET, OPEN_INPUT_FILE;
  s7_pointer OPEN_INPUT_STRING, OPEN_OUTPUT_FILE, OUTLET, IS_OUTPUT_PORT, IS_PAIR, PAIR_LINE_NUMBER, PEEK_CHAR;
  s7_pointer IS_PORT_CLOSED, PORT_FILENAME, PORT_LINE_NUMBER;
  s7_pointer IS_POSITIVE, IS_PROCEDURE, PROCEDURE_ARITY, PROCEDURE_DOCUMENTATION, FUNCLET, PROCEDURE_NAME, PROCEDURE_SOURCE;
  s7_pointer IS_DILAMBDA, PROVIDE;
  s7_pointer IS_PROVIDED, QUOTIENT, RANDOM, IS_RANDOM_STATE, RANDOM_STATE_TO_LIST, RATIONALIZE, IS_RATIONAL, READ, READ_BYTE, READ_CHAR, READ_LINE, IS_REAL;
  s7_pointer READ_STRING, REAL_PART, REMAINDER, REQUIRE, REVERSE, REVERSEB, ROUND, SET_CAR, SET_CDR, SIN, SINH, SORT, SQRT, STACKTRACE;
  s7_pointer STRING, STRING_DOWNCASE, STRING_UPCASE, STRING_LEQ, STRING_LT, STRING_EQ;
  s7_pointer STRING_GEQ, STRING_GT, IS_STRING, STRING_POSITION, STRING_TO_LIST, STRING_TO_NUMBER, STRING_TO_SYMBOL, STRING_APPEND, STRING_CI_LEQ, STRING_CI_LT;
  s7_pointer STRING_CI_EQ, STRING_CI_GEQ, STRING_CI_GT, STRING_COPY, STRING_FILL, STRING_LENGTH, STRING_REF, STRING_SET, SUBSTRING, SYMBOL;
  s7_pointer SYMBOL_ACCESS, IS_SYMBOL, SYMBOL_TO_KEYWORD, SYMBOL_TO_STRING, SYMBOL_TO_DYNAMIC_VALUE, SYMBOL_TO_VALUE;
  s7_pointer TAN, TANH, THROW, TO_BYTEVECTOR, TRUNCATE, VALUES, VECTOR, VECTOR_APPEND;
  s7_pointer IS_VECTOR, VECTOR_TO_LIST, VECTOR_DIMENSIONS, VECTOR_FILL, VECTOR_LENGTH, VECTOR_REF, VECTOR_SET, WITH_INPUT_FROM_FILE;
  s7_pointer WITH_INPUT_FROM_STRING, WITH_OUTPUT_TO_FILE, WITH_OUTPUT_TO_STRING, WRITE, WRITE_BYTE, WRITE_CHAR, WRITE_STRING, IS_ZERO;
  s7_pointer S7_FEATURES, LOAD_PATH, PI;

#if WITH_GMP
  s7_pointer BIGNUM, BIGNUM_PRECISION;
#endif
#if WITH_SYSTEM_EXTRAS
  s7_pointer IS_DIRECTORY, FILE_EXISTS, DELETE_FILE, GETENV, SYSTEM, DIRECTORY_TO_LIST, FILE_MTIME;
#endif

  /* these are the associated functions, not symbols */
  s7_pointer Vector_Set, String_Set, List_Set, Hash_Table_Set, Let_Set; /* Cons (see the setter stuff at the end) */

  s7_pointer LAMBDA, LAMBDA_STAR, QUOTE, UNQUOTE, MACROEXPAND, DEFINE_EXPANSION, BAFFLE, WITH_LET;
#if (!DISABLE_DEPRECATED)
  s7_pointer WITH_ENVIRONMENT;
#endif
  s7_pointer SET, QQ_List, QQ_Apply_Values, QQ_Append, Multivector;
  s7_pointer Apply, Vector;
  s7_pointer WRONG_TYPE_ARG, WRONG_TYPE_ARG_INFO, OUT_OF_RANGE, OUT_OF_RANGE_INFO, WTA1, WTA2, WTA3, WTA4, WTA5;
  s7_pointer SIMPLE_WRONG_TYPE_ARG_INFO, SIMPLE_OUT_OF_RANGE_INFO, DIVISION_BY_ZERO, DIVISION_BY_ZERO_ERROR, NO_CATCH, IO_ERROR, INVALID_ESCAPE_FUNCTION;
  s7_pointer FORMAT_ERROR, WRONG_NUMBER_OF_ARGS, READ_ERROR, SYNTAX_ERROR, TOO_MANY_ARGUMENTS, NOT_ENOUGH_ARGUMENTS;
  s7_pointer KEY_KEY, KEY_OPTIONAL, KEY_REST, KEY_ALLOW_OTHER_KEYS, KEY_READABLE;
  s7_pointer __FUNC__;
  s7_pointer Object_Set;               /* applicable object set method */
  s7_pointer FEED_TO;                  /* => */
  s7_pointer BODY, CLASS_NAME;
  s7_pointer QUOTE_UNCHECKED, BEGIN_UNCHECKED, CASE_UNCHECKED, SET_UNCHECKED, LAMBDA_UNCHECKED, LET_UNCHECKED, WITH_LET_UNCHECKED, WITH_LET_S;
  s7_pointer LET_STAR_UNCHECKED, LETREC_UNCHECKED, LETREC_STAR_UNCHECKED, COND_UNCHECKED, COND_SIMPLE, WITH_BAFFLE_UNCHECKED;
  s7_pointer SET_SYMBOL_C, SET_SYMBOL_S, SET_SYMBOL_Q, SET_SYMBOL_P, SET_SYMBOL_Z, SET_SYMBOL_A;
  s7_pointer SET_SYMBOL_SAFE_S, SET_SYMBOL_SAFE_SS, SET_SYMBOL_SAFE_SSS, SET_SYMBOL_UNKNOWN_G;
  s7_pointer SET_SYMBOL_SAFE_C, SET_PAIR_UNKNOWN_G, SET_FV_SCALED;
  s7_pointer SET_NORMAL, SET_PAIR, SET_PAIR_Z, SET_PAIR_A, SET_PAIR_ZA, SET_PAIR_P, SET_PWS, SET_ENV_S, SET_ENV_ALL_X, SET_PAIR_C, SET_PAIR_C_P;
  s7_pointer LAMBDA_STAR_UNCHECKED, DO_UNCHECKED, DEFINE_UNCHECKED, DEFINE_FUNCHECKED, DEFINE_STAR_UNCHECKED;
  s7_pointer CASE_SIMPLE, CASE_SIMPLER, CASE_SIMPLER_1, CASE_SIMPLER_SS;
  s7_pointer CASE_SIMPLEST, CASE_SIMPLEST_SS, CASE_SIMPLEST_ELSE, CASE_SIMPLEST_ELSE_C;
  s7_pointer LET_C, LET_S, LET_Q, LET_ALL_C, LET_ALL_S, LET_ALL_X;
  s7_pointer LET_STAR_ALL_X, LET_opCq, LET_opSSq;
  s7_pointer LET_NO_VARS, NAMED_LET, NAMED_LET_NO_VARS, NAMED_LET_STAR, LET_STAR2, IF_UNCHECKED, AND_UNCHECKED, AND_P, OR_UNCHECKED, OR_P;
  s7_pointer IF_P_P_P, IF_P_P, IF_B_P, IF_P_P_X, IF_A_P_X, IF_P_X_P, IF_S_P_P, IF_S_P, IF_S_P_X, IF_S_X_P, IF_P_FEED;
  s7_pointer IF_Z_P, IF_Z_P_P, IF_A_P, IF_A_P_P, IF_ANDP_P, IF_ANDP_P_P, IF_ORP_P, IF_ORP_P_P;
  s7_pointer WHEN_UNCHECKED, UNLESS_UNCHECKED, WHEN_S, UNLESS_S;

  s7_pointer COND_ALL_X, COND_S;
  s7_pointer SAFE_IF_Z_Z, SAFE_IF_A_Z, SAFE_IF_CC_X_P, SAFE_IF_CC_P_P;
  s7_pointer SAFE_IF_CS_P_P, SAFE_IF_CS_X_P, SAFE_IF_IS_NULL_S_P, SAFE_IF_CC_P, SAFE_IF_CS_P, SAFE_IF_CS_P_X, SAFE_IF_IS_NULL_Q_P;
  s7_pointer SAFE_IF_CSS_X_P, SAFE_IF_CSC_X_P, SAFE_IF_CSC_X_O_A;
  s7_pointer SAFE_IF_CSQ_P, SAFE_IF_CSQ_P_P, SAFE_IF_CSS_P, SAFE_IF_CSS_P_P, SAFE_IF_CSC_P, SAFE_IF_CSC_P_P;
  s7_pointer SAFE_IF_IS_PAIR_P, SAFE_IF_IS_PAIR_P_X, SAFE_IF_IS_PAIR_P_P, SAFE_IF_C_SS_P;
  s7_pointer SAFE_IF_IS_SYMBOL_P, SAFE_IF_IS_SYMBOL_P_P, SAFE_IF_NOT_S_P;
  s7_pointer INCREMENT_1, DECREMENT_1, SET_CONS, INCREMENT_SS, INCREMENT_SSS, INCREMENT_SZ, INCREMENT_C_TEMP, INCREMENT_SA, INCREMENT_SAA;
  s7_pointer LET_R, LET_ALL_R, LET_C_D, LET_R_P, LET_CAR_P, LET_ONE, LET_Z;
  s7_pointer SIMPLE_DO, SAFE_DOTIMES, SIMPLE_SAFE_DOTIMES, SAFE_DOTIMES_C_C, SAFE_DOTIMES_C_A, SAFE_DO;
  s7_pointer SIMPLE_DO_P, DOTIMES_P, SIMPLE_DO_FOREVER, SIMPLE_DO_A;
  s7_pointer DOX, DO_ALL_X;

  s7_pointer *safe_lists; /* prebuilt evaluator arg lists */

  s7_pointer autoload_table, libraries;
  const char ***autoload_names;
  int *autoload_names_sizes;
  bool **autoloaded_already;
  int autoload_names_loc, autoload_names_top;
  port_t *port_heap;

  int format_depth;
  int slash_str_size;
  char *slash_str;
  s7_ex *(*ex_fallback)(s7_scheme *sc, s7_pointer expr, s7_pointer locals);

  /* s7 env symbols */
  s7_pointer stack_top_symbol, symbol_table_is_locked_symbol, heap_size_symbol, gc_freed_symbol, gc_protected_objects_symbol;
  s7_pointer free_heap_size_symbol, file_names_symbol, symbol_table_symbol, hash_tables_symbol, gensyms_symbol, cpu_time_symbol;
  s7_pointer stack_size_symbol, rootlet_size_symbol, c_types_symbol, safety_symbol, maximum_stack_size_symbol, gc_stats_symbol;
  s7_pointer strings_symbol, vectors_symbol, input_ports_symbol, output_ports_symbol, continuations_symbol, c_objects_symbol;
  s7_pointer catches_symbol, exits_symbol, stack_symbol, default_rationalize_error_symbol, max_string_length_symbol, default_random_state_symbol;
  s7_pointer max_list_length_symbol, max_vector_length_symbol, max_vector_dimensions_symbol, default_hash_table_length_symbol;
  s7_pointer hash_table_float_epsilon_symbol, morally_equal_float_epsilon_symbol, initial_string_port_length_symbol;
  s7_pointer undefined_identifier_warnings_symbol;

  bool undefined_identifier_warnings;

  /* unbound-variable hook context */
  int ubh_cur_code_loc, ubh_value_loc, ubh_args_loc, ubh_code_loc, ubh_hook_loc;
  int ubh_x, ubh_z;
};

typedef enum {USE_DISPLAY, USE_WRITE, USE_READABLE_WRITE, USE_WRITE_WRONG} use_write_t;

#define NUM_SAFE_LISTS 16
#define INITIAL_AUTOLOAD_NAMES_SIZE 4

/* is_c_function can be array lookup
 *  the array lookup has exactly the same computational cost as the bit check
 */

#define T_FREE                 0
#define T_PAIR                 1
#define T_NIL                  2
#define T_UNIQUE               3
#define T_UNSPECIFIED          4
#define T_BOOLEAN              5
#define T_CHARACTER            6
#define T_SYMBOL               7
#define T_SYNTAX               8

#define T_INTEGER              9
#define T_RATIO               10
#define T_REAL                11
#define T_COMPLEX             12

#define T_BIG_INTEGER         13
#define T_BIG_RATIO           14
#define T_BIG_REAL            15
#define T_BIG_COMPLEX         16

/* only used in WITH_GMP case -- order matters.
 *
 * We could manipulate these types so that (type(x) | (type(y) << 1)) is unique and retains x&y type info:
 *   (1 2 9 10 (from 2 to 30) -> (2 3 5 6 10 11 13 14 18 19 21 22 26 27 29 30))
 *   (1 2 8 11 (from 2 to 31) -> (2 3 5 6 10 11 12 15 17 18 22 23 24 27 30 31))
 * and move the other types out beyond 30 so that wrong type args are easy to catch.
 * Then procedures that, for example, add 2 things need only one switch statement.
 * But timing tests indicate it's actually just as fast to trap the most common cases
 * by hand, leaving these type numbers alone.
 *
 * other cases: ((1 4 5) ((top 15) (3 6 7 9 11 12 13 14 15)))
 *              ((1 2 8 11) (top 31) (2 3 5 6 10 11 12 15 17 18 22 23 24 27 30 31))
 *              ((1 4 5 16 17) ((top 51) (3 6 7 9 11 12 13 14 15 18 19 24 25 26 27 33 35 36 37 38 39 48 49 50 51)))
 *              ((1 4 5 16 17 20) (top 60) 3 9 11 33 35 41 6 12 14 36 38 44 7 13 15 37 39 45 18 24 26 48 50 56 19 25 27 49 51 57 22 28 30 52 54 60)
 *              ((1 4 5 16 17 20 21) (top 63) 3 9 11 33 35 41 43 6 12 14 36 38 44 46 7 13 15 37 39 45 
 *                                            47 18 24 26 48 50 56 58 19 25 27 49 51 57 59 22 28 30 52 54 60 62 23 29 31 53 55 61 63)
 *              ((1 4 8 13 36 37 40 41) (top 123) 3 9 17 27 73 75 81 83 6 12 20 30 76 78 84 86 10 8 24 
 *                                            26 72 74 88 90 15 13 29 31 77 79 93 95 38 44 52 62 108 110 116 118 39 45 53 63 109 111 
 *                                            117 119 42 40 56 58 104 106 120 122 43 41 57 59 105 107 121 123
 *              the 1 2 8 11 32 35 41 42 case tops out at 126
 * 
 * so we actually could do the same thing with 8 types in the bignum case, 
 * even though we have only a byte for all the types! (top=n|n<<1 where n=top choice, normally close to n*3)
 */
 
/* can we use type bits for all ops?  10 bits more or less
 *   hloc is heap_location -- apparently used only during remove_from_heap!
 *   long long tf -> 1 MB extra runtime memory + 100GC, at best we'd save 200 = 2%
 */

#define T_STRING              17
#define T_C_OBJECT            18
#define T_CLOSURE             19
#define T_CLOSURE_STAR        20
#define T_CONTINUATION        21
#define T_VECTOR              22
#define T_INT_VECTOR          23
#define T_FLOAT_VECTOR        24
#define T_MACRO               25
#define T_BACRO               26
#define T_GOTO                27
#define T_CATCH               28
#define T_DYNAMIC_WIND        29
#define T_HASH_TABLE          30
#define T_ENVIRONMENT         31
#define T_STACK               32
#define T_COUNTER             33
#define T_SLOT                34
#define T_C_POINTER           35
#define T_C_MACRO             36
#define T_OUTPUT_PORT         37
#define T_INPUT_PORT          38
#define T_BAFFLE              39

#define T_C_FUNCTION_STAR     40
#define T_C_FUNCTION          41
#define T_C_ANY_ARGS_FUNCTION 42
#define T_C_OPT_ARGS_FUNCTION 43
#define T_C_RST_ARGS_FUNCTION 44

#define NUM_TYPES        45

/* T_STACK, T_SLOT, T_BAFFLE, and T_COUNTER are internal
 * I tried T_CASE_SELECTOR that turned a case statement into an array, but it was slower!
 */

static s7_pointer prepackaged_type_names[NUM_TYPES];

static bool t_number_p[NUM_TYPES], t_real_p[NUM_TYPES], t_rational_p[NUM_TYPES];
static bool t_simple_p[NUM_TYPES];
static bool t_big_number_p[NUM_TYPES];
static bool t_structure_p[NUM_TYPES];
static bool t_any_macro_p[NUM_TYPES];
static bool t_any_closure_p[NUM_TYPES];
static bool t_sequence_p[NUM_TYPES];
static bool t_vector_p[NUM_TYPES];
static bool t_applicable_p[NUM_TYPES];

static void init_types(void)
{
  int i;
  for (i = 0; i < NUM_TYPES; i++) 
    {
      t_number_p[i] = false;
      t_real_p[i] = false;
      t_rational_p[i] = false;
      t_simple_p[i] = false;
      t_structure_p[i] = false;
      t_any_macro_p[i] = false;
      t_any_closure_p[i] = false;
      t_sequence_p[i] = false;
      t_vector_p[i] = false;
      t_applicable_p[i] = false;
    }
  t_number_p[T_INTEGER] = true;
  t_number_p[T_RATIO] = true;
  t_number_p[T_REAL] = true;
  t_number_p[T_COMPLEX] = true;

  t_rational_p[T_INTEGER] = true;
  t_rational_p[T_RATIO] = true;
  
  t_real_p[T_INTEGER] = true;
  t_real_p[T_RATIO] = true;
  t_real_p[T_REAL] = true;

  t_big_number_p[T_BIG_INTEGER] = true;
  t_big_number_p[T_BIG_RATIO] = true;
  t_big_number_p[T_BIG_REAL] = true;
  t_big_number_p[T_BIG_COMPLEX] = true;

  t_structure_p[T_PAIR] = true;
  t_structure_p[T_VECTOR] = true;
  t_structure_p[T_HASH_TABLE] = true;
  t_structure_p[T_SLOT] = true;
  t_structure_p[T_ENVIRONMENT] = true;

  t_sequence_p[T_NIL] = true;
  t_sequence_p[T_PAIR] = true;
  t_sequence_p[T_STRING] = true;
  t_sequence_p[T_VECTOR] = true;
  t_sequence_p[T_INT_VECTOR] = true;
  t_sequence_p[T_FLOAT_VECTOR] = true;
  t_sequence_p[T_HASH_TABLE] = true;
  t_sequence_p[T_ENVIRONMENT] = true;
  t_sequence_p[T_C_OBJECT] = true;

  t_vector_p[T_VECTOR] = true;
  t_vector_p[T_INT_VECTOR] = true;
  t_vector_p[T_FLOAT_VECTOR] = true;

  t_applicable_p[T_PAIR] = true;
  t_applicable_p[T_STRING] = true;
  t_applicable_p[T_VECTOR] = true;
  t_applicable_p[T_INT_VECTOR] = true;
  t_applicable_p[T_FLOAT_VECTOR] = true;
  t_applicable_p[T_HASH_TABLE] = true;
  t_applicable_p[T_ENVIRONMENT] = true;
  t_applicable_p[T_C_OBJECT] = true;
  t_applicable_p[T_C_MACRO] = true;
  t_applicable_p[T_MACRO] = true;
  t_applicable_p[T_BACRO] = true;
  t_applicable_p[T_SYNTAX] = true;
  t_applicable_p[T_C_FUNCTION] = true;
  t_applicable_p[T_C_FUNCTION_STAR] = true;
  t_applicable_p[T_C_ANY_ARGS_FUNCTION] = true;
  t_applicable_p[T_C_OPT_ARGS_FUNCTION] = true;
  t_applicable_p[T_C_RST_ARGS_FUNCTION] = true;
  t_applicable_p[T_CLOSURE] = true;
  t_applicable_p[T_CLOSURE_STAR] = true;
  t_applicable_p[T_GOTO] = true;
  t_applicable_p[T_CONTINUATION] = true;

  t_any_macro_p[T_C_MACRO] = true;
  t_any_macro_p[T_MACRO] = true;
  t_any_macro_p[T_BACRO] = true;

  t_any_closure_p[T_CLOSURE] = true;
  t_any_closure_p[T_CLOSURE_STAR] = true;

  t_simple_p[T_NIL] = true; 
  t_simple_p[T_UNIQUE] = true; 
  t_simple_p[T_BOOLEAN] = true;
  t_simple_p[T_CHARACTER] = true;
  t_simple_p[T_SYMBOL] = true;
  t_simple_p[T_SYNTAX] = true;
  t_simple_p[T_C_MACRO] = true;
  t_simple_p[T_C_FUNCTION] = true;
  t_simple_p[T_C_FUNCTION_STAR] = true;
  t_simple_p[T_C_ANY_ARGS_FUNCTION] = true;
  t_simple_p[T_C_OPT_ARGS_FUNCTION] = true;
  t_simple_p[T_C_RST_ARGS_FUNCTION] = true;
  /* not completely sure about the next ones */
  t_simple_p[T_ENVIRONMENT] = true;
  t_simple_p[T_INPUT_PORT] = true;
  t_simple_p[T_OUTPUT_PORT] = true;
}


#define typeflag(p)                   ((p)->tf.flag)
#define typesflag(p)                  ((p)->tf.sflag)

#if DEBUGGING
  #define unchecked_type(p)           ((p)->tf.type_field)
  #define type(p) ({unsigned char _t_; _t_ = (p)->tf.type_field; if (_t_ == T_FREE) fprintf(stderr, "%p is free (line %d)\n", p, __LINE__); _t_;})
  #define set_type(p, f) \
  do { \
      p->previous_alloc_line = p->current_alloc_line; \
      p->previous_alloc_func = p->current_alloc_func; \
      p->previous_alloc_type = p->current_alloc_type; \
      p->current_alloc_line = __LINE__; \
      p->current_alloc_func = __func__; \
      p->current_alloc_type = f; \
      p->uses++; \
      if (((f) & 0xff) == T_FREE) fprintf(stderr, "%d: set free %p type to %x\n", __LINE__, p, f); \
      else if ((is_immutable(p)) && ((typeflag(p) != (f)))) fprintf(stderr, "%d: set immutable %p type %x to %x\n", __LINE__, p, unchecked_type(p), f); \
      typeflag(p) = f; \
    } while (0)
  #define clear_type(p) do {p->gc_line = __LINE__; p->gc_func = __func__; typeflag(p) = 0;} while (0)
#else
  #define unchecked_type(p)           ((p)->tf.type_field)
  #define type(p)                     ((p)->tf.type_field)
  #define set_type(p, f)              typeflag(p) = f
  #define clear_type(p)               typeflag(p) = T_FREE
#endif

#define is_number(P)                  t_number_p[type(P)]
#define is_integer(P)                 (type(P) == T_INTEGER)
#define is_real(P)                    t_real_p[type(P)]
#define is_rational(P)                t_rational_p[type(P)]
#define is_big_number(p)              t_big_number_p[type(p)]
#define is_simple_real(p)             typeflag(p) == T_REAL

#define is_free(p)                    (type(p) == T_FREE)
#define is_free_and_clear(p)          (typeflag(p) == T_FREE)
#define is_simple(P)                  t_simple_p[type(P)]
#define has_structure(P)              t_structure_p[type(P)]

#define is_any_macro(P)               t_any_macro_p[type(P)]
#define is_any_closure(P)             t_any_closure_p[type(P)]
#define is_procedure_or_macro(P)      ((t_any_macro_p[type(P)]) || ((typeflag(P) & T_PROCEDURE) != 0))

#define is_sequence(P)                ((t_sequence_p[type(P)]) || (has_methods(P)))
#define is_applicable(P)              (t_applicable_p[type(P)])
/* this misses #() which actually is not applicable to anything, probably "" also */


/* the layout of these bits does matter in several cases -- in particular, don't use the 2nd byte for anything
 *   that might shadow SYNTACTIC_PAIR and OPTIMIZED_PAIR.
 */
#define NOT_IN_HEAP -1
#define TYPE_BITS                     8

#define T_IMMUTABLE                   (1 << (TYPE_BITS + 0))
#define is_immutable(p)               ((typesflag(p) & T_IMMUTABLE) != 0)
#define set_immutable(p)              typesflag(p) |= T_IMMUTABLE
/* immutable means the value can't be changed via set! or bind -- this is separate from the symbol access stuff
 */
#define dont_copy(p)                  ((!is_pair(p)) || (is_immutable(p)) || (heap_location(p) == NOT_IN_HEAP))
/* dont_copy means the object is not copied when saved in a continuation */

#define T_SYNTACTIC                   (1 << (TYPE_BITS + 1))
#define is_syntactic(p)               ((typesflag(p) & T_SYNTACTIC) != 0)
#define SYNTACTIC_TYPE                (unsigned short)(T_SYMBOL | T_DONT_EVAL_ARGS | T_SYNTACTIC)
#define SYNTACTIC_PAIR                (unsigned short)(T_PAIR | T_SYNTACTIC)
/* this marks symbols that represent syntax objects, it should be in the 2nd byte
 */

#define T_PROCEDURE                   (1 << (TYPE_BITS + 2))
#define is_procedure(p)               ((typesflag(p) & T_PROCEDURE) != 0)
/* closure, c_function, applicable object, goto or continuation, should be in 2nd byte
 *   perhaps this should be T_APPLICABLE?
 */

#define T_OPTIMIZED                   (1 << (TYPE_BITS + 3))
#define set_optimized(p)              typesflag(p) |= T_OPTIMIZED
#define clear_optimized(p)            typesflag(p) &= (~T_OPTIMIZED)
#define OPTIMIZED_PAIR                (unsigned short)(T_PAIR | T_OPTIMIZED)
#define is_optimized(p)               (typesflag(p) == OPTIMIZED_PAIR)
/*   this is faster than the bit extraction above and the same speed as xor */
/* optimizer flag for an expression that has optimization info, it should be in the 2nd byte
 */

#define T_SAFE_CLOSURE                (1 << (TYPE_BITS + 4))
#define is_safe_closure(p)            ((typesflag(p) & T_SAFE_CLOSURE) != 0)
#define set_safe_closure(p)           typesflag(p) |= T_SAFE_CLOSURE
#define clear_safe_closure(p)         typesflag(p) &= (~T_SAFE_CLOSURE)
/* optimizer flag for a closure body that is completely simple (every expression is safe)
 *   set_safe_closure happens only in optimize_lambda, clear only in procedure_source, bits only here
 *   this has to be separate from T_SAFE_PROCEDURE, and should be in the 2nd byte
 */

#define T_DONT_EVAL_ARGS              (1 << (TYPE_BITS + 5))
#define dont_eval_args(p)             ((typesflag(p) & T_DONT_EVAL_ARGS) != 0)
/* this marks things that don't evaluate their arguments
 */

#define T_EXPANSION                   (1 << (TYPE_BITS + 6))
#define EXPANSION_TYPE                (unsigned short)(T_EXPANSION | T_SYMBOL)
/* #define is_expansion(p)               (typesflag(p) == EXPANSION_TYPE) */
#define is_expansion(p)               ((typesflag(p) & T_EXPANSION) != 0)
/* this marks the symbol associated with a run-time macro and distinguishes the value from an ordinary macro
 */

#define T_MULTIPLE_VALUE              (1 << (TYPE_BITS + 7))
#define is_multiple_value(p)          ((typesflag(p) & T_MULTIPLE_VALUE) != 0)
#define set_multiple_value(p)         typesflag(p) |= T_MULTIPLE_VALUE
#define multiple_value(p)             p
/* this bit marks a list (from "values") that is waiting for a
 *    chance to be spliced into its caller's argument list.  It is normally
 *    on only for a very short time.
 */

#define T_MATCHED                     T_MULTIPLE_VALUE
#define is_matched(p)                 ((typesflag(p) & T_MATCHED) != 0)
#define set_matched(p)                typesflag(p) |= T_MATCHED
#define clear_matched(p)              typesflag(p) &= (~T_MATCHED)
/* similar transient bit for tree walker 
 */

#define T_GLOBAL                      (1 << (TYPE_BITS + 8))
#define is_global(p)                  ((typeflag(p) & T_GLOBAL) != 0)
#define set_global(p)                 typeflag(p) |= T_GLOBAL
#define set_local(p)                  typeflag(p) = (typeflag(p) & ~(T_DONT_EVAL_ARGS | T_GLOBAL | T_SYNTACTIC))
/* this marks something defined (bound) at the top-level, and never defined locally 
 */

#define T_UNSAFE_DO                   T_GLOBAL
#define is_unsafe_do(p)               ((typeflag(p) & T_UNSAFE_DO) != 0)
#define set_unsafe_do(p)              typeflag(p) |= T_UNSAFE_DO

#define T_RETURNS_TEMP                (1 << (TYPE_BITS + 9))
#define returns_temp(p)               ((typeflag(p) & T_RETURNS_TEMP) != 0)
#define set_returns_temp(p)           typeflag(p) |= T_RETURNS_TEMP
/* this marks functions that guarantee their returned value is either a newly minted temporary cell
 *   (not one of the function args and not stored anywhere outside-accessible), or an immutable 
 *   constant like real_zero.
 */

#define T_COLLECTED                   T_RETURNS_TEMP
#define is_collected(p)               ((typeflag(p) & T_COLLECTED) != 0)
#define set_collected(p)              typeflag(p) |= T_COLLECTED
#define clear_collected(p)            typeflag(p) &= (~T_COLLECTED)
/* this is a transient flag used by the printer to catch cycles.  It affects only objects
 *   that have structure, whereas T_RETURNS_TEMP only affects functions.  We can't use a
 *   low bit (bit 7 for example), because collect_shared_info inspects the object's type.
 */

#define T_LINE_NUMBER                 (1 << (TYPE_BITS + 10))
#define has_line_number(p)            ((typeflag(p) & T_LINE_NUMBER) != 0)
#define set_has_line_number(p)        typeflag(p) |= T_LINE_NUMBER
/* pair in question has line/file info added during read, or the environment has function placement info
 */

#define T_LOADER_PORT                 T_LINE_NUMBER
#define is_loader_port(p)             ((typeflag(p) & T_LOADER_PORT) != 0)
#define set_loader_port(p)            typeflag(p) |= T_LOADER_PORT
#define clear_loader_port(p)          typeflag(p) &= (~T_LOADER_PORT)
/* to block random load-time reads from screwing up the load process, this bit marks a port used by the loader
 */

#define T_CYCLIC                      (1 << (TYPE_BITS + 11))
#define set_cyclic(p)                 typeflag(p) |= T_CYCLIC
#define is_cyclic(p)                  ((typeflag(p) & T_CYCLIC) != 0)
#define clear_cyclic(p)               typeflag(p) &= (~T_CYCLIC)
/* used by cyclic-sequences to mark objects already traversed */

#define T_OVERLAY                     (1 << (TYPE_BITS + 12))
#define set_overlay(p)                typeflag(p) |= T_OVERLAY
#define is_overlaid(p)                ((typeflag(p) & T_OVERLAY) != 0)
/* optimizer flag that marks a cell whose ecdr points to the previous cell in a list
 */

#define T_SAFE_PROCEDURE              (1 << (TYPE_BITS + 13))
#define is_safe_procedure(p)          ((typeflag(p) & T_SAFE_PROCEDURE) != 0)
/* c_functions that do not return or modify the arg list directly (no :rest arg in particular),
 *    and that can't call apply themselves either directly or via s7_call.
 *    I think the latter would be safe if they gc_protect the called function.  
 * This also includes applicable objects that are known safe (lists, vectors, etc -- equivalent to vector-ref).
 */

#define T_CHECKED                     (1 << (TYPE_BITS + 14))
#define set_checked(p)                typeflag(p) |= T_CHECKED
#define is_checked(p)                 ((typeflag(p) & T_CHECKED) != 0)
#define is_not_checked(p)             ((typeflag(p) & T_CHECKED) == 0)
#define clear_checked(p)              typeflag(p) &= (~T_CHECKED)


#define T_UNSAFE                      (1 << (TYPE_BITS + 15))
#define set_unsafe(p)                 typeflag(p) |= T_UNSAFE
#define set_unsafely_optimized(p)     typeflag(p) |= (T_UNSAFE | T_OPTIMIZED)
#define is_unsafe(p)                  ((typeflag(p) & T_UNSAFE) != 0)
#define clear_unsafe(p)               typeflag(p) &= (~T_UNSAFE)
#define is_safely_optimized(p)        ((typeflag(p) & (T_OPTIMIZED | T_UNSAFE)) == T_OPTIMIZED)
/* optimizer flag saying "this expression is not completely self-contained.  It might involve the stack, etc"
 */

#define T_STEP_SAFE                   T_UNSAFE
#define is_step_safe(p)               ((typeflag(p) & T_STEP_SAFE) != 0)
#define set_step_safe(p)              typeflag(p) |= T_STEP_SAFE

#define T_KEYWORD                     (1 << (TYPE_BITS + 16))
#define is_keyword(p)                 ((typeflag(p) & T_KEYWORD) != 0)
/* this bit distinguishes a symbol from a symbol that is also a keyword
 */

#define T_SETTER                      (1 << (TYPE_BITS + 17))
#define set_setter(p)                 typeflag(p) |= T_SETTER
#define is_setter(p)                  ((typeflag(p) & T_SETTER) != 0)
/* optimizer flag for a procedure that sets some variable (set-car! for example).
 */

#define T_MUTABLE                     (1 << (TYPE_BITS + 18))
#define is_mutable(p)                 ((typeflag(p) & T_MUTABLE) != 0)
/* #define set_mutable(p)             typeflag(p) |= T_MUTABLE */
#define clear_mutable(p)              typeflag(p) &= (~T_MUTABLE)
/* used for mutable numbers in clm2xen 
 */

#define T_BYTEVECTOR                  T_MUTABLE
#define is_bytevector(p)              ((typeflag(p) & T_BYTEVECTOR) != 0)
#define set_bytevector(p)             typeflag(p) |= T_BYTEVECTOR
/* marks a string that the caller considers a bytevector 
*/

#define T_PRINT_NAME                  (1 << (TYPE_BITS + 19))
#define has_print_name(p)             ((typeflag(p) & T_PRINT_NAME) != 0)
#define set_has_print_name(p)         typeflag(p) |= T_PRINT_NAME
/* marks numbers that have a saved version of their string representation 
 */

#define T_COPY_ARGS                   (1 << (TYPE_BITS + 20))
#define needs_copied_args(p)          ((typeflag(p) & T_COPY_ARGS) != 0)
/* this marks something that might mess with its argument list, it should not be in the 2nd byte
 */

#define T_GENSYM                      (1 << (TYPE_BITS + 21))
#define is_gensym(p)                  ((typeflag(p) & T_GENSYM) != 0)
/* symbol is from gensym (GC-able etc)
 *   since this is only relevant to symbols, this bit is used below in several other non-intersecting ways
 */

#define T_SIMPLE_ARGS                 T_GENSYM
#define has_simple_args(p)            ((typeflag(p) & T_SIMPLE_ARGS) != 0)
#define set_simple_args(p)            typeflag(p) |= T_SIMPLE_ARGS
/* are all lambda* default values simple? */

#define T_LIST_IN_USE                 T_GENSYM
#define list_is_in_use(p)             ((typeflag(p) & T_LIST_IN_USE) != 0)
#define set_list_in_use(p)            typeflag(p) |= T_LIST_IN_USE
#define clear_list_in_use(p)          typeflag(p) &= (~T_LIST_IN_USE)
/* these could all be one permanent list, indexed from inside, and this bit is never actually protecting anything across a call
 */

#define T_FUNCTION_ENV                T_GENSYM
#define is_function_env(p)            ((typeflag(p) & T_FUNCTION_ENV) != 0)
#define set_function_env(p)           typeflag(p) |= T_FUNCTION_ENV
/* this marks a funclet */

#define T_DOCUMENTED                  T_GENSYM
#define is_documented(p)              ((typeflag(p) & T_DOCUMENTED) != 0)
#define set_documented(p)             typeflag(p) |= T_DOCUMENTED
/* this marks a symbol that has documentation 
 */

#define T_HAS_METHODS                 (1 << (TYPE_BITS + 22))
#define has_methods(p)                ((typeflag(p) & T_HAS_METHODS) != 0)
#define set_has_methods(p)            typeflag(p) |= T_HAS_METHODS
#define clear_has_methods(p)          typeflag(p) &= (~T_HAS_METHODS)
/* this marks an environment or closure that is "opened" up to generic functions etc 
 * don't reuse this bit if possible
 */

#define T_HAS_REF_FALLBACK            T_MUTABLE
#define T_HAS_SET_FALLBACK            T_PRINT_NAME
#define has_ref_fallback(p)           ((typeflag(p) & T_HAS_REF_FALLBACK) != 0)
#define has_set_fallback(p)           ((typeflag(p) & T_HAS_SET_FALLBACK) != 0)
#define set_has_ref_fallback(p)       typeflag(p) |= T_HAS_REF_FALLBACK
#define set_has_set_fallback(p)       typeflag(p) |= T_HAS_SET_FALLBACK
#define set_all_methods(p, e)         typeflag(p) |= (typeflag(e) & (T_HAS_METHODS | T_HAS_REF_FALLBACK | T_HAS_SET_FALLBACK))

#define T_HAS_ACCESSOR                T_LINE_NUMBER
#define has_accessor(p)               ((typeflag(p) & T_HAS_ACCESSOR) != 0)
#define set_has_accessor(p)           typeflag(p) |= T_HAS_ACCESSOR
/* marks a slot or symbol that has a setter -- T_PRINT_NAME might be better here
 */

#define T_GC_MARK                     (1 << (TYPE_BITS + 23))
#define is_marked(p)                  ((typeflag(p) &  T_GC_MARK) != 0)
#define set_mark(p)                   typeflag(p) |= T_GC_MARK
#define clear_mark(p)                 typeflag(p) &= (~T_GC_MARK)
/* using bit 23 for this makes a big difference in the GC
 */

#define UNUSED_BITS 0 /* (1 << (TYPE_BITS + 11)) */


#if 0
  /* to find who is stomping on our symbols:
   */
  static char *object_to_truncated_string(s7_scheme *sc, s7_pointer p, int len);
  
  static void set_local_1(s7_scheme *sc, s7_pointer symbol, const char *func, int line) 
  {
    if ((is_global(symbol)) || (is_syntactic(symbol)))
      fprintf(stderr, "%s[%d]: %s%s%s in %s\n", func, line, BOLD_TEXT, DISPLAY(symbol), UNBOLD_TEXT, DISPLAY_80(sc->cur_code));
    typeflag(symbol) = (typeflag(symbol) & ~(T_DONT_EVAL_ARGS | T_GLOBAL | T_SYNTACTIC));
  }
  #define set_local(Symbol) set_local_1(sc, Symbol, __func__, __LINE__)
#endif


#define heap_location(p)              (p)->hloc
#define is_eof(p)                     (p == sc->EOF_OBJECT)
#define is_true(Sc, p)                ((p) != Sc->F)
#define is_false(Sc, p)               ((p) == Sc->F)
#ifdef _MSC_VER
  #define MS_WINDOWS 1
  #if 1
  static s7_pointer make_boolean(s7_scheme *sc, bool val) {if (val) return(sc->T); return(sc->F);}
  #else
  #define make_boolean(sc, Val)       (((Val) & 0xff) ? sc->T : sc->F)
  #endif
#else
  #define make_boolean(sc, Val)       ((Val) ? sc->T : sc->F)
#endif

#define is_pair(p)                    (type(p) == T_PAIR)
#define is_null(p)                    (p == sc->NIL)
#define is_not_null(p)                (p != sc->NIL)


#if (!DEBUGGING)
  #define ecdr(p)                     ((p)->object.cons.ecdr)
  #define set_ecdr(p, x)              (p)->object.cons.ecdr = x
  #define fcdr(p)                     ((p)->object.cons.fcdr)
  #define set_fcdr(p, x)              (p)->object.cons.fcdr = x
  #define gcdr(p)                     ((p)->object.ext_cons.gcdr)
  #define set_gcdr(p, x)              do {(p)->object.ext_cons.gcdr = x; typeflag(p) &= ~(T_OPTIMIZED | T_LINE_NUMBER);} while (0)
#else
    /* ---------------- debugging versions of e|f|gcdr ---------------- */
#define T_ECDR_SET                    1                                        /* T_HAS_METHODS */
#define ecdr_is_set(p)                (((p)->debugger_bits & T_ECDR_SET) != 0) /* ((typeflag(p) & T_ECDR_SET) != 0) */
#define set_ecdr_is_set(p)            (p)->debugger_bits |= T_ECDR_SET         /*  typeflag(p) |= T_ECDR_SET */
#define ecdr(p)                       ecdr_1(sc, p, __func__, __LINE__)
#define set_ecdr(p, x)                set_ecdr_1(sc, p, x, __func__, __LINE__)

static s7_pointer ecdr_1(s7_scheme *sc, s7_pointer p, const char *func, int line) 
{
  if (!ecdr_is_set(p))
    fprintf(stderr, "ecdr %s[%d]: %p->%p\n", func, line, p, p->object.cons.ecdr); 
  return(p->object.cons.ecdr);
}

static s7_pointer set_ecdr_1(s7_scheme *sc, s7_pointer p, s7_pointer x, const char *func, int line) 
{
  p->object.cons.ecdr = x;
  set_ecdr_is_set(p);
  return(x);
}

#define T_FCDR_SET                    2                                        /* T_GLOBAL */
#define fcdr_is_set(p)                (((p)->debugger_bits & T_FCDR_SET) != 0) /* ((typeflag(p) & T_FCDR_SET) != 0) */
#define set_fcdr_is_set(p)            (p)->debugger_bits |= T_FCDR_SET         /* typeflag(p) |= T_FCDR_SET */
#define fcdr(p)                       fcdr_1(sc, p, __func__, __LINE__)
#define set_fcdr(p, x)                set_fcdr_1(sc, p, x, __func__, __LINE__)

static s7_pointer fcdr_1(s7_scheme *sc, s7_pointer p, const char *func, int line) 
{
  if (!fcdr_is_set(p))
    fprintf(stderr, "fcdr %s[%d]: %p->%p\n", func, line, p, p->object.cons.fcdr); 
  return(p->object.cons.fcdr);
}

static void set_fcdr_1(s7_scheme *sc, s7_pointer p, s7_pointer x, const char *func, int line) 
{
  /* fprintf(stderr, "%d: set %p to %p\n", line, p, x); */ 
  p->object.cons.fcdr = x;
  set_fcdr_is_set(p);
}

/* gcdr collides with optimization and line number stuff (T_LINE_NUMBER, T_OPTIMIZED)
 */
#define T_GCDR_SET                    4                                        /* T_COPY_ARGS */
#define gcdr_is_set(p)                (((p)->debugger_bits & T_GCDR_SET) != 0) /* ((typeflag(p) & T_GCDR_SET) != 0) */
#define set_gcdr_is_set(p)            (p)->debugger_bits |= T_GCDR_SET         /* typeflag(p) |= T_GCDR_SET */
#define gcdr(p)                       gcdr_1(sc, p, __func__, __LINE__)
#define set_gcdr(p, x)                set_gcdr_1(sc, p, x, __func__, __LINE__)

static s7_pointer gcdr_1(s7_scheme *sc, s7_pointer p, const char *func, int line) 
{
  if (!gcdr_is_set(p))
    fprintf(stderr, "%s[%d]: gcdr %p->%p\n", func, line, p, p->object.ext_cons.gcdr); 
  return(p->object.ext_cons.gcdr);
}

static void set_gcdr_1(s7_scheme *sc, s7_pointer p, s7_pointer x, const char *func, int line) 
{
  if ((typeflag(p) & (T_OPTIMIZED | T_LINE_NUMBER)) != 0)
    {
      fprintf(stderr, "gcdr %s[%d]: set %p to %p (%s) but %s\n", func, line, p, x, DISPLAY(x), (has_line_number(p)) ? "line number set" : "is optimized");
      typeflag(p) &= ~(T_OPTIMIZED | T_LINE_NUMBER);
    }
  p->object.ext_cons.gcdr = x;
  set_gcdr_is_set(p);
}
#endif

#define car(p)                        ((p)->object.cons.car)
#define cdr(p)                        ((p)->object.cons.cdr)

#define caar(p)                       car(car(p))
#define cadr(p)                       car(cdr(p))
#define cdar(p)                       cdr(car(p))
#define cddr(p)                       cdr(cdr(p))

#define caaar(p)                      car(car(car(p)))
#define cadar(p)                      car(cdr(car(p)))
#define cdadr(p)                      cdr(car(cdr(p)))
#define caddr(p)                      car(cdr(cdr(p)))
#define caadr(p)                      car(car(cdr(p)))
#define cdaar(p)                      cdr(car(car(p)))
#define cdddr(p)                      cdr(cdr(cdr(p)))
#define cddar(p)                      cdr(cdr(car(p)))

#define caaadr(p)                     car(car(car(cdr(p))))
#define caadar(p)                     car(car(cdr(car(p))))
#define cadaar(p)                     car(cdr(car(car(p))))
#define cadddr(p)                     car(cdr(cdr(cdr(p))))
#define caaddr(p)                     car(car(cdr(cdr(p))))
#define cddddr(p)                     cdr(cdr(cdr(cdr(p))))
#define caddar(p)                     car(cdr(cdr(car(p))))
#define cdadar(p)                     cdr(car(cdr(car(p))))
#define cdaddr(p)                     cdr(car(cdr(cdr(p))))
#define caaaar(p)                     car(car(car(car(p))))
#define cadadr(p)                     car(cdr(car(cdr(p))))
#define cdaadr(p)                     cdr(car(car(cdr(p))))
#define cdaaar(p)                     cdr(car(car(car(p))))
#define cdddar(p)                     cdr(cdr(cdr(car(p))))
#define cddadr(p)                     cdr(cdr(car(cdr(p))))
#define cddaar(p)                     cdr(cdr(car(car(p))))

#define caddddr(p)                    car(cdr(cdr(cdr(cdr(p)))))


#if WITH_GCC
  /* slightly tricky because cons can be called recursively */
  #define cons(Sc, A, B)             ({s7_pointer _X_; NEW_CELL(sc, _X_); car(_X_) = A; cdr(_X_) = B; set_type(_X_, T_PAIR | T_SAFE_PROCEDURE); _X_;})
#else
  #define cons(Sc, A, B)              s7_cons(Sc, A, B)
#endif

#define list_1(Sc, A)                 cons(Sc, A, sc->NIL)
#define list_2(Sc, A, B)              cons_unchecked(Sc, A, cons(Sc, B, sc->NIL))
#define list_3(Sc, A, B, C)           cons_unchecked(Sc, A, cons_unchecked(Sc, B, cons(Sc, C, sc->NIL)))
#define list_4(Sc, A, B, C, D)        cons_unchecked(Sc, A, cons_unchecked(Sc, B, cons_unchecked(Sc, C, cons(Sc, D, sc->NIL))))

#define pair_line_number(p)           (p)->object.cons.line
#define pair_raw_hash(p)              (p)->object.cons.line

#define is_string(p)                  (type(p) == T_STRING)
#define string_value(p)               (p)->object.string.svalue
#define string_length(p)              (p)->object.string.length
#define string_hash(p)                (p)->object.string.hash
#define string_needs_free(p)          (p)->object.string.str_ext.needs_free

#define character(p)                  (p)->object.chr.c
#define upper_character(p)            (p)->object.chr.up_c
#define lower_character(p)            (p)->object.chr.down_c
#define is_char_alphabetic(p)         (p)->object.chr.alpha_c
#define is_char_numeric(p)            (p)->object.chr.digit_c
#define is_char_whitespace(p)         (p)->object.chr.space_c
#define is_char_uppercase(p)          (p)->object.chr.upper_c
#define is_char_lowercase(p)          (p)->object.chr.lower_c
#define character_name(p)             (p)->object.chr.c_name
#define character_name_length(p)      (p)->object.chr.length

#define optimize_data(p)              (p)->object.cons.dat.d.data
/* static unsigned short optimize_data(s7_pointer p) {if (!is_optimized(p)) abort(); return(p->object.cons.dat.d.data);} */
/* #define set_optimize_data(P, X)    do {(P)->object.cons.dat.d.data = (X); (P)->object.cons.dat.d.data_index = (X);} while (0) */

#define optimize_data_match(P, Q)     ((is_optimized(P)) && ((optimize_data(P) & 0xfffe) == Q))
#define optimize_data_index(p)        (p)->object.cons.dat.d.data_index
#define op_no_hop(P)                  (optimize_data(P) & 0xfffe)
#define clear_hop(P)                  set_optimize_data(P, op_no_hop(P))
static void set_optimize_data(s7_pointer p, unsigned short op) {p->object.cons.dat.d.data = op; optimize_data_index(p) = op;} 
#define clear_optimize_data(P)        set_optimize_data(P, 0)
#define optimize_op(p)                optimize_data_index(p)


#define is_symbol(p)                  (type(p) == T_SYMBOL)
#define symbol_name_cell(p)           (p)->object.sym.name
#define symbol_name(p)                string_value(symbol_name_cell(p))
#define symbol_name_length(p)         string_length(symbol_name_cell(p))
#define symbol_hash(p)                (symbol_name_cell(p))->object.string.hash
#define symbol_global_accessor_gc_index(p) symbol_name_cell(p)->object.string.str_ext.accessor 
#define symbol_has_accessor(p)        has_accessor(p)
#define symbol_set_has_accessor(p)    set_has_accessor(p)
#define symbol_id(p)                  (p)->object.sym.id

/* we need 64-bits here, since we don't want this thing to wrap around, and frames are created at a great rate 
 *    callgrind says this is faster than an unsigned int!
 */
#define symbol_syntax_op(p)           (p)->object.sym.op

#define global_slot(p)                (p)->object.sym.global_slot
#define initial_slot(p)               (symbol_name_cell(p))->object.string.initial_slot
#define local_slot(p)                 (p)->object.sym.local_slot
#define keyword_symbol(p)             (symbol_name_cell(p))->object.string.doc.ksym /* (p)->object.sym.ksym */
#define symbol_help(p)                (symbol_name_cell(p))->object.string.doc.documentation
#define symbol_tag(p)                 (symbol_name_cell(p))->object.string.tag
#define symbol_has_help(p)            (is_documented(symbol_name_cell(p)))
#define symbol_set_has_help(p)        set_documented(symbol_name_cell(p))

#define symbol_set_local(Symbol, Id, Slot) do {local_slot(Symbol) = Slot; symbol_id(Symbol) = Id;} while (0)
/* set slot before id in case Slot is an expression that tries to find the current Symbol slot (using its old Id obviously) */
/* I think symbol_id is set only here */


#define is_slot(p)                    (type(p) == T_SLOT)
#define slot_value(p)                 (p)->object.slt.val
#define slot_set_value(p, Val)        (p)->object.slt.val = Val
#define slot_symbol(p)                (p)->object.slt.sym
#define next_slot(p)                  (p)->object.slt.nxt
#define slot_pending_value(p)         (p)->object.slt.pending_value
#define slot_expression(p)            (p)->object.slt.expr
#define slot_accessor(p)              slot_expression(p)
#define slot_has_accessor(p)          has_accessor(p)
#define slot_set_has_accessor(p)      set_has_accessor(p)


#define is_syntax(p)                  (type(p) == T_SYNTAX)
#define syntax_symbol(p)              (p)->object.syn.symbol
#define syntax_opcode(p)              (p)->object.syn.op

#define pair_syntax_op(P)             (P)->object.cons.dat.op
#define pair_syntax_symbol(P)         car(ecdr(P))
#if (!DEBUGGING)
static void pair_set_syntax_symbol(s7_pointer p, s7_pointer op) {pair_syntax_symbol(p) = op; pair_syntax_op(ecdr(p)) = symbol_syntax_op(op);}
#else
#define pair_set_syntax_symbol(P, Op) set_syntax_op_1(sc, P, Op)
static void set_syntax_op_1(s7_scheme *sc, s7_pointer p, s7_pointer op) {pair_syntax_symbol(p) = op; pair_syntax_op(ecdr(p)) = symbol_syntax_op(op);}
#endif

#define let_id(p)                     (p)->object.envr.id
#define is_let(p)                     (type(p) == T_ENVIRONMENT)
#define let_slots(p)                  (p)->object.envr.slots
#define outlet(p)                     (p)->object.envr.nxt
#define funclet_function(p)           (p)->object.envr.edat.efnc.function
#define let_line(p)                   (p)->object.envr.edat.efnc.line
#define let_file(p)                   (p)->object.envr.edat.efnc.file
#define dox_slot1(p)                  (p)->object.envr.edat.dox.dox1
#define dox_slot2(p)                  (p)->object.envr.edat.dox.dox2

#define unique_name(p)                (p)->object.unq.name
#define unique_name_length(p)         (p)->object.unq.len
#define is_unspecified(p)             (type(p) == T_UNSPECIFIED)

#define vector_length(p)              ((p)->object.vector.length)
#define vector_element(p, i)          ((p)->object.vector.elements.objects[i])
#define vector_elements(p)            (p)->object.vector.elements.objects
#define vector_getter(p)              (p)->object.vector.vget
#define vector_setter(p)              (p)->object.vector.vset
#define int_vector_element(p, i)      ((p)->object.vector.elements.ints[i])
#define int_vector_elements(p)        (p)->object.vector.elements.ints
#define float_vector_element(p, i)    ((p)->object.vector.elements.floats[i])
#define float_vector_elements(p)      (p)->object.vector.elements.floats
#define is_int_vector(p)              (type(p) == T_INT_VECTOR)
#define is_float_vector(p)            (type(p) == T_FLOAT_VECTOR)

#define vector_ndims(p)               ((p)->object.vector.dim_info->ndims)
#define vector_dimension(p, i)        ((p)->object.vector.dim_info->dims[i])
#define vector_dimensions(p)          ((p)->object.vector.dim_info->dims)
#define vector_offset(p, i)           ((p)->object.vector.dim_info->offsets[i])
#define vector_offsets(p)             ((p)->object.vector.dim_info->offsets)
#define vector_dimension_info(p)      ((p)->object.vector.dim_info)
#define shared_vector(p)              ((p)->object.vector.dim_info->original)
#define vector_rank(p)                ((vector_dimension_info(p)) ? vector_ndims(p) : 1)
#define vector_has_dimensional_info(p) (vector_dimension_info(p))
#define vector_elements_allocated(p)  ((p)->object.vector.dim_info->elements_allocated)
#define vector_dimensions_allocated(p) ((p)->object.vector.dim_info->dimensions_allocated)

#define is_hash_table(p)              (type(p) == T_HASH_TABLE)
#define hash_table_length(p)          (p)->object.hasher.length
#define hash_table_element(p, i)      ((p)->object.hasher.elements[i])
#define hash_table_elements(p)        (p)->object.hasher.elements
#define hash_table_entries(p)         (p)->object.hasher.entries
#define hash_table_function(p)        (p)->object.hasher.hash_func
#define hash_table_eq_function(p)     (p)->object.hasher.eq_func

#define is_input_port(p)              (type(p) == T_INPUT_PORT) 
#if DEBUGGING
#define is_output_port(p)             (unchecked_type(p) == T_OUTPUT_PORT)
#else
#define is_output_port(p)             (type(p) == T_OUTPUT_PORT)
#endif
#define port_port(p)                  (p)->object.prt.port
#define port_type(p)                  (p)->object.prt.ptype
#define is_string_port(p)             (port_type(p) == STRING_PORT)
#define is_file_port(p)               (port_type(p) == FILE_PORT)
#define is_function_port(p)           (port_type(p) == FUNCTION_PORT)
#define port_line_number(p)           (p)->object.prt.line_number
#define port_file_number(p)           (p)->object.prt.file_number
#define port_filename(p)              port_port(p)->filename
#define port_filename_length(p)       port_port(p)->filename_length
#define port_file(p)                  port_port(p)->file
#define port_is_closed(p)             (p)->object.prt.is_closed
#define port_data(p)                  (p)->object.prt.data
#define port_data_size(p)             (p)->object.prt.size
#define port_position(p)              (p)->object.prt.point
#define port_needs_free(p)            port_port(p)->needs_free
#define port_output_function(p)       port_port(p)->output_function
#define port_input_function(p)        port_port(p)->input_function
#define port_original_input_string(p) port_port(p)->orig_str
#define port_read_character(p)        port_port(p)->read_character
#define port_read_line(p)             port_port(p)->read_line
#define port_display(p)               port_port(p)->display
#define port_write_character(p)       port_port(p)->write_character
#define port_write_string(p)          port_port(p)->write_string
#define port_read_semicolon(p)        port_port(p)->read_semicolon
#define port_read_white_space(p)      port_port(p)->read_white_space
#define port_read_name(p)             port_port(p)->read_name
#define port_read_sharp(p)            port_port(p)->read_sharp
#define port_gc_loc(p)                port_port(p)->gc_loc

#define is_c_function(f)              (type(f) >= T_C_FUNCTION)
#define is_c_function_star(f)         (type(f) == T_C_FUNCTION_STAR)
#define is_any_c_function(f)          (type(f) >= T_C_FUNCTION_STAR)
#define c_function_data(f)            (f)->object.fnc.c_proc
#define c_function_call(f)            (f)->object.fnc.ff
#define c_function_required_args(f)   (f)->object.fnc.required_args
#define c_function_optional_args(f)   (f)->object.fnc.optional_args
#define c_function_has_rest_arg(f)    (f)->object.fnc.rest_arg
#define c_function_all_args(f)        (f)->object.fnc.all_args
#define c_function_setter(f)          (f)->object.fnc.setter
#define c_function_name(f)            c_function_data(f)->name
#define c_function_name_length(f)     c_function_data(f)->name_length
#define c_function_documentation(f)   c_function_data(f)->doc
#define c_function_class(f)           c_function_data(f)->id
#define c_function_chooser(f)         c_function_data(f)->chooser
#define c_function_base(f)            c_function_data(f)->generic_ff

#define c_function_ext(f)             c_function_data(f)->ext
#define c_function_chooser_data(f)    c_function_ext(f)->chooser_data
#define c_function_looped(f)          c_function_ext(f)->looped_ff
#define c_function_let_looped(f)      c_function_ext(f)->let_looped_ff
#define c_function_ex_parser(f)       c_function_ext(f)->exf

#define c_function_arg_defaults(f)    c_function_ext(f)->arg_defaults
#define c_function_keyed_args(f)      c_function_ext(f)->keyed_args
#define c_function_call_args(f)       c_function_ext(f)->call_args
#define c_function_arg_names(f)       c_function_ext(f)->arg_names
#define c_function_simple_defaults(f) c_function_ext(f)->simple_defaults

void s7_function_set_returns_temp(s7_pointer f) {set_returns_temp(f);}
bool s7_function_returns_temp(s7_scheme *sc, s7_pointer f) {return((is_optimized(f)) && (ecdr(f)) && (returns_temp(ecdr(f))));}

#define c_call(f)                     ((s7_function)(fcdr(f)))
#define set_c_function(f, X)          do {set_ecdr(f, X); set_fcdr(f, (s7_pointer)(c_function_call(ecdr(f))));} while (0)

#define is_c_macro(p)                 (type(p) == T_C_MACRO)
#define c_macro_data(f)               (f)->object.fnc.c_proc
#define c_macro_call(f)               (f)->object.fnc.ff
#define c_macro_name(f)               c_macro_data(f)->name
#define c_macro_name_length(f)        c_macro_data(f)->name_length
#define c_macro_required_args(f)      (f)->object.fnc.required_args
#define c_macro_all_args(f)           (f)->object.fnc.all_args
#define c_macro_setter(f)             (f)->object.fnc.setter

#define continuation_data(p)          (p)->object.cwcc.continuation
#define continuation_stack(p)         (p)->object.cwcc.stack
#define continuation_stack_end(p)     (p)->object.cwcc.stack_end
#define continuation_stack_start(p)   (p)->object.cwcc.stack_start
#define continuation_stack_size(p)    (p)->object.cwcc.continuation->stack_size
#define continuation_stack_top(p)     (continuation_stack_end(p) - continuation_stack_start(p))
#define continuation_op_stack(p)      (p)->object.cwcc.op_stack
#define continuation_op_loc(p)        (p)->object.cwcc.continuation->op_stack_loc
#define continuation_op_size(p)       (p)->object.cwcc.continuation->op_stack_size
#define continuation_key(p)           (p)->object.cwcc.continuation->local_key

#define call_exit_goto_loc(p)         (p)->object.rexit.goto_loc
#define call_exit_op_loc(p)           (p)->object.rexit.op_stack_loc
#define call_exit_active(p)           (p)->object.rexit.active

#define s7_stack_top(Sc)              ((Sc)->stack_end - (Sc)->stack_start)

#define is_continuation(p)            (type(p) == T_CONTINUATION)
#define is_goto(p)                    (type(p) == T_GOTO)
#define is_macro(p)                   (type(p) == T_MACRO)
#define is_bacro(p)                   (type(p) == T_BACRO)

#define is_closure(p)                 (type(p) == T_CLOSURE)
#define is_closure_star(p)            (type(p) == T_CLOSURE_STAR)
#define closure_args(p)               (p)->object.func.args
#define closure_body(p)               (p)->object.func.body
#define closure_let(p)                (p)->object.func.env
#define closure_setter(p)             (p)->object.func.setter
#define closure_arity(p)              (p)->object.func.arity
#define CLOSURE_ARITY_NOT_SET         0x40000000
#define MAX_ARITY                     0x20000000
#define closure_arity_unknown(p)      (closure_arity(p) == CLOSURE_ARITY_NOT_SET)

#define catch_tag(p)                  (p)->object.rcatch.tag
#define catch_goto_loc(p)             (p)->object.rcatch.goto_loc
#define catch_op_loc(p)               (p)->object.rcatch.op_stack_loc
#define catch_handler(p)              (p)->object.rcatch.handler

#define catch_all_goto_loc(p)         (p)->object.envr.edat.ctall.goto_loc
#define catch_all_op_loc(p)           (p)->object.envr.edat.ctall.op_stack_loc
#define catch_all_result(p)           (p)->object.envr.edat.ctall.result

enum {DWIND_INIT, DWIND_BODY, DWIND_FINISH};
#define dynamic_wind_state(p)         (p)->object.winder.state
#define dynamic_wind_in(p)            (p)->object.winder.in
#define dynamic_wind_out(p)           (p)->object.winder.out
#define dynamic_wind_body(p)          (p)->object.winder.body

#define is_c_object(p)                (type(p) == T_C_OBJECT)
#define c_object_value(p)             (p)->object.c_obj.value
#define c_object_type(p)              (p)->object.c_obj.type
#define c_object_let(p)               (p)->object.c_obj.e

static c_object_t **object_types = NULL;
static int object_types_size = 0;
static int num_types = 0;
/* ideally these would be specific to the current s7, but I did not pass the s7_scheme pointer
 *   to s7_new_type and friends.
 */

#define c_object_info(p)              object_types[c_object_type(p)]
#define c_object_ref(p)               c_object_info(p)->ref
#define c_object_set(p)               c_object_info(p)->set
#define c_object_print(p)             c_object_info(p)->print
#define c_object_print_readably(p)    c_object_info(p)->print_readably
#define c_object_length(p)            c_object_info(p)->length
#define c_object_equal(p)             c_object_info(p)->equal
#define c_object_fill(p)              c_object_info(p)->fill
#define c_object_copy(p)              c_object_info(p)->copy
#define c_object_free(p)              c_object_info(p)->free
#define c_object_mark(p)              c_object_info(p)->gc_mark
#define c_object_reverse(p)           c_object_info(p)->reverse
#define c_object_outer_type(p)        c_object_info(p)->outer_type

#define raw_pointer(p)                (p)->object.c_pointer

#define is_counter(p)                 (type(p) == T_COUNTER)
#define counter_result(p)             (p)->object.ctr.result
#define counter_list(p)               (p)->object.ctr.list
#define counter_count(p)              (p)->object.ctr.val
#define counter_length(p)             (p)->object.ctr.len
#define counter_let(p)                (p)->object.ctr.env
#define counter_args(p)               (p)->object.ctr.args

#define baffle_key(p)                 (p)->object.baffle_key

#if __cplusplus && HAVE_COMPLEX_NUMBERS
  using namespace std;                /* the code has to work in C as well as C++, so we can't scatter std:: all over the place */
  typedef complex<s7_Double> s7_Complex;
  static s7_Double Real(complex<s7_Double> x) {return(real(x));} /* protect the C++ name */
  static s7_Double Imag(complex<s7_Double> x) {return(imag(x));}
#endif

#define integer(p)                    p->object.number.integer_value
#define real(p)                       p->object.number.real_value
#define numerator(p)                  p->object.number.fraction_value.numerator
#define denominator(p)                p->object.number.fraction_value.denominator
#define fraction(p)                   (((long double)numerator(p)) / ((long double)denominator(p)))
#define inverted_fraction(p)          (((long double)denominator(p)) / ((long double)numerator(p)))
#define real_part(p)                  p->object.number.complex_value.rl
#define imag_part(p)                  p->object.number.complex_value.im
#if HAVE_COMPLEX_NUMBERS
  #define as_c_complex(p)             CMPLX(real_part(p), imag_part(p))
#endif

#define NUM_SMALL_INTS 2048
#define small_int(Val)                small_ints[Val]
#define is_small(n)                   ((n & ~(NUM_SMALL_INTS - 1)) == 0)

#define print_name(p)                 (char *)(p->object.number.pval.name + 1)
#define print_name_length(p)          p->object.number.pval.name[0]

static void set_print_name(s7_pointer p, const char *name, int len)
{
  if ((len < PRINT_NAME_SIZE) &&
      (!is_mutable(p)))
    {
      set_has_print_name(p);
      print_name_length(p) = (unsigned char)(len & 0xff);
      strcpy(print_name(p), name);
    }
}


#if WITH_GCC
#define make_integer(Sc, N) \
  ({ s7_Int _N_; _N_ = N; (is_small(_N_) ? small_int(_N_) : ({ s7_pointer _X_; NEW_CELL(Sc, _X_); set_type(_X_, T_INTEGER); integer(_X_) = _N_; _X_;}) ); })

#define make_real(Sc, X) \
  ({ s7_Double _N_ = X; ((_N_ == 0.0) ? real_zero : ({ s7_pointer _X_; NEW_CELL(Sc, _X_); set_type(_X_, T_REAL); real(_X_) = _N_; _X_;}) ); })
                     /* the x == 0.0 check saves more than it costs */

#define remake_real(Sc, R, X) ({ s7_Double _x_; _x_ = X; ((is_simple_real(R)) ? ({real(R) = _x_; R;}) : make_real(Sc, _x_)); })

#define make_complex(Sc, R, I)						\
  ({ s7_Double im; im = I; ((im == 0.0) ? make_real(Sc, R) :       \
	({ s7_pointer _X_; NEW_CELL(Sc, _X_); set_type(_X_, T_COMPLEX); real_part(_X_) = R; imag_part(_X_) = im; _X_;}) ); })

#define real_to_double(Sc, X, Caller)   ({ s7_pointer _x_; _x_ = X; ((type(_x_) == T_REAL) ? real(_x_) : s7_number_to_real_with_caller(sc, _x_, Caller)); })
#define rational_to_double(Sc, X)       ({ s7_pointer _x_; _x_ = X; ((type(_x_) == T_INTEGER) ? (s7_Double)integer(_x_) : fraction(_x_)); })
#define number_to_double(Sc, X, Caller) ({ s7_pointer _x_; _x_ = X; ((type(_x_) == T_REAL) ? real(_x_) : s7_number_to_real_with_caller(sc, _x_, Caller)); })

#else

#define make_integer(Sc, N)             s7_make_integer(Sc, N)
#define make_real(Sc, X)                s7_make_real(Sc, X)
#define remake_real(Sc, R, X)           s7_remake_real(Sc, R, X)
#define make_complex(Sc, R, I)          s7_make_complex(Sc, R, I)
#define real_to_double(Sc, X, Caller)   s7_number_to_real_with_caller(Sc, X)
#define rational_to_double(Sc, X)       s7_number_to_real(Sc, X)
#define number_to_double(Sc, X, Caller) s7_number_to_real_with_caller(Sc, X)
#endif


#if WITH_GMP
  #define big_integer(p)             (p->object.number.big_integer)
  #define big_ratio(p)               (p->object.number.big_ratio)
  #define big_real(p)                (p->object.number.big_real)
  #define big_complex(p)             (p->object.number.big_complex)
#endif

#define S7_LLONG_MAX 9223372036854775807LL
#define S7_LLONG_MIN (-S7_LLONG_MAX - 1LL)

#define S7_LONG_MAX 2147483647LL
#define S7_LONG_MIN (-S7_LONG_MAX - 1LL)

#define S7_SHORT_MAX 32767
#define S7_SHORT_MIN -32768

static s7_Int s7_Int_max = 0, s7_Int_min = 0;

/* 9007199254740991LL is where a truncated double starts to skip integers (expt 2 53) = ca 1e16 
 *   :(ceiling (+ 1e16 1))
 *   10000000000000000
 *   :(> 9007199254740993.0 9007199254740992.0)
 *   #f ; in non-gmp 64-bit doubles
 *
 * but we can't fix this except in the gmp case because:
 *   :(integer-decode-float (+ (expt 2.0 62) 100))
 *   (4503599627370496 10 1)
 *   :(integer-decode-float (+ (expt 2.0 62) 500))
 *   (4503599627370496 10 1)
 *   :(> (+ (expt 2.0 62) 500) (+ (expt 2.0 62) 100))
 *   #f ; non-gmp again
 *
 * i.e. the bits are identical.  We can't even detect when it has happened, so should
 *   we just give an error for any floor (or whatever) of an arg>1e16?  (sin has a similar problem)?
 *   I think in the non-gmp case I'll throw an error in these cases because the results are
 *   bogus:
 *   :(floor (+ (expt 2.0 62) 512))
 *   4611686018427387904
 *   :(floor (+ (expt 2.0 62) 513))
 *   4611686018427388928
 *
 * another case at the edge: (round 9007199254740992.51) -> 9007199254740992
 *
 * This spells trouble for normal arithmetic in this range.  If no gmp,
 *    (- (+ (expt 2.0 62) 512) (+ (expt 2.0 62) 513)) = -1024.0 (should be -1.0)
 *    but we don't currently give an error in this case -- not sure what the right thing is.
 */


static int safe_strlen(const char *str)
{
  /* this is safer than strlen, and slightly faster */
  char *tmp = (char *)str;
  if ((!tmp) || (!(*tmp))) return(0);
  while (*tmp++) {};
  return(tmp - str - 1);
}


static int safe_strlen5(const char *str)
{
  /* safe_strlen but we quit counting if len>5 */
  char *tmp = (char *)str;
  char *end;
  if ((!tmp) || (!(*tmp))) return(0);
  end = (char *)(tmp + 6);
  while ((*tmp++) && (tmp < end)) {};
  return(tmp - str - 1);
}


static char *copy_string_with_length(const char *str, int len)
{
  char *newstr;
  newstr = (char *)malloc((len + 1) * sizeof(char));
  if (len != 0)
    memcpy((void *)newstr, (void *)str, len + 1);
  else newstr[0] = 0;
  return(newstr);
}


static char *copy_string(const char *str)
{
  return(copy_string_with_length(str, safe_strlen(str)));
}


#define strings_are_equal(Str1, Str2) (strcmp(Str1, Str2) == 0)
#define strings_are_equal_with_length(Str1, Str2, Len) (strncmp(Str1, Str2, Len) == 0)

/* newlib code here was slower -- this should only be used for internal strings -- scheme
 *   strings can have embedded nulls.
 */

static int safe_strcmp(const char *s1, const char *s2)
{
  int val;
  if (s1 == NULL)
    {
      if (s2 == NULL)
	return(0);
      return(-1);
    }
  if (s2 == NULL)
    return(1);

  val = strcmp(s1, s2); /* strcmp can return stuff like -97, but we want -1, 0, or 1 */

  if (val <= -1)
    return(-1);
  if (val >= 1)
    return(1);
  return(val);
}


/* forward decls */
static char *number_to_string_base_10(s7_pointer obj, int width, int precision, char float_choice, int *nlen, use_write_t choice);
static bool is_proper_list(s7_scheme *sc, s7_pointer lst);
static bool is_all_x_safe(s7_scheme *sc, s7_pointer p);
static s7_function all_x_eval(s7_scheme *sc, s7_pointer arg);
static void annotate_args(s7_scheme *sc, s7_pointer args);
static void annotate_arg(s7_scheme *sc, s7_pointer arg);
static void mark_embedded_objects(s7_pointer a); /* called by gc, calls c_obj's mark func */
static s7_pointer eval(s7_scheme *sc, opcode_t first_op);
static s7_pointer division_by_zero_error(s7_scheme *sc, s7_pointer caller, s7_pointer arg);
static s7_pointer file_error(s7_scheme *sc, const char *caller, const char *descr, const char *name);
static void s7_warn(s7_scheme *sc, int len, const char *ctrl, ...);
static s7_pointer safe_reverse_in_place(s7_scheme *sc, s7_pointer list);
static s7_pointer cons_unchecked(s7_scheme *sc, s7_pointer a, s7_pointer b);
static s7_pointer permanent_cons(s7_pointer a, s7_pointer b, unsigned int type);
static void free_object(s7_pointer a);
static s7_pointer make_atom(s7_scheme *sc, char *q, int radix, bool want_symbol, bool with_error);
static s7_pointer eval_error(s7_scheme *sc, const char *errmsg, s7_pointer obj);
static s7_pointer apply_error(s7_scheme *sc, s7_pointer obj, s7_pointer args);
static int remember_file_name(s7_scheme *sc, const char *file);
static const char *type_name(s7_scheme *sc, s7_pointer arg, int article);
static s7_pointer make_string_uncopied_with_length(s7_scheme *sc, char *str, int len);
static s7_pointer make_string_wrapper(s7_scheme *sc, const char *str);
static s7_pointer make_permanent_string_with_length_and_hash(const char *str, unsigned int len, unsigned int hash);
static s7_pointer splice_in_values(s7_scheme *sc, s7_pointer args);
static void pop_input_port(s7_scheme *sc);
static char *object_to_truncated_string(s7_scheme *sc, s7_pointer p, int len);
static token_t token(s7_scheme *sc);
static s7_pointer implicit_index(s7_scheme *sc, s7_pointer obj, s7_pointer indices);
static bool s7_is_morally_equal(s7_scheme *sc, s7_pointer x, s7_pointer y, shared_info *ci);
static void remove_from_symbol_table(s7_scheme *sc, s7_pointer sym);
static s7_pointer find_symbol_unchecked(s7_scheme *sc, s7_pointer symbol);
static s7_pointer unbound_variable(s7_scheme *sc, s7_pointer sym);
static bool body_is_safe(s7_scheme *sc, s7_pointer func, s7_pointer args, s7_pointer body, bool at_end, bool *bad_set);

#if WITH_GCC
#define find_symbol_checked(Sc, Sym) ({s7_pointer _x_; _x_ = find_symbol_unchecked(Sc, Sym); ((_x_) ? _x_ : unbound_variable(Sc, Sym));})
#else
#define find_symbol_checked(Sc, Sym) find_symbol_unchecked(Sc, Sym)
#endif

static s7_pointer find_method(s7_scheme *sc, s7_pointer env, s7_pointer symbol);
static s7_pointer find_let(s7_scheme *sc, s7_pointer obj);
static s7_pointer hash_table_iterate(s7_scheme *sc, s7_pointer iterator);
static s7_pointer g_make_hash_table_iterator(s7_scheme *sc, s7_pointer args);
static bool call_begin_hook(s7_scheme *sc);
static s7_pointer default_vector_setter(s7_scheme *sc, s7_pointer vec, s7_Int loc, s7_pointer val);
static s7_pointer default_vector_getter(s7_scheme *sc, s7_pointer vec, s7_Int loc);

static s7_pointer simple_wrong_type_arg_error_prepackaged(s7_scheme *sc, s7_pointer caller, s7_pointer arg, s7_pointer typnam, s7_pointer descr);
static s7_pointer wrong_type_arg_error_prepackaged(s7_scheme *sc, s7_pointer caller, s7_pointer arg_n, s7_pointer arg, s7_pointer typnam, s7_pointer descr);
static s7_pointer out_of_range_error_prepackaged(s7_scheme *sc, s7_pointer caller, s7_pointer arg_n, s7_pointer arg, s7_pointer descr);
static s7_pointer simple_out_of_range_error_prepackaged(s7_scheme *sc, s7_pointer caller, s7_pointer arg, s7_pointer descr);

/* putting off the type description until s7_error via the sc->GC_NIL marker below makes it possible
 *    for gcc to speed up the functions that call these as tail-calls.  1-2% overall speedup!
 */
#define simple_wrong_type_argument(Sc, Caller, Arg, Desired_Type) \
  simple_wrong_type_arg_error_prepackaged(Sc, symbol_name_cell(Caller), Arg, sc->GC_NIL, prepackaged_type_names[Desired_Type])

#define simple_wrong_type_argument_with_type(Sc, Caller, Arg, Type) \
  simple_wrong_type_arg_error_prepackaged(Sc, symbol_name_cell(Caller), Arg, sc->GC_NIL, Type)

#define wrong_type_argument(Sc, Caller, Arg_Num, Arg, Desired_Type)	\
  wrong_type_arg_error_prepackaged(Sc, symbol_name_cell(Caller), Arg_Num, Arg, sc->GC_NIL, prepackaged_type_names[Desired_Type])

#define wrong_type_argument_with_type(Sc, Caller, Arg_Num, Arg, Type)	\
  wrong_type_arg_error_prepackaged(Sc, symbol_name_cell(Caller), Arg_Num, Arg, sc->GC_NIL, Type)

#define wrong_type_argument_n(Sc, Caller, Num, Arg, Desired_Type)	\
  wrong_type_arg_error_prepackaged(Sc, symbol_name_cell(Caller), make_integer(Sc, Num), Arg, sc->GC_NIL, prepackaged_type_names[Desired_Type])

#define wrong_type_argument_n_with_type(Sc, Caller, Num, Arg, Type)	\
  wrong_type_arg_error_prepackaged(Sc, symbol_name_cell(Caller), make_integer(Sc, Num), Arg, sc->GC_NIL, Type)

#define simple_out_of_range(Sc, Caller, Arg, Description) \
  simple_out_of_range_error_prepackaged(Sc, symbol_name_cell(Caller), Arg, Description)

#define out_of_range(Sc, Caller, Arg_Num, Arg, Description)		\
  out_of_range_error_prepackaged(Sc, symbol_name_cell(Caller), Arg_Num, Arg, Description)


static s7_pointer CAR_A_LIST, CDR_A_LIST;
static s7_pointer CAAR_A_LIST, CADR_A_LIST, CDAR_A_LIST, CDDR_A_LIST;
static s7_pointer CAAAR_A_LIST, CAADR_A_LIST, CADAR_A_LIST, CADDR_A_LIST, CDAAR_A_LIST, CDADR_A_LIST, CDDAR_A_LIST, CDDDR_A_LIST;
static s7_pointer A_LIST, AN_ASSOCIATION_LIST, AN_OUTPUT_PORT, AN_INPUT_PORT, AN_OPEN_PORT, A_NORMAL_REAL, A_RATIONAL, A_FUNCTION, A_BOOLEAN;
static s7_pointer A_NUMBER, AN_ENVIRONMENT, A_PROCEDURE, A_PROPER_LIST, A_THUNK, SOMETHING_APPLICABLE, A_SYMBOL, A_NON_NEGATIVE_INTEGER;
static s7_pointer CONSTANT_ARG_ERROR, BAD_BINDING, A_FORMAT_PORT, AN_UNSIGNED_BYTE, A_BINDING, A_NON_CONSTANT_SYMBOL, AN_EQ_FUNC, A_SEQUENCE;
static s7_pointer ITS_TOO_LARGE, ITS_NEGATIVE, RESULT_IS_TOO_LARGE, ITS_NAN, ITS_INFINITE, TOO_MANY_INDICES, A_VALID_RADIX;
static s7_pointer AN_INPUT_STRING_PORT, AN_INPUT_FILE_PORT, AN_OUTPUT_STRING_PORT, AN_OUTPUT_FILE_PORT;
#if (!HAVE_COMPLEX_NUMBERS)
static s7_pointer NO_COMPLEX_NUMBERS;
#endif


/* --------------------------------------------------------------------------------
 * profiling junk
 */
#define WITH_COUNTS 0
#if WITH_COUNTS
#if 0
#if 1
#define NUM_COUNTS 65536
static int counts[NUM_COUNTS];
static void clear_counts(void) {int i; for (i = 0; i < NUM_COUNTS; i++) counts[i] = 0;}
void tick(int this) {counts[this]++;}
static void report_counts(s7_scheme *sc)
{
  int i, mx, mxi, total = 0;
  bool happy = true;

  for (i = 0; i < NUM_COUNTS; i++)
    total += counts[i];

  fprintf(stderr, "total: %d\n", total);
  while (happy)
    {
      mx = 0;
      for (i = 0; i < NUM_COUNTS; i++)
	{
	  if (counts[i] > mx)
	    {
	      mx = counts[i];
	      mxi = i;
	    }
	}
      if (mx > 0)
	{
	  /* if (mx > total/100) */
	    fprintf(stderr, "%s: %d (%f)\n", opt_names[mxi], mx, 100.0*mx/(float)total);
	  counts[mxi] = 0;
	}
      else happy = false;
    }
}
#else
#if 1
#define NUM_COUNTS 1000
static int counts[NUM_COUNTS][NUM_COUNTS];
static void clear_counts(void) {int i, j; for (i = 0; i < NUM_COUNTS; i++) for (j = 0; j < NUM_COUNTS; j++) counts[i][j] = 0;}
static void tick(int op1, int op2) {counts[op1][op2]++;}
static void report_counts(s7_scheme *sc)
{
  int i, j, mx, mxi, mxj;
  bool happy = true;
  fprintf(stderr, "\n");
  while (happy)
    {
      mx = 0;
      for (i = 0; i < NUM_COUNTS; i++)
	for (j = 0; j < NUM_COUNTS; j++)
	  {
	    if (counts[i][j] > mx)
	      {
		mx = counts[i][j];
		mxi = i;
		mxj = j;
	      }
	  }
      if (mx > 0)
	{
	  fprintf(stderr, "%d: %s %s\n", mx, real_op_names[mxi], real_op_names[mxj]);
	  counts[mxi][mxj] = 0;
	}
      else happy = false;
    }
}
#else
#define NUM_COUNTS 1000
static int counts[NUM_COUNTS];
static void clear_counts(void) {int i; for (i = 0; i < NUM_COUNTS; i++) counts[i] = 0;}
static void tick(int op) {counts[op]++;}
static void report_counts(s7_scheme *sc)
{
  int k, i, mx;
  bool happy = true;
  fprintf(stderr, "\n");
  while (happy)
    {
      mx = 0;
      for (k = 0; k < OP_MAX_DEFINED; k++)
	{
	  if (counts[k] > mx)
	    {
	      mx = counts[k];
	      i = k;
	    }
	}
      if (mx > 0)
	{
	  fprintf(stderr, "%s: %d\n", opt_names[i], counts[i]);
	  counts[i] = 0;
	}
      else happy = false;
    }
  /* fprintf(stderr, "\n"); */
}
#endif
#endif

static void init_hashes(s7_scheme *sc) {}

#else

void clear_counts(void) {}
static s7_pointer hashes;
void add_expr(s7_scheme *sc, s7_pointer expr);
void add_expr(s7_scheme *sc, s7_pointer expr)
{
  s7_pointer val;
  /* expr = sc->cur_code; */
  val = s7_hash_table_ref(sc, hashes, expr);
  if (val == sc->F)
    {
      if (!is_any_closure(expr))
	s7_hash_table_set(sc, hashes, expr, s7_make_integer(sc, 1));
    }
  else
    {
      s7_hash_table_set(sc, hashes, expr, s7_make_integer(sc, 1 + s7_integer(val)));
    }
}
static void init_hashes(s7_scheme *sc)
{
  hashes = s7_make_hash_table(sc, 65536);
  s7_gc_protect(sc, hashes);
}

typedef struct {
  s7_Int count;
  s7_pointer expr;
} datum;

static datum *new_datum(s7_Int ctr, s7_pointer e)
{
  datum *d;
  d = calloc(1, sizeof(datum));
  d->count = ctr;
  d->expr = e;
  return(d);
}
static int sort_data(const void *v1, const void *v2)
{
  datum *d1 = *(datum **)v1;
  datum *d2 = *(datum **)v2;
  if (d1->count > d2->count)
    return(-1);
  return(1);
}
static void report_counts(s7_scheme *sc)
{
  int len, i, loc = 0, entries, top_hist = 0;
  s7_pointer *elements;
  datum **data;
  int *lens, *hist;
  #define HIST_SIZE 100

  len = hash_table_length(hashes);
  elements = hash_table_elements(hashes);
  entries = hash_table_entries(hashes);
  data = (datum **)calloc(entries, sizeof(datum *));
  hist = (int *)calloc(HIST_SIZE, sizeof(int));

  lens = (int *)calloc(len, sizeof(int));
  for (i = 0; i < len; i++)
    {
      int k;
      s7_pointer x;
      for (k = 0, x = elements[i]; is_pair(x); x = cdr(x), k++)
	data[loc++] = new_datum(s7_integer(cdar(x)), caar(x));
      lens[i] = k;
    }

  {
    int n, ctr = 0, total = 0, ents;
    for (n = 0; n < len; n++)
      if (is_pair(elements[n])) 
	{
	  ctr++;
	  ents = s7_list_length(sc, elements[n]);
	  total += ents;
	  if (ents >= HIST_SIZE) ents = HIST_SIZE - 1;
	  hist[ents]++;
	  if (ents > top_hist) top_hist = ents;
	}
    fprintf(stderr, "\n%d locs of %d are in use for %d items\n", ctr, len, total);
    
    for (n = 0; n < 4; n++)
      {
	int mx, mxj = 0, j, a;
	s7_pointer p;
	mx = 0;
	
	for (j = 0; j < len; j++)
	  {
	    if (lens[j] > mx)
	      {
		mx = lens[j];
		mxj = j;
	      }
	  }
	if (mx > 0)
	  {
	    fprintf(stderr, "%d: %d\n", mxj, mx);
	    lens[mxj] = 0;
	    for (a = 0, p = elements[mxj]; (a < 10) && (is_pair(p)); p = cdr(p), a++)
	      fprintf(stderr, "    %s\n", DISPLAY_80(caar(p)));
	    if (is_pair(p)) fprintf(stderr, "    ...\n");
	  }
	else break;
      }

    for (n = 1; n < top_hist; n++)
      {
	fprintf(stderr, "%d ", hist[n]);
	if ((n % 20) == 0) fprintf(stderr, "\n");
      }
  }
  fprintf(stderr, "\n\n");

  qsort((void *)data, loc, sizeof(datum *), sort_data);
  if (loc > 400) loc = 400;
  for (i = 0; i < loc; i++)
    if (data[i]->count > 0)
      fprintf(stderr, "%lld: %s\n", data[i]->count, DISPLAY_80(data[i]->expr));

  free(data);
  free(hist);
  free(lens);
}
void add_code(s7_scheme *sc);
void add_code(s7_scheme *sc)
{
  add_expr(sc, sc->code);
}
/* use xen.h and s7 here */
#endif
#endif
/* -------------------------------------------------------------------------------- */


static int position_of(s7_pointer p, s7_pointer args)
{
  int i;
  for (i = 1; p != args; i++, args = cdr(args));
  return(i);
}

s7_pointer s7_method(s7_scheme *sc, s7_pointer obj, s7_pointer method)
{
  if (has_methods(obj))
    return(find_method(sc, find_let(sc, obj), method));
  return(sc->UNDEFINED);
}

static s7_pointer check_values(s7_scheme *sc, s7_pointer obj, s7_pointer args)
{
  if (has_methods(obj))
    {
      s7_pointer func;
      func = find_method(sc, find_let(sc, obj), sc->VALUES);
      if (func != sc->UNDEFINED)
	return(s7_apply_function(sc, func, args));
    }
  return(sc->GC_NIL);
}

#define check_method(Sc, Obj, Method, Args)     \
  {                                             \
    s7_pointer func; 	                        \
    if ((has_methods(Obj)) && ((func = find_method(Sc, find_let(Sc, Obj), Method)) != Sc->UNDEFINED)) \
      return(s7_apply_function(Sc, func, Args)); \
  }

#define check_two_methods(Sc, Obj, Method1, Method2, Args)	                            \
  if (has_methods(Obj))                                                                     \
    {                                                                                       \
      s7_pointer func;							                    \
      func = find_method(Sc, find_let(Sc, Obj), Method1);                           \
      if ((func == Sc->UNDEFINED) && (Method2)) func = find_method(Sc, find_let(Sc, Obj), Method2); \
      if (func != Sc->UNDEFINED) return(s7_apply_function(Sc, func, Args)); \
    }

/* unfortunately, in the simplest cases, where a function (like number?) accepts any argument,
 *   this costs about a factor of 1.5 in speed (we're doing the normal check like s7_is_number,
 *   but then have to check has_methods before returning #f).  We can't use the old form until
 *   openlet is seen because the prior code might use #_number? which gets the value
 *   before the switch.  These simple functions normally do not dominate timing info, so I'll 
 *   go ahead. It's mostly boilerplate:
 */

#define check_boolean_method(Sc, Checker, Method, Args)      \
  {                                                          \
    s7_pointer p;                                            \
    p = car(Args);                                           \
    if (Checker(p)) return(Sc->T);                           \
    check_method(Sc, p, Method, Args);                       \
    return(Sc->F);                                           \
  }

#define check_boolean_not_method(Sc, Checker, Method, Args)  \
  {                                                          \
    s7_pointer p, func;					     \
    p = find_symbol_checked(Sc, cadar(Args));                \
    if (Checker(p)) return(Sc->F);                           \
    if ((has_methods(p)) && ((func = find_method(Sc, find_let(Sc, p), Method)) != Sc->UNDEFINED) && \
	(s7_apply_function(Sc, func, list_1(Sc, p)) != Sc->F))		\
      return(Sc->F);                                         \
    return(Sc->T);                                           \
  }

/* the old form would be: return(make_boolean(Sc, Checker(car(Args))))
 *
 * has_methods can only be true of c_object, env, closure, so perhaps
 *   an arr[type] opt here?
 */



/* -------------------------------- constants -------------------------------- */

s7_pointer s7_f(s7_scheme *sc) 
{
  return(sc->F);
}


s7_pointer s7_t(s7_scheme *sc) 
{
  return(sc->T);
}


s7_pointer s7_nil(s7_scheme *sc) 
{
  return(sc->NIL);
}


bool s7_is_null(s7_scheme *sc, s7_pointer p) 
{
  return(is_null(p));
}


s7_pointer s7_undefined(s7_scheme *sc) 
{
  return(sc->UNDEFINED);
}


s7_pointer s7_unspecified(s7_scheme *sc) 
{
  return(sc->UNSPECIFIED);
}


bool s7_is_unspecified(s7_scheme *sc, s7_pointer val)
{
  return(is_unspecified(val));
}


s7_pointer s7_eof_object(s7_scheme *sc)          /* returns #<eof> -- not equivalent to "eof-object?" */
{
  return(sc->EOF_OBJECT);
}


static s7_pointer g_not(s7_scheme *sc, s7_pointer args)
{
  #define H_not "(not obj) returns #t if obj is #f, otherwise #t: (not ()) -> #f"
  return(make_boolean(sc, is_false(sc, car(args))));
}


bool s7_boolean(s7_scheme *sc, s7_pointer x)
{
  return(x != sc->F);
}


bool s7_is_boolean(s7_pointer x)
{
  return(type(x) == T_BOOLEAN);
}


s7_pointer s7_make_boolean(s7_scheme *sc, bool x)
{
  return(make_boolean(sc, x));
}


static s7_pointer g_is_boolean(s7_scheme *sc, s7_pointer args)
{
  #define H_is_boolean "(boolean? obj) returns #t if obj is #f or #t: (boolean? ()) -> #f"
  check_boolean_method(sc, s7_is_boolean, sc->IS_BOOLEAN, args);
}


bool s7_is_constant(s7_pointer p) 
{ 
  /* this means "always evaluates to the same thing", sort of
   *   not "evaluates to itself":
   *   (let ((x 'x)) (and (not (constant? x)) (equal? x (eval x))))
   *   (and (constant? (list + 1)) (not (equal? (list + 1) (eval (list + 1)))))
   */
  return((type(p) != T_SYMBOL) ||
	 ((typeflag(p) & (T_KEYWORD | T_IMMUTABLE)) != 0));
}


static s7_pointer g_is_constant(s7_scheme *sc, s7_pointer args)
{
  #define H_is_constant "(constant? obj) returns #t if obj is a constant (unsettable): (constant? pi) -> #t"
  check_boolean_method(sc, s7_is_constant, sc->IS_CONSTANT, args);
}



/* -------------------------------- GC -------------------------------- */

/* instead of saving the last released location, why not have a free list?
 *   (an int array growable, with a current top and size or whatever)
 */

#define is_gc_nil(p) ((p) == sc->GC_NIL)

int s7_gc_protect(s7_scheme *sc, s7_pointer x)
{
  unsigned int i, loc, size, new_size;

  loc = sc->protected_objects_loc++;
  size = sc->protected_objects_size;

  if (sc->protected_objects_loc >= size)
    sc->protected_objects_loc = 0;

  if (is_gc_nil(vector_element(sc->protected_objects, loc)))
    {
      vector_element(sc->protected_objects, loc) = x;
      return(loc);
    }
  
  for (i = 0; i < size; i++)
    {
      if (is_gc_nil(vector_element(sc->protected_objects, i)))
	{
	  vector_element(sc->protected_objects, i) = x;
	  return(i);
	}
    }
  
  new_size = 2 * size;
  vector_elements(sc->protected_objects) = (s7_pointer *)realloc(vector_elements(sc->protected_objects), new_size * sizeof(s7_pointer));
  for (i = size; i < new_size; i++)
    vector_element(sc->protected_objects, i) = sc->GC_NIL;
  vector_length(sc->protected_objects) = new_size;
  sc->protected_objects_size = new_size;
  sc->protected_objects_loc = size + 1;

  vector_element(sc->protected_objects, size) = x;
  return(size);
}


void s7_gc_unprotect(s7_scheme *sc, s7_pointer x)
{
  unsigned int i;

  for (i = 0; i < sc->protected_objects_size; i++)
    if (vector_element(sc->protected_objects, i) == x)
      {
	vector_element(sc->protected_objects, i) = sc->GC_NIL;
	sc->protected_objects_loc = i;
	return;
      }
}


void s7_gc_unprotect_at(s7_scheme *sc, int loc)
{
  if ((loc >= 0) &&
      (loc < (int)sc->protected_objects_size))
    {
#if DEBUGGING
      if (vector_element(sc->protected_objects, loc) == sc->GC_NIL)
	fprintf(stderr, "gc unprotect at %d, but it's already unprotected?\n", loc);
#endif
      vector_element(sc->protected_objects, loc) = sc->GC_NIL;
      sc->protected_objects_loc = loc;
    }
}


s7_pointer s7_gc_protected_at(s7_scheme *sc, int loc)
{
  s7_pointer obj;

  obj = sc->UNSPECIFIED;
  if ((loc >= 0) &&
      (loc < (int)sc->protected_objects_size))
    obj = vector_element(sc->protected_objects, loc);

  if (obj == sc->GC_NIL)
    return(sc->UNSPECIFIED);

  return(obj);
}

#define gc_protected_at(Sc, Loc) vector_element(Sc->protected_objects, Loc)
/* #define gc_reprotect(Sc, Loc, Val) vector_element(Sc->protected_objects, Loc) = Val */


static void (*mark_function[NUM_TYPES])(s7_pointer p);

#define S7_MARK(Obj) do {s7_pointer _p_; _p_ = Obj; (*mark_function[unchecked_type(_p_)])(_p_);} while (0)

static void mark_symbol(s7_pointer p)
{
  if (is_gensym(p))
    set_mark(p);
  /* don't set the mark bit of a normal symbol!  It wrecks the check against SYNTACTIC_TYPE,
   *   slowing everything down by a large amount.
   */
}

static void mark_noop(s7_pointer p)
{
}

/* ports can be alloc'd and freed at a frightening pace, so I think I'll make a special free_heap for them.
 */

static port_t *alloc_port(s7_scheme *sc)
{
  if (sc->port_heap)
    {
      port_t *p;
      p = sc->port_heap;
      sc->port_heap = (port_t *)(p->next);
      return(p);
    }
  return((port_t *)calloc(1, sizeof(port_t)));
}


static void free_port(s7_scheme *sc, port_t *p)
{
  p->next = (void *)(sc->port_heap);
  sc->port_heap = p;
}


static void sweep(s7_scheme *sc)
{
  int i, j;
  if (sc->strings_loc > 0)
    {
      /* unrolling this loop is not an improvement */
      for (i = 0, j = 0; i < sc->strings_loc; i++)
	{
	  s7_pointer s1;
	  s1 = sc->strings[i];
	  if (is_free_and_clear(s1))
	    {
	      if (string_needs_free(s1))
		free(string_value(s1));
	    }
	  else sc->strings[j++] = s1;
	}
      sc->strings_loc = j;
    }

  if (sc->gensyms_loc > 0)
    {
      for (i = 0, j = 0; i < sc->gensyms_loc; i++)
	{
	  s7_pointer s1;
	  s1 = sc->gensyms[i];
	  if (is_free_and_clear(s1))
	    {
	      remove_from_symbol_table(sc, s1); /* this uses symbol_name_cell data */
	      free(symbol_name(s1));
	      if ((is_documented(s1)) &&
		  (symbol_help(s1)))
		{
		  free(symbol_help(s1));
		  symbol_help(s1) = NULL;
		}
	      free(symbol_name_cell(s1));
	    }
	  else sc->gensyms[j++] = s1;
	}
      sc->gensyms_loc = j;
      if (j == 0) mark_function[T_SYMBOL] = mark_noop;
    }

  if (sc->c_objects_loc > 0)
    {
      for (i = 0, j = 0; i < sc->c_objects_loc; i++)
	{
	  if (is_free_and_clear(sc->c_objects[i]))
	    free_object(sc->c_objects[i]);
	  else sc->c_objects[j++] = sc->c_objects[i];
	}
      sc->c_objects_loc = j;
    }

  if (sc->vectors_loc > 0)
    {
      for (i = 0, j = 0; i < sc->vectors_loc; i++)
	{
	  if (is_free_and_clear(sc->vectors[i]))
	    {
	      s7_pointer a;
	      a = sc->vectors[i];

	      /* a multidimensional empty vector can have dimension info */
	      if (vector_dimension_info(a))
		{
		  if (vector_dimensions_allocated(a))
		    {
		      free(vector_dimensions(a));
		      free(vector_offsets(a));
		    }
		  if (vector_elements_allocated(a))
		    free(vector_elements(a));      /* I think this will work for any vector (int/float too) */
		  if (vector_dimension_info(a) != sc->wrap_only)
		    free(vector_dimension_info(a));
		}
	      else
		{
		  if (vector_length(a) != 0)
		    free(vector_elements(a));
		}
	    }
	  else sc->vectors[j++] = sc->vectors[i];
	  /* here (in the else branch) if a vector constant in a global function has been removed from the heap,
	   *   heap_location(v) == NOT_IN_HEAP, and we'll never see it freed, so if there were a lot of these, they might
	   *   glom up this loop.  Surely not a big deal!?
	   */
	}
      sc->vectors_loc = j;
    }

  if (sc->hash_tables_loc > 0)
    {
      for (i = 0, j = 0; i < sc->hash_tables_loc; i++)
	{
	  if (is_free_and_clear(sc->hash_tables[i]))
	    {
	      if (hash_table_length(sc->hash_tables[i]) > 0)
		free(hash_table_elements(sc->hash_tables[i]));
	    }
	  else sc->hash_tables[j++] = sc->hash_tables[i];
	}
      sc->hash_tables_loc = j;
    }

  if (sc->input_ports_loc > 0)
    {
      for (i = 0, j = 0; i < sc->input_ports_loc; i++)
	{
	  if (is_free_and_clear(sc->input_ports[i]))
	    {
	      s7_pointer a;
	      a = sc->input_ports[i];
	      if (port_needs_free(a))
		{
		  if (port_data(a))
		    {
		      free(port_data(a));
		      port_data(a) = NULL;
		      port_data_size(a) = 0;
		    }
		  port_needs_free(a) = false;
		}
	      
	      if (port_filename(a))
		{
		  free(port_filename(a));
		  port_filename(a) = NULL;
		}
	      free_port(sc, port_port(a));
	    }
	  else sc->input_ports[j++] = sc->input_ports[i];
	}
      sc->input_ports_loc = j;
    }

  if (sc->output_ports_loc > 0)
    {
      for (i = 0, j = 0; i < sc->output_ports_loc; i++)
	{
	  if (is_free_and_clear(sc->output_ports[i]))
	    {
	      s7_close_output_port(sc, sc->output_ports[i]); /* needed for free filename, etc */
	      free_port(sc, port_port(sc->output_ports[i]));
	    }
	  else sc->output_ports[j++] = sc->output_ports[i];
	}
      sc->output_ports_loc = j;
    }

  if (sc->continuations_loc > 0)
    {
      for (i = 0, j = 0; i < sc->continuations_loc; i++)
	{
	  if (is_free_and_clear(sc->continuations[i]))
	    {
	      s7_pointer c;
	      c = sc->continuations[i];
	      if (continuation_op_stack(c))
		{
		  free(continuation_op_stack(c));
		  continuation_op_stack(c) = NULL;
		}
	      free(continuation_data(c));
	    }
	  else sc->continuations[j++] = sc->continuations[i];
	}
      sc->continuations_loc = j;
    }

#if WITH_GMP
  if (sc->bigints_loc > 0)
    {
      for (i = 0, j = 0; i < sc->bigints_loc; i++)
	{
	  s7_pointer s1;
	  s1 = sc->bigints[i];
	  if (is_free_and_clear(s1))
	    {
	      mpz_clear(big_integer(s1));
	    }
	  else sc->bigints[j++] = s1;
	}
      sc->bigints_loc = j;
    }

  if (sc->bigratios_loc > 0)
    {
      for (i = 0, j = 0; i < sc->bigratios_loc; i++)
	{
	  s7_pointer s1;
	  s1 = sc->bigratios[i];
	  if (is_free_and_clear(s1))
	    {
	      mpq_clear(big_ratio(s1));
	    }
	  else sc->bigratios[j++] = s1;
	}
      sc->bigratios_loc = j;
    }

  if (sc->bigreals_loc > 0)
    {
      for (i = 0, j = 0; i < sc->bigreals_loc; i++)
	{
	  s7_pointer s1;
	  s1 = sc->bigreals[i];
	  if (is_free_and_clear(s1))
	    {
	      mpfr_clear(big_real(s1));
	    }
	  else sc->bigreals[j++] = s1;
	}
      sc->bigreals_loc = j;
    }

  if (sc->bignumbers_loc > 0)
    {
      for (i = 0, j = 0; i < sc->bignumbers_loc; i++)
	{
	  s7_pointer s1;
	  s1 = sc->bignumbers[i];
	  if (is_free_and_clear(s1))
	    {
	      mpc_clear(big_complex(s1));
	    }
	  else sc->bignumbers[j++] = s1;
	}
      sc->bignumbers_loc = j;
    }
#endif
}


static void add_string(s7_scheme *sc, s7_pointer p)
{
  if (sc->strings_loc == sc->strings_size)
    {
      sc->strings_size *= 2;
      sc->strings = (s7_pointer *)realloc(sc->strings, sc->strings_size * sizeof(s7_pointer));
    }
  sc->strings[sc->strings_loc++] = p;
}


static void add_gensym(s7_scheme *sc, s7_pointer p)
{
  if (sc->gensyms_loc == sc->gensyms_size)
    {
      sc->gensyms_size *= 2;
      sc->gensyms = (s7_pointer *)realloc(sc->gensyms, sc->gensyms_size * sizeof(s7_pointer));
    }
  sc->gensyms[sc->gensyms_loc++] = p;
  mark_function[T_SYMBOL] = mark_symbol;
}


static void add_c_object(s7_scheme *sc, s7_pointer p)
{
  if (sc->c_objects_loc == sc->c_objects_size)
    {
      sc->c_objects_size *= 2;
      sc->c_objects = (s7_pointer *)realloc(sc->c_objects, sc->c_objects_size * sizeof(s7_pointer));
    }
  sc->c_objects[sc->c_objects_loc++] = p;
}


static void add_hash_table(s7_scheme *sc, s7_pointer p)
{
  if (sc->hash_tables_loc == sc->hash_tables_size)
    {
      sc->hash_tables_size *= 2;
      sc->hash_tables = (s7_pointer *)realloc(sc->hash_tables, sc->hash_tables_size * sizeof(s7_pointer));
    }
  sc->hash_tables[sc->hash_tables_loc++] = p;
}


static void add_vector(s7_scheme *sc, s7_pointer p)
{
  if (sc->vectors_loc == sc->vectors_size)
    {
      sc->vectors_size *= 2;
      sc->vectors = (s7_pointer *)realloc(sc->vectors, sc->vectors_size * sizeof(s7_pointer));
    }
  sc->vectors[sc->vectors_loc++] = p;
}


static void add_input_port(s7_scheme *sc, s7_pointer p)
{
  if (sc->input_ports_loc == sc->input_ports_size)
    {
      sc->input_ports_size *= 2;
      sc->input_ports = (s7_pointer *)realloc(sc->input_ports, sc->input_ports_size * sizeof(s7_pointer));
    }
  sc->input_ports[sc->input_ports_loc++] = p;
}


static void add_output_port(s7_scheme *sc, s7_pointer p)
{
  if (sc->output_ports_loc == sc->output_ports_size)
    {
      sc->output_ports_size *= 2;
      sc->output_ports = (s7_pointer *)realloc(sc->output_ports, sc->output_ports_size * sizeof(s7_pointer));
    }
  sc->output_ports[sc->output_ports_loc++] = p;
}


static void add_continuation(s7_scheme *sc, s7_pointer p)
{
  if (sc->continuations_loc == sc->continuations_size)
    {
      sc->continuations_size *= 2;
      sc->continuations = (s7_pointer *)realloc(sc->continuations, sc->continuations_size * sizeof(s7_pointer));
    }
  sc->continuations[sc->continuations_loc++] = p;
}

#if WITH_GMP
static void add_bigint(s7_scheme *sc, s7_pointer p)
{
  if (sc->bigints_loc == sc->bigints_size)
    {
      sc->bigints_size *= 2;
      sc->bigints = (s7_pointer *)realloc(sc->bigints, sc->bigints_size * sizeof(s7_pointer));
    }
  sc->bigints[sc->bigints_loc++] = p;
}


static void add_bigratio(s7_scheme *sc, s7_pointer p)
{
  if (sc->bigratios_loc == sc->bigratios_size)
    {
      sc->bigratios_size *= 2;
      sc->bigratios = (s7_pointer *)realloc(sc->bigratios, sc->bigratios_size * sizeof(s7_pointer));
    }
  sc->bigratios[sc->bigratios_loc++] = p;
}


static void add_bigreal(s7_scheme *sc, s7_pointer p)
{
  if (sc->bigreals_loc == sc->bigreals_size)
    {
      sc->bigreals_size *= 2;
      sc->bigreals = (s7_pointer *)realloc(sc->bigreals, sc->bigreals_size * sizeof(s7_pointer));
    }
  sc->bigreals[sc->bigreals_loc++] = p;
}


static void add_bignumber(s7_scheme *sc, s7_pointer p)
{
  if (sc->bignumbers_loc == sc->bignumbers_size)
    {
      sc->bignumbers_size *= 2;
      sc->bignumbers = (s7_pointer *)realloc(sc->bignumbers, sc->bignumbers_size * sizeof(s7_pointer));
    }
  sc->bignumbers[sc->bignumbers_loc++] = p;
}
#endif


#define INIT_GC_CACHE_SIZE 64
static void init_gc_caches(s7_scheme *sc)
{
  sc->strings_size = INIT_GC_CACHE_SIZE * 16;
  sc->strings_loc = 0;
  sc->strings = (s7_pointer *)malloc(sc->strings_size * sizeof(s7_pointer));
  sc->gensyms_size = INIT_GC_CACHE_SIZE;
  sc->gensyms_loc = 0;
  sc->gensyms = (s7_pointer *)malloc(sc->gensyms_size * sizeof(s7_pointer));
  sc->vectors_size = INIT_GC_CACHE_SIZE * 8;
  sc->vectors_loc = 0;
  sc->vectors = (s7_pointer *)malloc(sc->vectors_size * sizeof(s7_pointer));
  sc->hash_tables_size = INIT_GC_CACHE_SIZE;
  sc->hash_tables_loc = 0;
  sc->hash_tables = (s7_pointer *)malloc(sc->hash_tables_size * sizeof(s7_pointer));
  sc->input_ports_size = INIT_GC_CACHE_SIZE;
  sc->input_ports_loc = 0;
  sc->input_ports = (s7_pointer *)malloc(sc->input_ports_size * sizeof(s7_pointer));
  sc->output_ports_size = INIT_GC_CACHE_SIZE;
  sc->output_ports_loc = 0;
  sc->output_ports = (s7_pointer *)malloc(sc->output_ports_size * sizeof(s7_pointer));
  sc->continuations_size = INIT_GC_CACHE_SIZE;
  sc->continuations_loc = 0;
  sc->continuations = (s7_pointer *)malloc(sc->continuations_size * sizeof(s7_pointer));
  sc->c_objects_size = INIT_GC_CACHE_SIZE;
  sc->c_objects_loc = 0;
  sc->c_objects = (s7_pointer *)malloc(sc->c_objects_size * sizeof(s7_pointer));
#if WITH_GMP
  sc->bigints_size = INIT_GC_CACHE_SIZE;
  sc->bigints_loc = 0;
  sc->bigints = (s7_pointer *)malloc(sc->bigints_size * sizeof(s7_pointer));
  sc->bigratios_size = INIT_GC_CACHE_SIZE;
  sc->bigratios_loc = 0;
  sc->bigratios = (s7_pointer *)malloc(sc->bigratios_size * sizeof(s7_pointer));
  sc->bigreals_size = INIT_GC_CACHE_SIZE;
  sc->bigreals_loc = 0;
  sc->bigreals = (s7_pointer *)malloc(sc->bigreals_size * sizeof(s7_pointer));
  sc->bignumbers_size = INIT_GC_CACHE_SIZE;
  sc->bignumbers_loc = 0;
  sc->bignumbers = (s7_pointer *)malloc(sc->bignumbers_size * sizeof(s7_pointer));
#endif
}

static void mark_vector_1(s7_pointer p, s7_Int top)
{
  s7_pointer *tp, *tend, *tend4;

  set_mark(p);

  tp = (s7_pointer *)(vector_elements(p));
  if (!tp) return;
  tend = (s7_pointer *)(tp + top);

  tend4 = (s7_pointer *)(tend - 4);
  while (tp <= tend4)
    {
      S7_MARK(*tp++);
      S7_MARK(*tp++);
      S7_MARK(*tp++);
      S7_MARK(*tp++);
    }

  while (tp < tend) 
    S7_MARK(*tp++);
}

static void mark_slot(s7_pointer p)
{
  if (!is_marked(p)) 
    {
      set_mark(p);
      S7_MARK(slot_value(p));
      if (slot_has_accessor(p))
	S7_MARK(slot_accessor(p));

      /* should this also mark any pending_value? -- currently we don't initialize it.
       *   perhaps a bit if pending_value is in use checked here.  This procedure is not
       *   called very often, but the pending value set happens a lot.
       *   slot_set_pending_value and slot_transfer_pending_value.
       */
    }
}

static void mark_let(s7_pointer env)
{
  s7_pointer x;
  for (x = env; is_let(x) && (!is_marked(x)); x = outlet(x)) 
    { 
      s7_pointer y;
      for (y = let_slots(x); is_slot(y); y = next_slot(y))
	mark_slot(y);
      set_mark(x);
    }
}


static void just_mark(s7_pointer p)
{
  set_mark(p);
}


static void mark_c_proc(s7_pointer p)
{
  if (!is_marked(p)) /* this is needed since it is remotely possible a function is its own setter(?!?) and otherwise we get an infinite loop */
    {
      set_mark(p);
      S7_MARK(c_function_setter(p));
    }
}

static void mark_c_proc_star(s7_pointer p)
{
  if (!is_marked(p)) 
    {
      set_mark(p);
      S7_MARK(c_function_setter(p));
      if (!c_function_simple_defaults(p))
	{
	  s7_pointer arg;
	  for (arg = c_function_call_args(p); is_pair(arg); arg = cdr(arg))
	    S7_MARK(car(arg));
	}
    }
}

static void mark_pair(s7_pointer p)
{
  if (!is_marked(p)) 
    {
      set_mark(p);
      S7_MARK(car(p));
      /* if the list is huge, the recursion to cdr(p) is problematic when there are strict limits on the stack size
       *  (C is not tail recursive apparently), so I'll try something else... (This form is faster according to callgrind).
       */
      for (p = cdr(p); is_pair(p) && (!is_marked(p)); p = cdr(p)) /* this order much faster */
	{
	  set_mark(p);
	  S7_MARK(car(p));
	}
      if (!is_marked(p)) S7_MARK(p);      /* checked is much faster */
    }
}

static void mark_counter(s7_pointer p)
{
  if (!is_marked(p)) 
    {
      set_mark(p);
      S7_MARK(counter_result(p));
      S7_MARK(counter_list(p));
      S7_MARK(counter_let(p));
      S7_MARK(counter_args(p));
    }
}


static void mark_closure(s7_pointer p)
{
  if (!is_marked(p)) 
    {
      set_mark(p);
      S7_MARK(closure_args(p));
      S7_MARK(closure_body(p));
      mark_let(closure_let(p));
      S7_MARK(closure_setter(p));
    }
}

static void mark_stack(s7_pointer p)
{
}

static void mark_stack_1(s7_pointer p, s7_Int top)
{
  s7_pointer *tp, *tend;

  set_mark(p);

  tp = (s7_pointer *)(vector_elements(p));
  if (!tp) return;
  tend = (s7_pointer *)(tp + top);
  
  while (tp < tend) 
    {
      S7_MARK(*tp++);
      S7_MARK(*tp++);
      S7_MARK(*tp++);
      tp++;
    }
}


static void mark_continuation(s7_pointer p)
{
  if (!is_marked(p)) 
    {
      unsigned int i;
      set_mark(p);
      mark_stack_1(continuation_stack(p), continuation_stack_top(p));
      for (i = 0; i < continuation_op_loc(p); i++)
	S7_MARK(continuation_op_stack(p)[i]);
    }
}

static void mark_vector(s7_pointer p)
{
  if (!is_marked(p)) 
    {
      /* If a subvector (an inner dimension) of a vector is the only remaining reference
       *    to the main vector, we want to make sure the main vector is not GC'd until
       *    the subvector is also GC-able.  The shared_vector field either points to the
       *    parent vector, or it is sc->F, so we need to check for a vector parent if
       *    the current is multidimensional (this will include 1-dim slices).  We need
       *    to keep the parent case separate (i.e. sc->F means the current is the original)
       *    so that we only free once (or remove_from_heap once).
       */
      if ((vector_has_dimensional_info(p)) &&
	  (s7_is_vector(shared_vector(p))))
	mark_vector(shared_vector(p));
      mark_vector_1(p, vector_length(p));
    }
}

static void mark_int_or_float_vector(s7_pointer p)
{
  if (!is_marked(p)) 
    {
      if ((vector_has_dimensional_info(p)) &&
	  (s7_is_vector(shared_vector(p))))
	mark_int_or_float_vector(shared_vector(p));
      set_mark(p);
    }
}

static void mark_c_object(s7_pointer p)
{
  if (!is_marked(p)) 
    {
      set_mark(p);
      mark_embedded_objects(p);
    }
}

static void mark_catch(s7_pointer p)
{
  if (!is_marked(p))
    {
      set_mark(p);
      S7_MARK(catch_tag(p));
      S7_MARK(catch_handler(p));
    }
}

static void mark_dynamic_wind(s7_pointer p)
{
  if (!is_marked(p)) 
    {
      set_mark(p);
      S7_MARK(dynamic_wind_in(p));
      S7_MARK(dynamic_wind_out(p));
      S7_MARK(dynamic_wind_body(p));
    }
}


static void mark_hash_table(s7_pointer p)
{
  if (!is_marked(p))
    {
      set_mark(p);
      if (hash_table_entries(p) > 0)
	{
	  s7_pointer *tp, *tend;
	  tp = (s7_pointer *)(hash_table_elements(p));
	  tend = (s7_pointer *)(tp + hash_table_length(p));
	  while (tp < tend)
	    {
	      if (is_pair(*tp))
		mark_pair(*tp);
	      tp++;
	    }
	}
    }
}


static void mark_input_port(s7_pointer p)
{
  if (!is_marked(p)) 
    {
      set_mark(p);
      set_mark(port_original_input_string(p));
    }
}


static void mark_syntax(s7_pointer p)
{
}


static void init_mark_functions(void)
{
  mark_function[T_FREE]                = mark_noop;
  mark_function[T_UNIQUE]              = mark_noop;
  mark_function[T_UNSPECIFIED]         = mark_noop;
  mark_function[T_NIL]                 = mark_noop;
  mark_function[T_BOOLEAN]             = mark_noop;
  mark_function[T_STRING]              = just_mark;
  mark_function[T_INTEGER]             = just_mark;
  mark_function[T_RATIO]               = just_mark;
  mark_function[T_REAL]                = just_mark;
  mark_function[T_COMPLEX]             = just_mark;
  mark_function[T_BIG_INTEGER]         = just_mark;
  mark_function[T_BIG_RATIO]           = just_mark;
  mark_function[T_BIG_REAL]            = just_mark;
  mark_function[T_BIG_COMPLEX]         = just_mark;
  mark_function[T_SYMBOL]              = mark_noop; /* this changes to mark_symbol when gensyms are in the heap */
  mark_function[T_PAIR]                = mark_pair;
  mark_function[T_CLOSURE]             = mark_closure;
  mark_function[T_CLOSURE_STAR]        = mark_closure;
  mark_function[T_CONTINUATION]        = mark_continuation;
  mark_function[T_CHARACTER]           = mark_noop;
  mark_function[T_INPUT_PORT]          = mark_input_port;
  mark_function[T_VECTOR]              = mark_vector;
  mark_function[T_INT_VECTOR]          = mark_int_or_float_vector;
  mark_function[T_FLOAT_VECTOR]        = mark_int_or_float_vector; /* need to track the shared vector case, else just_mark would work here */
  mark_function[T_MACRO]               = mark_closure;
  mark_function[T_BACRO]               = mark_closure;
  mark_function[T_C_OBJECT]            = mark_c_object;
  mark_function[T_GOTO]                = just_mark;
  mark_function[T_OUTPUT_PORT]         = just_mark;
  mark_function[T_CATCH]               = mark_catch;
  mark_function[T_DYNAMIC_WIND]        = mark_dynamic_wind;
  mark_function[T_HASH_TABLE]          = mark_hash_table;
  mark_function[T_SYNTAX]              = mark_syntax;
  mark_function[T_ENVIRONMENT]         = mark_let;
  mark_function[T_STACK]               = mark_stack;
  mark_function[T_COUNTER]             = mark_counter;
  mark_function[T_SLOT]                = mark_slot;
  mark_function[T_BAFFLE]              = just_mark;
  mark_function[T_C_MACRO]             = just_mark;
  mark_function[T_C_POINTER]           = just_mark;
  mark_function[T_C_FUNCTION]          = just_mark;  /* these change if needed to mark_c_proc when setter is a scheme function */
  mark_function[T_C_FUNCTION_STAR]     = just_mark;  /* change to mark_c_proc_star if defaults involve an expression */
  mark_function[T_C_ANY_ARGS_FUNCTION] = just_mark;
  mark_function[T_C_OPT_ARGS_FUNCTION] = just_mark;
  mark_function[T_C_RST_ARGS_FUNCTION] = just_mark;
}


static void mark_op_stack(s7_scheme *sc)
{
  s7_pointer *p, *tp;
  tp = sc->op_stack_now;
  p = sc->op_stack;
  while (p < tp)
    S7_MARK(*p++);
}


static void mark_rootlet(s7_scheme *sc)
{
  s7_pointer ge;
  s7_pointer *tmp, *top;

  ge = sc->rootlet;
  tmp = vector_elements(ge);
  top = (s7_pointer *)(tmp + sc->rootlet_entries);

  set_mark(ge);
  while (tmp < top)
    S7_MARK(slot_value(*tmp++));
}


void s7_mark_object(s7_pointer p)
{
  S7_MARK(p);
}

static void mark_permanent_objects(s7_scheme *sc)
{
  gc_obj *g;
  for (g = sc->permanent_objects; g; g = (gc_obj *)(g->nxt))
    S7_MARK(g->p);
}

static void unmark_permanent_objects(s7_scheme *sc)
{
  gc_obj *g;
  for (g = sc->permanent_objects; g; g = (gc_obj *)(g->nxt))
    clear_mark(g->p);
}



#ifndef _MSC_VER
  #include <time.h>
  #include <sys/time.h>
  static struct timeval start_time;
  static struct timezone z0;
#endif

#if DEBUGGING
static int gc_last_line;
static const char *gc_last_func;
#define gc(Sc) gc_1(Sc, __func__, __LINE__)
static int gc_1(s7_scheme *sc, const char *func, int line)
#else
static int gc(s7_scheme *sc)
#endif
{
  s7_cell **old_free_heap_top;
  /* mark all live objects (the symbol table is in permanent memory, not the heap) */
#if DEBUGGING
  #define GC_CALL(P, Tp) \
    p = (*tp++); \
    if (is_marked(p)) \
       clear_mark(p); \
    else \
      { \
        if (!is_free_and_clear(p))		\
          { \
	    p->debugger_bits = 0;\
            clear_type(p);	\
            (*fp++) = p;\
          }}

  gc_last_line = line;
  gc_last_func = func;
#else
  #define GC_CALL(P, Tp) p = (*tp++); if (is_marked(p)) clear_mark(p); else {if (!is_free_and_clear(p)) {clear_type(p); (*fp++) = p;}}
#endif

  if (sc->gc_stats)
    {
      fprintf(stdout, "gc ");
#ifndef _MSC_VER
      /* this is apparently deprecated in favor of clock_gettime -- what compile-time switch to use here?
       */
      gettimeofday(&start_time, &z0);
#endif
    }

  mark_rootlet(sc);
  S7_MARK(sc->args);
  mark_let(sc->envir);
  mark_let(sc->owlet);
  S7_MARK(sc->code);
  S7_MARK(sc->cur_code);
  mark_stack_1(sc->stack, s7_stack_top(sc));
  S7_MARK(sc->v);
  S7_MARK(sc->w);
  S7_MARK(sc->x);
  S7_MARK(sc->z);
  S7_MARK(sc->value);  

  S7_MARK(sc->temp1);
  S7_MARK(sc->temp2);
  S7_MARK(sc->temp3);

  set_mark(sc->input_port);
  S7_MARK(sc->input_port_stack);
  set_mark(sc->output_port);
  set_mark(sc->error_port);

  mark_pair(sc->temp_cell_1);
  mark_pair(sc->temp_cell_2);
  mark_pair(sc->temp_cell_3);
  mark_pair(sc->temp_cell_4);
  S7_MARK(car(sc->T1_1));
  S7_MARK(car(sc->T2_1));
  S7_MARK(car(sc->T2_2));
  S7_MARK(car(sc->T3_1));
  S7_MARK(car(sc->T3_2));
  S7_MARK(car(sc->T3_3));

  S7_MARK(car(sc->A4_1));
  S7_MARK(car(sc->A4_2));
  S7_MARK(car(sc->A4_3));
  S7_MARK(car(sc->A4_4));
  {
    int i;
    s7_pointer p;
    for (i = 1; i < NUM_SAFE_LISTS; i++)
      if (list_is_in_use(sc->safe_lists[i]))
	for (p = sc->safe_lists[i]; is_pair(p); p = cdr(p))
	  S7_MARK(car(p));
  }

  S7_MARK(sc->protected_objects);
  mark_permanent_objects(sc);
  
  /* now protect recent allocations using the free_heap cells above the current free_heap_top (if any).
   *
   * cells above sc->free_heap_top might be malloc'd garbage (after heap reallocation), so we keep track of 
   *   where the last actually freed cells were after the previous GC call.  We're trying to
   *   GC protect the previous GC_TEMPS_SIZE allocated pointers so that the caller doesn't have
   *   to gc-protect every temporary cell.
   *
   * There's one remaining possible problem.  s7_remove_from_heap frees cells outside
   *   the GC and might push free_heap_top beyond its previous_free_heap_top, then
   *   an immediate explicit gc call might not see those temp cells.
   */
  {
    s7_pointer *tmps, *tmps_top;

    tmps = sc->free_heap_top;
    tmps_top = tmps + GC_TEMPS_SIZE;
    if (tmps_top > sc->previous_free_heap_top)
      tmps_top = sc->previous_free_heap_top;

    while (tmps < tmps_top)
      S7_MARK(*tmps++);
  }
  mark_op_stack(sc);

  /* free up all unmarked objects */
  old_free_heap_top = sc->free_heap_top;

  {
    s7_pointer *fp, *tp, *heap_top;
    fp = sc->free_heap_top;

    tp = sc->heap;
    heap_top = (s7_pointer *)(sc->heap + sc->heap_size);

    while (tp < heap_top)          /* != here or ^ makes no difference */
      {
	s7_pointer p;
	/* from here down is GC_CALL, but I wanted one case explicit for readability
	 */
	p = (*tp++);

	if (is_marked(p))          /* this order is faster than checking typeflag(p) != T_FREE first */
	  clear_mark(p);
	else 
	  {
	    if (!is_free_and_clear(p)) /* if T_FREE, it's an already-free object -- the free_heap is usually not empty when we call the GC */
	      {
#if DEBUGGING
		p->debugger_bits = 0;
#endif
		clear_type(p); /* (this is needed -- otherwise we try to free some objects twice) */
		(*fp++) = p;
	      }
	  }

	/* this looks crazy, but it speeds up the entire GC process by 25%!
	 *   going from 16 to 32 saves .2% so it may not matter.
	 */
	GC_CALL(p, tp);
	GC_CALL(p, tp);
	GC_CALL(p, tp);

	GC_CALL(p, tp);
	GC_CALL(p, tp);
	GC_CALL(p, tp);
	GC_CALL(p, tp);

	GC_CALL(p, tp);
	GC_CALL(p, tp);
	GC_CALL(p, tp);
	GC_CALL(p, tp);

	GC_CALL(p, tp);
	GC_CALL(p, tp);
	GC_CALL(p, tp);
	GC_CALL(p, tp);

	GC_CALL(p, tp);
	GC_CALL(p, tp);
	GC_CALL(p, tp);
	GC_CALL(p, tp);

	GC_CALL(p, tp);
	GC_CALL(p, tp);
	GC_CALL(p, tp);
	GC_CALL(p, tp);

	GC_CALL(p, tp);
	GC_CALL(p, tp);
	GC_CALL(p, tp);
	GC_CALL(p, tp);

	GC_CALL(p, tp);
	GC_CALL(p, tp);
	GC_CALL(p, tp);
	GC_CALL(p, tp);
      }
    
    sc->free_heap_top = fp;
    sweep(sc);
  }

  unmark_permanent_objects(sc);
  sc->gc_freed = (int)(sc->free_heap_top - old_free_heap_top);

#if DEBUGGING
  {
    s7_pointer *fp;
    for (fp = old_free_heap_top; fp < sc->free_heap_top; fp++)
      {
	s7_pointer p;
	p = *fp;
	car(p) = NULL;
	cdr(p) = NULL;
	set_ecdr(p, NULL);
	set_fcdr(p, NULL);
	set_gcdr(p, NULL);
      }
  }
#endif

  if (sc->gc_stats)
    {
#ifndef _MSC_VER
      struct timeval t0;
      double secs;
      gettimeofday(&t0, &z0);
      secs = (t0.tv_sec - start_time.tv_sec) +  0.000001 * (t0.tv_usec - start_time.tv_usec);
      fprintf(stdout, "freed %d/%u, time: %f\n", (int)(sc->free_heap_top - old_free_heap_top), sc->heap_size, secs);
#else
      fprintf(stdout, "freed %d/%u\n", sc->gc_freed, sc->heap_size);
#endif
    }

  if (sc->begin_hook) call_begin_hook(sc);
  sc->previous_free_heap_top = sc->free_heap_top;
  return(sc->gc_freed); /* needed by cell allocator to decide when to increase heap size */
}

void s7_gc_stats(s7_scheme *sc, bool on)
{
  sc->gc_stats = on;
}

unsigned int s7_heap_size(s7_scheme *sc)
{
  return(sc->heap_size);
}

int s7_gc_freed(s7_scheme *sc)
{
  return(sc->gc_freed);
}


#define WITH_DUMP_HEAP 0
#if WITH_DUMP_HEAP
static s7_pointer g_dump_heap(s7_scheme *sc, s7_pointer args)
{
  FILE *fd;
  s7_pointer *tp;

  gc(sc);

  fd = fopen("heap.data", "w");
  for (tp = sc->heap; (*tp); tp++)
    {
      s7_pointer p;
      p = (*tp);
      if (!is_free_and_clear(p))
	fprintf(fd, "%s\n", s7_object_to_c_string(sc, p));
    }

  fflush(fd);
  fclose(fd);
  return(sc->NIL);
}
#endif


#define GC_TRIGGER_SIZE 64

#define NEW_CELL(Sc, Obj) \
  do {						\
    if (Sc->free_heap_top <= Sc->free_heap_trigger) try_to_call_gc(Sc); \
    Obj = (*(--(Sc->free_heap_top))); \
    } while (0)

#if (!DEBUGGING)
#define NEW_CELL_NO_CHECK(Sc, Obj) do {Obj = (*(--(Sc->free_heap_top)));} while (0)
  /* since sc->free_heap_trigger is GC_TRIGGER_SIZE above the free heap base, we don't need
   *   to check it repeatedly after the first such check.
   */
#else
#define NEW_CELL_NO_CHECK(Sc, Obj) \
  do {						\
    if (Sc->free_heap_top <= Sc->free_heap_trigger) fprintf(stderr, "%d: new_cell checked [%d]\n", __LINE__, (int)(sc->free_heap_top - sc->free_heap)); \
    Obj = (*(--(Sc->free_heap_top))); \
    } while (0)
#endif


static void expand_heap(s7_scheme *sc)
{
  /* alloc more heap */
  unsigned int old_size, old_free;
  unsigned int k;
  s7_cell *cells;

  old_size = sc->heap_size;
  
  old_free = sc->free_heap_top - sc->free_heap;
  if (sc->heap_size < 512000)
    sc->heap_size *= 2;
  else sc->heap_size += 512000;
  
  sc->heap = (s7_cell **)realloc(sc->heap, sc->heap_size * sizeof(s7_cell *));
  if (!(sc->heap))
    s7_warn(sc, 256, "heap reallocation failed! tried to get %lu bytes\n", (unsigned long)(sc->heap_size * sizeof(s7_cell *)));
  
  sc->free_heap = (s7_cell **)realloc(sc->free_heap, sc->heap_size * sizeof(s7_cell *));
  if (!(sc->free_heap))
    s7_warn(sc, 256, "free heap reallocation failed! tried to get %lu bytes\n", (unsigned long)(sc->heap_size * sizeof(s7_cell *)));	  
  
  sc->free_heap_trigger = (s7_cell **)(sc->free_heap + GC_TRIGGER_SIZE);
  sc->free_heap_top = sc->free_heap + old_free; /* incremented below, added old_free 21-Aug-12?!? */

  /* optimization suggested by K Matheussen */
  cells = (s7_cell *)calloc(sc->heap_size - old_size, sizeof(s7_cell));
  for (k = old_size; k < sc->heap_size; k++)
    {
      sc->heap[k] = &cells[k - old_size];
      (*sc->free_heap_top++) = sc->heap[k];
      heap_location(sc->heap[k]) = k;
    }
  sc->previous_free_heap_top = sc->free_heap_top;
}

static void try_to_call_gc(s7_scheme *sc)
{
  /* called only from NEW_CELL and cons */
  if (sc->gc_off)
    {
      /* we can't just return here!  Someone needs a new cell, and once the heap free list is exhausted, segfault */
      expand_heap(sc);
    }
  else
    {
      unsigned int freed_heap;
      freed_heap = gc(sc);
      if ((freed_heap < sc->heap_size / 2) &&
	  (freed_heap < 1000000)) /* if huge heap */
	expand_heap(sc);
    }
}

  /* originally I tried to mark each temporary value until I was done with it, but
   *   that way madness lies... By delaying GC of _every_ %$^#%@ pointer, I can dispense
   *   with hundreds of individual protections.  So the free_heap's last GC_TEMPS_SIZE
   *   allocated pointers are protected during the mark sweep.
   */


static s7_pointer g_gc(s7_scheme *sc, s7_pointer args)
{
  #define H_gc "(gc (on #t)) runs the garbage collector.  If 'on' is supplied, it turns the GC on or off. \
Evaluation produces a surprising amount of garbage, so don't leave the GC off for very long!"

  if (is_not_null(args))
    {
      if (!s7_is_boolean(car(args)))
	{
	  check_method(sc, car(args), sc->GC, args);
	  return(simple_wrong_type_argument_with_type(sc, sc->GC, car(args), make_string_wrapper(sc, "#f (turn GC off) or #t (turn it on)")));
	}
      sc->gc_off = (car(args) == sc->F);
      if (sc->gc_off) 
	return(sc->F);
    }

  gc(sc);
  return(sc->UNSPECIFIED);
}


s7_pointer s7_gc_on(s7_scheme *sc, bool on)
{
  sc->gc_off = !on;
  return(s7_make_boolean(sc, on));
}


#if (!WITH_THREADS)
static s7_cell *alloc_pointer(void)
{
  #define ALLOC_SIZE 256
  static unsigned int alloc_k = ALLOC_SIZE;
  static s7_cell *alloc_cells = NULL;

  if (alloc_k == ALLOC_SIZE)     /* if either no current block or the block is used up */
    {                            /*   make a new block */
      alloc_cells = (s7_cell *)calloc(ALLOC_SIZE, sizeof(s7_cell));
      alloc_k = 0;
    }
  return(&alloc_cells[alloc_k++]);
}
#else
#define alloc_pointer() (s7_cell *)calloc(1, sizeof(s7_cell))
#endif


static void add_permanent_object(s7_scheme *sc, s7_pointer obj)
{
  gc_obj *g;
  g = (gc_obj *)malloc(sizeof(gc_obj));
  g->p = obj;
  g->nxt = sc->permanent_objects;
  sc->permanent_objects = g;
}


static void s7_remove_from_heap(s7_scheme *sc, s7_pointer x)
{
  int loc;
  s7_pointer p;

  /* global functions are very rarely redefined, so we can remove the function body from
   *   the heap when it is defined.  If redefined, we currently lose the memory held by the
   *   old definition.  (It is not trivial to recover this memory because it is allocated
   *   in blocks, not by the pointer, I think, but s7_define is the point to try).
   * 
   * There is at least one problem with this: if, for example, a function has
   *    a quoted (constant) list, then uses list-set! to change an element of it,
   *    then a GC happens, and the new element is GC'd because no one in the heap
   *    points to it, then we call the function again, and it tries to access
   *    that element. 
   *
   *    (define (bad-idea)
   *      (let ((lst '(1 2 3)))
   *        (let ((result (list-ref lst 1)))
   *          (list-set! lst 1 (* 2.0 16.6))
   *          (gc)
   *          result)))
   * 
   *     put that in a file, load it (to force removal), than call bad-idea a few times.
   * so... if (*s7* 'safety) is not 0, remove-from-heap is disabled.
   */
  loc = heap_location(x);
  if (loc == NOT_IN_HEAP) return;

  switch (type(x))
    {
    case T_PAIR:
      heap_location(x) = NOT_IN_HEAP;
      p = alloc_pointer();
      clear_type(p);
      sc->heap[loc] = p;
      (*sc->free_heap_top++) = p;
      heap_location(p) = loc;

#if 0
      /* this code fixes the problem above, but at some cost (gc + mark_pair up by about 2% in the worst case (snd-test.scm)) */
      if ((car(x) == sc->QUOTE) &&
	  (is_pair(cadr(x))))
	{
	  add_permanent_object(sc, cdr(x));
	}
      else 
	{
	  s7_remove_from_heap(sc, car(x));
	  s7_remove_from_heap(sc, cdr(x));
	}
#else
      s7_remove_from_heap(sc, car(x));
      s7_remove_from_heap(sc, cdr(x));
#endif
      return;

    case T_VECTOR:
      add_permanent_object(sc, x);
      return;

    case T_HASH_TABLE:
    case T_ENVIRONMENT:
    case T_SYNTAX:
      return;

    case T_SYMBOL:
      if (is_gensym(x))
	{
	  int i;
	  sc->heap[loc] = alloc_pointer();
	  clear_type(sc->heap[loc]);
	  (*sc->free_heap_top++) = sc->heap[loc];
	  heap_location(sc->heap[loc]) = loc;

	  heap_location(x) = NOT_IN_HEAP;
	  for (i = 0; i < sc->gensyms_loc; i++)
	    if (sc->gensyms[i] == x)
	      {
		int j;
		for (j = i + 1; i < sc->gensyms_loc; i++, j++)
		  sc->gensyms[i] = sc->gensyms[j];
		sc->gensyms_loc--;
		if (sc->gensyms_loc == 0) mark_function[T_SYMBOL] = mark_noop;
		break;
	      }
	}
      return;

    case T_CLOSURE:
    case T_CLOSURE_STAR:
    case T_MACRO:
    case T_BACRO:
      heap_location(x) = NOT_IN_HEAP;
      p = alloc_pointer();
      clear_type(p);
      sc->heap[loc] = p;
      (*sc->free_heap_top++) = p;
      heap_location(p) = loc;

      s7_remove_from_heap(sc, closure_args(x));
      s7_remove_from_heap(sc, closure_body(x));
      return;

    default:
      break;
    }

  heap_location(x) = NOT_IN_HEAP;
  p = alloc_pointer();
  clear_type(p);
  sc->heap[loc] = p;
  (*sc->free_heap_top++) = p;
  heap_location(p) = loc;
}



/* -------------------------------- stacks -------------------------------- */

#define OP_STACK_INITIAL_SIZE 128
#define push_op_stack(Sc, Op) (*Sc->op_stack_now++) = Op
#define pop_op_stack(Sc)      (*(--(Sc->op_stack_now)))

static void initialize_op_stack(s7_scheme *sc)
{
  int i;
  sc->op_stack = (s7_pointer *)malloc(OP_STACK_INITIAL_SIZE * sizeof(s7_pointer));
  sc->op_stack_size = OP_STACK_INITIAL_SIZE;
  sc->op_stack_now = sc->op_stack;
  sc->op_stack_end = (s7_pointer *)(sc->op_stack + sc->op_stack_size);
  for (i = 0; i < OP_STACK_INITIAL_SIZE; i++)
    sc->op_stack[i] = sc->NIL;
}


static void resize_op_stack(s7_scheme *sc)
{
  int i, loc, new_size;
  loc = (int)(sc->op_stack_now - sc->op_stack);
  new_size = sc->op_stack_size * 2;
  sc->op_stack = (s7_pointer *)realloc((void *)(sc->op_stack), new_size * sizeof(s7_pointer));
  for (i = sc->op_stack_size; i < new_size; i++)
    sc->op_stack[i] = sc->NIL;
  sc->op_stack_size = new_size;
  sc->op_stack_now = (s7_pointer *)(sc->op_stack + loc);
  sc->op_stack_end = (s7_pointer *)(sc->op_stack + sc->op_stack_size);
}


#define stack_code(Stack, Loc)  vector_element(Stack, Loc - 3)
#define stack_let(Stack, Loc)   vector_element(Stack, Loc - 2)
#define stack_args(Stack, Loc)  vector_element(Stack, Loc - 1)
#define stack_op(Stack, Loc)    ((opcode_t)(vector_element(Stack, Loc)))

/* these macros are faster than the equivalent simple function calls.  If the s7_scheme struct is set up to reflect the
 *    stack order [code envir args op], we can use memcpy here: 
 *      #define pop_stack(Sc) do {Sc->stack_end -= 4; memcpy((void *)Sc, (void *)(Sc->stack_end), 4 * sizeof(s7_pointer));} while (0)
 *    but it is only slightly faster (.2% at best)!
 */

#define pop_stack(Sc) \
  do { \
      Sc->stack_end -= 4; \
      Sc->code =  Sc->stack_end[0]; \
      Sc->envir = Sc->stack_end[1]; \
      Sc->args =  Sc->stack_end[2]; \
      Sc->op =    (opcode_t)(Sc->stack_end[3]); \
  } while (0)

#define pop_stack_no_op(Sc) \
  do { \
      Sc->stack_end -= 4; \
      Sc->code =  Sc->stack_end[0]; \
      Sc->envir = Sc->stack_end[1]; \
      Sc->args =  Sc->stack_end[2]; \
  } while (0)

#define push_stack(Sc, Op, Args, Code) \
  do { \
      Sc->stack_end[0] = Code; \
      Sc->stack_end[1] = Sc->envir; \
      Sc->stack_end[2] = Args; \
      Sc->stack_end[3] = (s7_pointer)Op; \
      Sc->stack_end += 4; \
  } while (0)

/* next is used in reader, envir is needed */
#define push_stack_no_code(Sc, Op, Args) \
  do { \
      Sc->stack_end[1] = Sc->envir; \
      Sc->stack_end[2] = Args; \
      Sc->stack_end[3] = (s7_pointer)Op; \
      Sc->stack_end += 4; \
  } while (0)

#define push_stack_no_args(Sc, Op, Code) \
  do { \
      Sc->stack_end[0] = Code; \
      Sc->stack_end[1] = Sc->envir; \
      Sc->stack_end[3] = (s7_pointer)Op; \
      Sc->stack_end += 4; \
  } while (0)

/* since we don't GC mark the stack past the stack_top, push_stack_no_args and friends can cause pop_stack to set
 *   sc->code and sc->args to currently free objects.  
 */

#define main_stack_op(Sc)             ((opcode_t)(Sc->stack_end[-1]))
#define main_stack_args(Sc)           (Sc->stack_end[-2])
#define main_stack_let(Sc)    (Sc->stack_end[-3])
#define main_stack_code(Sc)           (Sc->stack_end[-4])
#define pop_main_stack(Sc)            Sc->stack_end -= 4

/* ideally we'd split out these cases in the optimizer, but for now... */
#define IF_BEGIN_POP_STACK(Sc) do {if (main_stack_op(Sc) == OP_BEGIN1) goto POP_BEGIN; goto START;} while (0)
#define IF_BEGIN_POP_STACK_ELSE_SET_VALUE(Sc, Sym) do {if (main_stack_op(Sc) == OP_BEGIN1) goto POP_BEGIN; sc->value = slot_value(Sym); goto START;} while (0)


static void stack_reset(s7_scheme *sc) 
{ 
  sc->stack_end = sc->stack_start;
  push_stack(sc, OP_BARRIER, sc->NIL, sc->NIL);
} 


static void increase_stack_size(s7_scheme *sc)
{
  unsigned int i, new_size, loc;  /* long long ints?? sc->stack_size also is an unsigned int */

  loc = s7_stack_top(sc);
  new_size = sc->stack_size * 2;
  /* how can we trap infinite recursions?  Is a warning in order here?
   *   I think I'll add 'maximum-stack-size
   *   size currently reaches 8192 in s7test
   */
  if (new_size > sc->maximum_stack_size)
    s7_error(sc, s7_make_symbol(sc, "stack-too-big"), list_1(sc, make_string_wrapper(sc, "stack has grown past (*s7* 'maximum-stack-size)")));

  vector_elements(sc->stack) = (s7_pointer *)realloc(vector_elements(sc->stack), new_size * sizeof(s7_pointer));
  if (vector_elements(sc->stack) == NULL)
    s7_error(sc, s7_make_symbol(sc, "stack-too-big"), list_1(sc, make_string_wrapper(sc, "no room to expand stack?")));

  for (i = sc->stack_size; i < new_size; i++)
    vector_element(sc->stack, i) = sc->NIL;
  vector_length(sc->stack) = new_size;
  sc->stack_size = new_size;

  sc->stack_start = vector_elements(sc->stack);
  sc->stack_end = (s7_pointer *)(sc->stack_start + loc);
  sc->stack_resize_trigger = (s7_pointer *)(sc->stack_start + sc->stack_size / 2);
} 

#define CHECK_STACK_SIZE(Sc) \
  if (Sc->stack_end >= Sc->stack_resize_trigger) \
    { \
      if ((Sc->begin_hook) && (call_begin_hook(Sc))) return(Sc->F); \
      increase_stack_size(Sc); \
    }  




/* -------------------------------- symbols -------------------------------- */

#define HASH_MULT 4

static unsigned int raw_string_hash(const unsigned char *key, unsigned int len)
{ 
  switch (len)
    {
    case 0: return(0);
    case 1: return(key[0]);
    case 2: return(key[0] * HASH_MULT + key[1]);
    case 3: return((key[0] * HASH_MULT + key[1]) * HASH_MULT + key[2]);
    case 4: return(((key[0] * HASH_MULT + key[1]) * HASH_MULT + key[2]) * HASH_MULT + key[3]);
    case 5: return((((key[0] * HASH_MULT + key[1]) * HASH_MULT + key[2]) * HASH_MULT + key[3]) * HASH_MULT + key[4]);
    case 6: return(((((key[0] * HASH_MULT + key[1]) * HASH_MULT + key[2]) * HASH_MULT + key[3]) * HASH_MULT + key[4]) * HASH_MULT + key[5]);

    case 7: 
      return((((((key[0] * HASH_MULT + key[1]) * HASH_MULT + key[2]) * HASH_MULT + key[3]) * HASH_MULT 
	       + key[4]) * HASH_MULT + key[5]) * HASH_MULT + key[6]);

    case 8: 
      return(((((((key[0] * HASH_MULT + key[1]) * HASH_MULT + key[2]) * HASH_MULT + key[3]) * HASH_MULT 
		+ key[4]) * HASH_MULT + key[5]) * HASH_MULT + key[6]) * HASH_MULT + key[7]);

    case 9: 
      return((((((((key[0] * HASH_MULT + key[1]) * HASH_MULT + key[2]) * HASH_MULT + key[3]) * HASH_MULT 
		 + key[4]) * HASH_MULT + key[5]) * HASH_MULT + key[6]) * HASH_MULT + key[7]) * HASH_MULT + key[8]);

    case 10: 
      return(((((((((key[0] * HASH_MULT + key[1]) * HASH_MULT + key[2]) * HASH_MULT + key[3]) * HASH_MULT 
		  + key[4]) * HASH_MULT + key[5]) * HASH_MULT + key[6]) * HASH_MULT + key[7]) * HASH_MULT 
	      + key[8]) * HASH_MULT + key[9]);

    case 11: 
      return((((((((((key[0] * HASH_MULT + key[1]) * HASH_MULT + key[2]) * HASH_MULT + key[3]) * HASH_MULT 
		   + key[4]) * HASH_MULT + key[5]) * HASH_MULT + key[6]) * HASH_MULT + key[7]) * HASH_MULT 
	       + key[8]) * HASH_MULT + key[9]) * HASH_MULT + key[10]);

    case 12: case 13: case 14: case 15:
      {
	unsigned int i, hashed;
	hashed = key[4];
	for (i = 5; i < len; i++)
	  hashed = key[i] + hashed * HASH_MULT;
	return(hashed);
      }

    default:
      {
	unsigned int i, hashed;
	hashed = key[8];
	for (i = 9; i < len; i++)
	  hashed = key[i] + hashed * HASH_MULT;
	return(hashed);
      }
    }
  /* hash_mult 4 just adding new: snd-test: H: 1136155, s: 786385, f: 572859
   *           4 add, add0 at end:          H: 2208589, s:1936257, f: 572814, z: 27708
   *           13:                          H: 1123916, s: 612287, f: 572909, z: 27631 [0 5652 1 6454 2 4148 top 8]!
   *           37:                          H: 1076203, s: 573052, f: 572851, z: 27708![0 5152 1 6818 2 4536 top 9]!
   * so 37 wins at hashing, we save 9 in strcmp, we lose 7 here to multiply by 37 -- we win 2 (callgrind says 5)
   * But 37 loses big in other cases -- not sure what to do!
   *
   * the switch statement costs about 50 here but saves about 110 in extreme cases
   * the simple form of this function (just a for loop) is about 40% slower
   * using a function array indexed by len is about the same (slightly slower)
   */
}

static s7_pointer make_symbol(s7_scheme *sc, const char *name);
static s7_pointer make_symbol_with_length(s7_scheme *sc, const char *name, unsigned int len);

static s7_pointer new_symbol(s7_scheme *sc, const char *name, unsigned int len, unsigned int hash, unsigned int location) 
{ 
  s7_pointer x, str, p; 

  if (sc->symbol_table_is_locked)
    return(s7_error(sc, sc->ERROR, sc->NIL));

  str = make_permanent_string_with_length_and_hash(name, len, hash);
  x = alloc_pointer();
  heap_location(x) = NOT_IN_HEAP;
  symbol_name_cell(x) = str;
  set_type(x, T_SYMBOL);
  global_slot(x) = sc->UNDEFINED; /* was sc->NIL; */
  initial_slot(x) = sc->UNDEFINED;
  symbol_set_local(x, 0LL, sc->NIL);
  symbol_tag(x) = 0;
  symbol_hash(x) = location;

  if (symbol_name_length(x) > 1)                           /* not 0, otherwise : is a keyword */
    {
      if (name[0] == ':')
	{
	  typeflag(x) |= (T_IMMUTABLE | T_KEYWORD); 
	  keyword_symbol(x) = make_symbol_with_length(sc, (char *)(name + 1), len - 1);
	  global_slot(x) = s7_make_slot(sc, sc->NIL, x, x);
	}
      else
	{
	  char c;
	  c = name[symbol_name_length(x) - 1];
	  if (c == ':')
	    {
	      char *kstr;
	      unsigned int klen;
	      klen = symbol_name_length(x) - 1;
	      kstr = (char *)malloc(symbol_name_length(x) * sizeof(char));
	      kstr[klen] = 0;
	      memcpy((void *)kstr, (void *)name, klen);
	      typeflag(x) |= (T_IMMUTABLE | T_KEYWORD); 
	      keyword_symbol(x) = make_symbol_with_length(sc, kstr, klen);
	      global_slot(x) = s7_make_slot(sc, sc->NIL, x, x);
	      free(kstr);
	    }
	}
    }
  p = permanent_cons(x, vector_element(sc->symbol_table, location), T_PAIR | T_IMMUTABLE);
  vector_element(sc->symbol_table, location) = p;
  pair_raw_hash(p) = hash;
  return(x); 
} 


static s7_pointer symbol_table_find_by_name(s7_scheme *sc, const char *name, unsigned int hash, unsigned int location) 
{ 
  s7_pointer x; 
  for (x = vector_element(sc->symbol_table, location); is_not_null(x); x = cdr(x)) 
    if ((hash == pair_raw_hash(x)) &&
	(strings_are_equal(name, symbol_name(car(x)))))
      return(car(x));
  return(sc->NIL); 
} 


static s7_pointer g_symbol_table(s7_scheme *sc, s7_pointer args)
{
  #define H_symbol_table "(symbol-table) returns a list containing the current symbol-table symbols"
  s7_pointer lst, x;
  int i;
  
  /* this can't be optimized by returning the actual symbol-table (a vector of lists), because
   *    gensyms can cause the table's lists and symbols to change at any time.  This wreaks havoc
   *    on traversals like for-each.  So, symbol-table returns a snap-shot of the table contents
   *    at the time it is called, and we call gc before making the list.  I suppose the next step
   *    is to check that we have room, and increase the heap here if necessary!
   *
   *    (define (for-each-symbol func num) (for-each (lambda (sym) (if (> num 0) (for-each-symbol func (- num 1)) (func sym))) (symbol-table)))
   *    (for-each-symbol (lambda (sym) (gensym) 1))
   */
  sc->w = sc->NIL;
  
  gc(sc);
  for (i = 0; i < vector_length(sc->symbol_table); i++) 
    for (x = vector_element(sc->symbol_table, i); is_not_null(x); x = cdr(x)) 
      sc->w = cons(sc, car(x), sc->w);

  lst = sc->w;
  sc->w = sc->NIL;
  return(lst);
}


bool s7_for_each_symbol_name(s7_scheme *sc, bool (*symbol_func)(const char *symbol_name, void *data), void *data)
{
  /* this includes the special constants #<unspecified> and so on for simplicity -- are there any others?
   */
  int i; 
  s7_pointer x; 

  for (i = 0; i < vector_length(sc->symbol_table); i++) 
    for (x = vector_element(sc->symbol_table, i); is_not_null(x); x = cdr(x)) 
      if (symbol_func(symbol_name(car(x)), data))
	return(true);

  return((symbol_func("#t", data))             ||
	 (symbol_func("#f", data))             ||
	 (symbol_func("#<unspecified>", data)) ||
	 (symbol_func("#<undefined>", data))   ||
	 (symbol_func("#<eof>", data))         ||
	 (symbol_func("#true", data))          ||
	 (symbol_func("#false", data)));
}


bool s7_for_each_symbol(s7_scheme *sc, bool (*symbol_func)(const char *symbol_name, s7_pointer value, void *data), void *data)
{
  int i; 
  s7_pointer x; 

  for (i = 0; i < vector_length(sc->symbol_table); i++) 
    for (x = vector_element(sc->symbol_table, i); is_not_null(x); x = cdr(x)) 
      if (symbol_func(symbol_name(car(x)), cdr(x), data))
	return(true);

  return(false);
}


static s7_pointer make_symbol_with_length(s7_scheme *sc, const char *name, unsigned int len) 
{
  s7_pointer x; 
  unsigned int hash, location;
    
  hash = raw_string_hash((const unsigned char *)name, len); 
  location = hash % SYMBOL_TABLE_SIZE;

  for (x = vector_element(sc->symbol_table, location); is_pair(x); x = cdr(x)) 
    if ((hash == pair_raw_hash(x)) &&
	(strings_are_equal_with_length(name, symbol_name(car(x)), len)))
      return(car(x));

  return(new_symbol(sc, name, len, hash, location)); 
} 


static s7_pointer make_symbol(s7_scheme *sc, const char *name) 
{
  return(make_symbol_with_length(sc, name, safe_strlen(name)));
}


s7_pointer s7_make_symbol(s7_scheme *sc, const char *name) 
{ 
  if (!name) return(sc->F);
  return(make_symbol_with_length(sc, name, safe_strlen(name)));
}


s7_pointer s7_symbol_table_find_name(s7_scheme *sc, const char *name)
{
  unsigned int hash, location;
  s7_pointer result;

  hash = raw_string_hash((const unsigned char *)name, safe_strlen(name));
  location = hash % SYMBOL_TABLE_SIZE;
  result = symbol_table_find_by_name(sc, name, hash, location); 
  if (is_null(result))
    return(NULL);

  return(result);
}


static void remove_from_symbol_table(s7_scheme *sc, s7_pointer sym)
{
  unsigned int loc;
  s7_pointer x; 

  loc = symbol_hash(sym);
  x = vector_element(sc->symbol_table, loc);
  if (car(x) == sym)
    {
      vector_element(sc->symbol_table, loc) = cdr(x);
      free(x);
    }
  else
    {
      s7_pointer y;
      for (y = x, x = cdr(x); is_not_null(x); y = x, x = cdr(x))
	{
	  if (car(x) == sym)
	    {
	      cdr(y) = cdr(x);
	      free(x);
	      break;
	    }
	}
    }
}


s7_pointer s7_gensym(s7_scheme *sc, const char *prefix)
{ 
  char *name;
  unsigned int len, hash, location;
  s7_pointer x;
  
  len = safe_strlen(prefix) + 32;
  name = (char *)malloc(len * sizeof(char));
  /* there's no point in heroic efforts here to avoid name collisions --
   *    the user can screw up no matter what we do.
   */
  len = snprintf(name, len, "{%s}-%d", prefix, sc->gensym_counter++);
  hash = raw_string_hash((const unsigned char *)name, len);
  location = hash % SYMBOL_TABLE_SIZE;
  x = new_symbol(sc, name, len, hash, location);  /* not T_GENSYM -- might be called from outside */
  free(name);
  return(x); 
} 


static bool s7_is_gensym(s7_pointer g) {return((is_symbol(g)) && (is_gensym(g)));}

static s7_pointer g_is_gensym(s7_scheme *sc, s7_pointer args) 
{
  #define H_is_gensym "(gensym? sym) returns #t if sym is a gensym"
  check_boolean_method(sc, s7_is_gensym, sc->IS_GENSYM, args); 
}


static s7_pointer g_gensym(s7_scheme *sc, s7_pointer args) 
{
  #define H_gensym "(gensym (prefix \"gensym\")) returns a new, unused symbol"

  const char *prefix;
  char *name;
  unsigned int len, nlen, hash, location;
  s7_pointer x, str, stc;

  /* get symbol name */
  if (is_not_null(args))
    {
      if (!is_string(car(args)))
	{
	  check_method(sc, car(args), sc->GENSYM, args);
	  return(simple_wrong_type_argument(sc, sc->GENSYM, car(args), T_STRING));
	}
      prefix = string_value(car(args));
    }
  else prefix = "gensym";
  len = safe_strlen(prefix) + 32;
  name = (char *)malloc(len * sizeof(char));
  nlen = snprintf(name, len, "{%s}-%d", prefix, sc->gensym_counter++); 
  hash = raw_string_hash((const unsigned char *)name, nlen);
  location = hash % SYMBOL_TABLE_SIZE;
      
  /* make-string for symbol name */
  str = (s7_cell *)calloc(1, sizeof(s7_cell));
  heap_location(str) = NOT_IN_HEAP;
  set_type(str, T_STRING | T_IMMUTABLE);
  string_length(str) = safe_strlen(name);
  string_value(str) = name;
  string_needs_free(str) = false;

  /* allocate the symbol in the heap so GC'd when inaccessible */
  NEW_CELL(sc, x);
  symbol_name_cell(x) = str;
  set_type(x, T_SYMBOL | T_GENSYM);
  global_slot(x) = sc->NIL;
  initial_slot(x) = sc->UNDEFINED;
  symbol_set_local(x, 0LL, sc->NIL);
  symbol_hash(x) = location;

  /* place new symbol in symbol-table, but using calloc so we can easily free it (remove it from the table) in GC sweep */
  stc = (s7_cell *)calloc(1, sizeof(s7_cell));
  heap_location(stc) = NOT_IN_HEAP;
  car(stc) = x;
  set_type(stc, T_PAIR | T_IMMUTABLE);
  cdr(stc) = vector_element(sc->symbol_table, location);
  vector_element(sc->symbol_table, location) = stc;
  pair_raw_hash(stc) = hash;

  add_gensym(sc, x);
  return(x); 
}


s7_pointer s7_name_to_value(s7_scheme *sc, const char *name)
{
  return(s7_symbol_value(sc, make_symbol(sc, name)));
}


bool s7_is_symbol(s7_pointer p)   
{ 
  return(is_symbol(p));
}


bool s7_is_syntax(s7_pointer p)   
{ 
  return(is_syntax(p));
}


static s7_pointer g_is_symbol(s7_scheme *sc, s7_pointer args)
{
  #define H_is_symbol "(symbol? obj) returns #t if obj is a symbol"
  check_boolean_method(sc, is_symbol, sc->IS_SYMBOL, args);
}


const char *s7_symbol_name(s7_pointer p)   
{ 
  return(symbol_name(p));
}


static s7_pointer g_symbol_to_string(s7_scheme *sc, s7_pointer args)
{
  #define H_symbol_to_string "(symbol->string sym) returns the symbol sym converted to a string"
  s7_pointer sym;

  sym = car(args);
  if (!is_symbol(sym))
    {
      check_method(sc, sym, sc->SYMBOL_TO_STRING, args);
      return(simple_wrong_type_argument(sc, sc->SYMBOL_TO_STRING, sym, T_SYMBOL));
    }
  /* s7_make_string uses strlen which stops at an embedded null
   */
  return(s7_make_string_with_length(sc, symbol_name(sym), symbol_name_length(sym)));    /* return a copy */
}


static s7_pointer symbol_to_string_uncopied;
static s7_pointer g_symbol_to_string_uncopied(s7_scheme *sc, s7_pointer args)
{
  s7_pointer sym;

  sym = car(args);
  if (!is_symbol(sym))
    {
      check_method(sc, sym, sc->SYMBOL_TO_STRING, args);
      return(simple_wrong_type_argument(sc, sc->SYMBOL_TO_STRING, sym, T_SYMBOL));
    }
  return(symbol_name_cell(sym));
}


static s7_pointer g_string_to_symbol_1(s7_scheme *sc, s7_pointer args, s7_pointer sym)
{
  s7_pointer str;
  str = car(args);

  if (!is_string(str))
    {
      check_method(sc, str, sym, args);
      return(simple_wrong_type_argument(sc, sym, str, T_STRING));
    }
  /* currently if the string has an embedded null, it marks the end of the new symbol name.
   *   I wonder if this is a bug...
   */
  return(make_symbol_with_length(sc, string_value(str), string_length(str)));
}


static s7_pointer g_string_to_symbol(s7_scheme *sc, s7_pointer args)
{
  #define H_string_to_symbol "(string->symbol str) returns the string str converted to a symbol"
  return(g_string_to_symbol_1(sc, args, sc->STRING_TO_SYMBOL));
}


static s7_pointer g_symbol(s7_scheme *sc, s7_pointer args)
{
  #define H_symbol "(symbol str) returns the string str converted to a symbol"
  return(g_string_to_symbol_1(sc, args, sc->SYMBOL));
}


static s7_pointer add_sym_to_list(s7_scheme *sc, s7_pointer sym) 
{
#if DEBUGGING
  if (!is_symbol(sym))
    fprintf(stderr, "%s[%d]: %s is not a symbol\n", __func__, __LINE__, DISPLAY(sym));
#endif
  symbol_tag(sym) = sc->syms_tag; 
  return(sym);
}

static void clear_syms_in_list(s7_scheme *sc) {sc->syms_tag++;}




/* -------------------------------- environments -------------------------------- */

#define NEW_FRAME(Sc, Old_Env, New_Env)  \
  do {                                   \
      s7_pointer _x_;                      \
      NEW_CELL(Sc, _x_);                   \
      let_id(_x_) = ++sc->let_number; \
      let_slots(_x_) = Sc->NIL;                  \
      outlet(_x_) = Old_Env;		         \
      set_type(_x_, T_ENVIRONMENT | T_IMMUTABLE); \
      New_Env = _x_;		   \
     } while (0)


static s7_pointer new_frame_in_env(s7_scheme *sc, s7_pointer old_env) 
{ 
  /* return(cons(sc, sc->NIL, old_env)); */
  s7_pointer x;
  NEW_CELL(sc, x);
  let_id(x) = ++sc->let_number;
  let_slots(x) = sc->NIL;
  outlet(x) = old_env;
  set_type(x, T_ENVIRONMENT | T_IMMUTABLE);
  return(x);
} 


static s7_pointer make_simple_let(s7_scheme *sc)
{
  s7_pointer frame;
  NEW_CELL(sc, frame);
  let_id(frame) = sc->let_number + 1;
  let_slots(frame) = sc->NIL;
  outlet(frame) = sc->envir;
  set_type(frame, T_ENVIRONMENT | T_IMMUTABLE);
  return(frame);
}


/* in all these macros, symbol_set_local should follow slot_set_value so that we can evaluate the
 *    slot's value in its old state.
 */
#define ADD_SLOT(Frame, Symbol, Value) \
  do { \
    s7_pointer _slot_, _sym_, _val_;			\
    _sym_ = Symbol; _val_ = Value;			\
    NEW_CELL_NO_CHECK(sc, _slot_);\
    slot_symbol(_slot_) = _sym_;\
    slot_set_value(_slot_, _val_);	\
    symbol_set_local(_sym_, let_id(Frame), _slot_); \
    set_type(_slot_, T_SLOT | T_IMMUTABLE);\
    next_slot(_slot_) = let_slots(Frame);\
    let_slots(Frame) = _slot_;\
  } while (0)

#define ADD_SLOT_CHECKED(Frame, Symbol, Value) \
  do { \
    s7_pointer _slot_, _sym_, _val_;			\
    _sym_ = Symbol; _val_ = Value;			\
    NEW_CELL(sc, _slot_);\
    slot_symbol(_slot_) = _sym_;\
    slot_set_value(_slot_, _val_);	\
    symbol_set_local(_sym_, let_id(Frame), _slot_); \
    set_type(_slot_, T_SLOT | T_IMMUTABLE);\
    next_slot(_slot_) = let_slots(Frame);\
    let_slots(Frame) = _slot_;\
  } while (0)

/* no set_local here -- presumably done earlier in check_* 
 */

#define NEW_FRAME_WITH_SLOT(Sc, Old_Env, New_Env, Symbol, Value) \
  do {                                   \
    s7_pointer _x_, _slot_, _sym_, _val_;	 \
      _sym_ = Symbol; _val_ = Value;		 \
      NEW_CELL(Sc, _x_);                   \
      let_id(_x_) = ++sc->let_number; \
      outlet(_x_) = Old_Env;		         \
      set_type(_x_, T_ENVIRONMENT | T_IMMUTABLE); \
      New_Env = _x_;		   \
      NEW_CELL_NO_CHECK(Sc, _slot_);\
      slot_symbol(_slot_) = _sym_;\
      slot_set_value(_slot_, _val_);	\
      symbol_set_local(_sym_, sc->let_number, _slot_); \
      set_type(_slot_, T_SLOT | T_IMMUTABLE);\
      next_slot(_slot_) = sc->NIL;\
      let_slots(_x_) = _slot_;\
     } while (0)


#define NEW_FRAME_WITH_TWO_SLOTS(Sc, Old_Env, New_Env, Symbol1, Value1, Symbol2, Value2) \
  do {                                   \
    s7_pointer _x_, _slot_, _sym1_, _val1_, _sym2_, _val2_;	\
      _sym1_ = Symbol1; _val1_ = Value1;				\
      _sym2_ = Symbol2; _val2_ = Value2;				\
      NEW_CELL(Sc, _x_);                   \
      let_id(_x_) = ++sc->let_number; \
      outlet(_x_) = Old_Env;		         \
      set_type(_x_, T_ENVIRONMENT | T_IMMUTABLE); \
      New_Env = _x_;		   \
      NEW_CELL_NO_CHECK(Sc, _slot_);\
      slot_symbol(_slot_) = _sym1_;\
      slot_set_value(_slot_, _val1_);	\
      symbol_set_local(_sym1_, sc->let_number, _slot_); \
      set_type(_slot_, T_SLOT | T_IMMUTABLE);\
      let_slots(_x_) = _slot_;\
      NEW_CELL_NO_CHECK(Sc, _x_);\
      slot_symbol(_x_) = _sym2_;\
      slot_set_value(_x_, _val2_);	\
      symbol_set_local(_sym2_, sc->let_number, _x_); \
      set_type(_x_, T_SLOT | T_IMMUTABLE);\
      next_slot(_x_) = sc->NIL;\
      next_slot(_slot_) = _x_;\
     } while (0)


#define NEW_FRAME_WITH_CHECKED_SLOT(Sc, Old_Env, New_Env, Symbol, Value) \
  do {                                   \
    s7_pointer _x_, _slot_, _sym_, _val_;	 \
      _sym_ = Symbol; _val_ = Value;		 \
      NEW_CELL(Sc, _x_);                   \
      let_id(_x_) = ++sc->let_number; \
      outlet(_x_) = Old_Env;		         \
      set_type(_x_, T_ENVIRONMENT | T_IMMUTABLE); \
      New_Env = _x_;		   \
      NEW_CELL_NO_CHECK(Sc, _slot_);\
      slot_symbol(_slot_) = _sym_;\
      slot_set_value(_slot_, _val_);		\
      symbol_set_local(_sym_, sc->let_number, _slot_); \
      set_type(_slot_, T_SLOT | T_IMMUTABLE);\
      next_slot(_slot_) = sc->NIL;\
      let_slots(_x_) = _slot_;\
     } while (0)


static s7_pointer old_frame_in_env(s7_scheme *sc, s7_pointer frame, s7_pointer next_frame)
{
  let_slots(frame) = sc->NIL;                  
  outlet(frame) = next_frame;
  set_type(frame, T_ENVIRONMENT | T_IMMUTABLE); 
  let_id(frame) = ++sc->let_number; 
  return(frame);
}


static s7_pointer old_frame_with_slot(s7_scheme *sc, s7_pointer env, s7_pointer val)
{
  s7_pointer x, sym;
  unsigned long long int id;

  id = ++sc->let_number;
  let_id(env) = id;
  x = let_slots(env);
  slot_set_value(x, val);
  sym = slot_symbol(x);
  symbol_set_local(sym, id, x);

  return(env);
}


static s7_pointer old_frame_with_two_slots(s7_scheme *sc, s7_pointer env, s7_pointer val1, s7_pointer val2)
{
  s7_pointer x, sym;
  unsigned long long int id;

  id = ++sc->let_number;
  let_id(env) = id;
  x = let_slots(env);
  slot_set_value(x, val1);
  sym = slot_symbol(x);
  symbol_set_local(sym, id, x);
  x = next_slot(x);
  slot_set_value(x, val2);
  sym = slot_symbol(x);
  symbol_set_local(sym, id, x);

  return(env);
}


static s7_pointer old_frame_with_three_slots(s7_scheme *sc, s7_pointer env, s7_pointer val1, s7_pointer val2, s7_pointer val3)
{
  s7_pointer x, sym;
  unsigned long long int id;

  id = ++sc->let_number;
  let_id(env) = id;
  x = let_slots(env);

  slot_set_value(x, val1);
  sym = slot_symbol(x);
  symbol_set_local(sym, id, x);
  x = next_slot(x);

  slot_set_value(x, val2);
  sym = slot_symbol(x);
  symbol_set_local(sym, id, x);
  x = next_slot(x);

  slot_set_value(x, val3);
  sym = slot_symbol(x);
  symbol_set_local(sym, id, x);

  return(env);
}


static s7_pointer permanent_slot(s7_pointer symbol, s7_pointer value)
{
  s7_pointer x;
  x = alloc_pointer();
  heap_location(x) = NOT_IN_HEAP;
  slot_symbol(x) = symbol;
  slot_set_value(x, value);
  set_type(x, T_SLOT | T_IMMUTABLE);
  return(x);
}


bool s7_is_let(s7_pointer e)
{
  return(is_let(e));
}


static s7_pointer g_is_let(s7_scheme *sc, s7_pointer args)
{
  #define H_is_let "(let? obj) returns #t if obj is an environment."
  check_boolean_method(sc, is_let, sc->IS_LET, args);
}


static s7_pointer find_let(s7_scheme *sc, s7_pointer obj)
{
  if (is_let(obj)) return(obj);
  switch (type(obj))
    {
    case T_ENVIRONMENT:
      return(obj);

    case T_MACRO:
    case T_BACRO:
    case T_CLOSURE:
    case T_CLOSURE_STAR:
      return(closure_let(obj));

    case T_C_OBJECT:
      return(c_object_let(obj));
    }
  return(sc->NIL);
}


static s7_pointer find_method(s7_scheme *sc, s7_pointer env, s7_pointer symbol) 
{ 
  s7_pointer x, y;

  for (x = env; is_let(x); x = outlet(x))
    for (y = let_slots(x); is_slot(y); y = next_slot(y))
      if (slot_symbol(y) == symbol)
	return(slot_value(y));

  return(sc->UNDEFINED);
} 


static int let_length(s7_scheme *sc, s7_pointer e)
{
  /* used by length, applicable_length, and some length optimizations */
  int i;
  s7_pointer p;

  if (e == sc->rootlet)
    return(sc->rootlet_entries);

  if (has_methods(e))
    {
      s7_pointer length_func;
      length_func = find_method(sc, e, sc->LENGTH);
      if (length_func != sc->UNDEFINED)
	return((int)s7_integer(s7_apply_function(sc, length_func, list_1(sc, e))));
    }

  for (i = 0, p = let_slots(e); is_slot(p); i++, p = next_slot(p));
  return(i);
}


s7_pointer s7_make_slot(s7_scheme *sc, s7_pointer env, s7_pointer symbol, s7_pointer value) 
{ 
  s7_pointer slot;

  if ((!is_let(env)) ||
      (env == sc->rootlet))
    {
      s7_pointer ge;

      if ((sc->safety == 0) &&
	  ((is_any_closure(value))  ||
	   (is_macro(value))        ||
	   (is_bacro(value))))
	{
	  s7_remove_from_heap(sc, closure_args(value));
	  s7_remove_from_heap(sc, closure_body(value));
	}

      ge = sc->rootlet;
      slot = permanent_slot(symbol, value);
      vector_element(ge, sc->rootlet_entries++) = slot;
      if (sc->rootlet_entries >= vector_length(ge))
	{
	  int i;
	  vector_length(ge) *= 2;
	  vector_elements(ge) = (s7_pointer *)realloc(vector_elements(ge), vector_length(ge) * sizeof(s7_pointer));
	  for (i = sc->rootlet_entries; i < vector_length(ge); i++)
	    vector_element(ge, i) = sc->NIL;
	}

      global_slot(symbol) = slot;
      if (symbol_id(symbol) == 0) /* never defined locally? */
	{
	  if (initial_slot(symbol) == sc->UNDEFINED)
	    initial_slot(symbol) = permanent_slot(symbol, value);
	  local_slot(symbol) = slot;
	  set_global(symbol);
	}

      if (is_gensym(symbol))
	s7_remove_from_heap(sc, symbol);
    }
  else 
    {
      set_local(symbol); 

      NEW_CELL(sc, slot);
      slot_symbol(slot) = symbol;
      slot_set_value(slot, value);
      set_type(slot, T_SLOT | T_IMMUTABLE);
      next_slot(slot) = let_slots(env);
      let_slots(env) = slot;
      /* this is called by varlet so we have to be careful about the resultant let_id
       *   check for greater to ensure shadowing stays in effect, and equal to do updates (set! in effect)
       */
      if (let_id(env) >= symbol_id(symbol))
	symbol_set_local(symbol, let_id(env), slot);
    }
  /* there are about the same number of frames as local variables -- this
   *   strikes me as surprising, but it holds up across a lot of code.
   */
  return(slot);
} 


static s7_pointer add_slot(s7_scheme *sc, s7_pointer variable, s7_pointer value) 
{ 
  /* this is called when it is guaranteed that there is a local environment */
  s7_pointer y;

  NEW_CELL(sc, y);
  slot_symbol(y) = variable;
  slot_set_value(y, value);
  set_type(y, T_SLOT | T_IMMUTABLE);
  next_slot(y) = let_slots(sc->envir);
  let_slots(sc->envir) = y;
  set_local(variable);
  symbol_set_local(variable, let_id(sc->envir), y);
  return(y);
} 


static s7_pointer make_slot(s7_scheme *sc, s7_pointer variable, s7_pointer value) 
{
  /* this is for a do-loop optimization -- an unattached slot */
  s7_pointer y;
  NEW_CELL(sc, y);
  slot_symbol(y) = variable;
  slot_set_value(y, value);
  set_type(y, T_SLOT | T_IMMUTABLE);
  return(y);
}


/* -------- unlet -------- */

#define UNLET_ENTRIES 512

static void save_unlet(s7_scheme *sc)
{
  /* there are ca 270 predefined functions (and another 80 or so other things): 348 at last count
   */
  int i, k = 0;
  s7_pointer x;
  s7_pointer *inits;

  sc->unlet = (s7_pointer)calloc(1, sizeof(s7_cell));
  set_type(sc->unlet, T_VECTOR);
  vector_length(sc->unlet) = UNLET_ENTRIES;
  vector_elements(sc->unlet) = (s7_pointer *)malloc(UNLET_ENTRIES * sizeof(s7_pointer));
  vector_getter(sc->unlet) = default_vector_getter;
  vector_setter(sc->unlet) = default_vector_setter;
  inits = vector_elements(sc->unlet);
  s7_vector_fill(sc, sc->unlet, sc->NIL);
  heap_location(sc->unlet) = NOT_IN_HEAP;

  for (i = 0; i < vector_length(sc->symbol_table); i++) 
    for (x = vector_element(sc->symbol_table, i); is_not_null(x); x = cdr(x)) 
      {
	s7_pointer sym;
	sym = car(x);
	if (is_slot(initial_slot(sym)))
	  {
	    s7_pointer val;
	    val = slot_value(initial_slot(sym));
	    if ((is_procedure(val)) || (is_syntax(val)))
	      inits[k++] = initial_slot(sym);
	    /* (let ((begin +)) (with-let (unlet) (begin 1 2))) */
	  }
      }
}


static s7_pointer g_unlet(s7_scheme *sc, s7_pointer args)
{
  /* add sc->unlet bindings to the current environment */
  #define H_unlet "(unlet) establishes the original bindings of all the predefined functions"

  /* slightly confusing:
   *    :((unlet) 'abs)
   *    #<undefined>
   *    :(defined? 'abs (unlet))
   *    #t
   * this is because unlet sets up a local environment of unshadowed symbols,
   *   and s7_let_ref below only looks at the local env chain (that is, if env is not
   *   the global env, then the global env is not searched).
   */
  int i;
  s7_pointer *inits;
  s7_pointer x;

  sc->w = new_frame_in_env(sc, sc->envir);
  inits = vector_elements(sc->unlet);

  for (i = 0; (i < UNLET_ENTRIES) && (is_slot(inits[i])); i++)
    {
      s7_pointer sym;
      x = slot_value(inits[i]);
      sym = slot_symbol(inits[i]);
      if (is_procedure(x))
	{
	  if (((!is_global(sym)) &&                  /* it might be shadowed locally */
	       (s7_symbol_local_value(sc, sym, sc->envir) != slot_value(global_slot(sym)))) ||
	      (x != slot_value(global_slot(sym))))   /* it's not shadowed, but has been changed globally */
	    s7_make_slot(sc, sc->w, sym, x);
	}
      else
	{
	  if ((is_syntax(x)) &&
	      (local_slot(sym) != sc->NIL))
	    s7_make_slot(sc, sc->w, sym, x);
	}
    }
  /* if (set! + -) then + needs to be overridden, but the local bit isn't set,
   *   so we have to check the actual values in the non-local case.
   *   (define (f x) (with-let (unlet) (+ x 1))) 
   */

  x = sc->w;
  sc->w = sc->NIL;
  return(x);
}


static s7_pointer g_is_openlet(s7_scheme *sc, s7_pointer args)
{
  #define H_is_openlet "(openlet? obj) returns #t is 'obj' has methods."
  check_method(sc, car(args), sc->IS_OPENLET, args);
  return(make_boolean(sc, has_methods(car(args))));
}


static s7_pointer g_openlet(s7_scheme *sc, s7_pointer args)
{
  #define H_openlet "(openlet e) marks the environment 'e for special handling by generic functions and so on."
  s7_pointer e;

  e = car(args);
  check_method(sc, e, sc->OPENLET, args);
  if (((is_let(e)) && (e != sc->rootlet)) ||
      (is_any_closure(e)) ||
      ((is_c_object(e)) && (c_object_let(e) != sc->NIL)))
    {
      set_has_methods(e);
      return(e);
    }
  return(simple_wrong_type_argument_with_type(sc, sc->OPENLET, e, AN_ENVIRONMENT));
}


static s7_pointer g_coverlet(s7_scheme *sc, s7_pointer args)
{
  #define H_coverlet "(coverlet e) undoes an earlier openlet."
  s7_pointer e;

  e = car(args);
  check_method(sc, e, sc->COVERLET, args);
  if (((is_let(e)) && (e != sc->rootlet)) ||
      (is_any_closure(e)) || 
      ((is_c_object(e)) && (c_object_let(e) != sc->NIL)))
    {
      clear_has_methods(e);
      return(e);
    }
  return(simple_wrong_type_argument_with_type(sc, sc->COVERLET, e, AN_ENVIRONMENT));
}


s7_pointer s7_openlet(s7_pointer e)
{
  set_has_methods(e);
  return(e);
}


bool s7_is_openlet(s7_pointer e)
{
  return(has_methods(e));
}

#if (!DISABLE_DEPRECATED)
s7_pointer s7_search_openlet(s7_scheme *sc, s7_pointer symbol, s7_pointer e)
{
  return(find_method(sc, find_let(sc, e), symbol));
}
#endif


static void append_let(s7_scheme *sc, s7_pointer new_e, s7_pointer old_e)
{
  s7_pointer x;

  if (old_e == sc->rootlet) 
    return;

  if (new_e != sc->rootlet)
    {
      for (x = let_slots(old_e); is_slot(x); x = next_slot(x))
	s7_make_slot(sc, new_e, slot_symbol(x), slot_value(x)); /* not ADD_SLOT here because we might run off the free heap end */
    }
  else
    {
      for (x = let_slots(old_e); is_slot(x); x = next_slot(x))
	{
	  s7_pointer sym, val;
	  sym = slot_symbol(x);
	  val = slot_value(x);
	  if (is_slot(global_slot(sym)))
	    slot_set_value(global_slot(sym), val);
	  else s7_make_slot(sc, new_e, sym, val);
	}
    }
}


static s7_pointer check_c_obj_env(s7_scheme *sc, s7_pointer old_e, s7_pointer caller)
{
  if (is_c_object(old_e))
    old_e = c_object_let(old_e);
  if (!is_let(old_e))
    return(simple_wrong_type_argument_with_type(sc, caller, old_e, AN_ENVIRONMENT));
  return(old_e);
}


static s7_pointer g_varlet(s7_scheme *sc, s7_pointer args)
{
  #define H_varlet "(varlet env ...) adds its arguments (an environment, a cons: symbol . value, or a pair of arguments, the symbol and its value) \
directly to the environment env, and returns the environment."

  s7_pointer x, e, sym, val, p;
  int i;

  e = car(args);
  if (is_null(e))
    e = sc->rootlet;
  else
    {
      check_method(sc, e, sc->VARLET, args);
      if (!is_let(e))
	return(wrong_type_argument_with_type(sc, sc->VARLET, small_int(1), e, AN_ENVIRONMENT));
    }
  
  for (i = 2, x = cdr(args); is_not_null(x); i++, x = cdr(x))
    {
      p = car(x);
      switch (type(p))
	{
	case T_SYMBOL:
	  if (is_keyword(p))
	    sym = keyword_symbol(p);
	  else sym = p;
	  i++;
	  if (!is_pair(cdr(x)))
	    return(wrong_type_argument_with_type(sc, sc->VARLET, make_integer(sc, i), p, A_BINDING));		
	  x = cdr(x);
	  val = car(x);
	  break;

	case T_PAIR:
	  sym = car(p);
	  val = cdr(p);
	  break;

	case T_ENVIRONMENT:
	  append_let(sc, e, check_c_obj_env(sc, p, sc->VARLET));
	  continue;
	  
	default:
	  sym = p;
	  val = sc->F;
	  break;
	}

      if (!is_symbol(sym))
	return(wrong_type_argument_with_type(sc, sc->VARLET, make_integer(sc, i), p, A_SYMBOL));
      if (is_immutable(sym))
	return(wrong_type_argument_with_type(sc, sc->VARLET, make_integer(sc, i), sym, A_NON_CONSTANT_SYMBOL));
	  
      if (e == sc->rootlet)
	{
	  if (is_slot(global_slot(sym)))
	    {
	      if (is_syntax(slot_value(global_slot(sym))))
		return(wrong_type_argument_with_type(sc, sc->VARLET, make_integer(sc, i), p, make_string_wrapper(sc, "a non-syntactic keyword")));
	      /*  without this check we can end up turning our code into gibberish:
	       *
	       * :(set! quote 1)
	       * ;can't set! quote
	       * :(varlet (rootlet) '(quote . 1))
	       * :quote
	       * 1
	       * or worse set quote to a function of one arg that tries to quote something -- infinite loop
	       */
	      slot_set_value(global_slot(sym), val);
	    }
	  else s7_make_slot(sc, e, sym, val);
	}
      else
	{
	  s7_pointer x;
	  bool found_it = false;
	  for (x = let_slots(e); is_slot(x); x = next_slot(x))
	    if (slot_symbol(x) == sym)
	      {
		slot_set_value(x, val);
		found_it = true;
		break;
	      }
	  if (!found_it)
	    s7_make_slot(sc, e, sym, val);
	}
    }
  return(e);
}


static s7_pointer g_cutlet(s7_scheme *sc, s7_pointer args)
{
  #define H_cutlet "(cutlet e field ...) removes a field or fields from an environment."
  s7_pointer e, fields;
  int i;
  #define THE_UN_ID -1

  e = car(args);
  if (is_null(e))
    e = sc->rootlet;
  else
    {
      check_method(sc, e, sc->CUTLET, args);
      if (!is_let(e))
	return(wrong_type_argument_with_type(sc, sc->CUTLET, small_int(1), e, AN_ENVIRONMENT));
    }
  /* besides removing the slot we have to make sure the symbol_id does not match else
   *   let-ref and others will use the old slot!  What's the un-id?  I'll try -1.  0 means global.
   */
  for (i = 1, fields = cdr(args); is_pair(fields); fields = cdr(fields), i++)
    {
      s7_pointer field, slot;
      field = car(fields);
      if (!is_symbol(field))
	return(wrong_type_argument_with_type(sc, sc->CUTLET, small_int(i), field, A_SYMBOL));
      if (e == sc->rootlet)
	{
	  if (is_slot(global_slot(field)))
	    {
	      symbol_id(field) = THE_UN_ID;
	      slot_value(global_slot(field)) = sc->UNDEFINED;
	      slot_value(local_slot(field)) = sc->UNDEFINED;
	    }
	}
      else
	{
	  slot = let_slots(e);
	  if (is_slot(slot))
	    {
	      if (slot_symbol(slot) == field)
		{
		  let_slots(e) = next_slot(let_slots(e));
		  symbol_id(field) = THE_UN_ID;
		}
	      else
		{
		  s7_pointer last_slot;
		  last_slot = slot;
		  for (slot = next_slot(let_slots(e)); is_slot(slot); last_slot = slot, slot = next_slot(slot))
		    {
		      if (slot_symbol(slot) == field)
			{
			  symbol_id(field) = THE_UN_ID;
			  next_slot(last_slot) = next_slot(slot);
			  break; /* or should we remove all fields of a given name? */
			}
		    }
		}
	    }
	}
    }
  return(e);
}


static s7_pointer sublet_1(s7_scheme *sc, s7_pointer e, s7_pointer bindings, s7_pointer caller)
{
  s7_pointer new_e;

  if (e == sc->rootlet)
    new_e = new_frame_in_env(sc, sc->NIL);
  else new_e = new_frame_in_env(sc, e);
  set_all_methods(new_e, e);

  if (!is_null(bindings))
    {
      int i;
      s7_pointer x;
      sc->temp3 = new_e;

      for (i = 2, x = bindings; is_not_null(x); i++, x = cdr(x))
	{
	  s7_pointer p, sym, val;

	  p = car(x);
	  switch (type(p))
	    {
	    case T_SYMBOL:
	      if (is_keyword(p))
		sym = keyword_symbol(p);
	      else sym = p;
	      i++;
	      if (!is_pair(cdr(x)))
		return(wrong_type_argument_with_type(sc, caller, make_integer(sc, i), p, A_BINDING));		
	      x = cdr(x);
	      val = car(x);
	      break;

	    case T_PAIR:
	      sym = car(p);
	      val = cdr(p);
	      break;

	    case T_ENVIRONMENT:
	      append_let(sc, new_e, check_c_obj_env(sc, p, caller));
	      continue;

	    default:
	      sym = p;
	      val = sc->F;
	      break;
	    }

	  if (!is_symbol(sym))
	    return(wrong_type_argument_with_type(sc, caller, make_integer(sc, i), p, A_SYMBOL));
	  if (is_immutable(sym))
	    return(wrong_type_argument_with_type(sc, caller, make_integer(sc, i), sym, A_NON_CONSTANT_SYMBOL));

	  s7_make_slot(sc, new_e, sym, val);
	  if (sym == sc->LET_REF_FALLBACK) 
	    set_has_ref_fallback(new_e);
	  else 
	    {
	      if (sym == sc->LET_SET_FALLBACK) 
		set_has_set_fallback(new_e);
	    }
	}
      sc->temp3 = sc->NIL;
    }
  return(new_e);
}


s7_pointer s7_sublet(s7_scheme *sc, s7_pointer e, s7_pointer bindings)
{
  return(sublet_1(sc, e, bindings, sc->SUBLET));
}


static s7_pointer g_sublet(s7_scheme *sc, s7_pointer args)
{
  #define H_sublet "(sublet env ...) adds its \
arguments (each an environment or a cons: symbol . value) to the environment env, and returns the \
new environment."

  s7_pointer e;

  e = car(args);
  if (is_null(e))
    e = sc->rootlet;
  else
    {
      check_method(sc, e, sc->SUBLET, args);
      if (!is_let(e))
	return(wrong_type_argument_with_type(sc, sc->SUBLET, small_int(1), e, AN_ENVIRONMENT));
    }
  return(sublet_1(sc, e, cdr(args), sc->SUBLET));
}


s7_pointer s7_inlet(s7_scheme *sc, s7_pointer args)
{
  #define H_inlet "(inlet ...) adds its \
arguments, each an environment, a cons: '(symbol . value), or a keyword/value pair, to a new environment, and returns the \
new environment."

  return(sublet_1(sc, sc->rootlet, args, sc->INLET));
}


s7_pointer s7_let_to_list(s7_scheme *sc, s7_pointer env)
{
  s7_pointer x;
  sc->temp3 = sc->w;
  sc->w = sc->NIL;
  if (env == sc->rootlet)
    {
      unsigned int i, lim2;
      s7_pointer *entries;

      entries = vector_elements(env);
      lim2 = sc->rootlet_entries;

#if DEBUGGING
      for (i = 0; i < lim2; i++)
	if (is_slot(entries[i]))
	  {
	    if (!is_symbol(slot_symbol(entries[i])))
	      {
		fprintf(stderr, " rootlet[%d/%d]: %p slot: ", i, lim2, entries[i]);
		fprintf(stderr, "%s ", DISPLAY(slot_symbol(entries[i])));
		fprintf(stderr, "%s", DISPLAY(slot_value(entries[i])));
		if (is_macro(slot_value(entries[i])))
		  fprintf(stderr, ": %s\n", DISPLAY(closure_args(entries[i])));
		else fprintf(stderr, "\n");
	      }
	  }
	else fprintf(stderr, "rootlet[%d] is not a slot?\n", i);
#endif

      if (lim2 & 1) lim2--;

      for (i = 0; i < lim2; )
	{
	  sc->w = cons_unchecked(sc, cons(sc, slot_symbol(entries[i]), slot_value(entries[i])), sc->w); i++;
	  sc->w = cons_unchecked(sc, cons_unchecked(sc, slot_symbol(entries[i]), slot_value(entries[i])), sc->w); i++;
	}
      if (lim2 < sc->rootlet_entries)
	sc->w = cons_unchecked(sc, cons(sc, slot_symbol(entries[i]), slot_value(entries[i])), sc->w); 
    }
  else
    {
      for (x = let_slots(env); is_slot(x); x = next_slot(x))
	sc->w = cons_unchecked(sc, cons(sc, slot_symbol(x), slot_value(x)), sc->w);
    }
  x = sc->w;
  sc->w = sc->temp3;
  return(x);
}


static s7_pointer g_let_to_list(s7_scheme *sc, s7_pointer args)
{
  #define H_let_to_list "(let->list env) returns env's bindings as a list of cons's: '(symbol . value)."

  s7_pointer env;
  env = car(args);
  check_method(sc, env, sc->LET_TO_LIST, args);
  if (!is_let(env))
    {
      if (is_c_object(env))
	env = c_object_let(env);
      if (!is_let(env))
        return(simple_wrong_type_argument_with_type(sc, sc->LET_TO_LIST, env, AN_ENVIRONMENT));
    }
  return(s7_let_to_list(sc, env));
}


static s7_pointer let_ref_1(s7_scheme *sc, s7_pointer env, s7_pointer symbol)
{
  s7_pointer x, y;
  /* (let ((a 1)) ((curlet) 'a)) 
   * ((rootlet) 'abs) 
   */
  if (env == sc->rootlet)
    {
      y = global_slot(symbol);
      if (is_slot(y))
	return(slot_value(y));
      return(sc->UNDEFINED);
    }

  if (let_id(env) == symbol_id(symbol))
    return(slot_value(local_slot(symbol))); /* this obviously has to follow the global-env check */

  for (x = env; is_let(x); x = outlet(x))
    for (y = let_slots(x); is_slot(y); y = next_slot(y))
      if (slot_symbol(y) == symbol)
	return(slot_value(y));

  /* now for a horrible kludge.  If a let is a mock-hash-table (for example), implicit
   *   indexing of the hash-table collides with the same thing for the let (field names
   *   versus keys), and we can't just try again here because that makes it too easy to
   *   get into infinite recursion.  So, 'let-ref-fallback...
   */
  if (has_ref_fallback(env))
    {
      check_method(sc, env, sc->LET_REF_FALLBACK, sc->w = list_2(sc, env, symbol));
    }
  return(sc->UNDEFINED);
}


s7_pointer s7_let_ref(s7_scheme *sc, s7_pointer env, s7_pointer symbol)
{
  if (!is_symbol(symbol))
    {
      check_method(sc, env, sc->LET_REF, sc->w = list_2(sc, env, symbol));
      return(wrong_type_argument_with_type(sc, sc->LET_REF, small_int(2), symbol, A_SYMBOL));
    }
  return(let_ref_1(sc, env, symbol));
}


static s7_pointer g_let_ref(s7_scheme *sc, s7_pointer args)
{
  #define H_let_ref "(let-ref env sym) returns the value of the symbol sym in the environment env"
  s7_pointer e, s;
  
  e = car(args);
  if (!is_let(e))
    return(wrong_type_argument_with_type(sc, sc->LET_REF, small_int(1), e, AN_ENVIRONMENT));

  if (is_pair(cdr(args)))
    s = cadr(args);
  else s = sc->F;

  if (!is_symbol(s))
    {
      check_method(sc, e, sc->LET_REF, args);
      return(wrong_type_argument_with_type(sc, sc->LET_REF, small_int(2), s, A_SYMBOL));
    }
  return(let_ref_1(sc, e, s));
}


static s7_pointer call_accessor(s7_scheme *sc, s7_pointer slot, s7_pointer old_value)
{
  s7_pointer func, new_value;

  new_value = sc->ERROR;
  func = slot_accessor(slot);

  if (is_procedure(func))
    {
      if (is_c_function(func))
	{
	  car(sc->T2_1) = slot_symbol(slot);
	  car(sc->T2_2) = old_value;
      	  new_value = c_function_call(func)(sc, sc->T2_1);
	}
      else
	{
	  bool old_off;
	  old_off = sc->gc_off;
	  sc->gc_off = true;
	  new_value = s7_apply_function(sc, func, list_2(sc, slot_symbol(slot), old_value));
	  sc->gc_off = old_off;
	}
    }
  else
    {
      if (is_any_macro(func))
	{
	  push_stack(sc, OP_EVAL_DONE, sc->args, sc->code); 
	  sc->args = list_1(sc, cons(sc, func, list_2(sc, slot_symbol(slot), old_value)));
	  sc->code = func;
	  push_stack(sc, OP_EVAL_MACRO, sc->NIL, sc->args);
	  eval(sc, OP_APPLY);
	  new_value = sc->value;
	}
      else return(old_value);
    }

  if (new_value == sc->ERROR)
    return(s7_error(sc, sc->ERROR,
		    list_3(sc, make_string_wrapper(sc, "can't set! ~S to ~S"), slot_symbol(slot), old_value)));
  return(new_value);
}


static s7_pointer let_set_1(s7_scheme *sc, s7_pointer env, s7_pointer symbol, s7_pointer value)
{
  s7_pointer x, y;

  if (env == sc->rootlet)
    {
      if (is_immutable(symbol))  /* (let-set! (rootlet) :key #f) */
	return(wrong_type_argument_with_type(sc, sc->LET_SET, small_int(2), symbol, A_NON_CONSTANT_SYMBOL));	
      y = global_slot(symbol);
      if (is_slot(y))
	{	
	  if (slot_has_accessor(y))
	    slot_set_value(y, call_accessor(sc, y, value));
	  else slot_set_value(y, value);
	  return(slot_value(y));
	}
      return(sc->UNDEFINED);
    }

  for (x = env; is_let(x); x = outlet(x))
    for (y = let_slots(x); is_slot(y); y = next_slot(y))
      if (slot_symbol(y) == symbol)
	{

	  if (slot_has_accessor(y))
	    slot_set_value(y, call_accessor(sc, y, value));
	  else slot_set_value(y, value);
	  return(slot_value(y));
	}

  if (has_set_fallback(env))
    {
      check_method(sc, env, sc->LET_SET_FALLBACK, sc->w = list_3(sc, env, symbol, value));
    }
  return(sc->UNDEFINED);
}


s7_pointer s7_let_set(s7_scheme *sc, s7_pointer env, s7_pointer symbol, s7_pointer value)
{
  if (!is_symbol(symbol))
    {
      check_method(sc, env, sc->LET_SET, sc->w = list_3(sc, env, symbol, value));
      return(wrong_type_argument_with_type(sc, sc->LET_SET, small_int(2), symbol, A_SYMBOL));
    }
  return(let_set_1(sc, env, symbol, value));
}


static s7_pointer g_let_set(s7_scheme *sc, s7_pointer args)
{
  /* (let ((a 1)) (set! ((curlet) 'a) 32) a) */
  #define H_let_set "(let-set! env sym val) sets the symbol sym's value in the environment env to val"
  s7_pointer e, s;

  e = car(args);
  if (!is_let(e))
    return(wrong_type_argument_with_type(sc, sc->LET_SET, small_int(1), e, AN_ENVIRONMENT));

  s = cadr(args);
  if (!is_symbol(s))
    {
      check_method(sc, e, sc->LET_SET, args);
      return(wrong_type_argument_with_type(sc, sc->LET_SET, small_int(2), s, A_SYMBOL));
    }
  return(let_set_1(sc, e, s, caddr(args)));
}


static s7_pointer reverse_slots(s7_scheme *sc, s7_pointer list)
{
  s7_pointer p = list, result, q;
  result = sc->NIL;

  while (is_slot(p))
    {
      q = next_slot(p);
      next_slot(p) = result;
      result = p;
      p = q;
    }
  return(result);
}

static void add_slot_in_reverse(s7_scheme *sc, s7_pointer e, s7_pointer x)
{
  /* used only in let_copy */
  if (is_slot(x))
    {
      add_slot_in_reverse(sc, e, next_slot(x));
      ADD_SLOT(e, slot_symbol(x), slot_value(x));
    }
}

static s7_pointer let_copy(s7_scheme *sc, s7_pointer env)
{
  if (is_let(env))
    {
      s7_pointer new_e;

      if (env == sc->rootlet)   /* (copy (rootlet)) or (copy (funclet abs)) etc */
	return(sc->rootlet);

      /* we can't make copy handle environments-as-objects specially because the
       *   make-object function in define-class uses copy to make a new object!
       *   So if it is present, we get it here, and then there's almost surely trouble.
       */
      new_e = new_frame_in_env(sc, outlet(env));
      set_all_methods(new_e, env);
      sc->temp3 = new_e;
      add_slot_in_reverse(sc, new_e, let_slots(env));
      /* We can't do a loop here then reverse the slots later because the symbol's local_slot has to
       *    match the unshadowed slot, not the last in the list:
       *    (let ((e1 (inlet 'a 1 'a 2))) (let ((e2 (copy e1))) (list (equal? e1 e2) (equal? (e1 'a) (e2 'a))))) 
       */
      return(new_e);
    }
  return(sc->NIL);
}


static s7_pointer g_rootlet(s7_scheme *sc, s7_pointer ignore)
{
  #define H_rootlet "(rootlet) returns the current top-level definitions (symbol bindings)."
  return(sc->rootlet);
}

/* as with the symbol-table, this function can lead to disaster -- user could
 *   clobber the environment etc.  But we want it to be editable and augmentable,
 *   so I guess I'll leave it alone.  (See curlet|funclet as well).
 */


s7_pointer s7_rootlet(s7_scheme *sc) 
{
  return(sc->rootlet);
}

s7_pointer s7_shadow_rootlet(s7_scheme *sc) 
{
  return(sc->shadow_rootlet);
}

s7_pointer s7_set_shadow_rootlet(s7_scheme *sc, s7_pointer let)
{
  sc->shadow_rootlet = let;
  return(let);
}


static s7_pointer g_curlet(s7_scheme *sc, s7_pointer args)
{
  #define H_curlet "(curlet) returns the current definitions (symbol bindings)"

  sc->capture_env_counter++;
  if (is_let(sc->envir))
    return(sc->envir);
  return(sc->rootlet);
}


s7_pointer s7_curlet(s7_scheme *sc) 
{
  sc->capture_env_counter++;
  return(sc->envir);
}


s7_pointer s7_set_curlet(s7_scheme *sc, s7_pointer e) 
{
  s7_pointer old_e;
  old_e = sc->envir;
  sc->envir = e;
  return(old_e);
}


s7_pointer s7_outlet(s7_pointer e)
{
  return(outlet(e));
}


static s7_pointer g_outlet(s7_scheme *sc, s7_pointer args)
{
  #define H_outlet "(outlet env) is the environment that contains env."
  s7_pointer env;

  env = car(args);
  if (!is_let(env))
    {
      check_method(sc, env, sc->OUTLET, args);
      return(simple_wrong_type_argument_with_type(sc, sc->OUTLET, env, AN_ENVIRONMENT));  
    }
  if ((env == sc->rootlet) ||
      (is_null(outlet(env))))
    return(sc->rootlet);
  return(outlet(env));
}


static s7_pointer g_set_outlet(s7_scheme *sc, s7_pointer args)
{
  /* (let ((a 1)) (let ((b 2)) (set! (outlet (curlet)) (rootlet)) ((curlet) 'a))) */
  s7_pointer env, new_outer;

  env = car(args);
  if (!is_let(env))
    {
      check_method(sc, env, sc->OUTLET, args);
      return(simple_wrong_type_argument_with_type(sc, sc->OUTLET, env, AN_ENVIRONMENT));  
    }

  new_outer = cadr(args);
  if (!is_let(new_outer))
    return(wrong_type_argument_with_type(sc, sc->OUTLET, small_int(2), new_outer, AN_ENVIRONMENT));  
  /*
  if (new_outer == env)
    return(s7_error(sc, sc->WRONG_TYPE_ARG, list_1(sc, make_string_wrapper(sc, "can't set the outlet of env to env!"))));
  */
  if (new_outer == sc->rootlet)
    new_outer = sc->NIL;

  if (env != sc->rootlet)
    outlet(env) = new_outer;
  return(new_outer);
}


static s7_pointer find_symbol(s7_scheme *sc, s7_pointer symbol)
{ 
  s7_pointer x;	

  if (let_id(sc->envir) == symbol_id(symbol))
    return(local_slot(symbol));	

  for (x = sc->envir; symbol_id(symbol) < let_id(x); x = outlet(x));

  if (let_id(x) == symbol_id(symbol))
    return(local_slot(symbol));	

  for (; is_let(x); x = outlet(x))
    {
      s7_pointer y; 
      for (y = let_slots(x); is_slot(y); y = next_slot(y))	
	if (slot_symbol(y) == symbol)
	  return(y); 
    }

  return(global_slot(symbol));
} 


s7_pointer s7_slot(s7_scheme *sc, s7_pointer symbol)
{
  return(find_symbol(sc, symbol));
}


s7_pointer s7_slot_value(s7_pointer slot)
{
  return(slot_value(slot));
}


s7_pointer s7_slot_set_value(s7_scheme *sc, s7_pointer slot, s7_pointer value)
{
  slot_set_value(slot, value);
  return(value);
}


static s7_pointer find_local_symbol(s7_scheme *sc, s7_pointer symbol, s7_pointer e)
{ 
  if (!is_let(e))
    return(global_slot(symbol));

  if (symbol_id(symbol) != 0)
    {
      s7_pointer y;
      for (y = let_slots(e); is_slot(y); y = next_slot(y))
	if (slot_symbol(y) == symbol)
	  return(y);
    }
  return(sc->UNDEFINED);
} 


s7_pointer s7_local_slot(s7_scheme *sc, s7_pointer symbol)
{
  s7_pointer y;
  for (y = let_slots(sc->envir); is_slot(y); y = next_slot(y))
    if (slot_symbol(y) == symbol)
      return(y);
  return(NULL);
}


s7_pointer s7_is_local_variable(s7_scheme *sc, s7_pointer symbol, s7_pointer e)
{
  /* search from env e inward, or actually from the current env out to e */
  s7_pointer x, y;
  for (x = sc->envir; (x != e) && (is_let(x)); x = outlet(x))
    for (y = let_slots(x); is_slot(y); y = next_slot(y))
      if (slot_symbol(y) == symbol)
	return(y);
  return(NULL);
}


s7_pointer s7_symbol_value(s7_scheme *sc, s7_pointer sym) /* was searching just the rootlet? */
{
  s7_pointer x;

  x = find_symbol(sc, sym);
  if (is_slot(x))
    return(slot_value(x));

  return(sc->UNDEFINED);
}


s7_pointer s7_value(s7_scheme *sc, s7_pointer sym)
{
  return(find_symbol_checked(sc, sym));
}


s7_pointer s7_car_value(s7_scheme *sc, s7_pointer lst)
{
  return(find_symbol_checked(sc, car(lst)));
}


s7_pointer s7_cadr_value(s7_scheme *sc, s7_pointer lst)
{
  return(find_symbol_checked(sc, cadr(lst)));
}


size_t s7_number_offset(s7_scheme *sc)
{
  return(offsetof(s7_cell, object.number.real_value));
}


size_t s7_slot_value_offset(s7_scheme *sc)
{
  return(offsetof(s7_cell, object.slt.val));
}


size_t s7_denominator_offset(s7_scheme *sc)
{
  return(offsetof(s7_cell, object.number.fraction_value.denominator));
}


s7_pointer s7_cadar_value(s7_scheme *sc, s7_pointer lst)
{
  return(find_symbol_checked(sc, cadar(lst)));
}


s7_pointer s7_symbol_local_value(s7_scheme *sc, s7_pointer sym, s7_pointer local_env)
{
  if (is_let(local_env))
    {
      s7_pointer x;
      for (x = local_env; is_let(x); x = outlet(x))
	{
	  s7_pointer y;
	  for (y = let_slots(x); is_slot(y); y = next_slot(y))
	    if (slot_symbol(y) == sym)
	      return(slot_value(y));
	}
    }
  return(s7_symbol_value(sc, sym)); 
}


#define find_global_symbol_checked(Sc, Sym) ((is_global(Sym)) ? slot_value(global_slot(Sym)) : find_symbol_checked(Sc, Sym))

static s7_pointer g_symbol_to_value(s7_scheme *sc, s7_pointer args)
{
  #define H_symbol_to_value "(symbol->value sym (env (curlet))) returns the binding of (the value associated with) the \
symbol sym in the given environment: (let ((x 32)) (symbol->value 'x)) -> 32"

  s7_pointer sym;
  sym = car(args);

  if (!is_symbol(sym))
    {
      check_method(sc, sym, sc->SYMBOL_TO_VALUE, args);
      return(wrong_type_argument(sc, sc->SYMBOL_TO_VALUE, small_int(1), sym, T_SYMBOL));
    }
  if (is_not_null(cdr(args)))
    {
      s7_pointer local_env;
      local_env = cadr(args);
      if (!is_let(local_env))
	{
	  check_method(sc, local_env, sc->SYMBOL_TO_VALUE, args);
	  return(wrong_type_argument_with_type(sc, sc->SYMBOL_TO_VALUE, small_int(2), local_env, AN_ENVIRONMENT));
	}
      if (local_env == sc->rootlet)
	{
	  s7_pointer x;
	  x = global_slot(sym);
	  if (is_slot(x))
	    return(slot_value(x));
	  return(sc->UNDEFINED);
	}
      return(s7_symbol_local_value(sc, sym, local_env));
    }
  
  if (is_global(sym))
    return(slot_value(global_slot(sym)));

  return(s7_symbol_value(sc, sym));
}


s7_pointer s7_symbol_set_value(s7_scheme *sc, s7_pointer sym, s7_pointer val)
{
  s7_pointer x;
  /* if immutable should this return an error? */
  x = find_symbol(sc, sym);
  if (is_slot(x))
    slot_set_value(x, val);
  return(val);
}


static s7_pointer find_dynamic_value(s7_scheme *sc, s7_pointer x, s7_pointer sym, long long int *id)
{ 
  for (; symbol_id(sym) < let_id(x); x = outlet(x));

  if (let_id(x) == symbol_id(sym))
    {
      (*id) = let_id(x);
      return(slot_value(local_slot(sym)));
    }
  for (; (is_let(x)) && (let_id(x) > (*id)); x = outlet(x))
    {
      s7_pointer y; 
      for (y = let_slots(x); is_slot(y); y = next_slot(y))	
	if (slot_symbol(y) == sym)
	  {
	    (*id) = let_id(x);
	    return(slot_value(y));
	  }
    }
  return(sc->GC_NIL);
} 


static s7_pointer g_symbol_to_dynamic_value(s7_scheme *sc, s7_pointer args)
{
  #define H_symbol_to_dynamic_value "(symbol->dynamic-value sym) returns the dynamic binding of the symbol sym"

  s7_pointer sym, val;
  long long int top_id;
  int i;

  sym = car(args);

  if (!is_symbol(sym))
    {
      check_method(sc, sym, sc->SYMBOL_TO_DYNAMIC_VALUE, args);
      return(wrong_type_argument(sc, sc->SYMBOL_TO_DYNAMIC_VALUE, small_int(1), sym, T_SYMBOL));
    }
  if (is_global(sym))
    return(slot_value(global_slot(sym)));

  if (let_id(sc->envir) == symbol_id(sym))
    return(slot_value(local_slot(sym)));	

  top_id = -1;
  val = find_dynamic_value(sc, sc->envir, sym, &top_id);
  if (top_id == symbol_id(sym))
    return(val);

  for (i = s7_stack_top(sc) - 1; i > 0; i -= 4)
    {
      s7_pointer cur_val;
      cur_val = find_dynamic_value(sc, stack_let(sc->stack, i), sym, &top_id);
      if (cur_val != sc->GC_NIL)
	val = cur_val;
      if (top_id == symbol_id(sym))
	return(val);
    }
  
  if (val == sc->GC_NIL)
    return(s7_symbol_value(sc, sym));
  return(val);
}


static s7_pointer make_closure(s7_scheme *sc, s7_pointer args, s7_pointer code, int type) 
{
  /* this is called every time a lambda form is evaluated, or during letrec, etc */

  s7_pointer x;
  NEW_CELL(sc, x);
  closure_args(x) = args;
  closure_body(x) = code;
  closure_setter(x) = sc->F;
  if (is_null(args))
    closure_arity(x) = 0;
  else closure_arity(x) = CLOSURE_ARITY_NOT_SET;
  closure_let(x) = sc->envir;
  if (is_safe_closure(code))
    {
      if (type == T_CLOSURE)
	set_type(x, T_CLOSURE | T_PROCEDURE | T_SAFE_CLOSURE | T_COPY_ARGS);
      else set_type(x, T_CLOSURE_STAR | T_PROCEDURE | T_SAFE_CLOSURE);
    }
  else
    {
      if (type == T_CLOSURE)
	set_type(x, T_CLOSURE | T_PROCEDURE | T_COPY_ARGS);
      else set_type(x, T_CLOSURE_STAR | T_PROCEDURE);
    }
  sc->capture_env_counter++;
  return(x);
}


#define make_closure_with_let(Sc, X, Args, Code, Env)	\
  do { \
       NEW_CELL(Sc, X); \
       closure_args(X) = Args;	\
       closure_body(X) = Code; \
       closure_setter(X) = sc->F; \
       if (is_null(Args)) closure_arity(X) = 0; else closure_arity(X) = CLOSURE_ARITY_NOT_SET; \
       closure_let(X) = Env; \
       if (is_safe_closure(closure_body(X))) \
          set_type(X, T_CLOSURE | T_PROCEDURE | T_SAFE_CLOSURE | T_COPY_ARGS); \
       else set_type(X, T_CLOSURE | T_PROCEDURE | T_COPY_ARGS); \
       sc->capture_env_counter++; \
      } while (0)


#define make_closure_without_capture(Sc, X, Args, Code, Env)	\
  do { \
       NEW_CELL(Sc, X); \
       closure_args(X) = Args;	\
       closure_body(X) = Code; \
       closure_setter(X) = sc->F; \
       if (is_null(Args)) closure_arity(X) = 0; else closure_arity(X) = CLOSURE_ARITY_NOT_SET; \
       closure_let(X) = Env; \
       if (is_safe_closure(closure_body(X))) \
          set_type(X, T_CLOSURE | T_PROCEDURE | T_SAFE_CLOSURE | T_COPY_ARGS); \
       else set_type(X, T_CLOSURE | T_PROCEDURE | T_COPY_ARGS); \
      } while (0)


static int closure_length(s7_scheme *sc, s7_pointer e)
{
  /* we can't use let_length(sc, closure_let(e)) because the closure_let(closure)
   *   changes.  So the open bit is not always on.  Besides, the fallbacks need to be for closures, not environments.
   */
  s7_pointer length_func;
  length_func = find_method(sc, closure_let(e), sc->LENGTH);
  if (length_func != sc->UNDEFINED)
    return((int)s7_integer(s7_apply_function(sc, length_func, list_1(sc, e))));

  /* there are cases where this should raise a wrong-type-arg error, but for now... */
  return(-1);
}


static s7_pointer g_is_defined(s7_scheme *sc, s7_pointer args)
{
  #define H_is_defined "(defined? obj (env (curlet)) ignore-globals) returns #t if obj has a binding (a value) in the environment env"
  s7_pointer sym;

  /* is this correct? 
   *    (defined? '_x) #f (symbol->value '_x) #<undefined>
   *    (define x #<undefined>) (defined? 'x) #t
   */

  sym = car(args);
  if (!is_symbol(sym))
    {
      check_method(sc, sym, sc->IS_DEFINED, args);
      return(wrong_type_argument(sc, sc->IS_DEFINED, small_int(1), sym, T_SYMBOL));
    }

  if (is_pair(cdr(args)))
    {
      s7_pointer e, b, x;
      e = cadr(args);
      if (!is_let(e))
	return(wrong_type_argument_with_type(sc, sc->IS_DEFINED, small_int(2), e, AN_ENVIRONMENT));

      if (is_pair(cddr(args)))
	{
	  b = caddr(args);
	  if (!s7_is_boolean(b))
	    {
	      check_method(sc, b, sc->IS_DEFINED, args);
	      return(wrong_type_argument_with_type(sc, sc->IS_DEFINED, small_int(3), b, A_BOOLEAN));
	    }
	}
      else b = sc->F;

      if (e == sc->rootlet)
	return(make_boolean(sc, global_slot(sym) != sc->UNDEFINED)); /* new_symbol initializes global_slot to #<undefined> */

      x = find_local_symbol(sc, sym, e); 
      if (is_slot(x))
	return(sc->T);

      if (b == sc->T)
	return(sc->F);

      /* here we can't fall back on find_symbol:
       *    (let ((b 2))
       *      (let ((e (curlet)))
       *        (let ((a 1))
       *          (if (defined? 'a e)
       *              (format #t "a: ~A in ~{~A ~}" (symbol->value 'a e) e))))
       *    "a: 1 in (b . 2)"
       *
       * but we also can't just return #f: 
       *    (let ((b 2))
       *      (let ((e (curlet)))
       *        (let ((a 1))
       *          (format #t "~A: ~A" (defined? 'abs e) (eval '(abs -1) e)))))
       *    "#f: 1"
       */
      return(make_boolean(sc, global_slot(sym) != sc->UNDEFINED));
    }
  else 
    {
      if (is_global(sym))
	return(sc->T);
    }
  return(make_boolean(sc, is_slot(find_symbol(sc, sym))));
}


bool s7_is_defined(s7_scheme *sc, const char *name)
{
  s7_pointer x;
  x = s7_symbol_table_find_name(sc, name);
  if (x)
    {
      x = find_symbol(sc, x);
      return(is_slot(x));
    }
  return(false);
}


void s7_define(s7_scheme *sc, s7_pointer envir, s7_pointer symbol, s7_pointer value) 
{
  s7_pointer x;
  if ((envir == sc->NIL) ||
      (envir == sc->rootlet)) 
    envir = sc->shadow_rootlet;
  x = find_local_symbol(sc, symbol, envir);
  if (is_slot(x)) 
    slot_set_value(x, value); 
  else 
    {
      s7_make_slot(sc, envir, symbol, value); /* I think this means C code can override "constant" defs */
      if ((envir == sc->shadow_rootlet) &&
	  (global_slot(symbol) == sc->UNDEFINED))
	{
	  set_global(symbol); /* is_global => global_slot is usable */
	  global_slot(symbol) = local_slot(symbol);
	}
    }
}


s7_pointer s7_define_variable(s7_scheme *sc, const char *name, s7_pointer value)
{
  s7_pointer sym;
  sym = make_symbol(sc, name);
  s7_define(sc, sc->NIL, sym, value);
  return(sym);
}


s7_pointer s7_define_variable_with_documentation(s7_scheme *sc, const char *name, s7_pointer value, const char *help)
{
  s7_pointer sym;
  sym = s7_define_variable(sc, name, value);
  symbol_set_has_help(sym);
  symbol_help(sym) = copy_string(help);
  return(sym);
}


s7_pointer s7_define_constant(s7_scheme *sc, const char *name, s7_pointer value)
{
  s7_pointer sym;
  sym = make_symbol(sc, name);
  s7_define(sc, sc->NIL, sym, value);
  set_immutable(sym);
  return(sym);
}

/* (define (func a) (let ((cvar (+ a 1))) cvar))
 * (define-constant cvar 23)
 * (func 1)
 * ;can't bind an immutable object: cvar
 */

s7_pointer s7_define_constant_with_documentation(s7_scheme *sc, const char *name, s7_pointer value, const char *help)
{
  s7_pointer sym;
  sym = s7_define_constant(sc, name, value);
  symbol_set_has_help(sym);
  symbol_help(sym) = copy_string(help);
  return(value);
}


char *s7_symbol_documentation(s7_scheme *sc, s7_pointer sym)
{
  if (is_keyword(sym)) return(NULL);
  if ((is_symbol(sym)) &&
      (symbol_has_help(sym)))
    return(symbol_help(sym));
  return(NULL);
}


char *s7_symbol_set_documentation(s7_scheme *sc, s7_pointer sym, const char *new_doc)
{
  if (is_keyword(sym)) return(NULL);
  if ((is_symbol(sym)) &&
      (symbol_has_help(sym)) &&
      (symbol_help(sym)))
    free(symbol_help(sym));
  symbol_set_has_help(sym);
  symbol_help(sym) = copy_string(new_doc);
  return(symbol_help(sym));
}



/* -------- keywords -------- */

bool s7_is_keyword(s7_pointer obj)
{
  return(is_keyword(obj));
}


static s7_pointer g_is_keyword(s7_scheme *sc, s7_pointer args)
{
  #define H_is_keyword "(keyword? obj) returns #t if obj is a keyword, (keyword? :key) -> #t"
  check_boolean_method(sc, is_keyword, sc->IS_KEYWORD, args);
}


s7_pointer s7_make_keyword(s7_scheme *sc, const char *key)
{
  s7_pointer sym;
  char *name;
  unsigned int len, slen;
  
  slen = safe_strlen(key);
  name = (char *)malloc((slen + 2) * sizeof(char));
  len = snprintf(name, slen + 2, ":%s", key);                     /* prepend ":" */
  sym = make_symbol_with_length(sc, name, len);
  typeflag(sym) |= (T_IMMUTABLE); 
  free(name);
  keyword_symbol(sym) = make_symbol_with_length(sc, key, slen);
  
  s7_make_slot(sc, sc->NIL, sym, sym); /* make it global, not in the local env! */
  return(sym);
}


static s7_pointer g_make_keyword(s7_scheme *sc, s7_pointer args)
{
  #define H_make_keyword "(make-keyword str) prepends ':' to str and defines that as a keyword"
  if (!is_string(car(args)))
    {
      check_method(sc, car(args), sc->MAKE_KEYWORD, args);
      return(simple_wrong_type_argument(sc, sc->MAKE_KEYWORD, car(args), T_STRING));
    }
  return(s7_make_keyword(sc, string_value(car(args))));
}


static s7_pointer g_keyword_to_symbol(s7_scheme *sc, s7_pointer args)
{
  #define H_keyword_to_symbol "(keyword->symbol key) returns a symbol with the same name as key but no prepended colon"
  s7_pointer sym;

  sym = car(args);
  if (!is_keyword(sym))
    {
      check_method(sc, sym, sc->KEYWORD_TO_SYMBOL, args);
      return(simple_wrong_type_argument_with_type(sc, sc->KEYWORD_TO_SYMBOL, sym, make_string_wrapper(sc, "a keyword")));
    }
  return(keyword_symbol(sym));
}


static s7_pointer g_symbol_to_keyword(s7_scheme *sc, s7_pointer args)
{
  #define H_symbol_to_keyword "(symbol->keyword sym) returns a keyword with the same name as sym, but with a colon prepended"
  if (!is_symbol(car(args)))
    {
      check_method(sc, car(args), sc->SYMBOL_TO_KEYWORD, args);
      return(simple_wrong_type_argument(sc, sc->SYMBOL_TO_KEYWORD, car(args), T_SYMBOL));
    }
  return(s7_make_keyword(sc, symbol_name(car(args))));
}



/* ---------------- uninterpreted pointers ---------------- */

bool s7_is_c_pointer(s7_pointer arg) 
{
  return(type(arg) == T_C_POINTER);
}


void *s7_c_pointer(s7_pointer p) 
{
  if ((is_number(p)) && 
      (s7_integer(p) == 0))
    return(NULL); /* special case where the null pointer has been cons'd up by hand */

  if (type(p) != T_C_POINTER)
    return(NULL);

  return(raw_pointer(p));
}


s7_pointer s7_make_c_pointer(s7_scheme *sc, void *ptr)
{
  s7_pointer x;
  NEW_CELL(sc, x);
  set_type(x, T_C_POINTER);
  raw_pointer(x) = ptr;
  return(x);
}


static s7_pointer g_is_c_pointer(s7_scheme *sc, s7_pointer args)
{
  #define H_is_c_pointer "(c-pointer? obj) returns #t if obj is a C pointer being held in s7."
  check_boolean_method(sc, s7_is_c_pointer, sc->IS_C_POINTER, args);
}


static s7_pointer g_c_pointer(s7_scheme *sc, s7_pointer args)
{
  #define H_c_pointer "(c-pointer int) returns a c-pointer object."
  if (!s7_is_integer(car(args)))
    {
      check_method(sc, car(args), sc->C_POINTER, args);
      return(wrong_type_argument(sc, sc->C_POINTER, small_int(1), car(args), T_INTEGER));
    }
  return(s7_make_c_pointer(sc, (void *)s7_integer(car(args))));
}



/* -------------------------------- continuations and gotos -------------------------------- */

static s7_pointer g_is_continuation(s7_scheme *sc, s7_pointer args)
{
  #define H_is_continuation "(continuation? obj) returns #t if obj is a continuation"
  check_boolean_method(sc, is_continuation, sc->IS_CONTINUATION, args);
}

/* is this the right thing?  It returns #f for call-with-exit ("goto") because
 *   that form of continuation can't continue (via a jump back to its context).
 *
 * how to recognize the call-with-exit function?  "goto" is an internal name.
 */

static s7_pointer protected_list_copy(s7_scheme *sc, s7_pointer a) 
{
  s7_pointer slow, fast, p;

  sc->w = cons(sc, car(a), sc->NIL);
  p = sc->w;

  slow = fast = cdr(a);
  while (true)
    {
      if (!is_pair(fast))
	{
	  if (is_null(fast))
	    return(sc->w);
	  cdr(p) = fast;
	  return(sc->w);
	}
      
      cdr(p) = cons(sc, car(fast), sc->NIL);
      p = cdr(p);

      fast = cdr(fast);
      if (!is_pair(fast)) 
	{
	  if (is_null(fast))
	    return(sc->w);
	  cdr(p) = fast;
	  return(sc->w);
	}
      /* if unrolled further, it's a lot slower? */
      cdr(p) = cons(sc, car(fast), sc->NIL);
      p = cdr(p);
      
      fast = cdr(fast);
      slow = cdr(slow);
      if (fast == slow) 
	return(sc->w);
    }
  return(sc->w);
}

static s7_pointer list_copy(s7_scheme *sc, s7_pointer x, s7_pointer y, bool step)
{
  if ((!is_pair(x)) ||
       (x == y))
    return(x);
  return(cons(sc, car(x), list_copy(sc, cdr(x), (step) ? cdr(y) : y, !step)));
}


static s7_pointer copy_arg_list(s7_scheme *sc, s7_pointer lst)
{
  /* lst can be dotted or circular here.  The circular list only happens in a case like:
   *    (dynamic-wind 
   *      (lambda () (eq? (let ((lst (cons 1 2))) (set-cdr! lst lst) lst) (call/cc (lambda (k) k))))
   *      (lambda () #f)
   *      (lambda () #f))
   *
   * on the first call, we know lst is a pair
   */
  return(protected_list_copy(sc, lst));
}


static s7_pointer copy_counter(s7_scheme *sc, s7_pointer obj)
{
  s7_pointer nobj;
  NEW_CELL(sc, nobj);
  counter_result(nobj) = counter_result(obj);
  counter_list(nobj) = counter_list(obj);
  counter_count(nobj) = counter_count(obj);
  counter_length(nobj) = counter_length(obj);
  counter_let(nobj) = counter_let(obj);
  counter_args(nobj) = counter_args(obj);
  set_type(nobj, T_COUNTER);
  return(nobj);
}


static s7_pointer copy_object(s7_scheme *sc, s7_pointer obj)
{
  /* called only in copy_stack -- the GC is off */
  s7_pointer nobj;
  int nloc;

  /* NEW_CELL(sc, nobj); */
  /* since the GC is off, NEW_CELL ignores the free_heap trigger, causing it to exhaust the free list sometimes,
   *   so we check explicitly -- we don't want to call the gc mark/sweep process here.
   */
  if (sc->free_heap_top <= sc->free_heap_trigger) expand_heap(sc);
  nobj = (*(--(sc->free_heap_top)));

  nloc = heap_location(nobj);
#if DEBUGGING
  if (nloc == 0) {fprintf(stderr, "free_list trouble\n"); abort();}
#endif
  memcpy((void *)nobj, (void *)obj, sizeof(s7_cell));
  heap_location(nobj) = nloc;

  /* nobj is safe here because the gc is off */
  if (dont_copy(car(obj)))
    car(nobj) = car(obj);
  else car(nobj) = copy_object(sc, car(obj));
  
  if ((dont_copy(cdr(obj))) ||
      (is_procedure_or_macro(obj)))
    cdr(nobj) = cdr(obj); /* closure_let in func cases */
  else cdr(nobj) = copy_object(sc, cdr(obj));
  
  return(nobj);
}


static s7_pointer copy_stack(s7_scheme *sc, s7_pointer old_v, int top)
{
  #define CC_INITIAL_STACK_SIZE 128
  int i, len;
  s7_pointer new_v;
  s7_pointer *nv, *ov;

  /* stacks can grow temporarily, so sc->stack_size grows, but we don't normally need all that
   *   leftover space here, so choose the original stack size if it's smaller.
   */
  len = vector_length(old_v);
  if ((len > CC_INITIAL_STACK_SIZE) &&
      (top < len / 4))
    len = top * 4;

  new_v = s7_make_vector(sc, len);
  set_type(new_v, T_STACK);
  /* we can't leave the upper stuff simply malloc-garbage because we're sure to call the GC.
   *   We also can't just copy the vector since that seems to confuse the gc mark process.
   */

  nv = vector_elements(new_v);
  ov = vector_elements(old_v);
  
  s7_gc_on(sc, false);

  for (i = 0; i < top; i += 4)
    {
      s7_pointer p;
      if (dont_copy(ov[i]))
	nv[i] = ov[i];
      else nv[i] = copy_object(sc, ov[i]);    /* code */

      nv[i + 1] = ov[i + 1];                  /* environment pointer */
      nv[i + 3] = ov[i + 3];                  /* op (constant int) */

      p = ov[i + 2];
      if (is_pair(p))                         /* args need not be a list (it can be a port or #f, etc) */
	nv[i + 2] = copy_arg_list(sc, p);     /* args (copy is needed -- see s7test.scm) */
      else
	{
	  if (is_counter(p))                  /* these can only occur in this context */
	    nv[i + 2] = copy_counter(sc, p);
	  else nv[i + 2] = p;                 /* is this a safe assumption? */
	}
    }
  s7_gc_on(sc, true);
  return(new_v);
}


static s7_pointer make_goto(s7_scheme *sc) 
{
  s7_pointer x;
  NEW_CELL(sc, x);
  call_exit_goto_loc(x) = s7_stack_top(sc);
  call_exit_op_loc(x) = (int)(sc->op_stack_now - sc->op_stack);
  call_exit_active(x) = true;
  set_type(x, T_GOTO | T_PROCEDURE);
  return(x);
}


static s7_pointer *copy_op_stack(s7_scheme *sc)
{
  int len;
  s7_pointer *ops;
  ops = (s7_pointer *)malloc(sc->op_stack_size * sizeof(s7_pointer));
  len = (int)(sc->op_stack_now - sc->op_stack);
  if (len > 0)
    memcpy((void *)ops, (void *)(sc->op_stack), len * sizeof(s7_pointer));
  return(ops);
}


/* (with-baffle . body) calls body guaranteeing that there can be no jumps into the
 *    middle of it from outside -- no outer evaluation of a continuation can jump across this
 *    barrier:  The flip-side of call-with-exit.
 *    It sets a T_BAFFLE var in a new env, that has a unique key.  Call/cc then always
 *    checks the env chain for any such variable, saving the localmost.  Apply of a continuation
 *    looks for such a saved variable, if none, go ahead, else check the current env (before the
 *    jump) for that variable.  If none, error, else go ahead.  This is different from a delimited
 *    continuation which simply delimits the extent of the continuation (why not use lambda?) -- we want to block it
 *    from coming at us from some unknown place.  
 */  

static s7_pointer make_baffle(s7_scheme *sc)
{
  s7_pointer x;
  NEW_CELL(sc, x);
  baffle_key(x) = sc->baffle_ctr++;
  set_type(x, T_BAFFLE);
  return(x);
}


static bool find_baffle(s7_scheme *sc, int key)
{
  /* search backwards through sc->envir for sc->BAFFLE with key as value
   */
  s7_pointer x, y;	
  for (x = sc->envir; is_let(x); x = outlet(x))
    for (y = let_slots(x); is_slot(y); y = next_slot(y))	
      if ((slot_symbol(y) == sc->BAFFLE) &&
	  (baffle_key(slot_value(y)) == key))
	return(true);
  return(false);
}


static int find_any_baffle(s7_scheme *sc)
{
  /* search backwards through sc->envir for any sc->BAFFLE
   */
  if (sc->baffle_ctr > 0)
    {
      s7_pointer x, y;	
      for (x = sc->envir; is_let(x); x = outlet(x))
	for (y = let_slots(x); is_slot(y); y = next_slot(y))	
	  if (slot_symbol(y) == sc->BAFFLE)
	    return(baffle_key(slot_value(y)));
    }
  return(-1);
}


s7_pointer s7_make_continuation(s7_scheme *sc) 
{
  s7_pointer x;
  int loc;
  if ((int)(sc->free_heap_top - sc->free_heap) < (int)(sc->heap_size / 4))
    gc(sc);

  /* this gc call is needed if there are lots of call/cc's -- by pure bad luck
   *   we can end up hitting the end of the gc free list time after time while
   *   in successive copy_stack's below, causing s7 to core up until it runs out of memory.
   */

  loc = s7_stack_top(sc);

  NEW_CELL(sc, x);
  continuation_data(x) = (continuation_t *)calloc(1, sizeof(continuation_t));
  continuation_stack(x) = copy_stack(sc, sc->stack, s7_stack_top(sc));
  continuation_stack_size(x) = vector_length(continuation_stack(x));   /* copy_stack can return a smaller stack than the current one */
  continuation_stack_start(x) = vector_elements(continuation_stack(x));
  continuation_stack_end(x) = (s7_pointer *)(continuation_stack_start(x) + loc);

  continuation_op_stack(x) = copy_op_stack(sc);
  continuation_op_loc(x) = (int)(sc->op_stack_now - sc->op_stack);
  continuation_op_size(x) = sc->op_stack_size;

  continuation_key(x) = find_any_baffle(sc);

  set_type(x, T_CONTINUATION | T_PROCEDURE);
  add_continuation(sc, x);
  return(x);
}


static bool check_for_dynamic_winds(s7_scheme *sc, s7_pointer c)
{
  int i, s_base = 0, c_base = -1;
  opcode_t op;

  for (i = s7_stack_top(sc) - 1; i > 0; i -= 4)
    {
      op = stack_op(sc->stack, i);
      switch (op)
	{
	case OP_DYNAMIC_WIND:
	  {
	    s7_pointer x;
	    int j;
	    x = stack_code(sc->stack, i);
	    for (j = 3; j < continuation_stack_top(c); j += 4)
	      if ((stack_op(continuation_stack(c), j) == OP_DYNAMIC_WIND) &&
		  (x == stack_code(continuation_stack(c), j)))
		{
		  s_base = i;
		  c_base = j;
		  break;
		}
	  
	    if (s_base != 0)
	      break;	  
	  
	    if (dynamic_wind_state(x) == DWIND_BODY)
	      {
		dynamic_wind_state(x) = DWIND_FINISH;
		if (dynamic_wind_out(x) != sc->F)
		  {
		    push_stack(sc, OP_EVAL_DONE, sc->args, sc->code); 
		    sc->args = sc->NIL;
		    sc->code = dynamic_wind_out(x);
		    eval(sc, OP_APPLY);
		  }
	      }
	  }
	  break;

	case OP_BARRIER:
	  if (i > continuation_stack_top(c))  /* otherwise it's some unproblematic outer eval-string? */
	    return(false);                    /*    but what if we've already evaluated a dynamic-wind closer? */
	  break;

	case OP_DEACTIVATE_GOTO:              /* here we're jumping out of an unrelated call-with-exit block */
	  if (i > continuation_stack_top(c))
	    call_exit_active(stack_args(sc->stack, i)) = false;
	  break;

	default:
	  break;
	}
    }
  
  for (i = c_base + 4; i < continuation_stack_top(c); i += 4)
    {
      op = stack_op(continuation_stack(c), i);

      if (op == OP_DYNAMIC_WIND)
	{
	  s7_pointer x;
	  x = stack_code(continuation_stack(c), i);
	  if (dynamic_wind_in(x) != sc->F)
	    {
	      push_stack(sc, OP_EVAL_DONE, sc->args, sc->code); 
	      sc->args = sc->NIL;
	      sc->code = dynamic_wind_in(x);
	      eval(sc, OP_APPLY);
	    }
	  dynamic_wind_state(x) = DWIND_BODY;
	}
      else
	{
	  if (op == OP_DEACTIVATE_GOTO)
	    call_exit_active(stack_args(continuation_stack(c), i)) = true;
	}
    }
  return(true);
}


static bool call_with_current_continuation(s7_scheme *sc)
{
  s7_pointer c;
  c = sc->code;

  /* check for (baffle ...) blocking the current attempt to continue
   */
  if (continuation_key(c) >= 0)
    {
      if (!(find_baffle(sc, continuation_key(c))))
	{
	  /* should this raise an error? */
	  return(false);
	}
    }

  if (!check_for_dynamic_winds(sc, c)) /* if OP_BARRIER on stack deeper than continuation top(?), but when does this happen? */
    return(true);

  /* we push_stack sc->code before calling an embedded eval above, so sc->code should still be c here, etc
   */

  sc->stack = copy_stack(sc, continuation_stack(c), continuation_stack_top(c));
  sc->stack_size = continuation_stack_size(c);
  sc->stack_start = vector_elements(sc->stack);
  sc->stack_end = (s7_pointer *)(sc->stack_start + continuation_stack_top(c));
  sc->stack_resize_trigger = (s7_pointer *)(sc->stack_start + sc->stack_size / 2);

  {
    int i, top;
    top = continuation_op_loc(c);
    sc->op_stack_now = (s7_pointer *)(sc->op_stack + top);
    sc->op_stack_size = continuation_op_size(c);
    sc->op_stack_end = (s7_pointer *)(sc->op_stack + sc->op_stack_size);
    for (i = 0; i < top; i++)
      sc->op_stack[i] = continuation_op_stack(c)[i];
  }

  if (is_null(sc->args))
    sc->value = sc->NIL;
  else
    {
      if (is_null(cdr(sc->args)))
	sc->value = car(sc->args);
      else sc->value = splice_in_values(sc, sc->args);
    }
  return(true);
}


static void call_with_exit(s7_scheme *sc)
{
  int i, new_stack_top;
  
  if (!call_exit_active(sc->code))
    s7_error(sc, sc->INVALID_ESCAPE_FUNCTION, list_1(sc, make_string_wrapper(sc, "call-with-exit escape procedure called outside its block")));
  call_exit_active(sc->code) = false;
  new_stack_top = call_exit_goto_loc(sc->code);
  sc->op_stack_now = (s7_pointer *)(sc->op_stack + call_exit_op_loc(sc->code));

  /* look for dynamic-wind in the stack section that we are jumping out of */
  for (i = s7_stack_top(sc) - 1; i > new_stack_top; i -= 4)
    {
      opcode_t op;

      op = stack_op(sc->stack, i);
      switch (op)
	{
	case OP_DYNAMIC_WIND:
	  {
	    s7_pointer lx;
	    lx = stack_code(sc->stack, i);
	    if (dynamic_wind_state(lx) == DWIND_BODY)
	      {
		dynamic_wind_state(lx) = DWIND_FINISH;
		if (dynamic_wind_out(lx) != sc->F)
		  {
		    push_stack(sc, OP_EVAL_DONE, sc->args, sc->code); 
		    sc->args = sc->NIL;
		    sc->code = dynamic_wind_out(lx);
		    eval(sc, OP_APPLY);
		  }
	      }
	  }
	  break;

	case OP_EVAL_STRING_2:
	  s7_close_input_port(sc, sc->input_port);
	  pop_input_port(sc);
	  break;

	case OP_BARRIER:                /* oops -- we almost certainly went too far */
	  return; 

	case OP_DEACTIVATE_GOTO:        /* here we're jumping into an unrelated call-with-exit block */
	  call_exit_active(stack_args(sc->stack, i)) = false;
	  break;

	  /* call/cc does not close files, but I think call-with-exit should */
	case OP_GET_OUTPUT_STRING_1:
	case OP_UNWIND_OUTPUT:
	  {
	    s7_pointer x;
	    x = stack_code(sc->stack, i);                /* "code" = port that we opened */
	    s7_close_output_port(sc, x);
	    x = stack_args(sc->stack, i);                /* "args" = port that we shadowed, if not #f */
	    if (x != sc->F)
	      sc->output_port = x;
	  }
	  break;

	case OP_UNWIND_INPUT:
	  s7_close_input_port(sc, stack_code(sc->stack, i)); /* "code" = port that we opened */
	  sc->input_port = stack_args(sc->stack, i);         /* "args" = port that we shadowed */
	  break;

	default:
	  break;
	}
    }
	    
  sc->stack_end = (s7_pointer *)(sc->stack_start + new_stack_top);

  /* the return value should have an implicit values call, just as in call/cc */
  if (is_null(sc->args))
    sc->value = sc->NIL;
  else
    {
      if (is_null(cdr(sc->args)))
	sc->value = car(sc->args);
      else sc->value = splice_in_values(sc, sc->args);
    }
}


static s7_pointer g_call_cc(s7_scheme *sc, s7_pointer args)
{
  #define H_call_cc "(call-with-current-continuation func) is always a mistake!"
  s7_pointer p;

  p = car(args);                             /* this is the procedure passed to call/cc */
  if (!is_procedure(p))                      /* this includes continuations */
    {
      check_two_methods(sc, p, sc->CALL_CC, sc->CALL_WITH_CURRENT_CONTINUATION, args);
      return(simple_wrong_type_argument_with_type(sc, sc->CALL_CC, p, A_PROCEDURE));
    }
  if (!s7_is_aritable(sc, p, 1))
    return(s7_error(sc, sc->WRONG_TYPE_ARG, 
		    list_2(sc, make_string_wrapper(sc, "call/cc procedure, ~A, should take one argument"), p)));

  sc->w = s7_make_continuation(sc);
  push_stack(sc, OP_APPLY, list_1(sc, sc->w), p);
  sc->w = sc->NIL;

  return(sc->NIL);
}

/* we can't naively optimize call/cc to call-with-exit if the continuation is only
 *   used as a function in the call/cc body because it might (for example) be wrapped
 *   in a lambda form that is being exported.  See b-func in s7test for an example.
 */


static s7_pointer g_call_with_exit(s7_scheme *sc, s7_pointer args)
{
  #define H_call_with_exit "(call-with-exit func) is call/cc without the ability to jump back into a previous computation."
  s7_pointer p, x;
  
  /* (call-with-exit (lambda (return) ...)) */
  p = car(args);
  if (!is_procedure(p))                           /* this includes continuations */
    {
      check_method(sc, p, sc->CALL_WITH_EXIT, args);
      return(simple_wrong_type_argument_with_type(sc, sc->CALL_WITH_EXIT, p, A_PROCEDURE));
    }
  x = make_goto(sc);
  push_stack(sc, OP_DEACTIVATE_GOTO, x, p); /* this means call-with-exit is not tail-recursive */
  push_stack(sc, OP_APPLY, cons_unchecked(sc, x, sc->NIL), p);

  /* if the lambda body calls the argument as a function, 
   *   it is applied to its arguments, apply notices that it is a goto, and...
   *   
   *      (conceptually...) sc->stack_top = call_exit_goto_loc(sc->code);      
   *      s_pop(sc, (is_not_null(sc->args)) ? car(sc->args) : sc->NIL);
   * 
   *   which jumps to the point of the goto returning car(args).
   *
   * There is one gotcha: we can't jump back in from outside, so if the caller saves the goto
   *   and tries to invoke it outside the call-with-exit block, we have to
   *   make sure it triggers an error.  So, if the escape is called, it then
   *   deactivates itself.  Otherwise the block returns, we pop to OP_DEACTIVATE_GOTO,
   *   and it finds the goto in sc->args.
   * Even worse:
   *
       (let ((cc #f))
         (call-with-exit
           (lambda (c3)
             (call/cc (lambda (ret) (set! cc ret)))
             (c3)))
         (cc))
   *
   * where we jump back into a call-with-exit body via call/cc, the goto has to be
   * re-established.
   *
   * I think call-with-exit could be based on catch, but it's a simpler notion,
   *   and certainly at the source level it is easier to read.
   */
  return(sc->NIL);
}



/* -------------------------------- numbers -------------------------------- */

#if WITH_GMP
  static char *big_number_to_string_with_radix(s7_pointer p, int radix, int width, int *nlen, use_write_t use_write);
  static bool big_numbers_are_eqv(s7_pointer a, s7_pointer b);
  static s7_pointer string_to_either_integer(s7_scheme *sc, const char *str, int radix);
  static s7_pointer string_to_either_ratio(s7_scheme *sc, const char *nstr, const char *dstr, int radix);
  static s7_pointer string_to_either_real(s7_scheme *sc, const char *str, int radix);
  static s7_pointer string_to_either_complex(s7_scheme *sc, char *q, char *slash1, char *ex1, bool has_dec_point1, 
					     char *plus, char *slash2, char *ex2, bool has_dec_point2, int radix, int has_plus_or_minus);
  static s7_pointer big_add(s7_scheme *sc, s7_pointer args);
  static s7_pointer big_subtract(s7_scheme *sc, s7_pointer args);
  static s7_pointer big_multiply(s7_scheme *sc, s7_pointer args);
  static s7_pointer big_divide(s7_scheme *sc, s7_pointer args);
  static s7_pointer big_random(s7_scheme *sc, s7_pointer args);
  static s7_pointer s7_Int_to_big_integer(s7_scheme *sc, s7_Int val);
  static s7_pointer s7_ratio_to_big_ratio(s7_scheme *sc, s7_Int num, s7_Int den);
  static s7_pointer s7_number_to_big_real(s7_scheme *sc, s7_pointer p);
  static s7_pointer promote_number(s7_scheme *sc, int type, s7_pointer x);
  static s7_pointer big_equal(s7_scheme *sc, s7_pointer args);
  static s7_pointer big_negate(s7_scheme *sc, s7_pointer args);
  static s7_pointer big_invert(s7_scheme *sc, s7_pointer args);
  static s7_pointer big_inexact_to_exact(s7_scheme *sc, s7_pointer args);
  static s7_pointer big_exact_to_inexact(s7_scheme *sc, s7_pointer args);
  static s7_pointer mpz_to_big_integer(s7_scheme *sc, mpz_t val);
  static s7_pointer mpq_to_big_ratio(s7_scheme *sc, mpq_t val);
  static s7_pointer mpfr_to_big_real(s7_scheme *sc, mpfr_t val);
  static s7_pointer mpc_to_big_complex(s7_scheme *sc, mpc_t val);
  static s7_Int big_integer_to_s7_Int(mpz_t n);
#endif


#define s7_Int_abs(x) ((x) >= 0 ? (x) : -(x))
/* can't use abs even in gcc -- it doesn't work with long long ints! */

#define s7_fabsl(x) (((x) < 0.0) ? -(x) : (x))
/* fabsl doesn't exist in netBSD! There is a __NetBSD__ flag I think
 */

static bool is_NaN(s7_Double x) {return(x != x);}
/* callgrind says this is faster than isnan, I think (very confusing data...) */

/* in c++11 I think we'll have to #define is_inf(x) std::isinf(x) or some such subterfuge
 *   does std::isinf work in all earlier versions?
 *   Another possibility: (x * 0) != 0
 */
#if __cplusplus
  #define is_inf(x) std::isinf(x)
#else
  #define is_inf(x) isinf(x)
#endif

#if MS_WINDOWS
/* need to provide inverse hyperbolic trig funcs and cbrt */

double asinh(double x);
double asinh(double x) 
{ 
  return(log(x + sqrt(1.0 + x * x))); 
} 


double acosh(double x);
double acosh(double x)
{ 
  return(log(x + sqrt(x * x - 1.0))); 
  /* perhaps less prone to numerical troubles (untested):
   *   2.0 * log(sqrt(0.5 * (x + 1.0)) + sqrt(0.5 * (x - 1.0)))
   */
} 


double atanh(double x);
double atanh(double x)
{ 
  return(log((1.0 + x) / (1.0 - x)) / 2.0); 
} 


double cbrt(double x);
double cbrt(double x)
{
  if (x >= 0.0)
    return(pow(x, 1.0 / 3.0));
  return(-pow(-x, 1.0 / 3.0));
}

static bool is_inf(s7_Double x) {return((x == x) && (is_NaN(x - x)));}

#endif


#if defined(__sun) && defined(__SVR4)
  static bool is_inf(s7_Double x) {return((x == x) && (is_NaN(x - x)));}
#endif


#if HAVE_COMPLEX_NUMBERS
#if __cplusplus
  #define _Complex_I (complex<s7_Double>(0.0, 1.0))
  #define creal(x) Real(x)
  #define cimag(x) Imag(x)
  #define carg(x) arg(x)
  #define cabs(x) abs(x)
  #define csqrt(x) sqrt(x)
  #define cpow(x, y) pow(x, y)
  #define clog(x) log(x)
  #define cexp(x) exp(x)
  #define csin(x) sin(x)
  #define ccos(x) cos(x)
  #define csinh(x) sinh(x)
  #define ccosh(x) cosh(x)
#else
  typedef double complex s7_Complex;
#endif


#if (!HAVE_COMPLEX_TRIG)
#if (__cplusplus)

  static s7_Complex ctan(s7_Complex z)   {return(csin(z) / ccos(z));} 
  static s7_Complex ctanh(s7_Complex z)  {return(csinh(z) / ccosh(z));} 
  static s7_Complex casin(s7_Complex z)  {return(-_Complex_I * clog(_Complex_I * z + csqrt(1.0 - z * z)));} 
  static s7_Complex cacos(s7_Complex z)  {return(-_Complex_I * clog(z + _Complex_I * csqrt(1.0 - z * z)));} 
  static s7_Complex catan(s7_Complex z)  {return(_Complex_I * clog((_Complex_I + z) / (_Complex_I - z)) / 2.0);} 
  static s7_Complex casinh(s7_Complex z) {return(clog(z + csqrt(1.0 + z * z)));} 
  static s7_Complex cacosh(s7_Complex z) {return(clog(z + csqrt(z * z - 1.0)));} 
  static s7_Complex catanh(s7_Complex z) {return(clog((1.0 + z) / (1.0 - z)) / 2.0);} 
#else

/* still not in FreeBSD! */
static s7_Complex clog(s7_Complex z) {return(log(fabs(cabs(z))) + carg(z) * _Complex_I);} 
static s7_Complex cpow(s7_Complex x, s7_Complex y) 
{ 
  s7_Double r = cabs(x); 
  s7_Double theta = carg(x); 
  s7_Double yre = creal(y); 
  s7_Double yim = cimag(y); 
  s7_Double nr = exp(yre * log(r) - yim * theta); 
  s7_Double ntheta = yre * theta + yim * log(r); 
  return(nr * cos(ntheta) + (nr * sin(ntheta)) * _Complex_I); /* make-polar */ 
} 

#if (!defined(__FreeBSD__)) || (__FreeBSD__ < 9) /* untested -- this orignally looked at __FreeBSD_version which apparently no longer exists */
  static s7_Complex cexp(s7_Complex z) {return(exp(creal(z)) * cos(cimag(z)) + (exp(creal(z)) * sin(cimag(z))) * _Complex_I);} 
#endif

#if (!defined(__FreeBSD__)) || (__FreeBSD__ < 10)
  static s7_Complex csin(s7_Complex z) {return(sin(creal(z)) * cosh(cimag(z)) + (cos(creal(z)) * sinh(cimag(z))) * _Complex_I);} 
  static s7_Complex ccos(s7_Complex z) {return(cos(creal(z)) * cosh(cimag(z)) + (-sin(creal(z)) * sinh(cimag(z))) * _Complex_I);} 
  static s7_Complex csinh(s7_Complex z) {return(sinh(creal(z)) * cos(cimag(z)) + (cosh(creal(z)) * sin(cimag(z))) * _Complex_I);} 
  static s7_Complex ccosh(s7_Complex z) {return(cosh(creal(z)) * cos(cimag(z)) + (sinh(creal(z)) * sin(cimag(z))) * _Complex_I);} 
  static s7_Complex ctan(s7_Complex z) {return(csin(z) / ccos(z));} 
  static s7_Complex ctanh(s7_Complex z) {return(csinh(z) / ccosh(z));} 
  static s7_Complex casin(s7_Complex z) {return(-_Complex_I * clog(_Complex_I * z + csqrt(1.0 - z * z)));} 
  static s7_Complex cacos(s7_Complex z) {return(-_Complex_I * clog(z + _Complex_I * csqrt(1.0 - z * z)));} 
  static s7_Complex catan(s7_Complex z) {return(_Complex_I * clog((_Complex_I + z) / (_Complex_I - z)) / 2.0);} 
  static s7_Complex catanh(s7_Complex z) {return(clog((1.0 + z) / (1.0 - z)) / 2.0);} 
  static s7_Complex casinh(s7_Complex z) {return(clog(z + csqrt(1.0 + z * z)));} 
  static s7_Complex cacosh(s7_Complex z) {return(clog(z + csqrt(z * z - 1.0)));} 
  /* perhaps less prone to numerical troubles (untested): 2.0 * clog(csqrt(0.5 * (z + 1.0)) + csqrt(0.5 * (z - 1.0))) */
#endif /* not FreeBSD 10 */
#endif /* not c++ */
#endif /* not HAVE_COMPLEX_TRIG */

#else  /* not HAVE_COMPLEX_NUMBERS */
  typedef double s7_Complex;
  #define _Complex_I 1
  #define creal(x) x
  #define cimag(x) x
  #define csin(x) sin(x)
  #define casin(x) x
  #define ccos(x) cos(x)
  #define cacos(x) x
  #define ctan(x) x
  #define catan(x) x
  #define csinh(x) x
  #define casinh(x) x
  #define ccosh(x) x
  #define cacosh(x) x
  #define ctanh(x) x
  #define catanh(x) x
  #define cexp(x) exp(x)
  #define cpow(x, y) pow(x, y)
  #define clog(x) log(x)
  #define csqrt(x) sqrt(x)
  #define conj(x) x
#endif


bool s7_is_number(s7_pointer p)
{
#if WITH_GMP
  return((is_number(p)) || (is_big_number(p)));
#else
  return(is_number(p));
#endif
}


bool s7_is_integer(s7_pointer p) 
{ 
#if WITH_GMP
  return((type(p) == T_INTEGER) ||
	 (type(p) == T_BIG_INTEGER));
#else
  return(is_integer(p));
#endif
}


bool s7_is_real(s7_pointer p) 
{ 
#if WITH_GMP
  return((is_real(p)) ||
	 (type(p) == T_BIG_INTEGER) ||
	 (type(p) == T_BIG_RATIO) ||
	 (type(p) == T_BIG_REAL));
#else
  return(is_real(p)); /* in GSL, a NaN or inf is not a real, or perhaps better, finite = not (nan or inf) */
#endif
}


bool s7_is_rational(s7_pointer p)
{
#if WITH_GMP
  return((is_rational(p)) ||
	 (type(p) == T_BIG_INTEGER) ||
	 (type(p) == T_BIG_RATIO));
#else
  return(is_rational(p));
#endif
}


bool s7_is_ratio(s7_pointer p)
{
#if WITH_GMP
  return((type(p) == T_RATIO) ||
	 (type(p) == T_BIG_RATIO));
#else
  return(type(p) == T_RATIO);
#endif
}


bool s7_is_complex(s7_pointer p)
{
#if WITH_GMP
  return((is_number(p)) || (is_big_number(p)));
#else
  return(is_number(p));
#endif
}


static s7_Int c_gcd(s7_Int u, s7_Int v)
{
  s7_Int a, b;
  
  if ((u == S7_LLONG_MIN) || (v == S7_LLONG_MIN))
    {
      /* can't take abs of these (below) so do it by hand */
      s7_Int divisor = 1;
      while (((u & 1) == 0) && ((v & 1) == 0))
	{
	  u /= 2;
	  v /= 2;
	  divisor *= 2;
	}
      return(divisor);
    }

  a = s7_Int_abs(u); 
  b = s7_Int_abs(v);
  while (b != 0)
    {
      s7_Int temp;
      temp = a % b;
      a = b;
      b = temp;
    }
  if (a < 0)
    return(-a);
  return(a);
}


static bool c_rationalize(s7_Double ux, s7_Double error, s7_Int *numer, s7_Int *denom)
{
  /*
    (define* (rat ux (err 0.0000001))
      ;; translated from CL code in Canny, Donald, Ressler, "A Rational Rotation Method for Robust Geometric Algorithms"
      (let ((x0 (- ux error))
	    (x1 (+ ux error)))
        (let ((i (ceiling x0))
	      (i0 (floor x0))
	      (i1 (ceiling x1))
	      (r 0))
          (if (>= x1 i)
	      i
	      (do ((p0 i0 (+ p1 (* r p0)))
	           (q0 1 (+ q1 (* r q0)))
	           (p1 i1 p0)
	           (q1 1 q0)
	           (e0 (- i1 x0) e1p)
	           (e1 (- x0 i0) (- e0p (* r e1p)))
	           (e0p (- i1 x1) e1)
	           (e1p (- x1 i0) (- e0 (* r e1))))
	          ((<= x0 (/ p0 q0) x1)
	           (/ p0 q0))
	        (set! r (min (floor (/ e0 e1))
			     (ceiling (/ e0p e1p)))))))))
  */
  
  double x0, x1;
  s7_Int i, i0, i1, p0, q0, p1, q1;
  double e0, e1, e0p, e1p;
  int tries = 0;
  /* don't use s7_Double here;  if it is "long double", the loop below will hang */

  /* #e1e19 is a killer -- it's bigger than most-positive-fixnum, but if we ceil(ux) below
   *   it turns into most-negative-fixnum.  1e19 is trouble in many places.
   */
  if ((ux > s7_Int_max) || (ux < s7_Int_min))
    {
      /* can't return false here because that confuses some of the callers!
       */
      if (ux > s7_Int_min) (*numer) = s7_Int_max; else (*numer) = s7_Int_min;
      (*denom) = 1;
      return(true);
    }

  if (error < 0.0) error = -error;
  x0 = ux - error;
  x1 = ux + error;
  i = (s7_Int)ceil(x0);
  
  if (error >= 1.0) /* aw good grief! */
    {
      if (x0 < 0)
	{
	  if (x1 < 0)
	    (*numer) = (s7_Int)floor(x1);
	  else (*numer) = 0;
	}
      else
	{
	  (*numer) = i;
	}
      (*denom) = 1;
      return(true);
    }
  
  if (x1 >= i)
    {
      if (i >= 0)
	(*numer) = i;
      else (*numer) = (s7_Int)floor(x1);
      (*denom) = 1;
      return(true);
    }

  i0 = (s7_Int)floor(x0);
  i1 = (s7_Int)ceil(x1);

  p0 = i0; 
  q0 = 1;
  p1 = i1; 
  q1 = 1; 
  e0 = i1 - x0;
  e1 = x0 - i0;
  e0p = i1 - x1;
  e1p = x1 - i0;

  while (true)
    {
      s7_Int old_p1, old_q1;
      double old_e0, old_e1, old_e0p, val, r, r1;
      val = (double)p0 / (double)q0;
      
      if (((x0 <= val) && (val <= x1)) ||
	  (e1 == 0)                    ||
	  (e1p == 0)                   ||
	  (tries > 100))
	{
	  (*numer) = p0;
	  (*denom) = q0;
	  return(true);
	}
      tries++;

      r = (s7_Int)floor(e0 / e1);
      r1 = (s7_Int)ceil(e0p / e1p);
      if (r1 < r) r = r1;

      /* do handles all step vars in parallel */
      old_p1 = p1;
      p1 = p0;
      old_q1 = q1;
      q1 = q0;
      old_e0 = e0;
      e0 = e1p;
      old_e0p = e0p;
      e0p = e1;
      old_e1 = e1;

      p0 = old_p1 + r * p0;
      q0 = old_q1 + r * q0;
      e1 = old_e0p - r * e1p;
      /* if the error is set too low, we can get e1 = 0 here: (rationalize (/ pi) 1e-17) */
      e1p = old_e0 - r * old_e1;
    }
  return(false);
}


s7_pointer s7_rationalize(s7_scheme *sc, s7_Double x, s7_Double error)
{
  s7_Int numer = 0, denom = 1;
  if (c_rationalize(x, error, &numer, &denom))
    return(s7_make_ratio(sc, numer, denom));
  return(make_real(sc, x));
}


static s7_Int number_to_numerator(s7_pointer n)
{
  if (type(n) == T_RATIO)
    return(numerator(n));
  return(integer(n));
}


static s7_Int number_to_denominator(s7_pointer n)
{
  if (type(n) == T_RATIO)
    return(denominator(n));
  return(1);
}


s7_pointer s7_make_integer(s7_scheme *sc, s7_Int n) 
{
  s7_pointer x;

  /* if ((n & ~(NUM_SMALL_INTS - 1)) == 0)   */         /* ((n >= 0) && (n < NUM_SMALL_INTS)) is slower */
  if (is_small(n))
    return(small_int(n));

  NEW_CELL(sc, x);
  set_type(x, T_INTEGER);
  integer(x) = n;

  return(x);
}


static s7_pointer make_mutable_integer(s7_scheme *sc, s7_Int n)
{
  s7_pointer x;
  NEW_CELL(sc, x);
  set_type(x, T_INTEGER | T_MUTABLE);
  integer(x) = n;
  return(x);
}


static s7_pointer make_permanent_integer(s7_Int i)
{
  s7_pointer p;
  p = (s7_pointer)calloc(1, sizeof(s7_cell));
  typeflag(p) = T_IMMUTABLE | T_INTEGER;
  heap_location(p) = NOT_IN_HEAP;
  integer(p) = i;
  return(p);
}


s7_pointer s7_make_real(s7_scheme *sc, s7_Double n) 
{
  s7_pointer x;
  /* in snd-test this is called about 40000000 times, primarily test 8/18/22 */

  if (n == 0.0)
    return(real_zero);

  NEW_CELL(sc, x);
  set_type(x, T_REAL);
  real(x) = n;
  
  return(x);
}


s7_pointer s7_make_mutable_real(s7_scheme *sc, s7_Double n) 
{
  s7_pointer x;
  NEW_CELL(sc, x);
  set_type(x, T_REAL | T_MUTABLE);
  real(x) = n;
  return(x);
}

bool s7_is_mutable(s7_pointer p)
{
  return(is_mutable(p));
}


static s7_pointer make_permanent_real(s7_Double n) 
{
  s7_pointer x;
  int nlen = 0;
  char *str;

  x = (s7_pointer)calloc(1, sizeof(s7_cell));
  set_type(x, T_IMMUTABLE | T_REAL);
  heap_location(x) = NOT_IN_HEAP;
  real(x) = n;

  str = number_to_string_base_10(x, 0, WRITE_REAL_PRECISION, 'g', &nlen, USE_WRITE);
  set_print_name(x, str, nlen);
  return(x);
}


s7_pointer s7_remake_real(s7_scheme *sc, s7_pointer rl, s7_Double n) 
{
  if (is_simple_real(rl))
    {
      real(rl) = n;
      return(rl);
    }
  return(make_real(sc, n));
}


s7_pointer s7_make_complex(s7_scheme *sc, s7_Double a, s7_Double b)
{
  s7_pointer x;
  NEW_CELL(sc, x);
  if (b == 0.0)
    {
      set_type(x, T_REAL);
      real(x) = a;
    }
  else
    {
      set_type(x, T_COMPLEX);
      real_part(x) = a;
      imag_part(x) = b;
    }
  return(x);
}


s7_pointer s7_make_ratio(s7_scheme *sc, s7_Int a, s7_Int b)
{
  s7_pointer x;
  s7_Int divisor;

  if (b == 0)
    return(division_by_zero_error(sc, make_string_wrapper(sc, "make-ratio"), list_2(sc, make_integer(sc, a), small_int(0))));
  if (a == 0)
    return(small_int(0));
  if (b == 1)
    return(make_integer(sc, a));

#if (!WITH_GMP)
  if (b == S7_LLONG_MIN)
    {
      if (a == b)
	return(small_int(1));

      /* we've got a problem... This should not trigger an error during reading -- we might have the
       *   ratio on a switch with-bignums or whatever, so its mere occurrence is just an annoyance.
       *   We'll try to do something...
       */
      if (a & 1)
	{
	  if (a == 1)
	    return(real_NaN);
	  /* not an error here? we can't get this in the ratio reader, I think, because the denominator is negative */
	  b = b + 1;
	  /* so (/ -1 most-negative-fixnum) -> 1/9223372036854775807 -- not ideal, but ... */
	}
      else
	{
	  a /= 2;
	  b /= 2;
	}
    }
#endif

  if (b < 0)
    {
      a = -a;
      b = -b;
    }
  divisor = c_gcd(a, b);
  if (divisor != 1)
    {
      a /= divisor;
      b /= divisor;
    }
  if (b == 1)
    return(make_integer(sc, a));
  
  NEW_CELL(sc, x);
  set_type(x, T_RATIO);
  numerator(x) = a;
  denominator(x) = b;

  return(x);
}
/* in fc19 as a guest running in virtualbox on OSX, the line  a /= divisor can abort with an arithmetic exception (SIGFPE)
 *    if leastfix/mostfix -- apparently this is a bug in virtualbox.
 */


static s7_pointer exact_to_inexact(s7_scheme *sc, s7_pointer x)
{
  /* this is tricky because a big int can mess up when turned into a double:
   *   (truncate (exact->inexact most-positive-fixnum)) -> -9223372036854775808
   */
  switch (type(x))
    {
    case T_INTEGER:
      return(make_real(sc, (s7_Double)(integer(x))));

    case T_RATIO:
      return(make_real(sc, (s7_Double)(fraction(x))));

    case T_REAL:
    case T_COMPLEX:              /* apparently (exact->inexact 1+i) is not an error */
      return(x);

    default:
      check_method(sc, x, sc->EXACT_TO_INEXACT, list_1(sc, x));
      return(simple_wrong_type_argument_with_type(sc, sc->EXACT_TO_INEXACT, x, A_NUMBER));
    }
}


#define WITH_OVERFLOW_ERROR true
#define WITHOUT_OVERFLOW_ERROR false

static s7_pointer inexact_to_exact(s7_scheme *sc, s7_pointer x, bool with_error)
{
  switch (type(x))
    {
    case T_INTEGER:
    case T_RATIO:
      return(x);

    case T_REAL:
      {
	s7_Int numer = 0, denom = 1;
	s7_Double val;

	val = s7_real(x);
	if ((is_inf(val)) || (is_NaN(val)))
	  {
	    if (with_error)
	      return(simple_wrong_type_argument_with_type(sc, sc->INEXACT_TO_EXACT, x, A_NORMAL_REAL));
	    return(sc->NIL);
	  }

	if ((val > S7_LLONG_MAX) ||
	    (val < S7_LLONG_MIN))
	  {
	    if (with_error)
	      return(simple_out_of_range(sc, sc->INEXACT_TO_EXACT, x, ITS_TOO_LARGE));
	    return(sc->NIL);
	  }

	if (c_rationalize(val, sc->default_rationalize_error, &numer, &denom))
	  return(s7_make_ratio(sc, numer, denom));
      }

    default:
      if (with_error)
	{
	  check_method(sc, x, sc->INEXACT_TO_EXACT, list_1(sc, x));
	  return(simple_wrong_type_argument(sc, sc->INEXACT_TO_EXACT, x, T_REAL));
	}
      return(sc->NIL);
    }
  return(x);
}


s7_Double s7_number_to_real_with_caller(s7_scheme *sc, s7_pointer x, const char *caller)
{
  if (type(x) == T_REAL)
    return(real(x));
  /* this is nearly always the case in current usage, so by avoiding the "switch" we can go twice as fast */

  switch (type(x))
    {
    case T_INTEGER: 
      return((s7_Double)integer(x));

    case T_RATIO:   
      return((s7_Double)numerator(x) / (s7_Double)denominator(x));

    case T_REAL:    
      return(real(x));

    case T_COMPLEX:
      return(real_part(x));

#if WITH_GMP
    case T_BIG_INTEGER:
      return((s7_Double)big_integer_to_s7_Int(big_integer(x)));

    case T_BIG_RATIO:
      return((s7_Double)((long double)big_integer_to_s7_Int(mpq_numref(big_ratio(x))) / (long double)big_integer_to_s7_Int(mpq_denref(big_ratio(x)))));

    case T_BIG_REAL:
      return((s7_Double)mpfr_get_d(big_real(x), GMP_RNDN));

    case T_BIG_COMPLEX:
      return((s7_Double)mpfr_get_d(mpc_realref(big_complex(x)), GMP_RNDN));
#endif
    }

  s7_wrong_type_arg_error(sc, caller, 0, x, "a real number"); 
  return(0.0);
}


s7_Double s7_number_to_real(s7_scheme *sc, s7_pointer x)
{
  return(s7_number_to_real_with_caller(sc, x, "s7_number_to_real"));
}


s7_Int s7_number_to_integer_with_caller(s7_scheme *sc, s7_pointer x, const char *caller)
{
  /* this is called in xen.c */
  if (type(x) == T_INTEGER)
    return(integer(x));

  switch (type(x))
    {
    case T_INTEGER: 
      return(integer(x));

    case T_RATIO:   
      return((s7_Int)numerator(x) / (s7_Double)denominator(x));

    case T_REAL:    
      return((s7_Int)real(x));

    case T_COMPLEX: 
      return((s7_Int)real_part(x));

#if WITH_GMP
    case T_BIG_INTEGER:
      return(big_integer_to_s7_Int(big_integer(x)));

    case T_BIG_RATIO:
      return((s7_Int)((double)big_integer_to_s7_Int(mpq_numref(big_ratio(x))) / (double)big_integer_to_s7_Int(mpq_denref(big_ratio(x)))));

    case T_BIG_REAL:
      return((s7_Int)mpfr_get_d(big_real(x), GMP_RNDN));

    case T_BIG_COMPLEX:
      return((s7_Int)mpfr_get_d(mpc_realref(big_complex(x)), GMP_RNDN));
#endif
    }
  s7_wrong_type_arg_error(sc, caller, 0, x, "an integer");
  return(0);
}

s7_Int s7_number_to_integer(s7_scheme *sc, s7_pointer x)
{
  return(s7_number_to_integer_with_caller(sc, x, "s7_number_to_integer"));
}


s7_Int s7_numerator(s7_pointer x)
{
  switch (type(x))
    {
    case T_INTEGER:
      return(integer(x));

    case T_RATIO:
      return(numerator(x));

#if WITH_GMP
    case T_BIG_INTEGER:
      return(big_integer_to_s7_Int(big_integer(x)));

    case T_BIG_RATIO:
      return(big_integer_to_s7_Int(mpq_numref(big_ratio(x))));
#endif
    }
  return(0);
}


s7_Int s7_denominator(s7_pointer x)
{
  switch (type(x))
    {
    case T_RATIO:
      return(denominator(x));

#if WITH_GMP
    case T_BIG_RATIO:
      return(big_integer_to_s7_Int(mpq_denref(big_ratio(x))));
#endif
    }
  return(1);
}


s7_Int s7_integer(s7_pointer p)
{
#if WITH_GMP
  if (type(p) == T_BIG_INTEGER)
    return(big_integer_to_s7_Int(big_integer(p)));
#endif
  return(integer(p));
}


s7_Double s7_real(s7_pointer p)
{
#if WITH_GMP
  if (type(p) == T_BIG_REAL)
    return((s7_Double)mpfr_get_d(big_real(p), GMP_RNDN));
#endif
  return(real(p));
}


#if (!WITH_GMP)
static s7_Complex s7_complex(s7_pointer p)
{
#if HAVE_COMPLEX_NUMBERS
  return(CMPLX(s7_real_part(p), s7_imag_part(p)));
#else
  return(0.0);
#endif
}


static s7_pointer s7_from_c_complex(s7_scheme *sc, s7_Complex z)
{
  return(s7_make_complex(sc, creal(z), cimag(z)));
}
#endif


static int integer_length(s7_Int a)
{
  static const int bits[256] =
    {0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
     6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 
     7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 
     7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 
     8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 
     8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 
     8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 
     8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8};

  #define I_8 256LL
  #define I_16 65536LL
  #define I_24 16777216LL
  #define I_32 4294967296LL
  #define I_40 1099511627776LL
  #define I_48 281474976710656LL
  #define I_56 72057594037927936LL

  /* a might be most-negative-fixnum! in Clisp: (integer-length -9223372036854775808) -> 63
   */
  if (a < 0) 
    {
      if (a == S7_LLONG_MIN) return(63);
      a = -a;
    }
  if (a < I_8) return(bits[a]);
  if (a < I_16) return(8 + bits[a >> 8]);
  if (a < I_24) return(16 + bits[a >> 16]);
  if (a < I_32) return(24 + bits[a >> 24]);
  if (a < I_40) return(32 + bits[a >> 32]);
  if (a < I_48) return(40 + bits[a >> 40]);
  if (a < I_56) return(48 + bits[a >> 48]);
  return(56 + bits[a >> 56]);
}


static int s7_int_max = 0, s7_int_min = 0, s7_int_bits = 0, s7_int_digits = 0; /* initialized later */
static int s7_int_digits_by_radix[17];


#if (!WITH_GMP)
static s7_pointer s7_negate(s7_scheme *sc, s7_pointer p)     /* can't use "negate" because it confuses C++! */
{
  switch (type(p))
    {
    case T_INTEGER: 
      return(make_integer(sc, -integer(p)));
      
    case T_RATIO:
      return(s7_make_ratio(sc, -numerator(p), denominator(p)));
      
    case T_REAL:
      return(make_real(sc, -real(p)));
      
    default:
      return(s7_make_complex(sc, -real_part(p), -imag_part(p)));
    }
}
#endif


static s7_pointer s7_invert(s7_scheme *sc, s7_pointer p)      /* s7_ to be consistent... */
{
  switch (type(p))
    {
    case T_INTEGER:
      return(s7_make_ratio(sc, 1, integer(p)));      /* a already checked, not 0 */
      
    case T_RATIO:
      return(s7_make_ratio(sc, denominator(p), numerator(p)));

    case T_REAL:
      return(make_real(sc, 1.0 / real(p)));

    case T_COMPLEX:
      {
	s7_Double r2, i2, den;
	r2 = real_part(p);
	i2 = imag_part(p);
	den = (r2 * r2 + i2 * i2);
	return(s7_make_complex(sc, r2 / den, -i2 / den));
      }

    default:
      return(wrong_type_argument_with_type(sc, sc->DIVIDE, small_int(1), p, A_NUMBER));
    }
}


static s7_pointer subtract_ratios(s7_scheme *sc, s7_pointer x, s7_pointer y)
{
  s7_Int d1, d2, n1, n2;
  d1 = number_to_denominator(x);
  n1 = number_to_numerator(x);
  d2 = number_to_denominator(y);
  n2 = number_to_numerator(y);
  
  if (d1 == d2)                                     /* the easy case -- if overflow here, it matches the int case */
    return(s7_make_ratio(sc, n1 - n2, d1));

#if (!WITH_GMP)
  if ((d1 > s7_int_max) || (d2 > s7_int_max) ||     /* before counting bits, check that overflow is possible */
      (n1 > s7_int_max) || (n2 > s7_int_max) ||
      (n1 < s7_int_min) || (n2 < s7_int_min))
    {
      int d1bits, d2bits;
      d1bits = integer_length(d1);
      d2bits = integer_length(d2);
      if (((d1bits + d2bits) > s7_int_bits) ||
	  ((d1bits + integer_length(n2)) > (s7_int_bits - 1)) ||
	  ((d2bits + integer_length(n1)) > (s7_int_bits - 1)))
	return(make_real(sc, ((long double)n1 / (long double)d1) - ((long double)n2 / (long double)d2)));
      return(s7_make_ratio(sc, n1 * d2 - n2 * d1, d1 * d2));
    }
#endif
  return(s7_make_ratio(sc, n1 * d2 - n2 * d1, d1 * d2));
}


static bool s7_is_negative(s7_pointer obj)
{
  switch (type(obj))
    {
    case T_INTEGER: 
      return(integer(obj) < 0);

    case T_RATIO:   
      return(numerator(obj) < 0);

#if WITH_GMP
    case T_BIG_INTEGER:
      return(mpz_cmp_ui(big_integer(obj), 0) < 0);

    case T_BIG_RATIO:
      return(mpq_cmp_ui(big_ratio(obj), 0, 1) < 0);

    case T_BIG_REAL:
      return(mpfr_cmp_ui(big_real(obj), 0) < 0);
#endif

    default:        
      return(real(obj) < 0);
    }
}


static bool s7_is_positive(s7_pointer x)
{
  switch (type(x))
    {
    case T_INTEGER: 
      return(integer(x) > 0);

    case T_RATIO:   
      return(numerator(x) > 0);

#if WITH_GMP
    case T_BIG_INTEGER:
      return(mpz_cmp_ui(big_integer(x), 0) > 0);

    case T_BIG_RATIO:
      return(mpq_cmp_ui(big_ratio(x), 0, 1) > 0);

    case T_BIG_REAL:
      return(mpfr_cmp_ui(big_real(x), 0) > 0);
#endif

    default:        
      return(real(x) > 0.0);
    }
}


static bool s7_is_zero(s7_pointer x)
{
  switch (type(x))
    {
    case T_INTEGER: 
      return(integer(x) == 0);

    case T_REAL:    
      return(real(x) == 0.0);

#if WITH_GMP
    case T_BIG_INTEGER:
      return(mpz_cmp_ui(big_integer(x), 0) == 0);

    case T_BIG_REAL:
      return(mpfr_zero_p(big_real(x)));
#endif

    default:        
      return(false); /* ratios and complex numbers here are already collapsed into integers and reals */
    }
}


static bool s7_is_one(s7_pointer x)
  {
  switch (type(x))
    {
    case T_INTEGER: 
      return(integer(x) == 1);

    case T_REAL:    
      return(real(x) == 1.0);

    default:        
      return(false);
    }
}


/* optimize exponents */
#define MAX_POW 32
static double pepow[17][MAX_POW], mepow[17][MAX_POW];

static void init_pows(void)
{
  int i, j;
  for (i = 2; i < 17; i++)        /* radix between 2 and 16 */
    for (j = 0; j < MAX_POW; j++) /* saved exponent between 0 and +/- MAX_POW */
      {
	pepow[i][j] = pow((double)i, (double)j);
	mepow[i][j] = pow((double)i, (double)(-j));
      }
}

static double ipow(int x, int y)
{
  if ((y < MAX_POW) && (y > (-MAX_POW)))
    {
      if (y >= 0)
	return(pepow[x][y]);
      return(mepow[x][-y]);
    }
  return(pow((double)x, (double)y));
}


static int s7_Int_to_string(char *p, s7_Int n, int radix, int width)
{
  static const char dignum[] = "0123456789abcdef";
  int i = 2, len, start = 0, end = 0;
  s7_Int pown = (s7_Int)1;
  bool sign;

  if ((radix < 2) || (radix > 16))
    return(0);
  if (n == 0)
    {
      if (width <= 1)
	{
	  p[0] = '0';
	  p[1] = '\0';
	  return(1);
	}
      for (i = 0; i < width - 1; i++) 
	p[i] = ' ';
      p[width - 1] = '0';
      p[width] = '\0';
      return(width);
    }

  if (n == S7_LLONG_MIN)
    {
      /* a special case -- we can't use abs on this because it goes to 0, we won't get here if gmp.
       * (number->string most-negative-fixnum 2) -> "-0" unless we do something special 
       */
      int j;
      p[0] = '-';
      /* build it backwards (will reverse digits below) */
      p[1] = dignum[-(n % (s7_Int)radix)];
      n /= (s7_Int)radix;
      n = -n;
      for (i = 2; n >= (s7_Int)radix; i++)
	{
	  p[i] = dignum[n % (s7_Int)radix];
	  n /= (s7_Int)radix;
	}
      p[i] = dignum[n];
      len = i;
      /* reverse digits (leave sign alone) */
      for (i = 1, j = len; i < j; i++, j--)
	{
	  char tmp;
	  tmp = p[i];
	  p[i] = p[j];
	  p[j] = tmp;
	}
      p[len + 1] = 0;
      return(len + 1);
      /* there has to be a better way... */
    }
      
  sign = (n < 0);
  n = s7_Int_abs(n); 

  /* the previous version that counted up to n, rather than dividing down below n, as here,
   *   could be confused by large ints on 64 bit machines
   */
  pown = n;
  for (i = 1; i < 100; i++)
    {
      if (pown < radix)
	break;
      pown /= (s7_Int)radix;
    }
  len = i - 1;

  if (sign) len++;

  if (width > len)                  /* (format #f "~10B" 123) */
    {
      start = width - len - 1;
      end += start;
      memset((void *)p, (int)' ', start);
    }

  if (sign)
    {
      p[start] = '-';
      end++;
    }

  for (i = start + len; i >= end; i--)
    {
      p[i] = dignum[n % (s7_Int)radix];
      n /= (s7_Int)radix;
    }
  p[len + start + 1] = '\0';
  return(len + start + 1);
}


static char *integer_to_string_base_10_no_width(s7_pointer obj, int *nlen) /* do not free the returned string */
{
  #define INT_TO_STR_SIZE 64
  static char int_to_str[INT_TO_STR_SIZE];

  if (has_print_name(obj))
    {
      (*nlen) = print_name_length(obj);
      return((char *)print_name(obj));
    }
  (*nlen) = snprintf(int_to_str, INT_TO_STR_SIZE, "%lld", (long long int)integer(obj));
  return(int_to_str);
}


#define BASE_10 10
static int num_to_str_size = 0;
static char *num_to_str = NULL;

static char *floatify(char *str, int *nlen)
{
  if ((strchr(str, 'e') == NULL) &&
      (strchr(str, '.') == NULL))
    {
      int len;
      len = *nlen;
      str[len]='.';
      str[len + 1]='0';
      str[len + 2]='\0';
      (*nlen) = len + 2;
    }
  return(str);
}

static const char *float_format_g = NULL;

static char *number_to_string_base_10(s7_pointer obj, int width, int precision, char float_choice, int *nlen, use_write_t choice) /* don't free result */
{
  /* the rest of s7 assumes nlen is set to the correct length */
  int len;
  len = 256 + width;
  if (len > num_to_str_size)
    {
      if (num_to_str) free(num_to_str);
      num_to_str_size = len;
      num_to_str = (char *)malloc(len);
    }
  /* bignums can't happen here */
  switch (type(obj))
    {
    case T_INTEGER:
      if (width == 0)
	return(integer_to_string_base_10_no_width(obj, nlen));
      (*nlen) = snprintf(num_to_str, num_to_str_size, "%*lld", width, (long long int)integer(obj));
      break;
      
    case T_RATIO:
      len = snprintf(num_to_str, num_to_str_size, "%lld/%lld", (long long int)numerator(obj), (long long int)denominator(obj));
      if (width > len)
	{
	  int spaces;
	  if (width >= num_to_str_size)
	    {
	      num_to_str_size = width + 1;
	      num_to_str = (char *)realloc(num_to_str, num_to_str_size * sizeof(char));
	    }
	  spaces = width - len;
	  num_to_str[width] = '\0';
	  memmove((void *)(num_to_str + spaces), (void *)num_to_str, len);    
	  memset((void *)num_to_str, (int)' ', spaces);	    
	  (*nlen) = width;
	}
      else (*nlen) = len;
      break;
      
    case T_REAL:
      {
	const char *frmt;
	if (sizeof(double) >= sizeof(s7_Double))
	  frmt = (float_choice == 'g') ? "%*.*g" : ((float_choice == 'f') ? "%*.*f" : "%*.*e");
	else frmt = (float_choice == 'g') ? "%*.*Lg" : ((float_choice == 'f') ? "%*.*Lf" : "%*.*Le");
	
	len = snprintf(num_to_str, num_to_str_size, frmt, width, precision, s7_real(obj));
	(*nlen) = len;
	floatify(num_to_str, nlen);
      }
      break;
      
    default:
      {
	if ((choice == USE_READABLE_WRITE) &&
	    ((is_NaN(real_part(obj))) || (is_NaN(imag_part(obj))) || ((is_inf(real_part(obj))) || (is_inf(imag_part(obj))))))
	  {
	    char rbuf[128], ibuf[128];
	    char *rp, *ip;
	    if (is_NaN(real_part(obj)))
	      rp = (char *)"nan.0";
	    else
	      {
		if (is_inf(real_part(obj)))
		  {
		    if (real_part(obj) < 0.0)
		      rp = (char *)"-inf.0";
		    else rp = (char *)"inf.0";
		  }
		else
		  {
		    snprintf(rbuf, 128, float_format_g, precision, real_part(obj));
		    rp = rbuf;
		  }
	      }
	    if (is_NaN(imag_part(obj)))
	      ip = (char *)"nan.0";
	    else
	      {
		if (is_inf(imag_part(obj)))
		  {
		    if (imag_part(obj) < 0.0)
		      ip = (char *)"-inf.0";
		    else ip = (char *)"inf.0";
		  }
		else
		  {
		    snprintf(ibuf, 128, float_format_g, precision, imag_part(obj));
		    ip = ibuf;
		  }
	      }
	    len = snprintf(num_to_str, num_to_str_size, "(make-rectangular %s %s)", rp, ip);
	  }
	else
	  {
	    const char *frmt;
	    if (sizeof(double) >= sizeof(s7_Double))
	      {
		if (imag_part(obj) >= 0.0)
		  frmt = (float_choice == 'g') ? "%.*g+%.*gi" : ((float_choice == 'f') ? "%.*f+%.*fi" : "%.*e+%.*ei"); 
		else frmt = (float_choice == 'g') ? "%.*g%.*gi" : ((float_choice == 'f') ? "%.*f%.*fi" :"%.*e%.*ei"); /* minus sign comes with the imag_part */
	      }
	    else 
	      {
		if (imag_part(obj) >= 0.0)
		  frmt = (float_choice == 'g') ? "%.*Lg+%.*Lgi" : ((float_choice == 'f') ? "%.*Lf+%.*Lfi" : "%.*Le+%.*Lei");
		else frmt = (float_choice == 'g') ? "%.*Lg%.*Lgi" : ((float_choice == 'f') ? "%.*Lf%.*Lfi" : "%.*Le%.*Lei");
	      }
	    
	    len = snprintf(num_to_str, num_to_str_size, frmt, precision, real_part(obj), precision, imag_part(obj));
	  }

	if (width > len)  /* (format #f "~20g" 1+i) */
	  {
	    int spaces;
	    if (width >= num_to_str_size)
	      {
		num_to_str_size = width + 1;
		num_to_str = (char *)realloc(num_to_str, num_to_str_size * sizeof(char));
	      }
	    spaces = width - len;
	    num_to_str[width] = '\0';
	    memmove((void *)(num_to_str + spaces), (void *)num_to_str, len);    
	    memset((void *)num_to_str, (int)' ', spaces);	    
	    (*nlen) = width;
	  }
	else (*nlen) = len;
      }
      break;
    }
  return(num_to_str);
}


static char *number_to_string_with_radix(s7_scheme *sc, s7_pointer obj, int radix, int width, int precision, char float_choice, int *nlen)
{
  /* the rest of s7 assumes nlen is set to the correct length */
  char *p, *n, *d;
  int len, str_len;

#if WITH_GMP
  if (s7_is_bignum(obj))
    return(big_number_to_string_with_radix(obj, radix, width, nlen, USE_WRITE));
  /* this ignores precision because it's way too hard to get the mpfr string to look like
   *   C's output -- we either have to call mpfr_get_str twice (the first time just to 
   *   find out what the exponent is and how long the string actually is), or we have
   *   to do messy string manipulations.  So (format #f "",3F" pi) ignores the "3" and
   *   prints the full string.
   */
#endif

  if (radix == 10)
    {
      p = number_to_string_base_10(obj, width, precision, float_choice, nlen, USE_WRITE);
      return(copy_string_with_length(p, *nlen));
    }

  switch (type(obj))
    {
    case T_INTEGER:
      if ((width == 0) &&
	  (radix == 10))
	{
	  p = integer_to_string_base_10_no_width(obj, nlen);
	  return(copy_string_with_length(p, *nlen));
	}
      p = (char *)malloc((128 + width) * sizeof(char));
      *nlen = s7_Int_to_string(p, s7_integer(obj), radix, width);
      return(p);
      break;
      
    case T_RATIO:
      {
	char n[128], d[128];
	s7_Int_to_string(n, numerator(obj), radix, 0);
	s7_Int_to_string(d, denominator(obj), radix, 0);
	p = (char *)malloc(256 * sizeof(char));
	len = snprintf(p, 256, "%s/%s", n, d);
	str_len = 256;
      }
      break;
      
    case T_REAL:
      {
	int i;
	s7_Int int_part;
	s7_Double x, frac_part, min_frac, base;
	bool sign = false;
	char n[128], d[256];

	x = s7_real(obj);

	if (is_NaN(x))
	  return(copy_string_with_length("nan.0", *nlen = 5));
	if (is_inf(x))
	  {
	    if (x < 0.0)
	      return(copy_string_with_length("-inf.0", *nlen = 6));    
	    return(copy_string_with_length("inf.0", *nlen = 5));    
	  }

	if (x < 0.0)
	  {
	    sign = true;
	    x = -x;
	  }

	int_part = (s7_Int)floor(x);
	frac_part = x - int_part;
	s7_Int_to_string(n, int_part, radix, 0);
	min_frac = (s7_Double)ipow(radix, -precision);

	for (i = 0, base = radix; (i < precision) && (frac_part > min_frac); i++, base *= radix)
	  {
	    s7_Int ipart;
	    ipart = (int)(frac_part * base);
	    if (ipart >= radix)         /* rounding confusion */
	      ipart = radix - 1;
	    frac_part -= (ipart / base);
	    if (ipart < 10)
	      d[i] = (char)('0' + ipart);
	    else d[i] = (char)('a' + ipart -  10);
	  }
	if (i == 0)
	  d[i++] = '0';
	d[i] = '\0';
	p = (char *)malloc(256 * sizeof(char));
	len = snprintf(p, 256, "%s%s.%s", (sign) ? "-" : "", n, d);
	str_len = 256;
      }
      break;

    default:
      p = (char *)malloc(512 * sizeof(char));
      n = number_to_string_with_radix(sc, make_real(sc, real_part(obj)), radix, 0, precision, float_choice, &len);
      d = number_to_string_with_radix(sc, make_real(sc, imag_part(obj)), radix, 0, precision, float_choice, &len);
      len = snprintf(p, 512, "%s%s%si", n, (imag_part(obj) < 0.0) ? "" : "+", d);
      str_len = 512;
      free(n);
      free(d);
      break;
    }

  if (width > len)
    {
      int spaces;
      if (width >= str_len)
	{
	  str_len = width + 1;
	  p = (char *)realloc(p, str_len * sizeof(char));
	}
      spaces = width - len;
      p[width] = '\0';
      memmove((void *)(p + spaces), (void *)p, len);    
      memset((void *)p, (int)' ', spaces);	    
      (*nlen) = width;
    }
  else (*nlen) = len;
  return(p);
}


char *s7_number_to_string(s7_scheme *sc, s7_pointer obj, int radix)
{
  int nlen = 0;
  return(number_to_string_with_radix(sc, obj, radix, 0, 20, 'g', &nlen)); 
  /* (log top 10) so we get all the digits in base 10 (??) */
}


static s7_pointer g_number_to_string(s7_scheme *sc, s7_pointer args)
{
  #define H_number_to_string "(number->string num (radix 10)) converts the number num into a string."
  s7_Int radix = 10;
  int size = 20, nlen = 0;
  char *res;
  s7_pointer x;

  x = car(args);
  if (!s7_is_number(x))
    {
      check_method(sc, x, sc->NUMBER_TO_STRING, args);
      return(wrong_type_argument_with_type(sc, sc->NUMBER_TO_STRING, small_int(1), x, A_NUMBER));
    }
  if (is_pair(cdr(args)))
    {
      s7_pointer y;
      y = cadr(args);
      if (s7_is_integer(y))
	radix = s7_integer(y);
      else 
	{
	  check_method(sc, y, sc->NUMBER_TO_STRING, args);
	  return(wrong_type_argument(sc, sc->NUMBER_TO_STRING, small_int(2), y, T_INTEGER));
	}
      if ((radix < 2) || (radix > 16))
	return(out_of_range(sc, sc->NUMBER_TO_STRING, small_int(2), y, A_VALID_RADIX));
    }

#if WITH_GMP
  if (s7_is_bignum(x))
    {
      res = big_number_to_string_with_radix(x, radix, 0, &nlen, USE_WRITE);
      return(make_string_uncopied_with_length(sc, res, nlen));
    }
#endif

  if (!is_rational(x))
    {
      /* if size = 20, (number->string .1) gives "0.10000000000000000555", but if it's less than 20,
       *    large numbers (or very small numbers) mess up the less significant digits.
       */
      if (is_real(x))
	{
	  s7_Double val;
	  val = fabs(s7_real(x));
	  if ((val < (S7_LONG_MAX / 4)) && (val > 1.0e-6))
	    size = WRITE_REAL_PRECISION;
	}
      else
	{
	  s7_Double rl;
	  rl = fabs(s7_real_part(x));
	  if ((rl < (S7_LONG_MAX / 4)) && (rl > 1.0e-6))
	    {
	      s7_Double im;
	      im = fabs(s7_imag_part(x));
	      if ((im < (S7_LONG_MAX / 4)) && (im > 1.0e-6))
		size = WRITE_REAL_PRECISION;
	    }
	}
    }

  if (radix != 10)
    {
      res = number_to_string_with_radix(sc, x, radix, 0, (radix == 10) ? size : 20, 'g', &nlen);
      return(make_string_uncopied_with_length(sc, res, nlen));
    }
  res = number_to_string_base_10(x, 0, size, 'g', &nlen, USE_WRITE);
  return(s7_make_string_with_length(sc, res, nlen));
}


#define CTABLE_SIZE 256
static bool *exponent_table, *slashify_table, *char_ok_in_a_name, *white_space, *number_table;
static int *digits;

static void init_ctables(void)
{
  int i;

  exponent_table = (bool *)calloc(CTABLE_SIZE, sizeof(bool));
  slashify_table = (bool *)calloc(CTABLE_SIZE, sizeof(bool));
  char_ok_in_a_name = (bool *)calloc(CTABLE_SIZE, sizeof(bool));
  white_space = (bool *)calloc(CTABLE_SIZE + 1, sizeof(bool));
  white_space++;      /* leave white_space[-1] false for white_space[EOF] */
  number_table = (bool *)calloc(CTABLE_SIZE, sizeof(bool));

  for (i = 1; i < CTABLE_SIZE; i++)
    char_ok_in_a_name[i] = true;
  char_ok_in_a_name[0] = false;
  char_ok_in_a_name[(unsigned char)'('] = false;  /* idiotic cast is for C++'s benefit */
  char_ok_in_a_name[(unsigned char)')'] = false;
  char_ok_in_a_name[(unsigned char)';'] = false;
  char_ok_in_a_name[(unsigned char)'\t'] = false;
  char_ok_in_a_name[(unsigned char)'\n'] = false;
  char_ok_in_a_name[(unsigned char)'\r'] = false;
  char_ok_in_a_name[(unsigned char)' '] = false;
  char_ok_in_a_name[(unsigned char)'"'] = false;
  /* double-quote is recent, but I want '(1 ."hi") to be parsed as '(1 . "hi") 
   * what about stuff like vertical tab?  or comma?
   */

  for (i = 0; i < CTABLE_SIZE; i++)
    white_space[i] = false;
  white_space[(unsigned char)'\t'] = true;
  white_space[(unsigned char)'\n'] = true;
  white_space[(unsigned char)'\r'] = true;
  white_space[(unsigned char)'\f'] = true;
  white_space[(unsigned char)'\v'] = true;
  white_space[(unsigned char)' '] = true;

  /* surely only 'e' is needed... */
  exponent_table[(unsigned char)'e'] = true; exponent_table[(unsigned char)'E'] = true;
#if WITH_AT_SIGN_AS_EXPONENT
  exponent_table[(unsigned char)'@'] = true; 
#endif
#if WITH_EXTRA_EXPONENT_MARKERS
  exponent_table[(unsigned char)'s'] = true; exponent_table[(unsigned char)'S'] = true; 
  exponent_table[(unsigned char)'f'] = true; exponent_table[(unsigned char)'F'] = true;
  exponent_table[(unsigned char)'d'] = true; exponent_table[(unsigned char)'D'] = true;
  exponent_table[(unsigned char)'l'] = true; exponent_table[(unsigned char)'L'] = true;
#endif

  for (i = 0; i < 32; i++)
    slashify_table[i] = true;
  for (i = 127; i < 160; i++)
    slashify_table[i] = true;
  slashify_table[(unsigned char)'\\'] = true;
  slashify_table[(unsigned char)'"'] = true;
  slashify_table[(unsigned char)'\n'] = false;

  digits = (int *)calloc(CTABLE_SIZE, sizeof(int));
  for (i = 0; i < CTABLE_SIZE; i++)
    digits[i] = 256;

  digits[(unsigned char)'0'] = 0; digits[(unsigned char)'1'] = 1; digits[(unsigned char)'2'] = 2; digits[(unsigned char)'3'] = 3; digits[(unsigned char)'4'] = 4;
  digits[(unsigned char)'5'] = 5; digits[(unsigned char)'6'] = 6; digits[(unsigned char)'7'] = 7; digits[(unsigned char)'8'] = 8; digits[(unsigned char)'9'] = 9;
  digits[(unsigned char)'a'] = 10; digits[(unsigned char)'A'] = 10;
  digits[(unsigned char)'b'] = 11; digits[(unsigned char)'B'] = 11;
  digits[(unsigned char)'c'] = 12; digits[(unsigned char)'C'] = 12;
  digits[(unsigned char)'d'] = 13; digits[(unsigned char)'D'] = 13;
  digits[(unsigned char)'e'] = 14; digits[(unsigned char)'E'] = 14;
  digits[(unsigned char)'f'] = 15; digits[(unsigned char)'F'] = 15;

  for (i = 0; i < CTABLE_SIZE; i++)
    number_table[i] = false;
  number_table[(unsigned char)'0'] = true;
  number_table[(unsigned char)'1'] = true;
  number_table[(unsigned char)'2'] = true;
  number_table[(unsigned char)'3'] = true;
  number_table[(unsigned char)'4'] = true;
  number_table[(unsigned char)'5'] = true;
  number_table[(unsigned char)'6'] = true;
  number_table[(unsigned char)'7'] = true;
  number_table[(unsigned char)'8'] = true;
  number_table[(unsigned char)'9'] = true;
  number_table[(unsigned char)'.'] = true;
  number_table[(unsigned char)'+'] = true;
  number_table[(unsigned char)'-'] = true;
  number_table[(unsigned char)'#'] = true;
}


#define is_white_space(C) white_space[C]
  /* this is much faster than C's isspace, and does not depend on the current locale.
   * if c == EOF (-1), it indexes into the empty (0) slot we preallocated below white_space 
   */


static s7_pointer check_sharp_readers(s7_scheme *sc, const char *name)
{
  s7_pointer reader, value, args;
  bool need_loader_port;
  value = sc->F;
  args = sc->F; 

  /* *#reader* is assumed to be an alist of (char . proc)
   *    where each proc takes one argument, the string from just beyond the "#" to the next delimiter.
   *    The procedure can call read-char to read ahead in the current-input-port.
   *    If it returns anything other than #f, that is the value of the sharp expression.
   * This search happens after #|, #t, and #f (and #nD for multivectors?).
   */

  need_loader_port = is_loader_port(sc->input_port);
  if (need_loader_port)
    clear_loader_port(sc->input_port);
  
  /* normally read* can't read from sc->input_port if it is in use by the loader,
   *   but here we are deliberately making that possible.
   */
  for (reader = slot_value(sc->sharp_readers); is_not_null(reader); reader = cdr(reader))
    {
      if (name[0] == s7_character(caar(reader)))
	{
	  if (args == sc->F) 
	    args = list_1(sc, s7_make_string(sc, name));
	  /* args is GC protected by s7_apply_function (placed on the stack */
	  value = s7_apply_function(sc, cdar(reader), args); /* this is much less error-safe than s7_call */
	  if (value != sc->F)
	    break;
	}
    }
  if (need_loader_port)
    set_loader_port(sc->input_port);
  return(value);
}


static s7_pointer g_sharp_readers_set(s7_scheme *sc, s7_pointer args)
{
  /* new value must be either () or a proper list of conses (char . func) */
  if (is_null(cadr(args))) return(cadr(args));
  if (is_pair(cadr(args)))
    {
      s7_pointer x;
      for (x = cadr(args); is_pair(x); x = cdr(x))
	{
	  if ((!is_pair(car(x))) ||
	      (!s7_is_character(caar(x))) ||
	      (!s7_is_procedure(cdar(x))))
	    return(sc->ERROR);
	}
      if (is_null(x))
	return(cadr(args));
    }
  return(sc->ERROR);
}


static bool is_abnormal(s7_pointer x)
{
  switch (type(x))
    {
    case T_INTEGER:
    case T_RATIO:
      return(false);

    case T_REAL:
      return(is_inf(real(x)) || 
	     is_NaN(real(x)));

    case T_COMPLEX:
      return(((is_inf(s7_real_part(x)))  || 
	      (is_inf(s7_imag_part(x)))  ||
	      (is_NaN(s7_real_part(x))) || 
	      (is_NaN(s7_imag_part(x)))));

#if WITH_GMP
    case T_BIG_INTEGER:
    case T_BIG_RATIO:
      return(false);
      
    case T_BIG_REAL:
      return((is_inf(s7_real_part(x))) || 
	     (is_NaN(s7_real_part(x))));

    case T_BIG_COMPLEX:
      return((is_inf(s7_real_part(x))) || 
	     (is_inf(s7_imag_part(x))) ||
	     (is_NaN(s7_real_part(x))) || 
	     (is_NaN(s7_imag_part(x))));
#endif

    default:
      return(true);
    }
}


#define NESTED_SHARP false
#define UNNESTED_SHARP true

#define SYMBOL_OK true
#define NO_SYMBOLS false

static s7_pointer make_sharp_constant(s7_scheme *sc, char *name, bool at_top, int radix, bool with_error) 
{
  /* name is the stuff after the '#', return sc->NIL if not a recognized #... entity */
  int len;
  s7_pointer x;

  if ((name[0] == 't') && 
      ((name[1] == '\0') || (strings_are_equal(name, "true"))))
    return(sc->T);
  
  if ((name[0] == 'f') && 
      ((name[1] == '\0') || (strings_are_equal(name, "false"))))
    return(sc->F);

  if (is_not_null(slot_value(sc->sharp_readers)))
    {
      x = check_sharp_readers(sc, name);
      if (x != sc->F)
	return(x);
    }
  
  len = safe_strlen5(name); /* just count up to 5 */
  if (len < 2)
    return(sc->NIL);
      
  switch (name[0])
    {
      /* -------- #< ... > -------- */
    case '<':
      if (strings_are_equal(name, "<unspecified>"))
	return(sc->UNSPECIFIED);

      if (strings_are_equal(name, "<undefined>"))
	return(sc->UNDEFINED);

      if (strings_are_equal(name, "<eof>"))
	return(sc->EOF_OBJECT);

      return(sc->NIL);
      break;
      

      /* -------- #o #d #x #b -------- */
    case 'o':   /* #o (octal) */
    case 'd':   /* #d (decimal) */
    case 'x':   /* #x (hex) */
    case 'b':   /* #b (binary) */
      {
	bool to_inexact = false, to_exact = false;
	int num_at = 1;
  
	if (name[1] == '#')
	  {
	    if (!at_top)
	      return(sc->NIL);
	    if ((len > 2) && ((name[2] == 'e') || (name[2] == 'i'))) /* r6rs includes caps here */
	      {
		if ((len > 3) && (name[3] == '#'))
		  return(sc->NIL);
		to_inexact = (name[2] == 'i');
		to_exact = (name[2] == 'e');
		num_at = 3;
	      }
	    else return(sc->NIL);
	  }
	/* the #b or whatever overrides any radix passed in earlier */
	x = make_atom(sc, (char *)(name + num_at), (name[0] == 'o') ? 8 : ((name[0] == 'x') ? 16 : ((name[0] == 'b') ? 2 : 10)), NO_SYMBOLS, with_error);

	/* #x#i1 apparently makes sense, so #x1.0 should also be accepted.
	 * here we can get #b#e0/0 or #b#e+1/0 etc.
	 * surely if #e1+i is an error (or #f), and #e#x1+i is an error,
	 *   #x#e1+i should also be an error, but #e1+0i is not an error I guess since there actually isn't any imaginary part
	 */
	if (is_abnormal(x))
	  return(sc->NIL);

	if ((!to_exact) && (!to_inexact))
	  return(x);

	if ((s7_imag_part(x) != 0.0) && (to_exact))  /* #x#e1+i */
	  return(sc->NIL);

#if WITH_GMP
	if (s7_is_bignum(x))
	  {
	    if (to_exact)
	      return(big_inexact_to_exact(sc, list_1(sc, x)));
	    return(big_exact_to_inexact(sc, list_1(sc, x)));
	  }
#endif
	if (to_exact)
	  return(inexact_to_exact(sc, x, with_error)); 
	return(exact_to_inexact(sc, x));
      }
      break;


      /* -------- #i -------- */
    case 'i':   /* #i<num> = ->inexact (see token for table of choices here) */
      if (name[1] == '#')
	{
	  /* there are special cases here: "#e0/0" or "#e#b0/0" -- all infs are complex: 
	   *    #i1/0=nan.0 but #i1/0+i=inf+1i so e->i is a no-op but i->e is not
	   *
	   * even trickier: a *#reader* like #t<num> could be used as #e#t13.25 so make_sharp_constant
	   *   needs to be willing to call the readers even when not at_top (i.e. when NESTED_SHARP).
	   */

	  if ((name[2] == 'e') ||                        /* #i#e1 -- assume these aren't redefinable? */
	      (name[2] == 'i'))
	    return(sc->NIL);

	  x = make_sharp_constant(sc, (char *)(name + 2), NESTED_SHARP, radix, with_error);
	  if (s7_is_number(x))
	    {
	      if (is_abnormal(x))
		return(sc->NIL);
#if WITH_GMP
	      if (s7_is_bignum(x))                        /* (string->number "#b#e-11e+111") */
		return(big_exact_to_inexact(sc, list_1(sc, x)));
#endif
	      return(exact_to_inexact(sc, x));
	    }
	  return(sc->NIL);
	}
      x = make_atom(sc, (char *)(name + 1), radix, NO_SYMBOLS, with_error);
      if (!s7_is_number(x))  /* not is_abnormal(x) -- #i0/0 -> nan etc */
	return(sc->NIL);
#if WITH_GMP
      if (s7_is_bignum(x))
	return(big_exact_to_inexact(sc, list_1(sc, x)));
#endif
      return(exact_to_inexact(sc, x));
      break;
  

      /* -------- #e -------- */
    case 'e':   /* #e<num> = ->exact */
      if (name[1] == '#')
	{
	  if ((name[2] == 'e') ||                        /* #e#e1 */
	      (name[2] == 'i'))
	    return(sc->NIL);

	  x = make_sharp_constant(sc, (char *)(name + 2), NESTED_SHARP, radix, with_error);
	  if (s7_is_number(x))
	    {
	      if (is_abnormal(x))                        /* (string->number "#e#b0/0") */
		return(sc->NIL);
	      if (!s7_is_real(x))                        /* (string->number "#e#b1+i") */
		return(sc->NIL);
#if WITH_GMP
	      return(big_inexact_to_exact(sc, list_1(sc, x)));
#endif
	      return(inexact_to_exact(sc, x, with_error));
	    }
	  return(sc->NIL);
	}

      x = make_atom(sc, (char *)(name + 1), radix, NO_SYMBOLS, with_error);
#if WITH_GMP
      /* #e1e310 is a simple case */
      if (s7_is_bignum(x))
	return(big_inexact_to_exact(sc, list_1(sc, x)));
#endif	
      if (is_abnormal(x))                                /* (string->number "#e0/0") */
	return(sc->NIL);
      if (!s7_is_real(x))                                /* (string->number "#e1+i") */
	return(sc->NIL);
      
#if WITH_GMP
      /* there are non-big floats that are greater than most-positive-fixnum:
       *    :(> .1e20 most-positive-fixnum) -> #t
       *    :(bignum? .1e20) -> #f
       * so we have to check that, not just is it a bignum.
       */
      return(big_inexact_to_exact(sc, list_1(sc, x)));
#endif
      return(inexact_to_exact(sc, x, with_error));
      break;


      /* -------- #_... -------- */
    case '_':
      /* now implement the #_<name> -> built-in value of <name> stuff 
       */
      {
	s7_pointer built_in_value;
	built_in_value = initial_slot(make_symbol(sc, (char *)(name + 1)));
	if (built_in_value != sc->UNDEFINED)
	  return(slot_value(built_in_value));
      }
      break;


      /* -------- #\... -------- */
    case '\\':
      if (name[2] == 0)                             /* the most common case: #\a */
	return(chars[(unsigned char)(name[1])]);
      /* not unsigned int here!  (unsigned int)255 (as a char) returns -1!! */
      switch (name[1])
	{
	case 'n':
	  if ((strings_are_equal(name + 1, "null")) || 
	      (strings_are_equal(name + 1, "nul")))
	    return(chars[0]);

	  if (strings_are_equal(name + 1, "newline"))
	    return(chars[(unsigned char)'\n']);
	  break;

	case 's':
	  if (strings_are_equal(name + 1, "space")) 
	    return(chars[(unsigned char)' ']);
	  break;

	case 'r':
	  if (strings_are_equal(name + 1, "return")) 
	    return(chars[(unsigned char)'\r']);
	  break;

	case 'l':
	  if (strings_are_equal(name + 1, "linefeed"))
	    return(chars[(unsigned char)'\n']);
	  break;

	case 't':
	  if (strings_are_equal(name + 1, "tab")) 
	    return(chars[(unsigned char)'\t']);
	  break;
	  
	case 'a':
	  /* the next 4 are for r7rs */
	  if (strings_are_equal(name + 1, "alarm")) 
	    return(chars[7]);
	  break;

	case 'b':
	  if (strings_are_equal(name + 1, "backspace")) 
	    return(chars[8]);
	  break;

	case 'e':
	  if (strings_are_equal(name + 1, "escape")) 
	    return(chars[0x1b]);
	  break;

	case 'd':
	  if (strings_are_equal(name + 1, "delete")) 
	    return(chars[0x7f]);
	  break;

	case 'x':
	  /* #\x is just x, but apparently #\x<num> is int->char? #\x65 -> #\e -- Guile doesn't have this
	   *
	   * r7rs has 2/3/4-byte "characters" of the form #\xcebb but this is not compatible with
	   *   make-string, string-length, and so on.  We'd either have to have 2-byte chars
	   *   so (string-length (make-string 3 #\xcebb)) = 3, or accept 6 here for number of chars.
	   *   Then substring and string-set! and so on have to use utf8 encoding throughout or
	   *   risk changing the string length unexpectedly.
	   */
	  {
	    /* sscanf here misses errors like #\x1.4, but make_atom misses #\x6/3,
	     *   #\x#b0, #\x#e0.0, #\x-0, #\x#e0e100 etc, so we have to do it at
	     *   an even lower level.
	     *
	     * another problem: #\xbdca2cbec overflows so lval is -593310740 -> segfault unless caught
	     */
	    bool happy = true;
	    char *tmp;
	    int lval = 0;
	    
	    tmp = (char *)(name + 2);
	    while ((*tmp) && (happy) && (lval >= 0))
	      {
		int dig;
		dig = digits[(int)(*tmp++)];
		if (dig < 16)
		  lval = dig + (lval * 16);
		else happy = false;
	      }
	    if ((happy) &&
		(lval < 256) &&
		(lval >= 0))
	      return(chars[lval]);
	  }
	  break;
	}
    }
  return(sc->NIL);
}


static s7_Int string_to_integer(const char *str, int radix, bool *overflow)
{
  bool negative = false;
  s7_Int lval = 0;
  int dig;
  char *tmp = (char *)str;
  char *tmp1;

  if (str[0] == '+')
    tmp++;
  else 
    {
      if (str[0] == '-')
	{
	  negative = true;
	  tmp++;
	}
    }
  while (*tmp == '0') {tmp++;};
  tmp1 = tmp;

 if (radix == 10)
    {
      while (true)
	{
	  dig = digits[(unsigned char)(*tmp++)];
	  if (dig < 10)
	    lval = dig + (lval * 10);
	  else break;
	  dig = digits[(unsigned char)(*tmp++)];
	  if (dig < 10)
	    lval = dig + (lval * 10);
	  else break;
	}
    }
  else
    {
      while (true)
	{
	  dig = digits[(unsigned char)(*tmp++)];
	  if (dig < radix)
	    lval = dig + (lval * radix);
	  else break;
	  dig = digits[(unsigned char)(*tmp++)];
	  if (dig < radix)
	    lval = dig + (lval * radix);
	  else break;
	}
    }

#if WITH_GMP
  (*overflow) = ((lval > S7_LONG_MAX) ||
		 ((tmp - tmp1) > s7_int_digits_by_radix[radix]));
  /* this tells the string->number readers to create a bignum.  We need to be very
   *    conservative here to catch contexts such as (/ 1/524288 19073486328125)
   */
#else
  if ((tmp - tmp1 - 2) > s7_int_digits_by_radix[radix])
    {
      /* I can't decide what to do with these non-gmp overflows.  Perhaps NAN in all cases? 
       *     overflow: 9223372036854775810 -> -9223372036854775806 -- this is not caught currently
       */
      (*overflow) = true;
      if (negative)
	return(s7_Int_min);       /* or INFINITY? */
      return(s7_Int_max);         /* 0/100000000000000000000000000000000000000000000000000000000000000000000 */
    }
#endif

  if (negative)
    return(-lval);
  return(lval);
}


/*  9223372036854775807                9223372036854775807
 * -9223372036854775808               -9223372036854775808
 * 0000000000000000000000000001.0     1.0
 * 1.0000000000000000000000000000     1.0
 * 1000000000000000000000000000.0e-40 1.0e-12
 * 0.0000000000000000000000000001e40  1.0e12
 * 1.0e00000000000000000001           10.0
 */

static s7_Double string_to_double_with_radix(const char *ur_str, int radix, bool *overflow)
{
  /* strtod follows LANG which is not what we want (only "." is decimal point in Scheme).
   *   To overcome LANG in strtod would require screwing around with setlocale which never works.
   *   So we use our own code -- according to valgrind, this function is much faster than strtod.
   *
   * comma as decimal point causes ambiguities: `(+ ,1 2) etc
   */

  int i, sign = 1, frac_len, int_len, dig, max_len, exponent = 0;
  long long int int_part = 0, frac_part = 0;
  char *str;
  char *ipart, *fpart;
  s7_Double dval = 0.0;

  /* there's an ambiguity in number notation here if we allow "1e1" or "1.e1" in base 16 (or 15) -- is e a digit or an exponent marker?
   *   but 1e+1, for example disambiguates it -- kind of messy! -- the scheme spec says "e" can only occur in base 10. 
   *   mpfr says "e" as exponent only in bases <= 10 -- else use '@' which works in any base.  This can only cause confusion
   *   in scheme, unfortunately, due to the idiotic scheme polar notation.  But we accept "s" and "l" as exponent markers
   *   so, perhaps for radix > 10, the exponent, if any, has to use one of S s L l?  Not "l"!  And "s" originally meant "short".
   *
   * '@' can now be used as the exponent marker (26-Mar-12).
   * Another slight ambiguity: 1+1/2i is parsed as 1 + 0.5i, not 1+1/(2i), or (1+1)/(2i) or (1+1/2)i etc
   */

  max_len = s7_int_digits_by_radix[radix];
  str = (char *)ur_str;

  if (*str == '+')
    str++;
  else
    {
      if (*str == '-')
	{
	  str++;
	  sign = -1;
	}
    }
  while (*str == '0') {str++;};

  ipart = str;
  while (digits[(int)(*str)] < radix) str++;
  int_len = str - ipart;

  if (*str == '.') str++;
  fpart = str;
  while (digits[(int)(*str)] < radix) str++;
  frac_len = str - fpart;

  if ((*str) && (exponent_table[(unsigned char)(*str)]))
    {
      int exp_negative = false;
      str++;
      if (*str == '+') 
	str++;
      else
	{
	  if (*str == '-')
	    {
	      str++;
	      exp_negative = true;
	    }
	}
      while ((dig = digits[(int)(*str++)]) < 10) /* exponent itself is always base 10 */
	exponent = dig + (exponent * 10);
      
      if (exponent < 0)         /* we overflowed, so make sure we notice it below (need to check for 0.0e... first) (Brian Damgaard) */
	exponent = 1000000;     /*   see below for examples -- this number needs to be very big but not too big for add */
      if (exp_negative) 
	exponent = -exponent;

      /*           2e12341234123123123123213123123123 -> 0.0
       * but exp len is not the decider: 2e00000000000000000000000000000000000000001 -> 20.0
       * 1st zero: 2e123412341231231231231
       * then:     2e12341234123123123123123123 -> inf
       * then:     2e123412341231231231231231231231231231 -> 0.0
       *           2e-123412341231231231231 -> inf
       * but:      0e123412341231231231231231231231231231
       */
    }

#if WITH_GMP
  /* 9007199254740995.0 */

  if (int_len + frac_len >= max_len)
    {
      (*overflow) = true;
      return(0.0);
    }
#endif
      
  str = ipart;
  if ((int_len + exponent) > max_len)
    {
      /*  12341234.56789e12                   12341234567889999872.0              1.234123456789e+19
       * -1234567890123456789.0              -1234567890123456768.0              -1.2345678901235e+18
       *  12345678901234567890.0              12345678901234567168.0              1.2345678901235e+19
       *  123.456e30                          123456000000000012741097792995328.0 1.23456e+32
       *  12345678901234567890.0e12           12345678901234569054409354903552.0  1.2345678901235e+31
       *  1.234567890123456789012e30          1234567890123456849145940148224.0   1.2345678901235e+30
       *  1e20                                100000000000000000000.0             1e+20
       *  1234567890123456789.0               1234567890123456768.0               1.2345678901235e+18
       *  123.456e16                          1234560000000000000.0               1.23456e+18
       *  98765432101234567890987654321.0e-5  987654321012345728401408.0          9.8765432101235e+23
       *  98765432101234567890987654321.0e-10 9876543210123456512.0               9.8765432101235e+18
       *  0.00000000000000001234e20           1234.0
       *  0.000000000000000000000000001234e30 1234.0
       *  0.0000000000000000000000000000000000001234e40 1234.0
       *  0.000000000012345678909876543210e15 12345.678909877
       *  0e1000                              0.0
       */

      for (i = 0; i < max_len; i++)
	{
	  dig = digits[(int)(*str++)];
	  if (dig < radix)
	    int_part = dig + (int_part * radix);
	  else break;
	}

      /* if the exponent is huge, check for 0 int_part and frac_part before complaining (0e1000 or 0.0e1000) 
       */
      if ((int_part == 0) &&
	  (exponent > max_len))
	{
	  /* if frac_part is also 0, return 0.0 */
	  if (frac_len == 0)
	    return(0.0);

	  str = fpart;
	  while ((dig = digits[(int)(*str++)]) < radix)
	    frac_part = dig + (frac_part * radix);
	  if (frac_part == 0)
	    return(0.0);

#if WITH_GMP
	  (*overflow) = true;
#endif
	}
      
#if WITH_GMP
      (*overflow) = ((int_part > 0) || (exponent > 20));    /* .1e310 is a tricky case */
#endif
      
      if (int_part != 0) /* 0.<310 zeros here>1e310 for example --
			  *   pow (via ipow) thinks it has to be too big, returns Nan,
			  *   then Nan * 0 -> Nan and the NaN propogates
			  */
	{
	  if (int_len <= max_len)
	    dval = int_part * ipow(radix, exponent);
	  else dval = int_part * ipow(radix, exponent + int_len - max_len);
	}
      else dval = 0.0;

      /* shift by exponent, but if int_len > max_len then we assumed (see below) int_len - max_len 0's on the left */
      /*   using int_to_int or table lookups here instead of pow did not make any difference in speed */

      if (int_len < max_len)
	{
	  int k, flen;
	  str = fpart;
	  
	  for (k = 0; (frac_len > 0) && (k < exponent); k += max_len)
	    {
	      if (frac_len > max_len) flen = max_len; else flen = frac_len;
	      frac_len -= max_len;

	      frac_part = 0;
	      for (i = 0; i < flen; i++)
		frac_part = digits[(int)(*str++)] + (frac_part * radix);

	      if (frac_part != 0)                                /* same pow->NaN problem as above can occur here */
		dval += frac_part * ipow(radix, exponent - flen - k);
	    }
	}
      else
	{
	  /* some of the fraction is in the integer part before the negative exponent shifts it over */
	  if (int_len > max_len)
	    {
	      int ilen;
	      /* str should be at the last digit we read */
	      ilen = int_len - max_len;                          /* we read these above */
	      if (ilen > max_len)
		ilen = max_len;

	      for (i = 0; i < ilen; i++)
		frac_part = digits[(int)(*str++)] + (frac_part * radix);

	      dval += frac_part * ipow(radix, exponent - ilen);
	    }
	}

      return(sign * dval);
    }

  /* int_len + exponent <= max_len */

  if (int_len <= max_len)
    {
      int int_exponent;

      /* a better algorithm (since the inaccuracies are in the radix^exponent portion):
       *   strip off leading zeros and possible sign,
       *   strip off digits beyond max_len, then remove any trailing zeros.
       *     (maybe fiddle with the lowest order digit here for rounding, but I doubt it matters)
       *   read digits until end of number or max_len reached, ignoring the decimal point
       *   get exponent and use it and decimal point location to position the current result integer
       * this always combines the same integer and the same exponent no matter how the number is expressed.
       */

      int_exponent = exponent;
      if (int_len > 0)
	{
	  char *iend;
	  iend = (char *)(str + int_len - 1);
	  while ((*iend == '0') && (iend != str)) {iend--; int_exponent++;}

	  while (str <= iend)
	    int_part = digits[(int)(*str++)] + (int_part * radix);
	}
      if (int_exponent != 0)
	dval = int_part * ipow(radix, int_exponent);
      else dval = (s7_Double)int_part;
    }
  else
    {
      int len, flen;
      long long int fpart = 0;

      /* 98765432101234567890987654321.0e-20    987654321.012346  
       * 98765432101234567890987654321.0e-29    0.98765432101235
       * 98765432101234567890987654321.0e-30    0.098765432101235
       * 98765432101234567890987654321.0e-28    9.8765432101235
       */

      len = int_len + exponent;
      for (i = 0; i < len; i++)
	int_part = digits[(int)(*str++)] + (int_part * radix);
      
      flen = -exponent;
      if (flen > max_len)
	flen = max_len;

      for (i = 0; i < flen; i++)
	fpart = digits[(int)(*str++)] + (fpart * radix);

      if (len <= 0)
	dval = int_part + fpart * ipow(radix, len - flen);
      else dval = int_part + fpart * ipow(radix, -flen);
    }

  if (frac_len > 0)
    {
      str = fpart;
      if (frac_len <= max_len)
	{
	  /* splitting out base 10 case saves very little here */
	  /* this ignores trailing zeros, so that 0.3 equals 0.300 */
	  char *fend;
	  
	  fend = (char *)(str + frac_len - 1);
	  while ((*fend == '0') && (fend != str)) {fend--; frac_len--;} /* (= .6 0.6000) */
	  
	  while (str <= fend)
	    frac_part = digits[(int)(*str++)] + (frac_part * radix);
	  dval += frac_part * ipow(radix, exponent - frac_len);
	  
	  /* fprintf(stderr, "frac: %lld, exp: (%d %d) %.20f, val: %.20f\n", frac_part, exponent, frac_len, ipow(radix, exponent - frac_len), dval); 
	   * 0.6:    frac:    6, exp: 0.10000000000000000555, val: 0.60000000000000008882
	   * 0.60:   frac:   60, exp: 0.01000000000000000021, val: 0.59999999999999997780
	   * 0.6000: frac: 6000, exp: 0.00010000000000000000, val: 0.59999999999999997780
	   * :(= 0.6 0.60)
	   * #f
	   * :(= #i3/5 0.6)
	   * #f
	   * so (string->number (number->string num)) == num only if both num's are the same text (or you get lucky)
	   * :(= 0.6 6e-1) ; but not 60e-2
	   * #t
	   *
	   * to fix the 0.60 case, we need to ignore trailing post-dot zeros.
	   */
	}
      else
	{
	  if (exponent <= 0)
	    {
	      for (i = 0; i < max_len; i++)
		frac_part = digits[(int)(*str++)] + (frac_part * radix);
	      
	      dval += frac_part * ipow(radix, exponent - max_len);
	    }
	  else
	    {
	      /* 1.0123456789876543210e1         10.12345678987654373771  
	       * 1.0123456789876543210e10        10123456789.87654304504394531250
	       * 0.000000010000000000000000e10   100.0
	       * 0.000000010000000000000000000000000000000000000e10 100.0
	       * 0.000000012222222222222222222222222222222222222e10 122.22222222222222
	       * 0.000000012222222222222222222222222222222222222e17 1222222222.222222
	       */
	      
	      int_part = 0;
	      for (i = 0; i < exponent; i++)
		int_part = digits[(int)(*str++)] + (int_part * radix);
	      
	      frac_len -= exponent;
	      if (frac_len > max_len)
		frac_len = max_len;
	      
	      for (i = 0; i < frac_len; i++)
		frac_part = digits[(int)(*str++)] + (frac_part * radix);
	      
	      dval += int_part + frac_part * ipow(radix, -frac_len);
	    }
	}
    }

#if WITH_GMP
  if ((int_part == 0) &&
      (frac_part == 0))
    return(0.0);
  (*overflow) = ((frac_len - exponent) > max_len);
#endif
  
  return(sign * dval);
}


static s7_pointer make_atom(s7_scheme *sc, char *q, int radix, bool want_symbol, bool with_error) 
{
  /* make symbol or number from string */
  #define IS_DIGIT(Chr, Rad) (digits[(unsigned char)Chr] < Rad)

  char c, *p;
  bool has_dec_point1 = false;

  p = q;
  c = *p++; 
  
  /* a number starts with + - . or digit, but so does 1+ for example */
  
  switch (c)
    {
    case '#':
      return(make_sharp_constant(sc, p, UNNESTED_SHARP, radix, with_error)); /* make_sharp_constant expects the '#' to be removed */

    case '+':
    case '-':
      c = *p++; 
      if (c == '.') 
	{ 
	  has_dec_point1 = true; 
	  c = *p++; 
	} 
      if ((!c) || (!IS_DIGIT(c, radix)))
	return((want_symbol) ? make_symbol(sc, q) : sc->F);
      break;

    case '.':
      has_dec_point1 = true; 
      c = *p++; 

      if ((!c) || (!IS_DIGIT(c, radix)))
	return((want_symbol) ? make_symbol(sc, q) : sc->F); 
      break;

    case '0':        /* these two are always digits */
    case '1':
      break;

    default:
      if (!IS_DIGIT(c, radix))
	return((want_symbol) ? make_symbol(sc, q) : sc->F); 
      break;
    }

  /* now it's possibly a number -- the first character(s) could be part of a number in the current radix */
  {
    char *slash1 = NULL, *slash2 = NULL, *plus = NULL, *ex1 = NULL, *ex2 = NULL;
    bool has_i = false, has_dec_point2 = false;
    int has_plus_or_minus = 0, current_radix;

#if (!WITH_GMP)
    bool overflow = false;
#endif
    current_radix = radix;  /* current_radix is 10 for the exponent portions, but radix for all the rest */
  
    for ( ; (c = *p) != 0; ++p)
      {
	/* what about embedded null? (string->number (string #\1 (integer->char 0) #\0)) 
	 *   currently we stop and return 1, but Guile returns #f
	 */
	if (!IS_DIGIT(c, current_radix))         /* moving this inside the switch statement was much slower */
	  {
	    current_radix = radix;
	    
	    switch (c)
	      {
		/* -------- decimal point -------- */
	      case '.':
		if ((!IS_DIGIT(p[1], current_radix)) &&
		    (!IS_DIGIT(p[-1], current_radix))) 
		  return((want_symbol) ? make_symbol(sc, q) : sc->F); 

		if (has_plus_or_minus == 0)
		  {
		    if ((has_dec_point1) || (slash1))
		      return((want_symbol) ? make_symbol(sc, q) : sc->F); 
		    has_dec_point1 = true;
		  }
		else
		  {
		    if ((has_dec_point2) || (slash2))
		      return((want_symbol) ? make_symbol(sc, q) : sc->F); 
		    has_dec_point2 = true;
		  }
		continue;

		
		/* -------- exponent marker -------- */
	      case 'e': case 'E':
#if WITH_EXTRA_EXPONENT_MARKERS
	      case 's': case 'S':
	      case 'd': case 'D':
	      case 'f': case 'F':
	      case 'l': case 'L':
#endif
		if (current_radix > 10)
		  return((want_symbol) ? make_symbol(sc, q) : sc->F); 
		/* see note above */
		/* fall through -- if '@' used, radices>10 are ok */
		
#if WITH_AT_SIGN_AS_EXPONENT
	      case '@':
#endif
		current_radix = 10;
		
		if (((ex1) ||
		     (slash1)) &&
		    (has_plus_or_minus == 0)) /* ee */
		  return((want_symbol) ? make_symbol(sc, q) : sc->F); 
		
		if (((ex2) ||
		     (slash2)) &&
		    (has_plus_or_minus != 0)) /* 1+1.0ee */
		  return((want_symbol) ? make_symbol(sc, q) : sc->F); 
		
		if ((!IS_DIGIT(p[-1], radix)) && /* was current_radix but that's always 10! */
		    (p[-1] != '.'))
		  return((want_symbol) ? make_symbol(sc, q) : sc->F); 
		
		if (has_plus_or_minus == 0)
		  {
		    ex1 = p;
		    has_dec_point1 = true; /* decimal point illegal from now on */
		  }
		else 
		  {
		    ex2 = p;
		    has_dec_point2 = true;
		  }
		p++;
		if ((*p == '-') || (*p == '+')) p++;
		if (IS_DIGIT(*p, current_radix))
		  continue;
		break;


		/* -------- internal + or - -------- */
	      case '+':
	      case '-':
		if (has_plus_or_minus != 0) /* already have the separator */
		  return((want_symbol) ? make_symbol(sc, q) : sc->F);
		
		if (c == '+') has_plus_or_minus = 1; else has_plus_or_minus = -1;
		plus = (char *)(p + 1);
		continue;
		
		/* ratio marker */
	      case '/':
		if ((has_plus_or_minus == 0) &&
		    ((ex1) ||
		     (slash1) ||
		     (has_dec_point1)))
		  return((want_symbol) ? make_symbol(sc, q) : sc->F);
		
		if ((has_plus_or_minus != 0) &&
		    ((ex2) ||
		     (slash2) ||
		     (has_dec_point2)))
		  return((want_symbol) ? make_symbol(sc, q) : sc->F);
		
		if (has_plus_or_minus == 0)
		  slash1 = (char *)(p + 1);
		else slash2 = (char *)(p + 1);
		
		if ((!IS_DIGIT(p[1], current_radix)) ||
		    (!IS_DIGIT(p[-1], current_radix)))
		  return((want_symbol) ? make_symbol(sc, q) : sc->F);
		
		continue;


		/* -------- i for the imaginary part -------- */
	      case 'i':
		if ((has_plus_or_minus != 0) && 
		    (!has_i))
		  {
		    has_i = true;
		    continue;
		  }
		break;

	      default:
		break;
	      }

	    return((want_symbol) ? make_symbol(sc, q) : sc->F);
	  }
      }
    
    if ((has_plus_or_minus != 0) &&        /* that is, we have an internal + or - */
	(!has_i))                          /*   but no i for the imaginary part */
      return((want_symbol) ? make_symbol(sc, q) : sc->F);

    if (has_i)
      {
#if (!WITH_GMP)
	s7_Double rl = 0.0, im = 0.0;
#else
	char e1 = 0, e2 = 0;
#endif
	s7_pointer result;
	int len;
	char ql1, pl1;
	
	len = safe_strlen(q);
	
	if (q[len - 1] != 'i')
	  return((want_symbol) ? make_symbol(sc, q) : sc->F);
	
	/* save original string */
	ql1 = q[len - 1];
	pl1 = (*(plus - 1));
#if WITH_GMP
	if (ex1) {e1 = *ex1; (*ex1) = '@';} /* for mpfr */
	if (ex2) {e2 = *ex2; (*ex2) = '@';}
#endif
	
	/* look for cases like 1+i */
	if ((q[len - 2] == '+') || (q[len - 2] == '-'))
	  q[len - 1] = '1';
	else q[len - 1] = '\0'; /* remove 'i' */
	
	(*((char *)(plus - 1))) = '\0';
	
	/* there is a slight inconsistency here:
	   1/0      -> nan.0
           1/0+0i   -> inf.0 (0/1+0i is 0.0)
	   #i1/0+0i -> inf.0
	   0/0      -> nan.0
	   0/0+0i   -> -nan.0
	*/

#if (!WITH_GMP)
	if ((has_dec_point1) ||
	    (ex1))
	  {
	    /* (string->number "1100.1+0.11i" 2) -- need to split into 2 honest reals before passing to non-base-10 str->dbl */
	    rl = string_to_double_with_radix(q, radix, &overflow);
	  }
	else
	  {
	    if (slash1)
	      {
		/* here the overflow could be innocuous if it's in the denominator and the numerator is 0
		 *    0/100000000000000000000000000000000000000-0i
		 */
		s7_Int num, den;
		num = string_to_integer(q, radix, &overflow);
		den = string_to_integer(slash1, radix, &overflow);
		
		if ((num == 0) && (den != 0))
		  {
		    rl = 0.0;
		    overflow = false;
		  }
		else rl = (s7_Double)num / (s7_Double)den;
	      }
	    else rl = (s7_Double)string_to_integer(q, radix, &overflow);
	    if (overflow) return(real_NaN);
	  }
	if (rl == -0.0) rl = 0.0;
	
	if ((has_dec_point2) ||
	    (ex2))
	  im = string_to_double_with_radix(plus, radix, &overflow);
	else
	  {
	    if (slash2)
	      {
		/* same as above: 0-0/100000000000000000000000000000000000000i
		 */
		s7_Int num, den;
		num = string_to_integer(plus, radix, &overflow);
		den = string_to_integer(slash2, radix, &overflow);
		if ((num == 0) && (den != 0))
		  {
		    im = 0.0;
		    overflow = false;
		  }
		else im = (s7_Double)num / (s7_Double)den;
	      }
	    else im = (s7_Double)string_to_integer(plus, radix, &overflow);
	    if (overflow) return(real_NaN);
	  }
	if ((has_plus_or_minus == -1) && 
	    (im != 0.0))
	  im = -im;
	result = s7_make_complex(sc, rl, im);
#else
	result = string_to_either_complex(sc, q, slash1, ex1, has_dec_point1, plus, slash2, ex2, has_dec_point2, radix, has_plus_or_minus);
#endif
	
	/* restore original string */
	q[len - 1] = ql1;
	(*((char *)(plus - 1))) = pl1;
#if WITH_GMP
	if (ex1) (*ex1) = e1;
	if (ex2) (*ex2) = e2;
#endif
	
	return(result);
      }
    
    /* not complex */
    if ((has_dec_point1) ||
	(ex1))
      {
	s7_pointer result;
	
	if (slash1)  /* not complex, so slash and "." is not a number */
	  return((want_symbol) ? make_symbol(sc, q) : sc->F);
	
#if (!WITH_GMP)
	result = make_real(sc, string_to_double_with_radix(q, radix, &overflow));
#else
	{
	  char old_e = 0;
	  if (ex1)
	    {
	      old_e = (*ex1);
#if WITH_AT_SIGN_AS_EXPONENT
	      (*ex1) = '@';
#else
	      (*ex1) = 'e';
#endif
	    }
	  result = string_to_either_real(sc, q, radix);
	  if (ex1)
	    (*ex1) = old_e;
	}
#endif
	return(result);
      }
    
    /* not real */
    if (slash1)
#if (!WITH_GMP)
      {
	s7_Int n, d;

	n = string_to_integer(q, radix, &overflow);
	d = string_to_integer(slash1, radix, &overflow);

	if ((n == 0) && (d != 0))                        /* 0/100000000000000000000000000000000000000 */
	  return(small_int(0));
	if ((d == 0) || (overflow))
	  return(real_NaN);
	/* it would be neat to return 1 from 10000000000000000000000000000/10000000000000000000000000000 
	 *   but q is the entire number ('/' included) and slash1 is the stuff after the '/', and every 
	 *   big number comes through here, so there's no clean and safe way to check that q == slash1.
	 */
	return(s7_make_ratio(sc, n, d));
      }
#else
    return(string_to_either_ratio(sc, q, slash1, radix));
#endif
    
    /* integer */
#if (!WITH_GMP)
    {
      s7_Int x;
      x = string_to_integer(q, radix, &overflow);
      if (overflow) 
	return((q[0] == '-') ? real_minus_infinity : real_infinity);
      return(make_integer(sc, x));
    }
#else
    return(string_to_either_integer(sc, q, radix));
#endif
  }
}


static s7_pointer s7_string_to_number(s7_scheme *sc, char *str, int radix)
{
  s7_pointer x;
  x = make_atom(sc, str, radix, NO_SYMBOLS, WITHOUT_OVERFLOW_ERROR);
  if (s7_is_number(x))  /* only needed because str might start with '#' and not be a number (#t for example) */
    return(x);
  return(sc->F);
}


static s7_pointer g_string_to_number_1(s7_scheme *sc, s7_pointer args, s7_pointer caller)
{
  #define H_string_to_number "(string->number str (radix 10)) converts str into a number. \
If str does not represent a number, string->number returns #f.  If 'str' has an embedded radix, \
the 'radix' it is ignored: (string->number \"#x11\" 2) -> 17 not 3."

  s7_Int radix = 0;
  char *str;

  if (!is_string(car(args)))
    {
      check_method(sc, car(args), caller, args);
      return(wrong_type_argument(sc, caller, small_int(1), car(args), T_STRING));
    }
  str = (char *)string_value(car(args));
  if ((!str) || (!(*str)))
    return(sc->F);

  if (is_pair(cdr(args)))
    {
      s7_pointer rad, p;
      rad = cadr(args);
      if (!s7_is_integer(rad))
	{
	  if (!s7_is_integer(p = check_values(sc, rad, cdr(args))))
	    {
	      check_method(sc, rad, caller, args);
	      return(wrong_type_argument(sc, caller, small_int(2), rad, T_INTEGER));
	    }
	  else rad = p;
	}
      radix = s7_integer(rad);
      if ((radix < 2) ||              /* what about negative int as base (Knuth), reals such as phi, and some complex like -1+i */
	  (radix > 16))               /* the only problem here is printing the number; perhaps put each digit in "()" in base 10: (123)(0)(34) */
	return(out_of_range(sc, caller, small_int(2), rad, A_VALID_RADIX));
    }
  else radix = 10;

  switch (str[0])
    {
    case 'n':
      if (safe_strcmp(str, "nan.0") == 0)
	return(real_NaN);
      break;

    case 'i':
      if (safe_strcmp(str, "inf.0") == 0)
	return(real_infinity);
      break;

    case '-':
      if ((str[1] == 'i') && (safe_strcmp((const char *)(str + 1), "inf.0") == 0))
	return(real_minus_infinity);
      break;

    case '+':
      if ((str[1] == 'i') && (safe_strcmp((const char *)(str + 1), "inf.0") == 0))
	return(real_infinity);
      break;
    }
  return(s7_string_to_number(sc, str, radix));
}


static s7_pointer g_string_to_number(s7_scheme *sc, s7_pointer args)
{
  return(g_string_to_number_1(sc, args, sc->STRING_TO_NUMBER));
}


static bool numbers_are_eqv(s7_pointer a, s7_pointer b)
{
  if (type(a) != type(b)) /* (eqv? 1 1.0) -> #f! */
    return(false);

  switch (type(a))
    {
    case T_INTEGER:
      return((integer(a) == integer(b)));
  
    case T_RATIO:
      return((numerator(a) == numerator(b)) &&
	     (denominator(a) == denominator(b)));

    case T_REAL:
      if (is_NaN(real(a))) 
	return(false);
      return(real(a) == real(b));
  
    default:
      if ((is_NaN(real_part(a))) ||
	  (is_NaN(imag_part(a))))
	return(false);
      return((real_part(a) == real_part(b)) &&
	     (imag_part(a) == imag_part(b)));
    }
  return(false);
}


static bool is_rational_via_method(s7_scheme *sc, s7_pointer p)
{
  if (s7_is_rational(p))
    return(true);
  if (has_methods(p))
    {
      s7_pointer f;
      f = find_method(sc, find_let(sc, p), sc->IS_RATIONAL);
      if (f != sc->UNDEFINED)
	return(is_true(sc, s7_apply_function(sc, f, cons(sc, p, sc->NIL))));
    }
  return(false);
}


#if (!WITH_GMP)
static s7_pointer abs_direct;
static s7_pointer g_abs_direct(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x;
  x = c_call(car(args))(sc, cdar(args));
  if (real(x) < 0.0)
    {
      if (is_simple_real(x))
	{
	  real(x) = -real(x);
	  return(x);
	}
      return(make_real(sc, -real(x)));
    }
  return(x);
}


static s7_pointer g_abs(s7_scheme *sc, s7_pointer args)
{
  #define H_abs "(abs x) returns the absolute value of the real number x"

  s7_pointer x;
  x = car(args);
  switch (type(x))
    {
    case T_INTEGER:
      if (integer(x) < 0)
	return(make_integer(sc, -integer(x)));
      return(x);

    case T_RATIO:
      if (numerator(x) < 0)
	return(s7_make_ratio(sc, -numerator(x), denominator(x)));
      return(x);

    case T_REAL:
      if (is_NaN(real(x)))                  /* (abs -nan.0) -> nan.0, not -nan.0 */
	return(real_NaN);
      if (real(x) < 0.0)
	return(make_real(sc, -real(x)));
      return(x);

#if (WITH_MAKE_COMPLEX)
    case T_COMPLEX:
      return(make_real(sc, hypot(imag_part(x), real_part(x))));
#endif

    default:
      check_method(sc, x, sc->ABS, args);
      return(simple_wrong_type_argument(sc, sc->ABS, x, T_REAL));
    }
}

#if (!WITH_MAKE_COMPLEX)
static s7_pointer g_magnitude(s7_scheme *sc, s7_pointer args)
{
  #define H_magnitude "(magnitude z) returns the magnitude of z"
  s7_pointer x;
  x = car(args);

  switch (type(x))
    {
    case T_INTEGER:
      if (integer(x) < 0)
	return(make_integer(sc, -integer(x)));
      /* but what to do here: (magnitude -9223372036854775808) -> -9223372036854775808? 
       *   same thing happens in abs, lcm and gcd: (gcd -9223372036854775808) -> -9223372036854775808
       */
      return(x);

    case T_RATIO:
      if (numerator(x) < 0)
	return(s7_make_ratio(sc, -numerator(x), denominator(x)));
      return(x);

    case T_REAL:
      if (is_NaN(real(x)))                 /* (magnitude -nan.0) -> nan.0, not -nan.0 */
	return(real_NaN);
      if (real(x) < 0.0)
	return(make_real(sc, -real(x)));
      return(x);

    case T_COMPLEX:
      return(make_real(sc, hypot(imag_part(x), real_part(x))));

    default:
      check_method(sc, x, sc->MAGNITUDE, args);
      return(simple_wrong_type_argument_with_type(sc, sc->MAGNITUDE, x, A_NUMBER));
    }
}
#endif

static s7_pointer g_angle(s7_scheme *sc, s7_pointer args)
{
  #define H_angle "(angle z) returns the angle of z"
  s7_pointer x;
  /* (angle inf+infi) -> 0.78539816339745 ? */

  x = car(args);
  switch (type(x))
    {
    case T_INTEGER:
      if (integer(x) < 0)
	return(real_pi);
      return(small_int(0));

    case T_RATIO:
      if (numerator(x) < 0)
	return(real_pi);
      return(small_int(0));

    case T_REAL:
      if (is_NaN(real(x))) return(x);
      if (real(x) < 0.0)
	return(real_pi);
      return(real_zero);

    case T_COMPLEX:
      return(make_real(sc, atan2(imag_part(x), real_part(x))));

    default:
      check_method(sc, x, sc->ANGLE, args);
      return(simple_wrong_type_argument_with_type(sc, sc->ANGLE, x, A_NUMBER));
    }
}


static s7_pointer g_rationalize(s7_scheme *sc, s7_pointer args)
{
  #define H_rationalize "(rationalize x err) returns the ratio with lowest denominator within err of x"
  s7_Double err;
  s7_pointer x;

  x = car(args);
  if (!s7_is_real(x))
    {
      check_method(sc, x, sc->RATIONALIZE, args);
      return(wrong_type_argument(sc, sc->RATIONALIZE, small_int(1), x, T_REAL));
    }

  if (is_not_null(cdr(args)))
    {
      s7_pointer ex;
      ex = cadr(args);
      if (!s7_is_real(ex))
	{
	  check_method(sc, ex, sc->RATIONALIZE, args);
	  return(wrong_type_argument(sc, sc->RATIONALIZE, small_int(2), ex, T_REAL));
	}
      err = number_to_double(sc, ex, "rationalize");
      if (is_NaN(err))
	return(out_of_range(sc, sc->RATIONALIZE, small_int(2), cadr(args), ITS_NAN));
      if (err < 0.0) err = -err;
    }
  else err = sc->default_rationalize_error;

  switch (type(x))
    {
    case T_INTEGER:
      {
	s7_Int a, b, pa;
	if (err < 1.0) return(x);
	a = s7_integer(x);
	if (a < 0) pa = -a; else pa = a;
	if (err >= pa) return(small_int(0));
	b = (s7_Int)err;
	pa -= b;
	if (a < 0)
	  return(make_integer(sc, -pa));
	return(make_integer(sc, pa));
      }

    case T_RATIO:
      if (err == 0.0)
	return(x);

    case T_REAL:
      {
	s7_Double rat;
	s7_Int numer = 0, denom = 1;

	rat = number_to_double(sc, x, "rationalize");

	if ((is_NaN(rat)) || (is_inf(rat)))
	  return(wrong_type_argument_with_type(sc, sc->RATIONALIZE, small_int(1), x, A_NORMAL_REAL));

	if (err >= fabs(rat)) 
	  return(small_int(0));

	if ((rat > 9.2233720368548e+18) || (rat < -9.2233720368548e+18))
	  return(out_of_range(sc, sc->RATIONALIZE, small_int(1), x, ITS_TOO_LARGE));

	if ((fabs(rat) + fabs(err)) < 1.0e-18)
	  err = 1.0e-18;
	/* (/ 1.0 most-positive-fixnum) is 1.0842021e-19, so if we let err be less than that,
	 * (rationalize 1e-19 1e-20) hangs, but this only affects the initial ceiling, I believe.
	 */

	if (fabs(rat) < fabs(err))
	  return(small_int(0));

	if (c_rationalize(rat, err, &numer, &denom))
	  return(s7_make_ratio(sc, numer, denom));

	return(sc->F);
      }
    }
  return(sc->F); /* make compiler happy */
}


#if (!WITH_MAKE_COMPLEX)
static s7_pointer g_make_polar(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x, y;
  s7_Double ang, mag;
  #define H_make_polar "(make-polar mag ang) returns a complex number with magnitude mag and angle ang"
  
  x = car(args);
  y = cadr(args);

  switch (type(x))
    {
    case T_INTEGER:
      switch (type(y))
	{
	case T_INTEGER:
	  if (integer(x) == 0) return(x);            /* (make-polar 0 1) -> 0 */
	  if (integer(y) == 0) return(x);            /* (make-polar 1 0) -> 1 */
	  mag = (s7_Double)integer(x);
	  ang = (s7_Double)integer(y);
	  break;

	case T_RATIO:
	  if (integer(x) == 0) return(x);
	  mag = (s7_Double)integer(x);
	  ang = (s7_Double)fraction(y);
	  break;

	case T_REAL:
	  ang = real(y);
	  if (ang == 0.0) return(x);
	  if (is_NaN(ang)) return(y);
	  if (is_inf(ang)) return(real_NaN);
	  if ((ang == M_PI) || (ang == -M_PI)) return(make_integer(sc, -integer(x)));
	  mag = (s7_Double)integer(x);
	  break;

	default:
	  check_method(sc, y, sc->MAKE_POLAR, args);
	  return(wrong_type_argument(sc, sc->MAKE_POLAR, small_int(2), y, T_REAL));
	}
      break;

    case T_RATIO:
      switch (type(y))
	{
	case T_INTEGER:
	  if (integer(y) == 0) return(x);
	  mag = (s7_Double)fraction(x);
	  ang = (s7_Double)integer(y);
	  break;

	case T_RATIO:
	  mag = (s7_Double)fraction(x);
	  ang = (s7_Double)fraction(y);
	  break;

	case T_REAL:
	  ang = real(y);
	  if (ang == 0.0) return(x);
	  if (is_NaN(ang)) return(y);
	  if (is_inf(ang)) return(real_NaN);
	  if ((ang == M_PI) || (ang == -M_PI)) return(s7_make_ratio(sc, -numerator(x), denominator(x)));
	  mag = fraction(x);
	  break;

	default:
	  check_method(sc, y, sc->MAKE_POLAR, args);
	  return(wrong_type_argument(sc, sc->MAKE_POLAR, small_int(2), y, T_REAL));
	}
      break;

    case T_REAL:
      mag = real(x);
      switch (type(y))
	{
	case T_INTEGER:
	  if (is_NaN(mag)) return(x);
	  if (integer(y) == 0) return(x);
	  ang = (s7_Double)integer(y);
	  break;

	case T_RATIO:
	  if (is_NaN(mag)) return(x);
	  ang = (s7_Double)fraction(y);
	  break;

	case T_REAL:
	  if (is_NaN(mag)) return(x);
	  ang = real(y);
	  if (ang == 0.0) return(x);
	  if (is_NaN(ang)) return(y);
	  if (is_inf(ang)) return(real_NaN);
	  break;

	default:
	  check_method(sc, y, sc->MAKE_POLAR, args);
	  return(wrong_type_argument(sc, sc->MAKE_POLAR, small_int(2), y, T_REAL));
	}
      break;

    default:
      check_method(sc, x, sc->MAKE_POLAR, args);
      return(wrong_type_argument(sc, sc->MAKE_POLAR, small_int(1), x, T_REAL));
    }

  return(s7_make_complex(sc, mag * cos(ang), mag * sin(ang)));

  /* since sin is inaccurate for large arguments, so is make-polar:
   *    (make-polar 1.0 1e40) -> -0.76267273202438+0.64678458842683i, not 8.218988919070239214448025364432557517335E-1-5.696334009536363273080341815735687231337E-1i
   */
}
#endif


static s7_pointer g_make_rectangular(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x, y;
#if WITH_MAKE_COMPLEX
  #define H_make_rectangular "(make-complex x1 x2) returns a complex number with real-part x1 and imaginary-part x2"
#else
  #define H_make_rectangular "(make-rectangular x1 x2) returns a complex number with real-part x1 and imaginary-part x2"
#endif
  
  x = car(args);
  y = cadr(args);

  switch (type(y))
    {
    case T_INTEGER:
      switch (type(x))
	{
	case T_INTEGER: 
	  if (integer(y) == 0) return(x); 
	  return(s7_make_complex(sc, (s7_Double)integer(x), (s7_Double)integer(y)));

	case T_RATIO:   
	  if (integer(y) == 0) return(x); 
	  return(s7_make_complex(sc, (s7_Double)fraction(x), (s7_Double)integer(y)));

	case T_REAL:    
	  if (integer(y) == 0) return(x); 
	  return(s7_make_complex(sc, real(x), (s7_Double)integer(y)));

	default:        
	  check_method(sc, x, sc->MAKE_RECTANGULAR, args);
	  return(wrong_type_argument(sc, sc->MAKE_RECTANGULAR, small_int(1), x, T_REAL));
	}
      
    case T_RATIO:
      switch (type(x))
	{
	case T_INTEGER: 
	  return(s7_make_complex(sc, (s7_Double)integer(x), (s7_Double)fraction(y)));

	case T_RATIO:   
	  return(s7_make_complex(sc, (s7_Double)fraction(x), (s7_Double)fraction(y)));

	case T_REAL:    
	  return(s7_make_complex(sc, real(x), (s7_Double)fraction(y)));

	default:        
	  check_method(sc, x, sc->MAKE_RECTANGULAR, args);
	  return(wrong_type_argument(sc, sc->MAKE_RECTANGULAR, small_int(1), x, T_REAL));
	}

    case T_REAL:
      switch (type(x))
	{
	case T_INTEGER: 
	  if (real(y) == 0.0) return(x); 
	  return(s7_make_complex(sc, (s7_Double)integer(x), real(y)));

	case T_RATIO:   
	  if (real(y) == 0.0) return(x); 
	  return(s7_make_complex(sc, (s7_Double)fraction(x), real(y)));

	case T_REAL:    
	  if (real(y) == 0.0) return(x); 
	  return(s7_make_complex(sc, real(x), real(y)));

	default:        
	  check_method(sc, x, sc->MAKE_RECTANGULAR, args);
	  return(wrong_type_argument(sc, sc->MAKE_RECTANGULAR, small_int(1), x, T_REAL));
	}

    default:
      check_method(sc, (is_let(x)) ? x : y, sc->MAKE_RECTANGULAR, args);
      return(wrong_type_argument(sc, sc->MAKE_RECTANGULAR, small_int(2), y, T_REAL));
    }
}


static s7_pointer g_exp(s7_scheme *sc, s7_pointer args)
{
  #define H_exp "(exp z) returns e^z, (exp 1) is 2.718281828459"
  s7_pointer x;

  x = car(args);
  switch (type(x))
    {
    case T_INTEGER:
      if (integer(x) == 0) return(small_int(1));                       /* (exp 0) -> 1 */
      return(make_real(sc, exp((s7_Double)(integer(x)))));

    case T_RATIO:
      return(make_real(sc, exp((s7_Double)fraction(x))));

    case T_REAL:
      return(make_real(sc, exp(real(x))));

    case T_COMPLEX:
#if HAVE_COMPLEX_NUMBERS
      return(s7_from_c_complex(sc, cexp(as_c_complex(x))));
      /* this is inaccurate for large arguments:
       *   (exp 0+1e20i) -> -0.66491178990701-0.74692189125949i, not 7.639704044417283004001468027378811228331E-1-6.45251285265780844205811711312523007406E-1i
       */
#else
      return(out_of_range(sc, sc->EXP, small_int(1), x, NO_COMPLEX_NUMBERS));
#endif

    default:
      check_method(sc, x, sc->EXP, args);
      return(simple_wrong_type_argument_with_type(sc, sc->EXP, x, A_NUMBER));
    }
}

/* another possibility:
 * since (log (make-polar 1 .1)) = 0+0.1i and so on,
 *       (log (real-part (log 0)) .1) (currently = -inf-nani) = -inf+.1i
 * but   (log 0 .1) = inf-nani
 *
 * :(log (make-polar (exp 1) .1))
 *  1+0.1i
 */

#if __cplusplus
#define LOG_2 1.4426950408889634074
#else
#define LOG_2 1.4426950408889634073599246810018921L /* (/ (log 2.0)) */ 
#endif


static s7_pointer g_log(s7_scheme *sc, s7_pointer args)
{
  #define H_log "(log z1 (z2 e)) returns log(z1) / log(z2) where z2 (the base) defaults to e: (log 8 2) = 3"
  s7_pointer x;
  
  x = car(args);

  if (!s7_is_number(x))
    {
      check_method(sc, x, sc->LOG, args);
      return(wrong_type_argument_with_type(sc, sc->LOG, small_int(1), x, A_NUMBER));
    }

  if (is_pair(cdr(args)))
    {
      s7_pointer y;

      y = cadr(args);
      if (!(s7_is_number(y)))
	{
	  check_method(sc, y, sc->LOG, args);
	  return(wrong_type_argument_with_type(sc, sc->LOG, small_int(2), y, A_NUMBER));
	}

      if (y == small_int(2))
	{
	  /* (define (2^n? x) (and (not (zero? x)) (zero? (logand x (- x 1))))) */
	  if (is_integer(x))
	    {
	      s7_Int ix;
	      ix = s7_integer(x);
	      if (ix > 0)
		{
		  s7_Double fx;
#if (__ANDROID__) || (MS_WINDOWS) || ((__GNUC__) && ((__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ <= 4))))
		  /* just a guess -- log2 gets a warning in gcc 4.3.2, but not in 4.4.4 */
		  fx = log((double)ix) / log(2.0);
#else
		  fx = log2((double)ix);
#endif
		  /* (s7_Int)fx rounds (log 8 2) to 2 in FreeBSD! */
#if ((__GNUC__) && ((__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ < 4))))
		  return(make_real(sc, fx));
#else
		  if ((ix & (ix - 1)) == 0)
		    return(make_integer(sc, (s7_Int)round(fx)));
		  return(make_real(sc, fx));
#endif
		}
	    }
	  if ((s7_is_real(x)) &&
	      (s7_is_positive(x)))
	    return(make_real(sc, log(real_to_double(sc, x, "log")) * LOG_2));
	  return(s7_from_c_complex(sc, clog(s7_complex(x)) * LOG_2));
	}

      if ((x == small_int(1)) && (y == small_int(1)))  /* (log 1 1) -> 0 (this is NaN in the bignum case) */
	return(small_int(0));                          

      /* (log 1 0) must be 0 since everyone says (expt 0 0) is 1 */
      if (s7_is_zero(y))
	{
	  if ((y == small_int(0)) &&
	      (x == small_int(1)))
	    return(y);
	  return(out_of_range(sc, sc->LOG, small_int(2), y, make_string_wrapper(sc, "can't be 0")));
	}

      if (s7_is_one(y))          /* this used to raise an error, but the bignum case is simpler if we return inf */
	{
	  if (s7_is_one(x))      /* but (log 1.0 1.0) -> 0.0 */
	    return(real_zero);
	  return(real_infinity); /* currently (log 1/0 1) is inf? */
	}

      if ((s7_is_real(x)) &&
	  (s7_is_real(y)) &&
	  (s7_is_positive(x)) &&
	  (s7_is_positive(y)))
	{
	  if ((s7_is_rational(x)) &&
	      (s7_is_rational(y)))
	    {
	      s7_Double res;
	      s7_Int ires;
	      res = log(rational_to_double(sc, x)) / log(rational_to_double(sc, y));
	      ires = (s7_Int)res;
	      if (res - ires == 0.0)
		return(make_integer(sc, ires));   /* (log 8 2) -> 3 or (log 1/8 2) -> -3 */
	      return(make_real(sc, res));         /* perhaps use rationalize here? (log 2 8) -> 1/3 */
	    }
	  return(make_real(sc, log(real_to_double(sc, x, "log")) / log(real_to_double(sc, y, "log"))));
	}
      return(s7_from_c_complex(sc, clog(s7_complex(x)) / clog(s7_complex(y))));
    }
  
  if (s7_is_real(x))
    {
      if (s7_is_positive(x))
	return(make_real(sc, log(real_to_double(sc, x, "log"))));
      return(s7_make_complex(sc, log(-real_to_double(sc, x, "log")), M_PI));
    }
  return(s7_from_c_complex(sc, clog(s7_complex(x))));
}


static s7_pointer g_sin(s7_scheme *sc, s7_pointer args)
{
  #define H_sin "(sin z) returns sin(z)"
  s7_pointer x;

  x = car(args);
  switch (type(x))
    {
    case T_REAL:
      return(make_real(sc, sin(real(x))));

    case T_INTEGER:
      if (integer(x) == 0) return(small_int(0));                      /* (sin 0) -> 0 */
      return(make_real(sc, sin((s7_Double)integer(x))));
      
    case T_RATIO:
      return(make_real(sc, sin((s7_Double)(fraction(x)))));

    case T_COMPLEX:
#if HAVE_COMPLEX_NUMBERS
      return(s7_from_c_complex(sc, csin(as_c_complex(x))));
#else
      return(out_of_range(sc, sc->SIN, small_int(1), x, NO_COMPLEX_NUMBERS));
#endif

    default:
      check_method(sc, x, sc->SIN, args);
      return(simple_wrong_type_argument_with_type(sc, sc->SIN, x, A_NUMBER));
    }

  /* sin is totally inaccurate over about 1e18.  There's a way to get true results,
   *   but it involves fancy "range reduction" techniques. 
   *   This means that lots of things are inaccurate:
   * (sin (remainder 1e22 (* 2 pi)))
   * -0.57876806033477
   * but it should be -8.522008497671888065747423101326159661908E-1
   * ---
   * (remainder 1e22 (* 2 pi)) -> 1.0057952155665e+22 !!
   *   it should be 5.263007914620499494429139986095833592117E0
   */
}


static s7_pointer g_cos(s7_scheme *sc, s7_pointer args)
{
  #define H_cos "(cos z) returns cos(z)"
  s7_pointer x;

  x = car(args);
  switch (type(x))
    {
    case T_REAL:
      return(make_real(sc, cos(real(x))));

    case T_INTEGER:
      if (integer(x) == 0) return(small_int(1));                     /* (cos 0) -> 1 */
      return(make_real(sc, cos((s7_Double)integer(x))));
      
    case T_RATIO:
      return(make_real(sc, cos((s7_Double)(fraction(x)))));

    case T_COMPLEX:
#if HAVE_COMPLEX_NUMBERS
      return(s7_from_c_complex(sc, ccos(as_c_complex(x))));
#else
      return(out_of_range(sc, sc->COS, small_int(1), x, NO_COMPLEX_NUMBERS));
#endif

    default:
      check_method(sc, x, sc->COS, args);
      return(simple_wrong_type_argument_with_type(sc, sc->COS, x, A_NUMBER));
    }
}

s7_pointer s7_cos(s7_scheme *sc, s7_pointer x)
{
  switch (type(x))
    {
    case T_REAL:
      return(make_real(sc, cos(real(x))));

    case T_INTEGER:
      return(make_real(sc, cos((s7_Double)integer(x))));
      
    case T_RATIO:
      return(make_real(sc, cos((s7_Double)(fraction(x)))));

    case T_COMPLEX:
#if HAVE_COMPLEX_NUMBERS
      return(s7_from_c_complex(sc, ccos(as_c_complex(x))));
#endif

    default:
      return(simple_wrong_type_argument_with_type(sc, sc->COS, x, A_NUMBER));
    }
}


static s7_pointer g_tan(s7_scheme *sc, s7_pointer args)
{
  #define H_tan "(tan z) returns tan(z)"
  s7_pointer x;

  x = car(args);
  switch (type(x))
    {
    case T_REAL:
      return(make_real(sc, tan(real(x))));
      
    case T_INTEGER:
      if (integer(x) == 0) return(small_int(0));                      /* (tan 0) -> 0 */
      return(make_real(sc, tan((s7_Double)(integer(x)))));

    case T_RATIO:
      return(make_real(sc, tan((s7_Double)(fraction(x)))));

    case T_COMPLEX:
#if HAVE_COMPLEX_NUMBERS
      if (imag_part(x) > 350.0)
	return(s7_make_complex(sc, 0.0, 1.0));
      if (imag_part(x) < -350.0)
	return(s7_make_complex(sc, 0.0, -1.0));
      return(s7_from_c_complex(sc, ctan(as_c_complex(x))));
#else
      return(out_of_range(sc, sc->TAN, small_int(1), x, NO_COMPLEX_NUMBERS));
#endif

    default:
      check_method(sc, x, sc->TAN, args);
      return(simple_wrong_type_argument_with_type(sc, sc->TAN, x, A_NUMBER));
    }
}


static s7_pointer g_asin(s7_scheme *sc, s7_pointer args)
{
  #define H_asin "(asin z) returns asin(z); (sin (asin x)) = x"
  s7_pointer n;

  n = car(args);
  switch (type(n))
    {
    case T_INTEGER:
      if (integer(n) == 0) return(small_int(0));                    /* (asin 0) -> 0 */

      /* in netBSD, (asin 2) returns 0.25383842987008+0.25383842987008i according to Peter Bex
       */

    case T_REAL:
    case T_RATIO:
      {
	s7_Double x, absx, recip;
	s7_Complex result;
	x = real_to_double(sc, n, "asin");
	absx = fabs(x);
	if (absx <= 1.0)
	  return(make_real(sc, asin(x)));
      
	/* otherwise use maxima code: */
	recip = 1.0 / absx;
 	result = (M_PI / 2.0) - (_Complex_I * clog(absx * (1.0 + (sqrt(1.0 + recip) * csqrt(1.0 - recip)))));
	if (x < 0.0)
	  return(s7_from_c_complex(sc, -result));
	return(s7_from_c_complex(sc, result));
      }

    case T_COMPLEX:
#if HAVE_COMPLEX_NUMBERS
      /* if either real or imag part is very large, use explicit formula, not casin */
      /*   this code taken from sbcl's src/code/irrat.lisp */
      /* break is around x+70000000i */

      if ((fabs(real_part(n)) > 1.0e7) ||
	  (fabs(imag_part(n)) > 1.0e7))
	{
	  s7_Complex sq1mz, sq1pz, z;
	  z = as_c_complex(n);
	  sq1mz = csqrt(1.0 - z);
	  sq1pz = csqrt(1.0 + z);
	  return(s7_make_complex(sc, 
				 atan(real_part(n) / creal(sq1mz * sq1pz)),
				 asinh(cimag(sq1pz * conj(sq1mz)))));
	}
      return(s7_from_c_complex(sc, casin(as_c_complex(n))));
#else
      return(out_of_range(sc, sc->ASIN, small_int(1), n, NO_COMPLEX_NUMBERS));
#endif

    default:
      check_method(sc, n, sc->ASIN, args);
      return(simple_wrong_type_argument_with_type(sc, sc->ASIN, n, A_NUMBER));
    }
}


static s7_pointer g_acos(s7_scheme *sc, s7_pointer args)
{
  #define H_acos "(acos z) returns acos(z); (cos (acos 1)) = 1"
  s7_pointer n;

  n = car(args);
  switch (type(n))
    {
    case T_INTEGER:
      if (integer(n) == 1) return(small_int(0));

    case T_REAL:
    case T_RATIO:
      {
	s7_Double x, absx, recip;
	s7_Complex result;
	x = real_to_double(sc, n, "acos");
	absx = fabs(x);
	if (absx <= 1.0)
	  return(make_real(sc, acos(x)));
      
	/* else follow maxima again: */
	recip = 1.0 / absx;
	if (x > 0.0)
	  result = _Complex_I * clog(absx * (1.0 + (sqrt(1.0 + recip) * csqrt(1.0 - recip))));
	else result = M_PI - _Complex_I * clog(absx * (1.0 + (sqrt(1.0 + recip) * csqrt(1.0 - recip))));
	return(s7_from_c_complex(sc, result));
      }

    case T_COMPLEX:
#if HAVE_COMPLEX_NUMBERS
      /* if either real or imag part is very large, use explicit formula, not cacos */
      /*   this code taken from sbcl's src/code/irrat.lisp */

      if ((fabs(real_part(n)) > 1.0e7) ||
	  (fabs(imag_part(n)) > 1.0e7))
	{
	  s7_Complex sq1mz, sq1pz, z;
	  z = as_c_complex(n);
	  sq1mz = csqrt(1.0 - z);
	  sq1pz = csqrt(1.0 + z);
	  return(s7_make_complex(sc, 
				 2.0 * atan(creal(sq1mz) / creal(sq1pz)),
				 asinh(cimag(sq1mz * conj(sq1pz)))));
	}
      return(s7_from_c_complex(sc, cacos(s7_complex(n))));
#else
      return(out_of_range(sc, sc->ACOS, small_int(1), n, NO_COMPLEX_NUMBERS));
#endif

    default:
      check_method(sc, n, sc->ACOS, args);
      return(simple_wrong_type_argument_with_type(sc, sc->ACOS, n, A_NUMBER));
    }
}


static s7_pointer g_atan(s7_scheme *sc, s7_pointer args)
{
  #define H_atan "(atan z) returns atan(z), (atan y x) returns atan(y/x)"
  s7_pointer x, y;

  /* currently (atan inf.0 inf.0) -> 0.78539816339745, and (atan inf.0 -inf.0) -> 2.3561944901923 (etc) */

  x = car(args);
  if (!is_pair(cdr(args)))
    {
      switch (type(x))
	{
	case T_INTEGER:
	  if (integer(x) == 0) return(small_int(0));                /* (atan 0) -> 0 */
	  
	case T_REAL:
	case T_RATIO:
	  return(make_real(sc, atan(real_to_double(sc, x, "atan"))));

	case T_COMPLEX:
#if HAVE_COMPLEX_NUMBERS
	  return(s7_from_c_complex(sc, catan(as_c_complex(x))));
#else
	  return(out_of_range(sc, sc->ATAN, small_int(1), x, NO_COMPLEX_NUMBERS));
#endif

	default:
	  check_method(sc, x, sc->ATAN, args);
	  return(simple_wrong_type_argument_with_type(sc, sc->ATAN, x, A_NUMBER));
	}
    }

  if (!s7_is_real(x))
    {
      check_method(sc, x, sc->ATAN, args);
      return(wrong_type_argument(sc, sc->ATAN, small_int(1), x, T_REAL));
    }
  y = cadr(args);
  if (!s7_is_real(y))
    {
      check_method(sc, y, sc->ATAN, args);
      return(wrong_type_argument(sc, sc->ATAN, small_int(2), y, T_REAL));
    }
  return(make_real(sc, atan2(real_to_double(sc, x, "atan"), real_to_double(sc, y, "atan"))));
}  


static s7_pointer g_sinh(s7_scheme *sc, s7_pointer args)
{
  #define H_sinh "(sinh z) returns sinh(z)"
  s7_pointer x;

  x = car(args);
  switch (type(x))
    {
    case T_INTEGER:
      if (integer(x) == 0) return(small_int(0));                    /* (sinh 0) -> 0 */

    case T_REAL:
    case T_RATIO:
      return(make_real(sc, sinh(real_to_double(sc, x, "sinh"))));

    case T_COMPLEX:
#if HAVE_COMPLEX_NUMBERS
      return(s7_from_c_complex(sc, csinh(as_c_complex(x))));
#else
      return(out_of_range(sc, sc->SINH, small_int(1), x, NO_COMPLEX_NUMBERS));
#endif

    default:
      check_method(sc, x, sc->SINH, args);
      return(simple_wrong_type_argument_with_type(sc, sc->SINH, x, A_NUMBER));
    }
}


static s7_pointer g_cosh(s7_scheme *sc, s7_pointer args)
{
  #define H_cosh "(cosh z) returns cosh(z)"
  s7_pointer x;

  x = car(args);
  switch (type(x))
    {
    case T_INTEGER:
      if (integer(x) == 0) return(small_int(1));                   /* (cosh 0) -> 1 */

    case T_REAL:
    case T_RATIO:
      /* this is not completely correct when optimization kicks in.
       * :(define (hi) (do ((i 0 (+ i 1))) ((= i 1)) (display (cosh i))))
       * hi
       * :(hi)
       * 1.0()
       * :(cosh 0)
       * 1
       */
      return(make_real(sc, cosh(real_to_double(sc, x, "cosh"))));

    case T_COMPLEX:
#if HAVE_COMPLEX_NUMBERS
      return(s7_from_c_complex(sc, ccosh(as_c_complex(x))));
#else
      return(out_of_range(sc, sc->COSH, small_int(1), x, NO_COMPLEX_NUMBERS));
#endif

    default:
      check_method(sc, x, sc->COSH, args);
      return(simple_wrong_type_argument_with_type(sc, sc->COSH, x, A_NUMBER));
    }
}


static s7_pointer g_tanh(s7_scheme *sc, s7_pointer args)
{
  #define H_tanh "(tanh z) returns tanh(z)"
  s7_pointer x;

  x = car(args);
  switch (type(x))
    {
    case T_INTEGER:
      if (integer(x) == 0) return(small_int(0));                   /* (tanh 0) -> 0 */

    case T_REAL:
    case T_RATIO:
      return(make_real(sc, tanh(real_to_double(sc, x, "tanh"))));

    case T_COMPLEX:
#if HAVE_COMPLEX_NUMBERS
      if (real_part(x) > 350.0)
	return(real_one);               /* closer than 0.0 which is what ctanh is about to return! */
      if (real_part(x) < -350.0)
	return(make_real(sc, -1.0));              /* closer than -0.0 which is what ctanh is about to return! */
      return(s7_from_c_complex(sc, ctanh(as_c_complex(x))));
#else
      return(out_of_range(sc, sc->TANH, small_int(1), x, NO_COMPLEX_NUMBERS));
#endif

    default:
      check_method(sc, x, sc->TANH, args);
      return(simple_wrong_type_argument_with_type(sc, sc->TANH, x, A_NUMBER));
    }
}


static s7_pointer g_asinh(s7_scheme *sc, s7_pointer args)
{
  #define H_asinh "(asinh z) returns asinh(z)"
  s7_pointer x;

  x = car(args);
  switch (type(x))
    {
    case T_INTEGER:
      if (integer(x) == 0) return(small_int(0));

    case T_REAL:
    case T_RATIO:
      return(make_real(sc, asinh(real_to_double(sc, x, "asinh"))));

    case T_COMPLEX:
#if HAVE_COMPLEX_NUMBERS
      return(s7_from_c_complex(sc, casinh(as_c_complex(x))));
#else
      return(out_of_range(sc, sc->ASINH, small_int(1), x, NO_COMPLEX_NUMBERS));
#endif

    default:
      check_method(sc, x, sc->ASINH, args);
      return(simple_wrong_type_argument_with_type(sc, sc->ASINH, x, A_NUMBER));
    }
}


static s7_pointer g_acosh(s7_scheme *sc, s7_pointer args)
{
  #define H_acosh "(acosh z) returns acosh(z)"
  s7_pointer x;

  x = car(args);
  switch (type(x))
    {
    case T_INTEGER:
      if (integer(x) == 1) return(small_int(0));

    case T_REAL:
    case T_RATIO:
      if (real_to_double(sc, x, "acosh") >= 1.0)
	return(make_real(sc, acosh(real_to_double(sc, x, "acosh"))));

    case T_COMPLEX:
#if HAVE_COMPLEX_NUMBERS
      return(s7_from_c_complex(sc, cacosh(s7_complex(x)))); /* not as_c_complex because x might not be complex */
#else
      /* since we can fall through to this branch, we need a better error message than "must be a number, not 0.0" */
      return(out_of_range(sc, sc->ACOSH, small_int(1), x, NO_COMPLEX_NUMBERS));
#endif

    default:
      check_method(sc, x, sc->ACOSH, args);
      return(simple_wrong_type_argument_with_type(sc, sc->ACOSH, x, A_NUMBER));
    }
}


static s7_pointer g_atanh(s7_scheme *sc, s7_pointer args)
{
  #define H_atanh "(atanh z) returns atanh(z)"
  s7_pointer x;

  x = car(args);
  switch (type(x))
    {
    case T_INTEGER:
      if (integer(x) == 0) return(small_int(0));                    /* (atanh 0) -> 0 */

    case T_REAL:
    case T_RATIO:
      if (fabs(real_to_double(sc, x, "atanh")) < 1.0)
	return(make_real(sc, atanh(real_to_double(sc, x, "atanh"))));

      /* if we can't distinguish x from 1.0 even with long doubles, we'll get inf.0:
       *    (atanh 9223372036854775/9223372036854776) -> 18.714973875119
       *    (atanh 92233720368547758/92233720368547757) -> inf.0
       */
    case T_COMPLEX:
#if HAVE_COMPLEX_NUMBERS
      return(s7_from_c_complex(sc, catanh(s7_complex(x))));
#else
      return(out_of_range(sc, sc->ATANH, small_int(1), x, NO_COMPLEX_NUMBERS));
#endif

    default:
      check_method(sc, x, sc->ATANH, args);
      return(simple_wrong_type_argument_with_type(sc, sc->ATANH, x, A_NUMBER));
    }
}


static s7_pointer g_sqrt(s7_scheme *sc, s7_pointer args)
{
  #define H_sqrt "(sqrt z) returns the square root of z"
  s7_pointer n;
  s7_Double sqx;

  n = car(args);
  switch (type(n))
    {
    case T_INTEGER:
      if (integer(n) >= 0)
	{
	  s7_Int ix;
	  sqx = sqrt((s7_Double)integer(n));
	  ix = (s7_Int)sqx;
	  if ((ix * ix) == integer(n))
	    return(make_integer(sc, ix));
	  return(make_real(sc, sqx));
	  /* Mark Weaver notes that 
	   *     (zero? (- (sqrt 9007199136250226) 94906265.0)) -> #t
	   * but (* 94906265 94906265) -> 9007199136250225 -- oops
	   * at least we return a real here, not an incorrect integer and
	   *     (sqrt 9007199136250225) -> 94906265
	   */
	}
      sqx = (s7_Double)integer(n); /* we're trying to protect against (sqrt -9223372036854775808) where we can't negate the integer argument */
      return(s7_make_complex(sc, 0.0, sqrt((s7_Double)(-sqx))));
      break;

    case T_RATIO:
      sqx = fraction(n);
      if (sqx > 0.0) /* else it's complex, so it can't be a ratio */
	{
	  s7_Int nm = 0, dn = 1;
	  if (c_rationalize(sqx, 1.0e-16, &nm, &dn)) /* 1e-16 so that (sqrt 1/1099511627776) returns 1/1048576 */
	    {
	      if ((nm * nm == numerator(n)) &&
		  (dn * dn == denominator(n)))
		return(s7_make_ratio(sc, nm, dn));
	    }
	  return(make_real(sc, sqrt(sqx)));
	}
      return(s7_make_complex(sc, 0.0, sqrt(-sqx)));
      break;

    case T_REAL:
      if (is_NaN(real(n)))
	return(real_NaN);
      if (real(n) >= 0.0)
	return(make_real(sc, sqrt(real(n))));
      return(s7_make_complex(sc, 0.0, sqrt(-real(n))));
      break;

    case T_COMPLEX:
#if HAVE_COMPLEX_NUMBERS
      return(s7_from_c_complex(sc, csqrt(as_c_complex(n))));
      break;
#else
      return(out_of_range(sc, sc->SQRT, small_int(1), n, NO_COMPLEX_NUMBERS));
#endif

    default:
      check_method(sc, n, sc->SQRT, args);
      return(simple_wrong_type_argument_with_type(sc, sc->SQRT, n, A_NUMBER));
    }
}


static s7_Int int_to_int(s7_Int x, s7_Int n)
{
  /* from GSL */
  s7_Int value = 1;
  do {
    if (n & 1) value *= x;
    n >>= 1;
    x *= x;
  } while (n);
  return(value);
}


static const long long int nth_roots[63] = {
  S7_LLONG_MAX, S7_LLONG_MAX, 3037000499LL, 2097151, 55108, 6208, 1448, 511, 234, 127, 78, 52, 38, 28, 22, 
  18, 15, 13, 11, 9, 8, 7, 7, 6, 6, 5, 5, 5, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 
  2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2};

static const long int_nth_roots[31] = {
  S7_LONG_MAX, S7_LONG_MAX, 46340, 1290, 215, 73, 35, 21, 14, 10, 8, 7, 5, 5, 4, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2};

static bool int_pow_ok(s7_Int x, s7_Int y)
{
  if (s7_int_bits > 31)
    return((y < 63) &&
	   (nth_roots[y] >= s7_Int_abs(x)));
  return((y < 31) &&
	 (int_nth_roots[y] >= s7_Int_abs(x)));
}


static s7_pointer g_expt(s7_scheme *sc, s7_pointer args)
{
  #define H_expt "(expt z1 z2) returns z1^z2"
  s7_pointer n, pw;
  
  n = car(args);
  if (!s7_is_number(n))
    {
      check_method(sc, n, sc->EXPT, args);
      return(wrong_type_argument_with_type(sc, sc->EXPT, small_int(1), n, A_NUMBER));
    }
  pw = cadr(args);
  if (!s7_is_number(pw))
    {
      check_method(sc, pw, sc->EXPT, args);
      return(wrong_type_argument_with_type(sc, sc->EXPT, small_int(2), pw, A_NUMBER));
    }
  /* this provides more than 2 args to expt:
   *  if (is_not_null(cddr(args)))
   *    return(g_expt(sc, list_2(sc, car(args), g_expt(sc, cdr(args)))));
   *
   * but it's unusual in scheme to process args in reverse order, and the
   * syntax by itself is ambiguous (does (expt 2 2 3) = 256 or 64?)
   */

  if (s7_is_zero(n))
    {
      if (s7_is_zero(pw))
	{
	  if ((s7_is_integer(n)) && (s7_is_integer(pw)))       /* (expt 0 0) -> 1 */
	    return(small_int(1));
	  return(real_zero);                                   /* (expt 0.0 0) -> 0.0 */
	}

      if (s7_is_real(pw))
	{
	  if (s7_is_negative(pw))                              /* (expt 0 -1) */
	    return(division_by_zero_error(sc, sc->EXPT, args));  
	  /* (Clisp gives divide-by-zero error here, Guile returns inf.0) */

	  if ((!s7_is_rational(pw)) &&                         /* (expt 0 most-positive-fixnum) */
	      (is_NaN(s7_real(pw))))                           /* (expt 0 +nan.0) */
	    return(pw);
	}
      else
	{                                                      /* (expt 0 a+bi) */
	  if (real_part(pw) < 0.0)                             /* (expt 0 -1+i) */
	    return(division_by_zero_error(sc, sc->EXPT, args));  
	  if ((is_NaN(real_part(pw))) ||                       /* (expt 0 0+1/0i) */
	      (is_NaN(imag_part(pw))))
	    return(real_NaN);
	}

      if ((s7_is_integer(n)) && (s7_is_integer(pw)))           /* pw != 0, (expt 0 2312) */
	return(small_int(0));
      return(real_zero);                                       /* (expt 0.0 123123) */
    }

  if (s7_is_one(pw))
    {
      if (s7_is_integer(pw))
	return(n);
      if (is_rational(n))
	return(make_real(sc, rational_to_double(sc, n)));
      return(n);
    }
  
  if (type(pw) == T_INTEGER)
    {
      s7_Int y;
      y = integer(pw);
      if (y == 0)
	{
	  if (is_rational(n))                                 /* (expt 3 0) */
	    return(small_int(1));
	  if ((is_NaN(s7_real_part(n))) ||                    /* (expt 1/0 0) -> NaN */
	      (is_NaN(s7_imag_part(n))))                      /* (expt (make-rectangular 0 1/0) 0) -> NaN */
	    return(n);                                        
	  return(real_one);                                   /* (expt 3.0 0) */
	}

      switch (type(n))
	{
	case T_INTEGER:
	  {
	    s7_Int x;
	    x = s7_integer(n);
	    if (x == 1)                                       /* (expt 1 y) */
	      return(n);
	    
	    if (x == -1)
	      {
		if (y == S7_LLONG_MIN)                        /* (expt -1 most-negative-fixnum) */
		  return(small_int(1));
		
		if (s7_Int_abs(y) & 1)                        /* (expt -1 odd-int) */
		  return(n);
		return(small_int(1));                         /* (expt -1 even-int) */
	      }
	    
	    if (y == S7_LLONG_MIN)                            /* (expt x most-negative-fixnum) */
	      return(small_int(0));                      
	    if (x == S7_LLONG_MIN)                            /* (expt most-negative-fixnum y) */
	      return(make_real(sc, pow((double)x, (double)y)));
	    
	    if (int_pow_ok(x, s7_Int_abs(y)))
	      {
		if (y > 0)
		  return(make_integer(sc, int_to_int(x, y)));
		return(s7_make_ratio(sc, 1, int_to_int(x, -y)));
	      }
	  }
	  break;

	case T_RATIO:
	  {
	    s7_Int nm, dn;
	    
	    nm = numerator(n);
	    dn = denominator(n);
	    
	    if (y == S7_LLONG_MIN)
	      {
		if (s7_Int_abs(nm) > dn)
		  return(small_int(0));              /* (expt 4/3 most-negative-fixnum) -> 0? */
		return(real_infinity);               /* (expt 3/4 most-negative-fixnum) -> inf? */
	      }
	    
	    if ((int_pow_ok(nm, s7_Int_abs(y))) &&
		(int_pow_ok(dn, s7_Int_abs(y))))
	      {
		if (y > 0)
		  return(s7_make_ratio(sc, int_to_int(nm, y), int_to_int(dn, y)));
		return(s7_make_ratio(sc, int_to_int(dn, -y), int_to_int(nm, -y)));
	      }
	  }
	  break;
	  /* occasionally int^rat can be int but it happens so infrequently it's probably not worth checking
	   *  one possibly easy case: (expt 1 1/2) -> 1 (-1?) etc
	   */

	case T_REAL:
	  /* (expt -1.0 most-positive-fixnum) should be -1.0
	   * (expt -1.0 (+ (expt 2 53) 1)) -> -1.0
	   * (expt -1.0 (- 1 (expt 2 54))) -> -1.0
	   */
	  if (real(n) == -1.0)
	    {
	      if (y == S7_LLONG_MIN)
		return(real_one);
		
	      if (s7_Int_abs(y) & 1)
		return(n);
	      return(real_one);
	    }
	  break;

	case T_COMPLEX:
#if HAVE_COMPLEX_NUMBERS
	  if ((s7_real_part(n) == 0.0) &&
	      ((s7_imag_part(n) == 1.0) ||
	       (s7_imag_part(n) == -1.0)))
	    {
	      bool yp, np;
	      yp = (y > 0);
	      np = (s7_imag_part(n) > 0.0);
	      switch (s7_Int_abs(y) % 4)
		{
		case 0: return(real_one);
		case 1: return(s7_make_complex(sc, 0.0, (yp == np) ? 1.0 : -1.0));
		case 2: return(make_real(sc, -1.0));
		case 3: return(s7_make_complex(sc, 0.0, (yp == np) ? -1.0 : 1.0));
		}
	    }
#else
	  return(out_of_range(sc, sc->EXPT, small_int(2), n, NO_COMPLEX_NUMBERS));
#endif
	  break;
	}
    }

  if ((s7_is_real(n)) &&
      (s7_is_real(pw)))
    {
      s7_Double x, y;

      if ((type(pw) == T_RATIO) &&
	  (numerator(pw) == 1))
	{
	  if (denominator(pw) == 2)
	    return(g_sqrt(sc, args));
	  if (denominator(pw) == 3)
	    return(make_real(sc, cbrt(real_to_double(sc, n, "expt")))); /* (expt 27 1/3) should be 3, not 3.0... */
 
	  /* but: (expt 512/729 1/3) -> 0.88888888888889
	   */
	  /* and 4 -> sqrt(sqrt...) etc? */
	}

      x = real_to_double(sc, n, "expt");
      y = real_to_double(sc, pw, "expt");

      if (is_NaN(x)) return(n);
      if (is_NaN(y)) return(pw);
      if (y == 0.0) return(real_one);

      if (x > 0.0)
	return(make_real(sc, pow(x, y)));
      /* tricky cases abound here: (expt -1 1/9223372036854775807)
       */
    }
  
  /* (expt 0+i 1e+16) = 0.98156860153485-0.19111012657867i ? 
   * (expt 0+i 1+1/0i) = 0.0 ??
   */
  return(s7_from_c_complex(sc, cpow(s7_complex(n), s7_complex(pw))));
}


#if (!WITH_GMP)
static s7_pointer expt_temp_s;
static s7_pointer g_expt_temp_s(s7_scheme *sc, s7_pointer args)
{
  s7_pointer arg1, arg2;

  arg1 = car(args);
  arg2 = cadr(args);
  if ((is_simple_real(arg2)) &&
      (real(arg1) > 0.0))
    return(remake_real(sc, arg1, pow(real(arg1), real(arg2))));
  
  return(g_expt(sc, args));
}
#endif


static s7_Int c_lcm(s7_Int a, s7_Int b)
{
  if ((a == 0) || (b == 0)) return(0);
  if (a < 0) a = -a;
  if (b < 0) b = -b;
  return((a / c_gcd(a, b)) * b);
}


static s7_pointer g_lcm(s7_scheme *sc, s7_pointer args)
{
  #define H_lcm "(lcm ...) returns the least common multiple of its rational arguments"
  s7_Int n = 1, d = 0;
  s7_pointer p;

  if (!is_pair(args))
    return(small_int(1));

  if (!is_pair(cdr(args)))
    {
      if (!is_rational(car(args)))
	{
	  check_method(sc, car(args), sc->LCM, args);
	  return(wrong_type_argument_with_type(sc, sc->LCM, small_int(1), car(args), A_RATIONAL));
	}
      return(g_abs(sc, args));
    }

  for (p = args; is_pair(p); p = cdr(p))
    {
      s7_pointer x;
      x = car(p);
      switch (type(x))
	{
	case T_INTEGER:
	  n = c_lcm(n, integer(x));
	  if (d != 0) d = 1;
	  break;

	case T_RATIO:
	  n = c_lcm(n, s7_numerator(x));
	  if (d == 0)
	    {
	      if (p == args)
		d = s7_denominator(x); 
	      else d = 1;
	    }
	  else d = c_gcd(d, s7_denominator(x));
	  break;

	default:
	  check_method(sc, x, sc->LCM, cons(sc, (d <= 1) ? make_integer(sc, n) : s7_make_ratio(sc, n, d), p));
	  return(wrong_type_argument_n_with_type(sc, sc->LCM, position_of(p, args), x, A_RATIONAL));
	}
      if (n < 0) return(simple_out_of_range(sc, sc->LCM, args, RESULT_IS_TOO_LARGE));
      if (n == 0)
	{
	  for (p = cdr(p); is_pair(p); p = cdr(p))
	    if (!is_rational_via_method(sc, car(p)))
	      return(wrong_type_argument_n_with_type(sc, sc->LCM, position_of(p, args), x, A_RATIONAL));
	  return(small_int(0));
	}
    }

  if (d <= 1)
    return(make_integer(sc, n));
  return(s7_make_ratio(sc, n, d));
}


static s7_pointer g_gcd(s7_scheme *sc, s7_pointer args)
{
  #define H_gcd "(gcd ...) returns the greatest common divisor of its rational arguments"
  s7_Int n = 0, d = 1;
  s7_pointer p;

  if (!is_pair(args))
    return(small_int(0));

  if (!is_pair(cdr(args)))
    {
      if (!is_rational(car(args)))
	{
	  check_method(sc, car(args), sc->GCD, args);
	  return(wrong_type_argument_with_type(sc, sc->GCD, small_int(1), car(args), A_RATIONAL));
	}
      return(g_abs(sc, args));
    }

  for (p = args; is_pair(p); p = cdr(p))
    {
      s7_pointer x;
      x = car(p);
      switch (type(x))
	{
	case T_INTEGER:
	  n = c_gcd(n, integer(x));
	  break;

	case T_RATIO:
	  n = c_gcd(n, s7_numerator(x));
	  d = c_lcm(d, s7_denominator(x));
	  if (d < 0) return(simple_out_of_range(sc, sc->GCD, args, RESULT_IS_TOO_LARGE));
	  break;

	default:
	  check_method(sc, x, sc->GCD, cons(sc, (d <= 1) ? make_integer(sc, n) : s7_make_ratio(sc, n, d), p));
	  return(wrong_type_argument_n_with_type(sc, sc->GCD, position_of(p, args), x, A_RATIONAL));
	}
      if (n < 0) return(simple_out_of_range(sc, sc->GCD, args, RESULT_IS_TOO_LARGE));
    }

  if (d <= 1)
    return(make_integer(sc, n));
  return(s7_make_ratio(sc, n, d));
}


static s7_pointer s7_truncate(s7_scheme *sc, s7_pointer caller, s7_Double xf)   /* can't use "truncate" -- it's in unistd.h */
{
  if ((xf > S7_LLONG_MAX) ||
      (xf < S7_LLONG_MIN))
    return(simple_out_of_range(sc, caller, make_real(sc, xf), ITS_TOO_LARGE));

  if (xf > 0.0)
    return(make_integer(sc, (s7_Int)floor(xf)));
  return(make_integer(sc, (s7_Int)ceil(xf)));
}


static s7_pointer g_quotient(s7_scheme *sc, s7_pointer args)
{
  #define H_quotient "(quotient x1 x2) returns the integer quotient of x1 and x2; (quotient 4 3) = 1"
  /* (define (quo x1 x2) (truncate (/ x1 x2))) ; slib 
   */  
  s7_pointer x, y;
  s7_Int d1, d2, n1, n2;

  x = car(args);
  y = cadr(args);

  switch (type(x))
    {
    case T_INTEGER:
      switch (type(y))
	{
	case T_INTEGER:
	  if (integer(y) == 0) 
	    return(division_by_zero_error(sc, sc->QUOTIENT, args));
	  if ((integer(y) == -1) && (integer(x) == S7_LLONG_MIN))   /* (quotient most-negative-fixnum -1) */
	    return(simple_out_of_range(sc, sc->QUOTIENT, args, ITS_TOO_LARGE));
	  return(make_integer(sc, integer(x) / integer(y)));

	case T_RATIO:
	  n1 = integer(x);
	  d1 = 1;
	  n2 = numerator(y);
	  d2 = denominator(y);
	  goto RATIO_QUO_RATIO;

	case T_REAL:
	  if (real(y) == 0.0) 
	    return(division_by_zero_error(sc, sc->QUOTIENT, args));
	  if ((is_inf(real(y))) || (is_NaN(real(y))))
	    return(wrong_type_argument_with_type(sc, sc->QUOTIENT, small_int(2), y, A_NORMAL_REAL));
	  return(s7_truncate(sc, sc->QUOTIENT, (s7_Double)integer(x) / real(y)));

	default:    
	  check_method(sc, y, sc->QUOTIENT, args);    
	  return(wrong_type_argument(sc, sc->QUOTIENT, small_int(2), y, T_REAL));
	}

    case T_RATIO:
      switch (type(y))
	{
	case T_INTEGER:
	  if (integer(y) == 0) 
	    return(division_by_zero_error(sc, sc->QUOTIENT, args));
	  n1 = numerator(x);
	  d1 = denominator(x);
	  n2 = integer(y);
	  d2 = 1;
	  goto RATIO_QUO_RATIO;

	case T_RATIO:
	  n1 = numerator(x);
	  d1 = denominator(x);
	  n2 = numerator(y);
	  d2 = denominator(y);
	RATIO_QUO_RATIO:
	  if (d1 == d2)
	    return(make_integer(sc, n1 / n2));              /* (quotient 3/9223372036854775807 1/9223372036854775807) */
	  if (n1 == n2)
	    return(make_integer(sc, d2 / d1));              /* (quotient 9223372036854775807/2 9223372036854775807/8) */

	  if ((integer_length(n1) + integer_length(d2) >= s7_int_bits) ||
	      (integer_length(n2) + integer_length(d1) >= s7_int_bits))
	    return(s7_truncate(sc, sc->QUOTIENT, ((long double)n1 / (long double)n2) * ((long double)d2 / (long double)d1)));
	  /* this can lose:
	   *   (quotient 1 2305843009213693952/4611686018427387903) -> 2, not 1
	   *   (quotient 21053343141/6701487259 3587785776203/1142027682075) -> 1, not 0
	   */
	  return(make_integer(sc, (n1 * d2) / (n2 * d1)));  /* (quotient 922337203685477580 1/3) */

	case T_REAL:
	  if (real(y) == 0.0) 
	    return(division_by_zero_error(sc, sc->QUOTIENT, args));
	  if ((is_inf(real(y))) || (is_NaN(real(y))))
	    return(wrong_type_argument_with_type(sc, sc->QUOTIENT, small_int(2), y, A_NORMAL_REAL));
	  return(s7_truncate(sc, sc->QUOTIENT, (s7_Double)fraction(x) / real(y)));

	default:        
	  check_method(sc, y, sc->QUOTIENT, args);
	  return(wrong_type_argument(sc, sc->QUOTIENT, small_int(2), y, T_REAL));
	}

    case T_REAL:
      if ((is_inf(real(x))) || (is_NaN(real(x))))
	return(wrong_type_argument_with_type(sc, sc->QUOTIENT, small_int(1), x, A_NORMAL_REAL));

      /* if infs allowed we need to return infs/nans, else:
       *    (quotient inf.0 1e-309) -> -9223372036854775808
       *    (quotient inf.0 inf.0) -> -9223372036854775808
       */

      switch (type(y))
	{
	case T_INTEGER:
	  if (integer(y) == 0) 
	    return(division_by_zero_error(sc, sc->QUOTIENT, args));
	  return(s7_truncate(sc, sc->QUOTIENT, real(x) / (s7_Double)integer(y)));

	case T_RATIO:
	  return(s7_truncate(sc, sc->QUOTIENT, real(x) / (s7_Double)fraction(y)));

	case T_REAL:
	  if (real(y) == 0.0) 
	    return(division_by_zero_error(sc, sc->QUOTIENT, args));
	  if ((is_inf(real(y))) || (is_NaN(real(y))))
	    return(wrong_type_argument_with_type(sc, sc->QUOTIENT, small_int(2), y, A_NORMAL_REAL));
	  return(s7_truncate(sc, sc->QUOTIENT, real(x) / real(y)));

	default:        
	  check_method(sc, y, sc->QUOTIENT, args);
	  return(wrong_type_argument(sc, sc->QUOTIENT, small_int(2), y, T_REAL));
	}

    default:
      check_method(sc, x, sc->QUOTIENT, args);
      return(wrong_type_argument(sc, sc->QUOTIENT, small_int(1), x, T_REAL));
    }
}


static s7_pointer g_remainder(s7_scheme *sc, s7_pointer args)
{
  #define H_remainder "(remainder x1 x2) returns the remainder of x1/x2; (remainder 10 3) = 1"
  /* (define (rem x1 x2) (- x1 (* x2 (quo x1 x2)))) ; slib 
   */
  s7_pointer x, y;
  s7_Int quo, d1, d2, n1, n2;
  s7_Double pre_quo;

  x = car(args);
  y = cadr(args);

  switch (type(x))
    {
    case T_INTEGER:
      switch (type(y))
	{
	case T_INTEGER:
	  if (integer(y) == 0) 
	    return(division_by_zero_error(sc, sc->REMAINDER, args));
	  if ((integer(y) == 1) || (integer(y) == -1))
	    return(make_integer(sc, 0));
	  /* (remainder most-negative-fixnum -1) will segfault with arithmetic exception */
	  return(make_integer(sc, integer(x) % integer(y)));

	case T_RATIO:
	  n1 = integer(x);
	  d1 = 1;
	  n2 = numerator(y);
	  d2 = denominator(y);
	  goto RATIO_REM_RATIO;

	case T_REAL:
	  if (real(y) == 0.0) 
	    return(division_by_zero_error(sc, sc->REMAINDER, args));
	  if ((is_inf(real(y))) || (is_NaN(real(y))))
	    return(wrong_type_argument_with_type(sc, sc->REMAINDER, small_int(2), y, A_NORMAL_REAL));

	  pre_quo = (s7_Double)integer(x) / real(y);
	  if ((pre_quo > S7_LLONG_MAX) || (pre_quo < S7_LLONG_MIN))
	    return(simple_out_of_range(sc, sc->REMAINDER, args, ITS_TOO_LARGE));
	  if (pre_quo > 0.0) quo = (s7_Int)floor(pre_quo); else quo = (s7_Int)ceil(pre_quo);
	  return(make_real(sc, integer(x) - real(y) * quo));

	default:        
	  check_method(sc, y, sc->REMAINDER, args);
	  return(wrong_type_argument(sc, sc->REMAINDER, small_int(2), y, T_REAL));
	}

    case T_RATIO:
      switch (type(y))
	{
	case T_INTEGER:
	  n2 = integer(y);
	  if (n2 == 0) 
	    return(division_by_zero_error(sc, sc->REMAINDER, args));
	  n1 = numerator(x);
	  d1 = denominator(x);
	  d2 = 1;
	  goto RATIO_REM_RATIO;

	case T_RATIO:
	  n1 = numerator(x);
	  d1 = denominator(x);
	  n2 = numerator(y);
	  d2 = denominator(y);
	RATIO_REM_RATIO:
	  if (d1 == d2)
	    quo = (s7_Int)(n1 / n2);
	  else
	    {
	      if (n1 == n2)
		quo = (s7_Int)(d2 / d1);
	      else
		{
		  if ((integer_length(n1) + integer_length(d2) >= s7_int_bits) ||
		      (integer_length(n2) + integer_length(d1) >= s7_int_bits))
		    {
		      pre_quo = ((long double)n1 / (long double)n2) * ((long double)d2 / (long double)d1);
		      if ((pre_quo > S7_LLONG_MAX) || (pre_quo < S7_LLONG_MIN))
			return(simple_out_of_range(sc, sc->REMAINDER, args, ITS_TOO_LARGE));
		      if (pre_quo > 0.0) quo = (s7_Int)floor(pre_quo); else quo = (s7_Int)ceil(pre_quo);
		    }
		  else quo = (n1 * d2) / (n2 * d1);
		}
	    }
	  if (quo == 0)
	    return(x);

	  if ((d1 == d2) &&
	      ((integer_length(n2) + integer_length(quo)) < s7_int_bits))
	    return(s7_make_ratio(sc, n1 - n2 * quo, d1));
      
	  if ((integer_length(n1) + integer_length(d2) < s7_int_bits) &&
	      (integer_length(d1) + integer_length(d2) < s7_int_bits) &&
	      (integer_length(n2) + integer_length(d1) + integer_length(quo) < s7_int_bits))
	    return(s7_make_ratio(sc, n1 * d2 - n2 * d1 * quo, d1 * d2));
	  return(simple_out_of_range(sc, sc->REMAINDER, args, make_string_wrapper(sc, "intermediate (a/b) is too large")));

	case T_REAL:
	  {
	    s7_Double frac;
	    if (real(y) == 0.0) 
	      return(division_by_zero_error(sc, sc->REMAINDER, args));
	    if ((is_inf(real(y))) || (is_NaN(real(y))))
	      return(wrong_type_argument_with_type(sc, sc->REMAINDER, small_int(2), y, A_NORMAL_REAL));
	    frac = fraction(x);
	    pre_quo = frac / real(y);
	    if ((pre_quo > S7_LLONG_MAX) || (pre_quo < S7_LLONG_MIN))
	      return(simple_out_of_range(sc, sc->REMAINDER, args, ITS_TOO_LARGE));
	    if (pre_quo > 0.0) quo = (s7_Int)floor(pre_quo); else quo = (s7_Int)ceil(pre_quo);
	    return(make_real(sc, frac - real(y) * quo));
	  }

	default:        
	  check_method(sc, y, sc->REMAINDER, args);
	  return(wrong_type_argument(sc, sc->REMAINDER, small_int(2), y, T_REAL));
	}

    case T_REAL:
      if ((is_inf(real(x))) || (is_NaN(real(x))))
	return(wrong_type_argument_with_type(sc, sc->REMAINDER, small_int(1), x, A_NORMAL_REAL));

      switch (type(y))
	{
	case T_INTEGER:
	  if (integer(y) == 0) 
	    return(division_by_zero_error(sc, sc->REMAINDER, args));
	  pre_quo = real(x) / (s7_Double)integer(y);
	  if ((pre_quo > S7_LLONG_MAX) || (pre_quo < S7_LLONG_MIN))
	    return(simple_out_of_range(sc, sc->REMAINDER, args, ITS_TOO_LARGE));
	  if (pre_quo > 0.0) quo = (s7_Int)floor(pre_quo); else quo = (s7_Int)ceil(pre_quo);
	  return(make_real(sc, real(x) - integer(y) * quo));

	case T_RATIO:
	  {
	    /* bad cases here start around 1e16: (remainder 1e15 3/13) -> 0.0 with loss of digits earlier
	     *   would long double help?  
	     */
	    s7_Double frac;
	    frac = fraction(y);
	    pre_quo = real(x) / frac;
	    if ((pre_quo > S7_LLONG_MAX) || (pre_quo < S7_LLONG_MIN))
	      return(simple_out_of_range(sc, sc->REMAINDER, args, ITS_TOO_LARGE));
	    if (pre_quo > 0.0) quo = (s7_Int)floor(pre_quo); else quo = (s7_Int)ceil(pre_quo);
	    return(make_real(sc, real(x) - frac * quo));
	  }

	case T_REAL:
	  if (real(y) == 0.0) 
	    return(division_by_zero_error(sc, sc->REMAINDER, args));
	  if ((is_inf(real(y))) || (is_NaN(real(y))))
	    return(wrong_type_argument_with_type(sc, sc->REMAINDER, small_int(2), y, A_NORMAL_REAL));

	  pre_quo = real(x) / real(y);
	  if ((pre_quo > S7_LLONG_MAX) || (pre_quo < S7_LLONG_MIN))
	    return(simple_out_of_range(sc, sc->REMAINDER, args, ITS_TOO_LARGE));
	  if (pre_quo > 0.0) quo = (s7_Int)floor(pre_quo); else quo = (s7_Int)ceil(pre_quo);

	  return(make_real(sc, real(x) - real(y) * quo));

	  /* see under sin -- this calculation is completely bogus if "a" is large
	   * (quotient 1e22 (* 2 pi)) -> -9223372036854775808 -- should this return arithmetic-overflow?
	   *          but it should be 1591549430918953357688, 
	   * (remainder 1e22 (* 2 pi)) -> 1.0057952155665e+22
	   * -- the "remainder" is greater than the original argument!
	   * Clisp gives 0.0 here, as does sbcl
	   * currently s7 throws an error (out-of-range).
	   */

	default:        
	  check_method(sc, y, sc->REMAINDER, args);
	  return(wrong_type_argument(sc, sc->REMAINDER, small_int(2), y, T_REAL));
	}

    default:
      check_method(sc, x, sc->REMAINDER, args);
      return(wrong_type_argument(sc, sc->REMAINDER, small_int(1), x, T_REAL));
    }
}


#define REAL_TO_INT_LIMIT 9.2233727815085e+18
/* unfortunately, this limit is only a max in a sense: (ceiling 9223372036854770.9) => 9223372036854770
 *    see s7test for more examples
 */

static s7_pointer g_floor(s7_scheme *sc, s7_pointer args)
{
  #define H_floor "(floor x) returns the integer closest to x toward -inf"
  s7_pointer x;

  x = car(args);
  switch (type(x))
    {
    case T_INTEGER:   
      return(x);

    case T_RATIO: 
      {
	s7_Int val;
	val = numerator(x) / denominator(x); 
	/* C "/" truncates? -- C spec says "truncation toward 0" */
	/* we're avoiding "floor" here because the int->double conversion introduces inaccuracies for big numbers */
	if (numerator(x) < 0) /* not "val" because it might be truncated to 0 */
	  return(make_integer(sc, val - 1));
	return(make_integer(sc, val));
      }

    case T_REAL:
      {
	s7_Double z;
	z = real(x);
	if (is_NaN(z))
	  return(simple_out_of_range(sc, sc->FLOOR, x, ITS_NAN));
	if (fabs(z) > REAL_TO_INT_LIMIT)
	  return(simple_out_of_range(sc, sc->FLOOR, x, ITS_TOO_LARGE));
	return(make_integer(sc, (s7_Int)floor(z))); 
	/* floor here rounds down, whereas a straight int<=real coercion apparently rounds towards 0 */
      }

    case T_COMPLEX:
    default:
      check_method(sc, x, sc->FLOOR, args);
      return(simple_wrong_type_argument(sc, sc->FLOOR, x, T_REAL));
    }
  /* we can't assume things like floor return an integer because there might be methods in play,
   *   or C-side extensions like + for string-append.
   */
}


static s7_pointer g_ceiling(s7_scheme *sc, s7_pointer args)
{
  #define H_ceiling "(ceiling x) returns the integer closest to x toward inf"
  s7_pointer x;

  x = car(args);
  switch (type(x))
    {
    case T_INTEGER:   
      return(x);

    case T_RATIO:
      {
	s7_Int val;
	val = numerator(x) / denominator(x);
	if (numerator(x) < 0)
	  return(make_integer(sc, val));
	return(make_integer(sc, val + 1));
      }

    case T_REAL:
      {
	s7_Double z;
	z = real(x);
	if (is_NaN(z))
	  return(simple_out_of_range(sc, sc->CEILING, x, ITS_NAN));
	if ((is_inf(z)) ||
	    (z > REAL_TO_INT_LIMIT) ||
	    (z < -REAL_TO_INT_LIMIT))
	  return(simple_out_of_range(sc, sc->CEILING, x, ITS_TOO_LARGE));
	return(make_integer(sc, (s7_Int)ceil(real(x)))); 
      }

    case T_COMPLEX:
    default:
      check_method(sc, x, sc->CEILING, args);
      return(simple_wrong_type_argument(sc, sc->CEILING, x, T_REAL));
    }
}


static s7_pointer g_truncate(s7_scheme *sc, s7_pointer args)
{
  #define H_truncate "(truncate x) returns the integer closest to x toward 0"
  s7_pointer x;

  x = car(args);
  switch (type(x))
    {
    case T_INTEGER: 
      return(x);

    case T_RATIO: 
      return(make_integer(sc, (s7_Int)(numerator(x) / denominator(x)))); /* C "/" already truncates */

    case T_REAL:
      {
	s7_Double z;
	z = real(x);
	if (is_NaN(z))
	  return(simple_out_of_range(sc, sc->TRUNCATE, x, ITS_NAN));
	if (is_inf(z))
	  return(simple_out_of_range(sc, sc->TRUNCATE, x, ITS_INFINITE));
	return(s7_truncate(sc, sc->TRUNCATE, real(x))); 
      }

    case T_COMPLEX:
    default:
      check_method(sc, x, sc->TRUNCATE, args);
      return(simple_wrong_type_argument(sc, sc->TRUNCATE, x, T_REAL));
    }
}


static s7_Double round_per_R5RS(s7_Double x) 
{
  s7_Double fl, ce, dfl, dce;

  fl = floor(x);
  ce = ceil(x);
  dfl = x - fl;
  dce = ce - x;
  
  if (dfl > dce) return(ce);
  if (dfl < dce) return(fl);
  if (fmod(fl, 2.0) == 0.0) return(fl);
  return(ce);
}


static s7_pointer g_round(s7_scheme *sc, s7_pointer args)
{
  #define H_round "(round x) returns the integer closest to x"
  s7_pointer x;

  x = car(args);
  switch (type(x))
    {
    case T_INTEGER: 
      return(x);

    case T_RATIO: 
      {
	s7_Int truncated, remains;
	long double frac;

	truncated = numerator(x) / denominator(x);
	remains = numerator(x) % denominator(x);
	frac = s7_fabsl((long double)remains / (long double)denominator(x));

	if ((frac > 0.5) ||
	    ((frac == 0.5) &&
	     (truncated % 2 != 0)))
	  {
	    if (numerator(x) < 0)
	      return(make_integer(sc, truncated - 1));
	    return(make_integer(sc, truncated + 1));
	  }
	return(make_integer(sc, truncated));
      }

    case T_REAL:
      {
	s7_Double z;
	z = real(x);
	if (is_NaN(z))
	  return(simple_out_of_range(sc, sc->ROUND, x, ITS_NAN));
	if ((is_inf(z)) ||
	    (z > REAL_TO_INT_LIMIT) ||
	    (z < -REAL_TO_INT_LIMIT))
	  return(simple_out_of_range(sc, sc->ROUND, x, ITS_TOO_LARGE));
	return(make_integer(sc, (s7_Int)round_per_R5RS(z)));
      }

    case T_COMPLEX:
    default:
      check_method(sc, x, sc->ROUND, args);
      return(simple_wrong_type_argument(sc, sc->ROUND, x, T_REAL));
    }
}


static s7_Int c_mod(s7_Int x, s7_Int y)
{
  s7_Int z;
  /* if (y == 0) return(x); */ /* else arithmetic exception, but we're checking for this elsewhere */
  z = x % y;
  if (((y < 0) && (z > 0)) ||
      ((y > 0) && (z < 0)))
    return(z + y);
  return(z);
}


/*
(define (exptmod a b n) ; from the net somewhere
  (cond ((zero? b) 1)
        ((even? b) (exptmod (modulo (* a a) n) (quotient b 2) n))
        (else (modulo (* a (exptmod (modulo (* a a) n) (quotient b 2) n)) n))))

(define (compare a b n) 
  (list (exptmod a b n) 
        (modulo (expt a b) n)))

;; using gmp
:(compare 4 20 7)
(2 2)
:(compare 4 20 3)
(1 1)
:(compare 117 30 11)
(1 1)
:(compare 1170 310 13)
(0 0)
:(compare 1178 31 13)
(5 5)
:(compare 1178 31 131211)
(59921 59921)
:(compare 117812 31 131211)
(85196 85196)
:(compare 117812 311 131211)
(91715 91715)
:(compare 117812 311 13121112334)
(4399226350 4399226350)

;; could this be built-into the non-gmp case?
*/

static s7_pointer g_modulo(s7_scheme *sc, s7_pointer args)
{
  #define H_modulo "(modulo x1 x2) returns x1 mod x2; (modulo 4 3) = 1.  The arguments can be real numbers."
  /* (define (mod x1 x2) (- x1 (* x2 (floor (/ x1 x2))))) from slib 
   * (mod x 0) = x according to "Concrete Mathematics"
   */
  s7_pointer x, y;
  s7_Double a, b;
  s7_Int n1, n2, d1, d2;

  x = car(args);
  y = cadr(args);

  switch (type(x))
    {
    case T_INTEGER:
      switch (type(y))
	{
	case T_INTEGER:
	  if (integer(y) == 0) 
	    return(x);
	  if ((integer(y) == 1) || (integer(y) == -1))
	    return(small_int(0));
	  /* (modulo most-negative-fixnum -1) will segfault with arithmetic exception */
	  return(make_integer(sc, c_mod(integer(x), integer(y))));

	case T_RATIO:
	  n1 = integer(x);
	  d1 = 1;
	  n2 = numerator(y);
	  d2 = denominator(y);
	  goto RATIO_MOD_RATIO;

	case T_REAL:
	  b = real(y);
	  if (b == 0.0) return(x);
	  if (is_NaN(b)) return(y);
	  if (is_inf(b)) return(real_NaN);
	  a = (s7_Double)integer(x);
	  return(make_real(sc, a - b * (s7_Int)floor(a / b)));

	default:
	  check_method(sc, y, sc->MODULO, args);
	  return(wrong_type_argument(sc, sc->MODULO, small_int(2), y, T_REAL));
	}

    case T_RATIO:
      switch (type(y))
	{
	case T_INTEGER:
	  if (integer(y) == 0) return(x);
	  n1 = numerator(x);
	  d1 = denominator(x);
	  n2 = integer(y);

	  if ((n2 > 0) && (n1 > 0) && (n2 > n1)) return(x);
	  if ((n2 < 0) && (n1 < 0) && (n2 < n1)) return(x);

	  if (n2 == S7_LLONG_MIN)
	    return(simple_out_of_range(sc, sc->MODULO, y, make_string_wrapper(sc, "intermediate (a/b) is too large")));	
	  /* the problem here is that (modulo 3/2 most-negative-fixnum)
	   * will segfault with signal SIGFPE, Arithmetic exception, so try to trap it.
	   */

	  d2 = 1;
	  goto RATIO_MOD_RATIO;

	case T_RATIO:
	  n1 = numerator(x);
	  d1 = denominator(x);
	  n2 = numerator(y); /* can't be 0 */
	  d2 = denominator(y);
	  if (d1 == d2)
	    return(s7_make_ratio(sc, c_mod(n1, n2), d1));

	RATIO_MOD_RATIO:

	  if ((n1 == n2) &&
	      (d1 > d2))
	    return(x);                 /* signs match so this should be ok */

	  if ((integer_length(n1) + integer_length(d2) < s7_int_bits) &&
	      (integer_length(n2) + integer_length(d1) < s7_int_bits) &&
	      (integer_length(d1) + integer_length(d2) < s7_int_bits))
	    {
	      s7_Int n1d2, n2d1, fl;
	      n1d2 = n1 * d2;
	      n2d1 = n2 * d1;
	      
	      if (n2d1 == 1)
		return(small_int(0));
	      
	      /* can't use "floor" here (int->float ruins everything) */
	      fl = (s7_Int)(n1d2 / n2d1);
	      if (((n1 < 0) && (n2 > 0)) ||
		  ((n1 > 0) && (n2 < 0)))
		fl -= 1;
	      
	      if (fl == 0)
		return(x);
	      
	      if (integer_length(n2d1) + integer_length(fl) < s7_int_bits)
		return(s7_make_ratio(sc, n1d2 - (n2d1 * fl), d1 * d2));
	    }

	  /* there are cases here we might want to catch:
	   *    (modulo 9223372036 1/9223372036) -> error, not 0?
	   *    (modulo 1 1/9223372036854775807) -> error, not 0?
	   */
	  return(simple_out_of_range(sc, sc->MODULO, x, make_string_wrapper(sc, "intermediate (a/b) is too large")));	
      
	case T_REAL:
	  b = real(y);
	  if (b == 0.0) return(x);
	  if (is_NaN(b)) return(y);
	  if (is_inf(b)) return(real_NaN);
	  a = fraction(x);
	  return(make_real(sc, a - b * (s7_Int)floor(a / b)));

	default:
	  check_method(sc, y, sc->MODULO, args);
	  return(wrong_type_argument(sc, sc->MODULO, small_int(2), y, T_REAL));
	}

    case T_REAL:
      a = real(x);

      switch (type(y))
	{
	case T_INTEGER:
	  if (is_NaN(a)) return(x);
	  if (is_inf(a)) return(real_NaN);
	  if (integer(y) == 0) return(x);
	  b = (s7_Double)integer(y);
	  return(make_real(sc, a - b * (s7_Int)floor(a / b)));	  

	case T_RATIO:
	  if (is_NaN(a)) return(x);
	  if (is_inf(a)) return(real_NaN);
	  b = fraction(y);
	  return(make_real(sc, a - b * (s7_Int)floor(a / b)));	  

	case T_REAL:
	  if (is_NaN(a)) return(x);
	  if (is_inf(a)) return(real_NaN);
	  b = real(y);
	  if (b == 0.0) return(x);
	  if (is_NaN(b)) return(y);
	  if (is_inf(b)) return(real_NaN);
	  return(make_real(sc, a - b * (s7_Int)floor(a / b)));

	default:
	  check_method(sc, y, sc->MODULO, args);
	  return(wrong_type_argument(sc, sc->MODULO, small_int(2), y, T_REAL));
	}

    default:
      check_method(sc, x, sc->MODULO, args);
      return(wrong_type_argument(sc, sc->MODULO, small_int(1), x, T_REAL));
    }
}


static s7_pointer mod_si;
static s7_pointer g_mod_si(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x;
  s7_Int y;

  x = find_symbol_checked(sc, car(args));
  y = integer(cadr(args));

  if (is_integer(x))
    {
      s7_Int z;
      /* here we know y is positive */
      z = integer(x) % y;
      if (z < 0)
	return(make_integer(sc, z + y));
      return(make_integer(sc, z));
    }
  
  if (is_simple_real(x))
    {
      s7_Double a, b;
      a = real(x);
      if (is_NaN(a)) return(x);
      if (is_inf(a)) return(real_NaN);
      b = (s7_Double)y;
      return(make_real(sc, a - b * (s7_Int)floor(a / b)));	  
    }

  if (s7_is_ratio(x))
    return(g_modulo(sc, list_2(sc, x, cadr(args))));

  check_method(sc, x, sc->MODULO, list_2(sc, x, cadr(args)));
  return(wrong_type_argument(sc, sc->MODULO, small_int(1), x, T_REAL));
}

static s7_pointer g_is_zero(s7_scheme *sc, s7_pointer args);
static s7_pointer mod_si_is_zero;
static s7_pointer g_mod_si_is_zero(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x;
  s7_Int y;

  /* car is (modulo symbol integer), cadr is 0 or not present (if zero?) */
  x = find_symbol_checked(sc, cadar(args));
  y = integer(caddar(args));

  if (is_integer(x))
    return(make_boolean(sc, (integer(x) % y) == 0));
  
  if (is_simple_real(x))
    return(make_boolean(sc, (fmod(real(x), (s7_Double)y) == 0.0)));

  if (s7_is_ratio(x))
    return(sc->F);

  {
    s7_pointer func;
    if ((func = find_method(sc, find_let(sc, x), sc->MODULO)) != sc->UNDEFINED)
      return(g_is_zero(sc, list_1(sc, s7_apply_function(sc, func, list_2(sc, x, caddar(args))))));
  }
  return(wrong_type_argument(sc, sc->MODULO, small_int(1), x, T_REAL));
}
#endif
/* !WITH_GMP */


static int reduce_fraction(s7_Int *numer, s7_Int *denom)
{
  /* we're assuming in several places that we have a normal s7 rational after returning,
   *    so the denominator needs to be positive.
   */
  s7_Int divisor;

  if (*numer == 0)
    {
      *denom = 1;
      return(T_INTEGER);
    }
  if (*denom < 0)
    {
      *denom = -*denom;
      *numer = -*numer;
    }
  divisor = c_gcd(*numer, *denom);
  if (divisor != 1)
    {
      *numer /= divisor;
      *denom /= divisor;
    }
  if (*denom == 1)
    return(T_INTEGER);
  return(T_RATIO);
}



/* ---------------------------------------- add ---------------------------------------- */

static s7_pointer g_add(s7_scheme *sc, s7_pointer args)
{
  #define H_add "(+ ...) adds its arguments"
  s7_pointer x, p;
  s7_Int num_a, den_a;
  s7_Double rl_a, im_a;

#if (!WITH_GMP)
  if (is_null(args))
    return(small_int(0));
#endif
    
  x = car(args);
  p = cdr(args);
  if (is_null(p))
    {
      if (!is_number(x))
	{
	  check_method(sc, x, sc->ADD, args);
	  return(simple_wrong_type_argument_with_type(sc, sc->ADD, x, A_NUMBER));
	}
      return(x);
    }

  switch (type(x))
    {
    case T_INTEGER:
      num_a = integer(x);

    ADD_INTEGERS:
#if WITH_GMP
      if ((num_a > S7_LONG_MAX) ||
	  (num_a < S7_LONG_MIN))
	return(big_add(sc, cons(sc, s7_Int_to_big_integer(sc, num_a), p)));
#endif
      x = car(p);
      p = cdr(p);

      switch (type(x))
	{
	case T_INTEGER:
	  den_a = num_a + integer(x);
	  if (den_a < 0)
	    {
	      if ((num_a > 0) && (integer(x) > 0))
		{
		  rl_a = (s7_Double)num_a + (s7_Double)integer(x);
		  if (is_null(p)) return(make_real(sc, rl_a));
		  goto ADD_REALS;
		}
	    }
	  else
	    {
	      if ((num_a < 0) && (integer(x) < 0))
		{
		  rl_a = (s7_Double)num_a + (s7_Double)integer(x);
		  if (is_null(p)) return(make_real(sc, rl_a));

		  /* this is not ideal!  piano.scm has its own noise generator that wants integer
		   *    arithmetic to overflow as an integer.  Perhaps 'safety==0 would not check
		   *    anywhere?
		   */
		  goto ADD_REALS;
		}
	    }
	  if (is_null(p)) return(make_integer(sc, den_a));
	  num_a = den_a;
	  /* (+ 4611686018427387904 4611686018427387904) -> -9223372036854775808 
	   * (+ most-positive-fixnum most-positive-fixnum) -> -2
	   * (+ most-negative-fixnum most-negative-fixnum) -> 0
	   * can't check result - arg: (- 0 most-negative-fixnum) -> most-negative-fixnum
	   */
	  goto ADD_INTEGERS;
	  
	case T_RATIO:
	  den_a = denominator(x);
	  if ((integer_length(num_a) + integer_length(den_a) + integer_length(numerator(x))) >= s7_int_bits)
	    {
	      if (is_null(p))
		{
		  if (num_a == 0)                /* (+ 0 1/9223372036854775807) */
		    return(x);
		  return(make_real(sc, num_a + fraction(x)));
		}
	      rl_a = (s7_Double)num_a + fraction(x);
	      goto ADD_REALS;
	    }
	  if (is_null(p)) return(s7_make_ratio(sc, numerator(x) + (num_a * den_a), den_a));
	  num_a = numerator(x) + (num_a * den_a);

	  /* overflow examples:
	   *   (+ 100000 1/142857142857140) -> -832205957599110323/28571428571428
	   *   (+ 4611686018427387904 3/4) -> 3/4
	   * see s7test for more
	   */
	  goto ADD_RATIOS;
	  
	case T_REAL:
	  if (is_null(p)) return(make_real(sc, num_a + real(x)));
	  rl_a = (s7_Double)num_a + real(x);
	  goto ADD_REALS;
	  
	case T_COMPLEX:
	  if (is_null(p)) return(s7_make_complex(sc, num_a + real_part(x), imag_part(x)));
	  rl_a = (s7_Double)num_a + real_part(x);
	  im_a = imag_part(x);
	  goto ADD_COMPLEX;
	  
	default:
	  check_method(sc, x, sc->ADD, cons(sc, s7_make_integer(sc, num_a), cons(sc, x, p))); /* we may need copy_list(sc, p) throughout -- not sure about this */
	  return(wrong_type_argument_n_with_type(sc, sc->ADD, position_of(p, args) - 1, x, A_NUMBER)); /* p is 1 past the current arg */
	}
      break;

    case T_RATIO:
      num_a = numerator(x);
      den_a = denominator(x);
    ADD_RATIOS:
#if WITH_GMP
      if ((num_a > S7_LONG_MAX) ||
	  (den_a > S7_LONG_MAX) ||
	  (num_a < S7_LONG_MIN))
	return(big_add(sc, cons(sc, s7_ratio_to_big_ratio(sc, num_a, den_a), p)));
#endif
      x = car(p);
      p = cdr(p);

      switch (type(x))
	{
	case T_INTEGER:
	  if ((integer_length(integer(x)) + integer_length(den_a) + integer_length(num_a)) >= s7_int_bits)
	    {
	      /* (+ 3/4 4611686018427387904) -> 3/4
	       * (+ 1/17179869184 1073741824) -> 1/17179869184
	       * (+ 1/8589934592 1073741824) -> -9223372036854775807/8589934592
	       */
	      if (is_null(p))
		return(make_real(sc, (s7_Double)integer(x) + ((long double)num_a / (long double)den_a)));
	      rl_a = (s7_Double)integer(x) + ((long double)num_a / (long double)den_a);
	      goto ADD_REALS;
	    }
	  if (is_null(p)) return(s7_make_ratio(sc, num_a + (den_a * integer(x)), den_a));
	  num_a += (den_a * integer(x));
	  if (reduce_fraction(&num_a, &den_a) == T_INTEGER)
	    goto ADD_INTEGERS;
	  goto ADD_RATIOS;

	case T_RATIO:
	  {
	    s7_Int d1, d2, n1, n2;
	    d1 = den_a;
	    n1 = num_a;
	    d2 = denominator(x);
	    n2 = numerator(x);
	    if (d1 == d2)                                     /* the easy case -- if overflow here, it matches the int case */
	      {
		if (is_null(p))
		  return(s7_make_ratio(sc, n1 + n2, d1));
		num_a += n2;                  /* d1 can't be zero */
	      }
	    else
	      {
#if (!WITH_GMP)
		if ((d1 > s7_int_max) || (d2 > s7_int_max) ||     /* before counting bits, check that overflow is possible */
		    (n1 > s7_int_max) || (n2 > s7_int_max) ||
		    (n1 < s7_int_min) || (n2 < s7_int_min))
		  {
		    int d1bits, d2bits;
		    d1bits = integer_length(d1);
		    d2bits = integer_length(d2);
		    if (((d1bits + d2bits) > s7_int_bits) ||
			((d1bits + integer_length(n2)) > (s7_int_bits - 1)) ||
			((d2bits + integer_length(n1)) > (s7_int_bits - 1)))
		      {
			if (is_null(p))
			  return(make_real(sc, ((long double)n1 / (long double)d1) + ((long double)n2 / (long double)d2)));
			rl_a = ((long double)n1 / (long double)d1) + ((long double)n2 / (long double)d2);
			/* this can lose:
			 *   (+ 1 1/9223372036854775807 -1) -> 0.0 not 1/9223372036854775807
			 */
			goto ADD_REALS;
		      }
		  }
#endif
		if (is_null(p))
		  return(s7_make_ratio(sc, n1 * d2 + n2 * d1, d1 * d2));
		num_a = n1 * d2 + n2 * d1;
		den_a = d1 * d2;
	      }
	    /* (+ 1/100 99/100 (- most-positive-fixnum 2)) should not be converted to real
	     */
	  if (reduce_fraction(&num_a, &den_a) == T_INTEGER)
	    goto ADD_INTEGERS;
	  goto ADD_RATIOS;
	  }

	case T_REAL:
	  if (is_null(p)) return(make_real(sc, ((long double)num_a / (long double)den_a) + real(x)));
	  rl_a = ((long double)num_a / (long double)den_a) + real(x);
	  goto ADD_REALS;
	  
	case T_COMPLEX:
	  if (is_null(p)) return(s7_make_complex(sc, ((long double)num_a / (long double)den_a) + real_part(x), imag_part(x)));
	  rl_a = ((long double)num_a / (long double)den_a) + real_part(x);
	  im_a = imag_part(x);
	  goto ADD_COMPLEX;
	  
	default:
	  check_method(sc, x, sc->ADD, cons(sc, s7_make_ratio(sc, num_a, den_a), cons(sc, x, p)));
	  return(wrong_type_argument_n_with_type(sc, sc->ADD, position_of(p, args) - 1, x, A_NUMBER));
	}
      break;

    case T_REAL:
      rl_a = real(x);

    ADD_REALS:
      x = car(p);
      p = cdr(p);

      switch (type(x))
	{
	case T_INTEGER:
	  if (is_null(p)) return(make_real(sc, rl_a + integer(x)));
	  rl_a += (s7_Double)integer(x);
	  goto ADD_REALS;

	case T_RATIO:
	  if (is_null(p)) return(make_real(sc, rl_a + fraction(x)));
	  rl_a += (s7_Double)fraction(x);
	  goto ADD_REALS;

	case T_REAL:
	  if (is_null(p)) return(make_real(sc, rl_a + real(x)));
	  rl_a += real(x);
	  goto ADD_REALS;
	  
	case T_COMPLEX:
	  if (is_null(p)) return(s7_make_complex(sc, rl_a + real_part(x), imag_part(x)));
	  rl_a += real_part(x);
	  im_a = imag_part(x);
	  goto ADD_COMPLEX;
	  
	default:
	  check_method(sc, x, sc->ADD, cons(sc, make_real(sc, rl_a), cons(sc, x, p)));
	  return(wrong_type_argument_n_with_type(sc, sc->ADD, position_of(p, args) - 1, x, A_NUMBER));
	}
      break;

    case T_COMPLEX:
      rl_a = real_part(x);
      im_a = imag_part(x);

    ADD_COMPLEX:
      x = car(p);
      p = cdr(p);

      switch (type(x))
	{
	case T_INTEGER:
	  if (is_null(p)) return(s7_make_complex(sc, rl_a + integer(x), im_a));
	  rl_a += (s7_Double)integer(x);
	  goto ADD_COMPLEX;

	case T_RATIO:
	  if (is_null(p)) return(s7_make_complex(sc, rl_a + fraction(x), im_a));
	  rl_a += (s7_Double)fraction(x);
	  goto ADD_COMPLEX;

	case T_REAL:
	  if (is_null(p)) return(s7_make_complex(sc, rl_a + real(x), im_a));
	  rl_a += real(x);
	  goto ADD_COMPLEX;
	  
	case T_COMPLEX:
	  if (is_null(p)) return(s7_make_complex(sc, rl_a + real_part(x), im_a + imag_part(x)));
	  rl_a += real_part(x);
	  im_a += imag_part(x);
	  if (im_a == 0.0)
	    goto ADD_REALS;
	  goto ADD_COMPLEX;
	  
	default:
	  check_method(sc, x, sc->ADD, cons(sc, s7_make_complex(sc, rl_a, im_a), cons(sc, x, p)));
	  return(wrong_type_argument_n_with_type(sc, sc->ADD, position_of(p, args) - 1, x, A_NUMBER));
	}
      break;

    default:
      check_method(sc, x, sc->ADD, args);
      return(wrong_type_argument_with_type(sc, sc->ADD, small_int(1), x, A_NUMBER));
    }
}


static s7_pointer add_1, add_2, add_1s, add_s1, add_cs1, add_si, add_si_i, add_is, add_sf, add_fs, add_2_temp, add_s_temp, add_temp_s, add_s_direct;

static s7_pointer add_ratios(s7_scheme *sc, s7_pointer x, s7_pointer y)
{
  s7_Int d1, d2, n1, n2;
  d1 = number_to_denominator(x);
  n1 = number_to_numerator(x);
  d2 = number_to_denominator(y);
  n2 = number_to_numerator(y);

  if (d1 == d2)                                     /* the easy case -- if overflow here, it matches the int case */
    return(s7_make_ratio(sc, n1 + n2, d1));

  if ((d1 > s7_int_max) || (d2 > s7_int_max) ||     /* before counting bits, check that overflow is possible */
      (n1 > s7_int_max) || (n2 > s7_int_max) ||
      (n1 < s7_int_min) || (n2 < s7_int_min))
    {
      int d1bits, d2bits;
      d1bits = integer_length(d1);
      d2bits = integer_length(d2);
      if (((d1bits + d2bits) > s7_int_bits) ||
	  ((d1bits + integer_length(n2)) > (s7_int_bits - 1)) ||
	  ((d2bits + integer_length(n1)) > (s7_int_bits - 1)))
	return(make_real(sc, ((long double)n1 / (long double)d1) + ((long double)n2 / (long double)d2)));
    }
  return(s7_make_ratio(sc, n1 * d2 + n2 * d1, d1 * d2));
}


static s7_pointer g_add_1(s7_scheme *sc, s7_pointer args)
{
  if (!s7_is_number(car(args)))
    {
      check_method(sc, car(args), sc->ADD, args);
      return(wrong_type_argument_with_type(sc, sc->ADD, small_int(1), car(args), A_NUMBER));
    }
  return(car(args));
}

static s7_pointer g_add_2(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x, y;
  x = car(args);
  y = cadr(args);

  if (type(x) == type(y))
    {
      if (type(x) == T_REAL)
	return(make_real(sc, real(x) + real(y)));
      else
	{
	  switch (type(x))
	    {
	    case T_INTEGER: return(make_integer(sc, integer(x) + integer(y)));
	    case T_RATIO:   return(add_ratios(sc, x, y));
	    case T_REAL:    return(make_real(sc, real(x) + real(y)));
	    case T_COMPLEX: return(make_complex(sc, real_part(x) + real_part(y), imag_part(x) + imag_part(y)));
	    default: 
	      if (!is_number(x))
		{
		  check_method(sc, x, sc->ADD, args);
		  return(wrong_type_argument_with_type(sc, sc->ADD, small_int(1), x, A_NUMBER));
		}
	      check_method(sc, y, sc->ADD, args);	  
	      return(wrong_type_argument_with_type(sc, sc->ADD, small_int(2), y, A_NUMBER));
	    }
	}
    }

  switch (type(x))
    {
    case T_INTEGER: 
      switch (type(y))
	{
	case T_INTEGER: return(make_integer(sc, integer(x) + integer(y)));
	case T_RATIO:   return(add_ratios(sc, x, y));
	case T_REAL:    return(make_real(sc, integer(x) + real(y)));
	case T_COMPLEX: return(make_complex(sc, integer(x) + real_part(y), imag_part(y)));
	default:        
	  check_method(sc, y, sc->ADD, args);	  
	  return(wrong_type_argument_with_type(sc, sc->ADD, small_int(2), y, A_NUMBER));
	}
      
    case T_RATIO:
      switch (type(y))
	{
	case T_INTEGER:
	case T_RATIO:   return(add_ratios(sc, x, y));
	case T_REAL:    return(make_real(sc, fraction(x) + real(y)));
	case T_COMPLEX: return(s7_make_complex(sc, fraction(x) + real_part(y), imag_part(y)));
	default:        
	  check_method(sc, y, sc->ADD, args);	  
	  return(wrong_type_argument_with_type(sc, sc->ADD, small_int(2), y, A_NUMBER));
	}
      
    case T_REAL:
      switch (type(y))
	{
	case T_INTEGER: return(make_real(sc, real(x) + integer(y)));
	case T_RATIO:   return(make_real(sc, real(x) + fraction(y)));
	case T_REAL:    return(make_real(sc, real(x) + real(y)));
	case T_COMPLEX: return(make_complex(sc, real(x) + real_part(y), imag_part(y)));
	default:       
	  check_method(sc, y, sc->ADD, args);	  
	  return(wrong_type_argument_with_type(sc, sc->ADD, small_int(2), y, A_NUMBER));
	}

    case T_COMPLEX:
      switch (type(y))
	{
	case T_INTEGER: return(s7_make_complex(sc, real_part(x) + integer(y), imag_part(x)));
	case T_RATIO:   return(s7_make_complex(sc, real_part(x) + fraction(y), imag_part(x)));
	case T_REAL:    return(s7_make_complex(sc, real_part(x) + real(y), imag_part(x)));
	case T_COMPLEX: return(make_complex(sc, real_part(x) + real_part(y), imag_part(x) + imag_part(y)));
	default:        
	  check_method(sc, y, sc->ADD, args);	  
	  return(wrong_type_argument_with_type(sc, sc->ADD, small_int(2), y, A_NUMBER));
	}

    default:
      check_method(sc, x, sc->ADD, args);
      return(wrong_type_argument_with_type(sc, sc->ADD, small_int(1), x, A_NUMBER));
    }
  return(x);
}

static s7_pointer g_add_s1(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x;

  x = car(args);
  if (type(x) == T_INTEGER)
    return(make_integer(sc, integer(x) + 1));

  switch (type(x))
    {
    case T_INTEGER: return(make_integer(sc, integer(x) + 1));
    case T_RATIO:   return(add_ratios(sc, x, small_int(1)));
    case T_REAL:    return(make_real(sc, real(x) + 1.0));
    case T_COMPLEX: return(s7_make_complex(sc, real_part(x) + 1.0, imag_part(x)));
    default:        
      check_method(sc, x, sc->ADD, args);
      return(wrong_type_argument_with_type(sc, sc->ADD, small_int(1), x, A_NUMBER));
    }
  return(x);
}

static s7_pointer g_add_cs1(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x;
  x = find_symbol_checked(sc, car(args));

  if (is_integer(x))
    return(make_integer(sc, integer(x) + 1));

  switch (type(x))
    {
    case T_INTEGER: return(make_integer(sc, integer(x) + 1));
    case T_RATIO:   return(add_ratios(sc, x, small_int(1)));
    case T_REAL:    return(make_real(sc, real(x) + 1.0));
    case T_COMPLEX: return(s7_make_complex(sc, real_part(x) + 1.0, imag_part(x)));
    default:        
      check_method(sc, x, sc->ADD, list_2(sc, x, small_int(1)));
      return(wrong_type_argument_with_type(sc, sc->ADD, small_int(1), x, A_NUMBER));
    }
  return(x);
}

static s7_pointer g_add_1s(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x;

  x = cadr(args);
  if (is_integer(x))
    return(make_integer(sc, integer(x) + 1));

  switch (type(x))
    {
    case T_INTEGER: return(make_integer(sc, integer(x) + 1));
    case T_RATIO:   return(add_ratios(sc, x, small_int(1)));
    case T_REAL:    return(make_real(sc, real(x) + 1.0));
    case T_COMPLEX: return(s7_make_complex(sc, real_part(x) + 1.0, imag_part(x)));
    default:        
      check_method(sc, x, sc->ADD, args);
      return(wrong_type_argument_with_type(sc, sc->ADD, small_int(2), x, A_NUMBER));
    }
  return(x);
}

static s7_pointer g_add_si(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x;
  s7_Int n;

  x = find_symbol_checked(sc, car(args));
  n = integer(cadr(args));
  if (is_integer(x))
    return(make_integer(sc, integer(x) + n));

  switch (type(x))
    {
    case T_INTEGER: return(make_integer(sc, integer(x) + n));
    case T_RATIO:   return(add_ratios(sc, x, cadr(args)));
    case T_REAL:    return(make_real(sc, real(x) + n));
    case T_COMPLEX: return(s7_make_complex(sc, real_part(x) + n, imag_part(x)));
    default:        
      check_method(sc, x, sc->ADD, list_2(sc, x, cadr(args)));
      return(wrong_type_argument_with_type(sc, sc->ADD, small_int(1), x, A_NUMBER));
    }
  return(x);
}

static s7_pointer g_add_is(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x;
  s7_Int n;

  x = find_symbol_checked(sc, cadr(args));
  n = integer(car(args));
  switch (type(x))
    {
    case T_INTEGER: return(make_integer(sc, integer(x) + n));
    case T_RATIO:   return(add_ratios(sc, x, cadr(args)));
    case T_REAL:    return(make_real(sc, real(x) + n));
    case T_COMPLEX: return(s7_make_complex(sc, real_part(x) + n, imag_part(x)));
    default:        
      check_method(sc, x, sc->ADD, list_2(sc, x, car(args)));
      return(wrong_type_argument_with_type(sc, sc->ADD, small_int(2), x, A_NUMBER));
    }
  return(x);
}

static s7_pointer g_add_sf(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x;
  s7_Double n;

  x = find_symbol_checked(sc, car(args));
  n = real(cadr(args));
  switch (type(x))
    {
    case T_INTEGER: return(make_real(sc, integer(x) + n));
    case T_RATIO:   return(make_real(sc, fraction(x) + n));
    case T_REAL:    return(make_real(sc, real(x) + n));
    case T_COMPLEX: return(s7_make_complex(sc, real_part(x) + n, imag_part(x)));
    default:        
      check_method(sc, x, sc->ADD, list_2(sc, x, cadr(args)));
      return(wrong_type_argument_with_type(sc, sc->ADD, small_int(1), x, A_NUMBER));
    }
  return(x);
}

static s7_pointer g_add_fs(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x;
  s7_Double n;

  x = find_symbol_checked(sc, cadr(args));
  n = real(car(args));
  switch (type(x))
    {
    case T_INTEGER: return(make_real(sc, integer(x) + n));
    case T_RATIO:   return(make_real(sc, fraction(x) + n));
    case T_REAL:    return(make_real(sc, real(x) + n));
    case T_COMPLEX: return(s7_make_complex(sc, real_part(x) + n, imag_part(x)));
    default:        
      check_method(sc, x, sc->ADD, list_2(sc, x, car(args)));
      return(wrong_type_argument_with_type(sc, sc->ADD, small_int(2), x, A_NUMBER));
    }
  return(x);
}

static s7_pointer add_f_sf;
static s7_pointer g_add_f_sf(s7_scheme *sc, s7_pointer args)
{
  /* (+ x (* s y)) */
  s7_pointer vargs, s;
  s7_Double x, y;

  x = real(car(args));
  vargs = cdadr(args);
  s = find_symbol_checked(sc, car(vargs));
  y = real(cadr(vargs));

  if (type(s) == T_REAL)
    return(make_real(sc, x + (real(s) * y)));

  switch (type(s))
    {
    case T_INTEGER: return(make_real(sc, x + (integer(s) * y)));
    case T_RATIO:   return(make_real(sc, x + (fraction(s) * y)));
    case T_REAL:    return(make_real(sc, x + real(s) * y));
    case T_COMPLEX: return(s7_make_complex(sc, x + (real_part(s) * y), imag_part(s) * y));
    default:
      {
	s7_pointer func;
	if ((func = find_method(sc, find_let(sc, s), sc->MULTIPLY)) != sc->UNDEFINED)
	  return(g_add_2(sc, list_2(sc, car(args), s7_apply_function(sc, func, list_2(sc, s, cadr(vargs))))));
	return(wrong_type_argument_with_type(sc, sc->MULTIPLY, small_int(1), s, A_NUMBER));
      }
    }
  return(s);
}


static s7_pointer add_ss_1ss;
s7_pointer g_add_ss_1ss(s7_scheme *sc, s7_pointer args);
s7_pointer g_add_ss_1ss(s7_scheme *sc, s7_pointer args)
{
  /* (+ (* s1 s2) (* (- 1.0 s1) s3)) */
  s7_pointer s1, s2, s3;
  s1 = find_symbol_checked(sc, cadr(car(args)));
  s2 = find_symbol_checked(sc, ecdr(args)); /* caddr(car(args))) */
  s3 = find_symbol_checked(sc, fcdr(args)); /* caddr(cadr(args))) */

  if ((type(s1) == T_REAL) &&
      (type(s2) == T_REAL) &&
      (type(s3) == T_REAL))
    return(make_real(sc, (real(s1) * real(s2))  + ((1.0 - real(s1)) * real(s3))));

  if ((is_real(s1)) &&
      (is_real(s2)) &&
      (is_real(s3)))
    return(make_real(sc, (number_to_double(sc, s1, "*") * number_to_double(sc, s2, "*"))  + ((1.0 - number_to_double(sc, s1, "*")) * number_to_double(sc, s3, "*"))));

  {
    s7_Double r1, r2, r3, rs1, i1, i2, i3, is1;
    r1 = s7_real_part(s1);
    rs1 = 1.0 - r1;
    r2 = s7_real_part(s2);
    r3 = s7_real_part(s3);
    i1 = s7_imag_part(s1);
    is1 = -i1;
    i2 = s7_imag_part(s2);
    i3 = s7_imag_part(s3);
    return(s7_make_complex(sc, 
			   (r1 * r2 - i1 * i2) + (rs1 * r3 - is1 * i3),
			   (r1 * i2 + r2 * i1) + (rs1 * i3 + r3 * is1)));
  }
  /* (let () 
   *   (define (hi a b c) (+ (* a b) (* (- 1.0 a) c))) 
   *   (define (hi1 a b c) (+ (* b a) (* c (- 1 a)))) 
   *   (define (ho a b c) (list (hi a b c) (hi1 a b c)))
   *   (ho 1.4 2.5+i 3.1)) 
   */
}


static s7_pointer g_add_2_temp(s7_scheme *sc, s7_pointer args)
{
  s7_pointer arg1;
  arg1 = car(args);
  return(remake_real(sc, arg1, real(arg1) + real(cadr(args))));
}


static s7_pointer g_a_s_t(s7_scheme *sc, s7_pointer arg1, s7_pointer arg2, s7_pointer args)
{
  s7_Double y;
  y = real(arg2);
  switch (type(arg1))
    {
    case T_INTEGER: return(remake_real(sc, arg2, integer(arg1) + y));
    case T_RATIO:   return(remake_real(sc, arg2, fraction(arg1) + y));
    case T_REAL:    return(remake_real(sc, arg2, real(arg1) + y));
    case T_COMPLEX: return(s7_make_complex(sc, real_part(arg1) + y, imag_part(arg1)));
    default:       
      check_method(sc, arg1, sc->MULTIPLY, args); 
      return(wrong_type_argument_with_type(sc, sc->MULTIPLY, small_int(1), arg1, A_NUMBER));
    }
}


static s7_pointer g_add_s_temp(s7_scheme *sc, s7_pointer args)
{
  s7_pointer arg1, arg2;

  arg1 = car(args);
  arg2 = cadr(args);
  if (is_simple_real(arg1))
    return(remake_real(sc, arg2, real(arg1) + real(arg2)));
  
  return(g_a_s_t(sc, arg1, arg2, args));
}


static s7_pointer add_3_temp;
static s7_pointer g_add_3_temp(s7_scheme *sc, s7_pointer args)
{
  s7_pointer arg1;
  arg1 = car(args);
  return(remake_real(sc, arg1, real(arg1) + real(cadr(args)) + real(caddr(args))));
}


static s7_pointer g_add_s_direct(s7_scheme *sc, s7_pointer args)
{
  s7_pointer arg1, arg2;

  arg1 = find_symbol_checked(sc, car(args));
  arg2 = c_call(cadr(args))(sc, cdadr(args));
  if (is_simple_real(arg1))
    return(remake_real(sc, arg2, real(arg1) + real(arg2)));
  
  return(g_a_s_t(sc, arg1, arg2, args));
}


static s7_pointer g_add_temp_s(s7_scheme *sc, s7_pointer args)
{
  s7_pointer arg1, arg2;

  arg1 = car(args);
  arg2 = cadr(args);
  if (is_simple_real(arg2))
    return(remake_real(sc, arg1, real(arg1) + real(arg2)));
  
  return(g_a_s_t(sc, arg2, arg1, args));
}

static s7_pointer g_add_si_i(s7_scheme *sc, s7_pointer args)
{
  /* (+ (* x n) k) */
  s7_pointer x;
  s7_Int k, n;
  
  x = find_symbol_checked(sc, cadar(args));
  n = integer(caddar(args));
  k = integer(cadr(args));
  if (is_integer(x)) return(make_integer(sc, k + integer(x) * n));

  switch (type(x))
    {
    case T_INTEGER: return(make_integer(sc, k + integer(x) * n));
    case T_RATIO:   return(s7_make_ratio(sc, k * denominator(x) + numerator(x) * n, denominator(x)));
    case T_REAL:    return(make_real(sc, k + real(x) * n));
    case T_COMPLEX: return(s7_make_complex(sc, k + real_part(x) * n, imag_part(x) * n));
    default:
      {
        s7_pointer func;
	if ((func = find_method(sc, find_let(sc, x), sc->MULTIPLY)) != sc->UNDEFINED)
	  return(g_add_2(sc, list_2(sc, s7_apply_function(sc, func, list_2(sc, x, caddar(args))), cadr(args))));
	return(wrong_type_argument_with_type(sc, sc->ADD, small_int(1), x, A_NUMBER));
      }
    }
  return(x);
}

/* (let () (define (hi a) (+ a 1)) ((apply let '((x 32)) (list (procedure-source hi))) 12))
 */


/* ---------------------------------------- subtract ---------------------------------------- */

static s7_pointer g_subtract(s7_scheme *sc, s7_pointer args)
{
  #define H_subtract "(- x1 ...) subtracts its trailing arguments from the first, or negates the first if only one it is given"
  s7_pointer x, p;
  s7_Int num_a, den_a;
  s7_Double rl_a, im_a;

  x = car(args);
  p = cdr(args);

#if (!WITH_GMP)
  if (is_null(p))
    {
      if (!is_number(x))
	{
	  check_method(sc, x, sc->MINUS, args);
	  return(simple_wrong_type_argument_with_type(sc, sc->MINUS, x, A_NUMBER));
	}
      return(s7_negate(sc, x));
    }
#endif

  switch (type(x))
    {
    case T_INTEGER:
      num_a = integer(x);

    SUBTRACT_INTEGERS:
#if WITH_GMP
      if ((num_a > S7_LONG_MAX) ||
	  (num_a < S7_LONG_MIN))
	return(big_subtract(sc, cons(sc, s7_Int_to_big_integer(sc, num_a), p)));
#endif
      x = car(p);
      p = cdr(p);

      switch (type(x))
	{
	case T_INTEGER:
	  den_a = num_a - integer(x);
	  if (den_a < 0)
	    {
	      if ((num_a > 0) && (integer(x) < 0))
		{
		  rl_a = (s7_Double)num_a - (s7_Double)integer(x);
		  if (is_null(p)) return(make_real(sc, rl_a));
		  goto SUBTRACT_REALS;
		}
	      /* (- most-positive-fixnum most-negative-fixnum) -> -1 (1.8446744073709551615E19)
	       */
	    }
	  else
	    {
	      if ((num_a < 0) && (integer(x) > 0))
		{
		  rl_a = (s7_Double)num_a - (s7_Double)integer(x);
		  if (is_null(p)) return(make_real(sc, rl_a));
		  goto SUBTRACT_REALS;
		}
	      /* (- most-negative-fixnum most-positive-fixnum) -> 1 (-1.8446744073709551615E19)
	       */
	    }
	  if (is_null(p)) return(make_integer(sc, den_a));
	  num_a = den_a;
	  goto SUBTRACT_INTEGERS;
	  
	case T_RATIO:
	  den_a = denominator(x);
	  if ((integer_length(num_a) + integer_length(den_a) + integer_length(numerator(x))) > s7_int_bits)
	    {
	      if (is_null(p))
		return(make_real(sc, num_a - fraction(x)));
	      rl_a = (s7_Double)num_a - fraction(x);
	      goto SUBTRACT_REALS;
	    }
	  if (is_null(p)) return(s7_make_ratio(sc, (num_a * denominator(x)) - numerator(x), denominator(x)));
	  den_a = denominator(x);
	  num_a = (num_a * denominator(x)) - numerator(x);
	  if (reduce_fraction(&num_a, &den_a) == T_INTEGER)
	    goto SUBTRACT_INTEGERS;
	  goto SUBTRACT_RATIOS;
	  
	case T_REAL:
	  if (is_null(p)) return(make_real(sc, num_a - real(x)));
	  rl_a = (s7_Double)num_a - real(x);
	  goto SUBTRACT_REALS;
	  
	case T_COMPLEX:
	  if (is_null(p)) return(s7_make_complex(sc, num_a - real_part(x), -imag_part(x)));
	  rl_a = (s7_Double)num_a - real_part(x);
	  im_a = -imag_part(x);
	  goto SUBTRACT_COMPLEX;
	  
	default:
	  check_method(sc, x, sc->MINUS, cons(sc, s7_make_integer(sc, num_a), cons(sc, x, p)));
	  return(wrong_type_argument_n_with_type(sc, sc->MINUS, position_of(p, args) - 1, x, A_NUMBER)); /* p is 1 past the current arg */
	}
      break;

    case T_RATIO:
      num_a = numerator(x);
      den_a = denominator(x);
    SUBTRACT_RATIOS:
#if WITH_GMP
      if ((num_a > S7_LONG_MAX) ||
	  (den_a > S7_LONG_MAX) ||
	  (num_a < S7_LONG_MIN))
	return(big_subtract(sc, cons(sc, s7_ratio_to_big_ratio(sc, num_a, den_a), p)));
#endif
      x = car(p);
      p = cdr(p);

      switch (type(x))
	{
	case T_INTEGER:
	  if ((integer_length(integer(x)) + integer_length(num_a) + integer_length(den_a)) > s7_int_bits) 
	    {
	      if (is_null(p))
		return(make_real(sc, ((long double)num_a / (long double)den_a) - integer(x)));
	      rl_a = ((long double)num_a / (long double)den_a) - integer(x);
	      goto SUBTRACT_REALS;
	    }

	  if (is_null(p)) return(s7_make_ratio(sc, num_a - (den_a * integer(x)), den_a));
	  num_a -= (den_a * integer(x));
	  if (reduce_fraction(&num_a, &den_a) == T_INTEGER)
	    goto SUBTRACT_INTEGERS;
	  goto SUBTRACT_RATIOS;

	case T_RATIO:
	  {
	    s7_Int d1, d2, n1, n2;
	    d1 = den_a;
	    n1 = num_a;
	    d2 = denominator(x);
	    n2 = numerator(x);
	    if (d1 == d2)                                     /* the easy case -- if overflow here, it matches the int case */
	      {
		if (is_null(p))
		  return(s7_make_ratio(sc, n1 - n2, d1));
		num_a -= n2;                  /* d1 can't be zero */
	      }
	    else
	      {
#if (!WITH_GMP)
		if ((d1 > s7_int_max) || (d2 > s7_int_max) ||     /* before counting bits, check that overflow is possible */
		    (n1 > s7_int_max) || (n2 > s7_int_max) ||
		    (n1 < s7_int_min) || (n2 < s7_int_min))
		  {
		    int d1bits, d2bits;
		    d1bits = integer_length(d1);
		    d2bits = integer_length(d2);
		    if (((d1bits + d2bits) > s7_int_bits) ||
			((d1bits + integer_length(n2)) > (s7_int_bits - 1)) ||
			((d2bits + integer_length(n1)) > (s7_int_bits - 1)))
		      {
			if (is_null(p))
			  return(make_real(sc, ((long double)n1 / (long double)d1) - ((long double)n2 / (long double)d2)));
			rl_a = ((long double)n1 / (long double)d1) - ((long double)n2 / (long double)d2);
			goto SUBTRACT_REALS;
		      }
		  }
#endif
		if (is_null(p))
		  return(s7_make_ratio(sc, n1 * d2 - n2 * d1, d1 * d2));
		num_a = n1 * d2 - n2 * d1;
		den_a = d1 * d2;
	      }
	  if (reduce_fraction(&num_a, &den_a) == T_INTEGER)
	    goto SUBTRACT_INTEGERS;
	  goto SUBTRACT_RATIOS;
	  }

	case T_REAL:
	  if (is_null(p)) return(make_real(sc, ((long double)num_a / (long double)den_a) - real(x)));
	  rl_a = ((long double)num_a / (long double)den_a) - real(x);
	  goto SUBTRACT_REALS;
	  
	case T_COMPLEX:
	  if (is_null(p)) return(s7_make_complex(sc, ((long double)num_a / (long double)den_a) - real_part(x), -imag_part(x)));
	  rl_a = ((long double)num_a / (long double)den_a) - real_part(x);
	  im_a = -imag_part(x);
	  goto SUBTRACT_COMPLEX;
	  
	default:
	  check_method(sc, x, sc->MINUS, cons(sc, s7_make_ratio(sc, num_a, den_a), cons(sc, x, p)));
	  return(wrong_type_argument_n_with_type(sc, sc->MINUS, position_of(p, args) - 1, x, A_NUMBER));
	}
      break;

    case T_REAL:
      rl_a = real(x);

    SUBTRACT_REALS:
      x = car(p);
      p = cdr(p);

      switch (type(x))
	{
	case T_INTEGER:
	  if (is_null(p)) return(make_real(sc, rl_a - integer(x)));
	  rl_a -= (s7_Double)integer(x);
	  goto SUBTRACT_REALS;

	case T_RATIO:
	  if (is_null(p)) return(make_real(sc, rl_a - fraction(x)));
	  rl_a -= (s7_Double)fraction(x);
	  goto SUBTRACT_REALS;

	case T_REAL:
	  if (is_null(p)) return(make_real(sc, rl_a - real(x)));
	  rl_a -= real(x);
	  goto SUBTRACT_REALS;
	  
	case T_COMPLEX:
	  if (is_null(p)) return(s7_make_complex(sc, rl_a - real_part(x), -imag_part(x)));
	  rl_a -= real_part(x);
	  im_a = -imag_part(x);
	  goto SUBTRACT_COMPLEX;
	  
	default:
	  check_method(sc, x, sc->MINUS, cons(sc, make_real(sc, rl_a), cons(sc, x, p)));
	  return(wrong_type_argument_n_with_type(sc, sc->MINUS, position_of(p, args) - 1, x, A_NUMBER));
	}
      break;

    case T_COMPLEX:
      rl_a = real_part(x);
      im_a = imag_part(x);

    SUBTRACT_COMPLEX:
      x = car(p);
      p = cdr(p);

      switch (type(x))
	{
	case T_INTEGER:
	  if (is_null(p)) return(s7_make_complex(sc, rl_a - integer(x), im_a));
	  rl_a -= (s7_Double)integer(x);
	  goto SUBTRACT_COMPLEX;

	case T_RATIO:
	  if (is_null(p)) return(s7_make_complex(sc, rl_a - fraction(x), im_a));
	  rl_a -= (s7_Double)fraction(x);
	  goto SUBTRACT_COMPLEX;

	case T_REAL:
	  if (is_null(p)) return(s7_make_complex(sc, rl_a - real(x), im_a));
	  rl_a -= real(x);
	  goto SUBTRACT_COMPLEX;
	  
	case T_COMPLEX:
	  if (is_null(p)) return(s7_make_complex(sc, rl_a - real_part(x), im_a - imag_part(x)));
	  rl_a -= real_part(x);
	  im_a -= imag_part(x);
	  if (im_a == 0.0)
	    goto SUBTRACT_REALS;
	  goto SUBTRACT_COMPLEX;
	  
	default:
	  check_method(sc, x, sc->MINUS, cons(sc, s7_make_complex(sc, rl_a, im_a), cons(sc, x, p)));
	  return(wrong_type_argument_n_with_type(sc, sc->MINUS, position_of(p, args) - 1, x, A_NUMBER));
	}
      break;

    default:
      check_method(sc, x, sc->MINUS, args);
      return(wrong_type_argument_with_type(sc, sc->MINUS, small_int(1), x, A_NUMBER));
    }
}


static s7_pointer subtract_1, subtract_s1, subtract_cs1, subtract_2, subtract_csn;

static s7_pointer g_subtract_1(s7_scheme *sc, s7_pointer args)
{
  s7_pointer p;

  p = car(args);
  switch (type(p))
    {
    case T_INTEGER: 
#if WITH_GMP
      if (integer(p) == S7_LLONG_MIN)
	return(big_negate(sc, list_1(sc, promote_number(sc, T_BIG_INTEGER, p))));
#endif	
      return(make_integer(sc, -integer(p)));
      
    case T_RATIO:
      return(s7_make_ratio(sc, -numerator(p), denominator(p)));
      
    case T_REAL:
      return(make_real(sc, -real(p)));
      
    case T_COMPLEX:
      return(s7_make_complex(sc, -real_part(p), -imag_part(p)));

    default:
      check_method(sc, p, sc->MINUS, args);
      return(wrong_type_argument_with_type(sc, sc->MINUS, small_int(1), car(args), A_NUMBER));
    }
}

static s7_pointer g_subtract_2(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x, y;

  x = car(args);
  y = cadr(args);

  if (type(x) == type(y))
    {
      if (type(x) == T_REAL)
	return(make_real(sc, real(x) - real(y)));
      else
	{
	  switch (type(x))
	    {
	    case T_INTEGER: return(make_integer(sc, integer(x) - integer(y)));
	    case T_RATIO:   return(g_subtract(sc, args));
	    case T_REAL:    return(make_real(sc, real(x) - real(y)));
	    case T_COMPLEX: return(make_complex(sc, real_part(x) - real_part(y), imag_part(x) - imag_part(y)));
	    default: 
	      if (!is_number(x))
		{
		  check_method(sc, x, sc->MINUS, args);
		  return(wrong_type_argument_with_type(sc, sc->MINUS, small_int(1), x, A_NUMBER));
		}
	      check_method(sc, y, sc->MINUS, args);	  
	      return(wrong_type_argument_with_type(sc, sc->MINUS, small_int(2), y, A_NUMBER));
	    }
	}
    }

  switch (type(x))
    {
    case T_INTEGER: 
      switch (type(y))
	{
	case T_INTEGER: return(make_integer(sc, integer(x) - integer(y)));
	case T_RATIO:   return(g_subtract(sc, args));
	case T_REAL:    return(make_real(sc, integer(x) - real(y)));
	case T_COMPLEX: return(make_complex(sc, integer(x) - real_part(y), -imag_part(y)));
	default:        
	  check_method(sc, y, sc->MINUS, args);
	  return(wrong_type_argument_with_type(sc, sc->MINUS, small_int(2), y, A_NUMBER));
	}
      
    case T_RATIO:
      switch (type(y))
	{
	case T_INTEGER:
	case T_RATIO:   return(g_subtract(sc, args));
	case T_REAL:    return(make_real(sc, fraction(x) - real(y)));
	case T_COMPLEX: return(s7_make_complex(sc, fraction(x) - real_part(y), -imag_part(y)));
	default:        
	  check_method(sc, y, sc->MINUS, args);
	  return(wrong_type_argument_with_type(sc, sc->MINUS, small_int(2), y, A_NUMBER));
	}
      
    case T_REAL:
      switch (type(y))
	{
	case T_INTEGER: return(make_real(sc, real(x) - integer(y)));
	case T_RATIO:   return(make_real(sc, real(x) - fraction(y)));
	case T_REAL:    return(make_real(sc, real(x) - real(y)));
	case T_COMPLEX: return(make_complex(sc, real(x) - real_part(y), -imag_part(y)));
	default:        
	  check_method(sc, y, sc->MINUS, args);
	  return(wrong_type_argument_with_type(sc, sc->MINUS, small_int(2), y, A_NUMBER));
	}

    case T_COMPLEX:
      switch (type(y))
	{
	case T_INTEGER: return(s7_make_complex(sc, real_part(x) - integer(y), imag_part(x)));
	case T_RATIO:   return(s7_make_complex(sc, real_part(x) - fraction(y), imag_part(x)));
	case T_REAL:    return(s7_make_complex(sc, real_part(x) - real(y), imag_part(x)));
	case T_COMPLEX: return(make_complex(sc, real_part(x) - real_part(y), imag_part(x) - imag_part(y)));
	default:        
	  check_method(sc, y, sc->MINUS, args);
	  return(wrong_type_argument_with_type(sc, sc->MINUS, small_int(2), y, A_NUMBER));
	}

    default:
      check_method(sc, x, sc->MINUS, args);
      return(wrong_type_argument_with_type(sc, sc->MINUS, small_int(1), x, A_NUMBER));
    }
  return(x);
}


static s7_pointer g_subtract_cs1(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x;
  x = find_symbol_checked(sc, car(args));
  if (is_integer(x))
    return(make_integer(sc, integer(x) - 1));

  switch (type(x))
    {
    case T_INTEGER: return(make_integer(sc, integer(x) - 1));
    case T_RATIO:   return(subtract_ratios(sc, x, small_int(1)));
    case T_REAL:    return(make_real(sc, real(x) - 1.0));
    case T_COMPLEX: return(s7_make_complex(sc, real_part(x) - 1.0, imag_part(x)));
    default:        
      check_method(sc, x, sc->MINUS, list_2(sc, x, small_int(1)));
      return(wrong_type_argument_with_type(sc, sc->MINUS, small_int(1), x, A_NUMBER));
    }
  return(x);
}

static s7_pointer g_subtract_s1(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x;
  x = car(args);
  /* this one seems to hit reals as often as integers */
  switch (type(x))
    {
    case T_INTEGER: return(make_integer(sc, integer(x) - 1));
    case T_RATIO:   return(subtract_ratios(sc, x, small_int(1)));
    case T_REAL:    return(make_real(sc, real(x) - 1.0));
    case T_COMPLEX: return(s7_make_complex(sc, real_part(x) - 1.0, imag_part(x)));
    default:        
      check_method(sc, x, sc->MINUS, args);
      return(wrong_type_argument_with_type(sc, sc->MINUS, small_int(1), x, A_NUMBER));
    }
  return(x);
}

static s7_pointer g_subtract_csn(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x;
  s7_Int n;

  x = find_symbol_checked(sc, car(args));
  n = s7_integer(cadr(args));
  if (is_integer(x))
    return(make_integer(sc, integer(x) - n));

  switch (type(x))
    {
    case T_INTEGER: return(make_integer(sc, integer(x) - n));
    case T_RATIO:   return(subtract_ratios(sc, x, cadr(args)));
    case T_REAL:    return(make_real(sc, real(x) - n));
    case T_COMPLEX: return(s7_make_complex(sc, real_part(x) - n, imag_part(x)));
    default:        
      check_method(sc, x, sc->MINUS, list_2(sc, x, cadr(args)));
      return(wrong_type_argument_with_type(sc, sc->MINUS, small_int(1), x, A_NUMBER));
    }
  return(x);
}

static s7_pointer subtract_sf;
static s7_pointer g_subtract_sf(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x;
  s7_Double n;

  x = find_symbol_checked(sc, car(args));
  n = real(cadr(args));
  switch (type(x))
    {
    case T_INTEGER: return(make_real(sc, integer(x) - n));
    case T_RATIO:   return(make_real(sc, fraction(x) - n));
    case T_REAL:    return(make_real(sc, real(x) - n));
    case T_COMPLEX: return(s7_make_complex(sc, real_part(x) - n, imag_part(x)));
    default:        
      check_method(sc, x, sc->MINUS, list_2(sc, x, cadr(args)));
      return(wrong_type_argument_with_type(sc, sc->MINUS, small_int(1), x, A_NUMBER));
    }
  return(x);
}

static s7_pointer subtract_2f;
static s7_pointer g_subtract_2f(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x;
  s7_Double n;

  x = car(args);
  n = real(cadr(args));
  switch (type(x))
    {
    case T_INTEGER: return(make_real(sc, integer(x) - n));
    case T_RATIO:   return(make_real(sc, fraction(x) - n));
    case T_REAL:    return(make_real(sc, real(x) - n));
    case T_COMPLEX: return(s7_make_complex(sc, real_part(x) - n, imag_part(x)));
    default:        
      check_method(sc, x, sc->MINUS, args);
      return(wrong_type_argument_with_type(sc, sc->MINUS, small_int(1), x, A_NUMBER));
    }
  return(x);
}

static s7_pointer subtract_fs;
static s7_pointer g_subtract_fs(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x;
  s7_Double n;

  x = find_symbol_checked(sc, cadr(args));
  n = real(car(args));
  switch (type(x))
    {
    case T_INTEGER: return(make_real(sc, n - integer(x)));
    case T_RATIO:   return(make_real(sc, n - fraction(x)));
    case T_REAL:    return(make_real(sc, n - real(x)));
    case T_COMPLEX: return(s7_make_complex(sc, n - real_part(x), -imag_part(x)));
    default:        
      check_method(sc, x, sc->MINUS, list_2(sc, car(args), x));
      return(wrong_type_argument_with_type(sc, sc->MINUS, small_int(2), x, A_NUMBER));
    }
  return(x);
}

static s7_pointer subtract_sf_f;
static s7_pointer g_subtract_sf_f(s7_scheme *sc, s7_pointer args)
{
  /* (- (* s y) x) */
  s7_pointer vargs, s;
  s7_Double x, y;

  x = real(cadr(args));
  vargs = cdar(args);
  s = find_symbol_checked(sc, car(vargs));
  y = real(cadr(vargs));

  if (type(s) == T_REAL)
    return(make_real(sc, (real(s) * y) - x));

  switch (type(s))
    {
    case T_INTEGER: return(make_real(sc, (integer(s) * y) - x));
    case T_RATIO:   return(make_real(sc, (fraction(s) * y) - x));
    case T_REAL:    return(make_real(sc, (real(s) * y) - x));
    case T_COMPLEX: return(s7_make_complex(sc, (real_part(s) * y) - x, imag_part(s) * y));
    default:        
      {
        s7_pointer func;
	if ((func = find_method(sc, find_let(sc, s), sc->MULTIPLY)) != sc->UNDEFINED)
	  return(g_subtract_2(sc, list_2(sc, s7_apply_function(sc, func, list_2(sc, s, cadr(vargs))), cadr(args))));
	return(wrong_type_argument_with_type(sc, sc->MULTIPLY, small_int(1), s, A_NUMBER));
      }
    }
  return(s);
}

static s7_pointer subtract_f_sqr;
static s7_pointer g_subtract_f_sqr(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x;
  s7_Double y;

  y = real(car(args));
  x = find_symbol_checked(sc, cadr(cadr(args)));
  if (type(x) == T_REAL)
    return(make_real(sc, y - (real(x) * real(x))));

  switch (type(x))
    {
    case T_INTEGER: return(make_real(sc, y - (integer(x) * integer(x))));
    case T_RATIO:   return(make_real(sc, y - (fraction(x) * fraction(x))));
    case T_REAL:    return(make_real(sc, y - (real(x) * real(x))));
    case T_COMPLEX: return(s7_make_complex(sc, y - real_part(x) * real_part(x) + imag_part(x) * imag_part(x), 2.0 * real_part(x) * imag_part(x)));
    default:
      /* complicated -- look for * method, if any get (* x x), then go to g_subtract_2 with that and the original y
       *   can't use check_method here because it returns from the caller.
       */
      {
        s7_pointer func;
	if ((func = find_method(sc, find_let(sc, x), sc->MULTIPLY)) != sc->UNDEFINED)
	  return(g_subtract_2(sc, list_2(sc, car(args), s7_apply_function(sc, func, list_2(sc, x, x)))));
	return(wrong_type_argument_with_type(sc, sc->MULTIPLY, small_int(1), x, A_NUMBER));
      }
    }
  return(x);
}

static s7_pointer subtract_2_temp;
static s7_pointer g_subtract_2_temp(s7_scheme *sc, s7_pointer args)
{
  s7_pointer arg1;
  arg1 = car(args);
  return(remake_real(sc, arg1, real(arg1) - real(cadr(args))));
}


/* ---------------------------------------- multiply ---------------------------------------- */

static s7_pointer g_multiply(s7_scheme *sc, s7_pointer args)
{
  #define H_multiply "(* ...) multiplies its arguments"
  s7_pointer x, p;
  s7_Int num_a, den_a;
  s7_Double rl_a, im_a;

#if (!WITH_GMP)
  if (is_null(args))
    return(small_int(1));
#endif
    
  x = car(args);
  p = cdr(args);
  if (is_null(p))
    {
      if (!is_number(x))
	{
	  check_method(sc, x, sc->MULTIPLY, args);	  
	  return(simple_wrong_type_argument_with_type(sc, sc->MULTIPLY, x, A_NUMBER));
	}
      return(x);
    }

  switch (type(x))
    {
    case T_INTEGER:
      num_a = integer(x);

    MULTIPLY_INTEGERS:
#if WITH_GMP
      if ((num_a > S7_LONG_MAX) ||
	  (num_a < S7_LONG_MIN))
	return(big_multiply(sc, cons(sc, s7_Int_to_big_integer(sc, num_a), p)));
#endif
      x = car(p);
      p = cdr(p);
      switch (type(x))
	{
	case T_INTEGER:
#if WITH_GMP
	  if ((integer(x) > S7_LONG_MAX) ||
	      (integer(x) < S7_LONG_MIN))
	    return(big_multiply(sc, cons(sc, s7_Int_to_big_integer(sc, num_a), cons(sc, x, p))));
#endif

	  /* perhaps put all the math-safety stuff on the 'safety switch?
	   *    (* 256 17179869184 4194304) -> 0 which is annoying
	   *    (* 134217728 137438953472) -> 0
	   */
	  if ((integer_length(num_a) + integer_length(integer(x))) >= s7_int_bits)
	    {
	      if (is_null(p))
		return(make_real(sc, (s7_Double)num_a * (s7_Double)integer(x)));
	      rl_a = (s7_Double)num_a * (s7_Double)integer(x);
	      goto MULTIPLY_REALS;
	    }
	  if (is_null(p)) return(make_integer(sc, num_a * integer(x)));
	  num_a *= integer(x);
	  goto MULTIPLY_INTEGERS;

	case T_RATIO:
	  if ((integer_length(num_a) + integer_length(numerator(x))) >= s7_int_bits)
	    {
	      if (is_null(p))
		return(make_real(sc, (s7_Double)num_a * fraction(x)));
	      rl_a = (s7_Double)num_a * fraction(x);
	      goto MULTIPLY_REALS;
	    }
	  if (is_null(p)) return(s7_make_ratio(sc, numerator(x) * num_a, denominator(x)));
	  den_a = denominator(x);
	  num_a *= numerator(x);
	  if (reduce_fraction(&num_a, &den_a) == T_INTEGER)
	    goto MULTIPLY_INTEGERS;
	  goto MULTIPLY_RATIOS;
	  
	case T_REAL:
	  if (is_null(p)) return(make_real(sc, num_a * real(x)));
	  rl_a = num_a * real(x);
	  goto MULTIPLY_REALS;
	  
	case T_COMPLEX:
	  if (is_null(p)) return(s7_make_complex(sc, num_a * real_part(x), num_a * imag_part(x)));
	  rl_a = num_a * real_part(x);
	  im_a = num_a * imag_part(x);
	  goto MULTIPLY_COMPLEX;
	  
	default:
	  check_method(sc, x, sc->MULTIPLY, cons(sc, s7_make_integer(sc, num_a), cons(sc, x, p)));
	  return(wrong_type_argument_n_with_type(sc, sc->MULTIPLY, position_of(p, args) - 1, x, A_NUMBER)); /* p is 1 past the current arg */
	}
      break;

    case T_RATIO:
      num_a = numerator(x);
      den_a = denominator(x);
    MULTIPLY_RATIOS:
#if WITH_GMP
      if ((num_a > S7_LONG_MAX) ||
	  (den_a > S7_LONG_MAX) ||
	  (num_a < S7_LONG_MIN))
	return(big_multiply(sc, cons(sc, s7_ratio_to_big_ratio(sc, num_a, den_a), p)));
#endif
      x = car(p);
      p = cdr(p);

      switch (type(x))
	{
	case T_INTEGER:
	  /* as in +, this can overflow:
	   *   (* 8 -9223372036854775807 8) -> 64
	   *   (* 3/4 -9223372036854775807 8) -> 6
	   *   (* 8 -9223372036854775808 8) -> 0
	   *   (* -1 9223372036854775806 8) -> 16
	   *   (* -9223372036854775808 8 1e+308) -> 0.0
	   */
	  if ((integer_length(num_a) + integer_length(integer(x))) >= s7_int_bits)
	    {
	      if (is_null(p))
		return(make_real(sc, ((s7_Double)integer(x) / (s7_Double)den_a) * (s7_Double)num_a));
	      rl_a = ((s7_Double)integer(x) / (s7_Double)den_a) * (s7_Double)num_a;
	      goto MULTIPLY_REALS;
	    }
	  if (is_null(p)) return(s7_make_ratio(sc, num_a * integer(x), den_a));
	  num_a *= integer(x);
	  if (reduce_fraction(&num_a, &den_a) == T_INTEGER)
	    goto MULTIPLY_INTEGERS;
	  goto MULTIPLY_RATIOS;

	case T_RATIO:
	  {
	    s7_Int d1, d2, n1, n2;
	    d1 = den_a;
	    n1 = num_a;
	    d2 = denominator(x);
	    n2 = numerator(x);
#if (!WITH_GMP)
	    if ((d1 > s7_int_max) || (d2 > s7_int_max) ||     /* before counting bits, check that overflow is possible */
		(n1 > s7_int_max) || (n2 > s7_int_max) ||     /*    (* 1/524288 1/19073486328125) for example */
		(n1 < s7_int_min) || (n2 < s7_int_min))
	      {
		if ((integer_length(d1) + integer_length(d2) > s7_int_bits) ||
		    (integer_length(n1) + integer_length(n2) > s7_int_bits))
		  {
		    if (is_null(p))
		      return(make_real(sc, ((long double)n1 / (long double)d1) * ((long double)n2 / (long double)d2)));
		    rl_a = ((long double)n1 / (long double)d1) * ((long double)n2 / (long double)d2);
		    goto MULTIPLY_REALS;
		  }
	      }
#endif
	    if (is_null(p)) return(s7_make_ratio(sc, n1 * n2, d1 * d2));
	    num_a *= n2;
	    den_a *= d2;
	    if (reduce_fraction(&num_a, &den_a) == T_INTEGER)
	      goto MULTIPLY_INTEGERS;
	    goto MULTIPLY_RATIOS;
	  }

	case T_REAL:
	  if (is_null(p)) return(make_real(sc, ((long double)num_a / (long double)den_a) * real(x)));
	  rl_a = ((long double)num_a / (long double)den_a) * real(x);
	  goto MULTIPLY_REALS;
	  
	case T_COMPLEX:
	  {
	    s7_Double frac;
	    frac = ((long double)num_a / (long double)den_a);
	    if (is_null(p)) return(s7_make_complex(sc, frac * real_part(x), frac * imag_part(x)));
	    rl_a = frac * real_part(x);
	    im_a = frac * imag_part(x);
	    goto MULTIPLY_COMPLEX;
	  }
	  
	default:
	  check_method(sc, x, sc->MULTIPLY, cons(sc, s7_make_ratio(sc, num_a, den_a), cons(sc, x, p)));
	  return(wrong_type_argument_n_with_type(sc, sc->MULTIPLY, position_of(p, args) - 1, x, A_NUMBER));
	}
      break;

    case T_REAL:
      rl_a = real(x);

    MULTIPLY_REALS:
      x = car(p);
      p = cdr(p);

      switch (type(x))
	{
	case T_INTEGER:
	  if (is_null(p)) return(make_real(sc, rl_a * integer(x)));
	  rl_a *= integer(x);
	  goto MULTIPLY_REALS;

	case T_RATIO:
	  if (is_null(p)) return(make_real(sc, rl_a * fraction(x)));
	  rl_a *= (s7_Double)fraction(x);
	  goto MULTIPLY_REALS;

	case T_REAL:
	  if (is_null(p)) return(make_real(sc, rl_a * real(x)));
	  rl_a *= real(x);
	  goto MULTIPLY_REALS;
	  
	case T_COMPLEX:
	  if (is_null(p)) return(s7_make_complex(sc, rl_a * real_part(x), rl_a * imag_part(x)));
	  im_a = rl_a * imag_part(x);
	  rl_a *= real_part(x);
	  goto MULTIPLY_COMPLEX;
	  
	default:
	  check_method(sc, x, sc->MULTIPLY, cons(sc, make_real(sc, rl_a), cons(sc, x, p)));
	  return(wrong_type_argument_n_with_type(sc, sc->MULTIPLY, position_of(p, args) - 1, x, A_NUMBER));
	}
      break;

    case T_COMPLEX:
      rl_a = real_part(x);
      im_a = imag_part(x);

    MULTIPLY_COMPLEX:
      x = car(p);
      p = cdr(p);

      switch (type(x))
	{
	case T_INTEGER:
	  if (is_null(p)) return(s7_make_complex(sc, rl_a * integer(x), im_a * integer(x)));
	  rl_a *= integer(x);
	  im_a *= integer(x);
	  goto MULTIPLY_COMPLEX;

	case T_RATIO:
	  {
	    s7_Double frac;
	    frac = fraction(x);
	    if (is_null(p)) return(s7_make_complex(sc, rl_a * frac, im_a * frac));
	    rl_a *= frac;
	    im_a *= frac;
	    goto MULTIPLY_COMPLEX;
	  }

	case T_REAL:
	  if (is_null(p)) return(s7_make_complex(sc, rl_a * real(x), im_a * real(x)));
	  rl_a *= real(x);
	  im_a *= real(x);
	  goto MULTIPLY_COMPLEX;
	  
	case T_COMPLEX:
	  {
	    s7_Double r1, r2, i1, i2;
	    r1 = rl_a;
	    i1 = im_a;
	    r2 = real_part(x);
	    i2 = imag_part(x);
	    if (is_null(p))
	      return(s7_make_complex(sc, r1 * r2 - i1 * i2, r1 * i2 + r2 * i1));
	    rl_a = r1 * r2 - i1 * i2;
	    im_a = r1 * i2 + r2 * i1;
	    if (im_a == 0.0)
	      goto MULTIPLY_REALS;
	    goto MULTIPLY_COMPLEX;
	  }

	default:
	  check_method(sc, x, sc->MULTIPLY, cons(sc, s7_make_complex(sc, rl_a, im_a), cons(sc, x, p)));
	  return(wrong_type_argument_n_with_type(sc, sc->MULTIPLY, position_of(p, args) - 1, x, A_NUMBER));
	}
      break;

    default:
      check_method(sc, x, sc->MULTIPLY, args);
      return(wrong_type_argument_with_type(sc, sc->MULTIPLY, small_int(1), x, A_NUMBER));
    }
}


static s7_pointer multiply_2, multiply_fs, multiply_sf, multiply_is, multiply_si;
static s7_pointer multiply_2_temp, multiply_3_temp, multiply_1_any, multiply_s_temp, multiply_temp_s, multiply_s_direct;
static s7_pointer g_multiply_2(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x, y;

  x = car(args);
  y = cadr(args);

  if (type(x) == type(y))
    {
      if (type(x) == T_REAL)
	return(make_real(sc, real(x) * real(y)));
      else
	{
	  switch (type(x))
	    {
	    case T_INTEGER: return(make_integer(sc, integer(x) * integer(y))); 
	    case T_RATIO:   return(g_multiply(sc, args));
	    case T_REAL:    return(make_real(sc, real(x) * real(y)));
	    case T_COMPLEX: 
	      {
		s7_Double r1, r2, i1, i2;
		r1 = real_part(x);
		r2 = real_part(y);
		i1 = imag_part(x);
		i2 = imag_part(y);
		return(s7_make_complex(sc, r1 * r2 - i1 * i2, r1 * i2 + r2 * i1));
	      }
	    default: 
	      if (!is_number(x))
		{
		  check_method(sc, x, sc->MULTIPLY, args);
		  return(wrong_type_argument_with_type(sc, sc->MULTIPLY, small_int(1), x, A_NUMBER));
		}
	      check_method(sc, y, sc->MULTIPLY, args);	  
	      return(wrong_type_argument_with_type(sc, sc->MULTIPLY, small_int(2), y, A_NUMBER));
	    }
	}
    }

  switch (type(x))
    {
    case T_INTEGER: 
      switch (type(y))
	{
	case T_INTEGER: return(make_integer(sc, integer(x) * integer(y)));
	case T_RATIO:   return(g_multiply(sc, args));
	case T_REAL:    return(make_real(sc, integer(x) * real(y)));
	case T_COMPLEX: return(s7_make_complex(sc, integer(x) * real_part(y), integer(x) * imag_part(y)));
	default:        
	  check_method(sc, y, sc->MULTIPLY, args);
	  return(wrong_type_argument_with_type(sc, sc->MULTIPLY, small_int(2), y, A_NUMBER));
	}
      
    case T_RATIO:
      switch (type(y))
	{
	case T_INTEGER:
	case T_RATIO:    return(g_multiply(sc, args));
	case T_REAL:     return(make_real(sc, fraction(x) * real(y)));
	case T_COMPLEX:  
	  {
	    s7_Double frac;
	    frac = fraction(x);
	    return(s7_make_complex(sc, frac * real_part(y), frac * imag_part(y)));
	  }
	default:        
	  check_method(sc, y, sc->MULTIPLY, args);
	  return(wrong_type_argument_with_type(sc, sc->MULTIPLY, small_int(2), y, A_NUMBER));
	}
      
    case T_REAL:
      switch (type(y))
	{
	case T_INTEGER: return(make_real(sc, real(x) * integer(y)));
	case T_RATIO:   return(make_real(sc, real(x) * fraction(y)));
	case T_REAL:    return(make_real(sc, real(x) * real(y)));
	case T_COMPLEX: return(s7_make_complex(sc, real(x) * real_part(y), real(x) * imag_part(y)));
	default:        
	  check_method(sc, y, sc->MULTIPLY, args);
	  return(wrong_type_argument_with_type(sc, sc->MULTIPLY, small_int(2), y, A_NUMBER));
	}

    case T_COMPLEX:
      switch (type(y))
	{
	case T_INTEGER: return(s7_make_complex(sc, real_part(x) * integer(y), imag_part(x) * integer(y)));
	case T_RATIO:   
	  {
	    s7_Double frac;
	    frac = fraction(y);
	    return(s7_make_complex(sc, real_part(x) * frac, imag_part(x) * frac));
	  }
	case T_REAL:    return(s7_make_complex(sc, real_part(x) * real(y), imag_part(x) * real(y)));
	case T_COMPLEX:
	  {
	    s7_Double r1, r2, i1, i2;
	    r1 = real_part(x);
	    r2 = real_part(y);
	    i1 = imag_part(x);
	    i2 = imag_part(y);
	    return(s7_make_complex(sc, r1 * r2 - i1 * i2, r1 * i2 + r2 * i1));
	  }
	default:        
	  check_method(sc, y, sc->MULTIPLY, args);
	  return(wrong_type_argument_with_type(sc, sc->MULTIPLY, small_int(2), y, A_NUMBER));
	}

    default:
      check_method(sc, x, sc->MULTIPLY, args);
      return(wrong_type_argument_with_type(sc, sc->MULTIPLY, small_int(1), x, A_NUMBER));      
    }
  return(x);
}

/* all of these mess up if overflows occur 
 *  (let () (define (f x) (* x 9223372036854775806)) (f -63)) -> -9223372036854775682, but (* -63 9223372036854775806) -> -5.810724383218509e+20
 *  how to catch this?  (affects * - +)
 */

static s7_pointer g_multiply_si(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x;
  s7_Int n;

  x = find_symbol_checked(sc, car(args));
  n = integer(cadr(args));

  switch (type(x))
    {
    case T_INTEGER: return(make_integer(sc, integer(x) * n));
    case T_RATIO:   return(s7_make_ratio(sc, numerator(x) * n, denominator(x)));
    case T_REAL:    return(make_real(sc, real(x) * n));
    case T_COMPLEX: return(s7_make_complex(sc, real_part(x) * n, imag_part(x) * n));
    default:        
      check_method(sc, x, sc->MULTIPLY, list_2(sc, x, cadr(args)));
      return(wrong_type_argument_with_type(sc, sc->MULTIPLY, small_int(1), x, A_NUMBER));
    }
  return(x);
}

static s7_pointer g_multiply_is(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x;
  s7_Int n;

  x = find_symbol_checked(sc, cadr(args));
  n = integer(car(args));

  switch (type(x))
    {
    case T_INTEGER: return(make_integer(sc, integer(x) * n));
    case T_RATIO:   return(s7_make_ratio(sc, numerator(x) * n, denominator(x)));
    case T_REAL:    return(make_real(sc, real(x) * n));
    case T_COMPLEX: return(s7_make_complex(sc, real_part(x) * n, imag_part(x) * n));
    default:        
      check_method(sc, x, sc->MULTIPLY, list_2(sc, car(args), x));
      return(wrong_type_argument_with_type(sc, sc->MULTIPLY, small_int(2), x, A_NUMBER));
    }
  return(x);
}

static s7_pointer g_multiply_fs(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x;
  s7_Double scl;

  scl = real(car(args));
  x = find_symbol_checked(sc, cadr(args));

  switch (type(x))
    {
    case T_INTEGER: return(make_real(sc, integer(x) * scl));
    case T_RATIO:   return(make_real(sc, numerator(x) * scl / denominator(x)));
    case T_REAL:    return(make_real(sc, real(x) * scl));
    case T_COMPLEX: return(s7_make_complex(sc, real_part(x) * scl, imag_part(x) * scl));
    default:        
      check_method(sc, x, sc->MULTIPLY, list_2(sc, car(args), x));
      return(wrong_type_argument_with_type(sc, sc->MULTIPLY, small_int(1), x, A_NUMBER));
    }
  return(x);
}

static s7_pointer g_multiply_sf(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x;
  s7_Double scl;

  scl = real(cadr(args));
  x = find_symbol_checked(sc, car(args));

  switch (type(x))
    {
    case T_INTEGER: return(make_real(sc, integer(x) * scl));
    case T_RATIO:   return(make_real(sc, numerator(x) * scl / denominator(x)));
    case T_REAL:    return(make_real(sc, real(x) * scl));
    case T_COMPLEX: return(s7_make_complex(sc, real_part(x) * scl, imag_part(x) * scl));
    default:       
      check_method(sc, x, sc->MULTIPLY, list_2(sc, x, cadr(args)));
      return(wrong_type_argument_with_type(sc, sc->MULTIPLY, small_int(2), x, A_NUMBER));
    }
  return(x);
}


static s7_pointer g_multiply_2_temp(s7_scheme *sc, s7_pointer args)
{
  /* not obvious that the free here saves any time -- conflicting stats in callgrind
   */
  s7_pointer arg1;
  arg1 = car(args);
  return(remake_real(sc, arg1, real(arg1) * real(cadr(args))));
}


static s7_pointer g_multiply_3_temp(s7_scheme *sc, s7_pointer args)
{
  s7_pointer arg1;
  arg1 = car(args);
  return(remake_real(sc, arg1, real(arg1) * real(cadr(args)) * real(caddr(args))));
}


static s7_pointer g_m_s_t(s7_scheme *sc, s7_pointer arg1, s7_pointer arg2, s7_pointer args)
{
  s7_Double y;
  y = real(arg2);
  switch (type(arg1))
    {
    case T_INTEGER: return(remake_real(sc, arg2, integer(arg1) * y));
    case T_RATIO:   return(remake_real(sc, arg2, numerator(arg1) * y / denominator(arg1)));
    case T_REAL:    return(remake_real(sc, arg2, real(arg1) * y));
    case T_COMPLEX: return(s7_make_complex(sc, real_part(arg1) * y, imag_part(arg1) * y));
    default:       
      check_method(sc, arg1, sc->MULTIPLY, args); 
      return(wrong_type_argument_with_type(sc, sc->MULTIPLY, small_int(1), arg1, A_NUMBER));
    }
}


static s7_pointer g_multiply_s_direct(s7_scheme *sc, s7_pointer args)
{
  s7_pointer arg1, arg2;

  arg1 = find_symbol_checked(sc, car(args));
  arg2 = c_call(cadr(args))(sc, cdadr(args));
  if (is_simple_real(arg1))
    return(remake_real(sc, arg2, real(arg1) * real(arg2)));
  
  return(g_m_s_t(sc, arg1, arg2, args));
}


static s7_pointer g_multiply_s_temp(s7_scheme *sc, s7_pointer args)
{
  /* not obvious that the free here saves any time -- conflicting stats in callgrind
   */
  s7_pointer arg1, arg2;

  arg1 = car(args);
  arg2 = cadr(args);
  if (is_simple_real(arg1))
    return(remake_real(sc, arg2, real(arg1) * real(arg2)));
  
  return(g_m_s_t(sc, arg1, arg2, args));
}


static s7_pointer g_multiply_1_any(s7_scheme *sc, s7_pointer args)
{
  s7_pointer arg1, arg2;
  s7_Double product;
  arg1 = car(args);
  product = real(arg1);
  for (arg2 = cdr(args); is_pair(arg2); arg2 = cdr(arg2))
    {
      if (type(car(arg2)) == T_REAL)
	product *= real(car(arg2));
      else
	{
	  arg2 = g_multiply(sc, arg2);
	  if (is_simple_real(arg2))
	    return(remake_real(sc, arg1, product * real(arg2)));
	  real(arg1) = product;
	  return(g_m_s_t(sc, arg2, arg1, args));
	}
    }
  return(remake_real(sc, arg1, product));
}


static s7_pointer g_multiply_temp_s(s7_scheme *sc, s7_pointer args)
{
  s7_pointer arg1, arg2;

  arg1 = car(args);
  arg2 = cadr(args);
  if (is_simple_real(arg2))
    return(remake_real(sc, arg1, real(arg1) * real(arg2)));
  
  return(g_m_s_t(sc, arg2, arg1, args));
}


#if (!WITH_GMP)
static s7_pointer sqr_ss;
static s7_pointer g_sqr_ss(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x;
  x = find_symbol_checked(sc, car(args));

  switch (type(x))
    {
    case T_INTEGER: return(s7_make_integer(sc, integer(x) * integer(x)));
    case T_RATIO:   return(s7_make_ratio(sc, numerator(x) * numerator(x), denominator(x) * denominator(x)));
    case T_REAL:    return(make_real(sc, real(x) * real(x)));
    case T_COMPLEX: return(s7_make_complex(sc, real_part(x) * real_part(x) - imag_part(x) * imag_part(x), 2.0 * real_part(x) * imag_part(x)));
    default:        
      check_method(sc, x, sc->MULTIPLY, list_2(sc, x, x));
      return(wrong_type_argument_with_type(sc, sc->MULTIPLY, small_int(1), x, A_NUMBER));
    }
  return(x);
}

static s7_pointer mul_1ss;
static s7_pointer g_mul_1ss(s7_scheme *sc, s7_pointer args)
{
  /* (* (- 1.0 x) y) */
  s7_pointer x, y;

  x = find_symbol_checked(sc, caddr(car(args)));
  y = find_symbol_checked(sc, cadr(args));

  if ((type(x) == T_REAL) &&
      (type(y) == T_REAL))
    return(make_real(sc, real(y) * (1.0 - real(x))));

  if ((is_real(x)) &&
      (is_real(y)))
    return(make_real(sc, number_to_double(sc, y, "*") * (1.0 - number_to_double(sc, x, "*"))));
  else
    {
      s7_Double r1, r2, i1, i2;
      if (!is_number(x))
	{
	  s7_pointer func;
	  if ((func = find_method(sc, find_let(sc, x), sc->MINUS)) != sc->UNDEFINED)
	    return(g_multiply_2(sc, list_2(sc, s7_apply_function(sc, func, list_2(sc, real_one, x)), y)));
	  return(wrong_type_argument_with_type(sc, sc->MINUS, small_int(2), x, A_NUMBER));
	}
      if (!is_number(y))
	{
	  s7_pointer func;
	  if ((func = find_method(sc, find_let(sc, y), sc->MULTIPLY)) != sc->UNDEFINED)
	    return(s7_apply_function(sc, func, list_2(sc, g_subtract(sc, list_2(sc, real_one, x)), y)));
	  return(wrong_type_argument_with_type(sc, sc->MULTIPLY, small_int(2), y, A_NUMBER));
	}

      r1 = 1.0 - s7_real_part(x);
      r2 = s7_real_part(y);
      i1 = -s7_imag_part(x);
      i2 = s7_imag_part(y);
      return(s7_make_complex(sc, r1 * r2 - i1 * i2, r1 * i2 + r2 * i1));
    }
}

static s7_pointer multiply_cs_cos;
static s7_pointer g_multiply_cs_cos(s7_scheme *sc, s7_pointer args)
{
  /* ([*] -2.0 r (cos x)) */
  s7_pointer r, x;
  
  r = find_symbol_checked(sc, cadr(args));
  x = find_symbol_checked(sc, cadr(caddr(args)));

  if ((type(r) == T_REAL) &&
      (type(x) == T_REAL))
    return(make_real(sc, real(car(args)) * real(r) * cos(real(x))));

  if ((is_real(r)) &&
      (is_real(x)))
    return(make_real(sc, real(car(args)) * number_to_double(sc, r, "*") * cos(number_to_double(sc, x, "*"))));
  return(g_multiply(sc, list_3(sc, car(args), r, g_cos(sc, list_1(sc, x)))));
}

static s7_pointer mul_s_sin_s, mul_s_cos_s;
static s7_pointer g_mul_s_sin_s(s7_scheme *sc, s7_pointer args)
{
  /* (* s (sin s)) */
  s7_pointer x, y;

  x = find_symbol_checked(sc, car(args));
  y = find_symbol_checked(sc, cadadr(args));

  if ((is_real(x)) && (is_real(y)))
    return(make_real(sc, number_to_double(sc, x, "*") * sin(number_to_double(sc, y, "sin"))));

  return(g_multiply(sc, list_2(sc, x, g_sin(sc, list_1(sc, y)))));
}

static s7_pointer g_mul_s_cos_s(s7_scheme *sc, s7_pointer args)
{
  /* (* s (cos s)) */
  s7_pointer x, y;

  x = find_symbol_checked(sc, car(args));
  y = find_symbol_checked(sc, cadadr(args));

  if ((is_real(x)) && (is_real(y)))
    return(make_real(sc, number_to_double(sc, x, "*") * cos(number_to_double(sc, y, "cos"))));

  return(g_multiply(sc, list_2(sc, x, g_cos(sc, list_1(sc, y)))));
}
#endif


/* ---------------------------------------- divide ---------------------------------------- */

static bool is_number_via_method(s7_scheme *sc, s7_pointer p)
{
  if (s7_is_number(p))
    return(true);
  if (has_methods(p))
    {
      s7_pointer f;
      f = find_method(sc, find_let(sc, p), sc->IS_NUMBER);
      if (f != sc->UNDEFINED)
	return(is_true(sc, s7_apply_function(sc, f, cons(sc, p, sc->NIL))));
    }
  return(false);
}

static s7_pointer g_divide(s7_scheme *sc, s7_pointer args)
{
  #define H_divide "(/ x1 ...) divides its first argument by the rest, or inverts the first if there is only one argument"
  s7_pointer x, p;
  s7_Int num_a, den_a;
  s7_Double rl_a, im_a;

  x = car(args);
  p = cdr(args);
  if (is_null(p))
    {
      if (!is_number(x))
	{
	  check_method(sc, x, sc->DIVIDE, args);
	  return(simple_wrong_type_argument_with_type(sc, sc->DIVIDE, x, A_NUMBER));
	}
      if (s7_is_zero(x))
	return(division_by_zero_error(sc, sc->DIVIDE, args));
      return(s7_invert(sc, x));
    }

  switch (type(x))
    {
    case T_INTEGER:
      num_a = integer(x);
      if (num_a == 0)
	{
	  bool return_nan = false, return_real_zero = false;
	  for (; is_pair(p); p = cdr(p))
	    {
	      s7_pointer n;
	      n = car(p);
	      if (!s7_is_number(n))
		{
		  n = check_values(sc, n, p);
		  if (!s7_is_number(n))
		    return(wrong_type_argument_n_with_type(sc, sc->DIVIDE, position_of(p, args), n, A_NUMBER));
		}
	      if (s7_is_zero(n))
		return(division_by_zero_error(sc, sc->DIVIDE, args));
	      if (type(n) > T_RATIO)
		{
		  return_real_zero = true;		  
		  if (is_NaN(real(n)))
		    return_nan = true;
		}
	    }
	  if (return_nan)
	    return(real_NaN);
	  if (return_real_zero)
	    return(real_zero);
	  return(small_int(0));
	}

    DIVIDE_INTEGERS:
#if WITH_GMP
      if ((num_a > S7_LONG_MAX) ||
	  (num_a < S7_LONG_MIN))
	return(big_divide(sc, cons(sc, s7_Int_to_big_integer(sc, num_a), p)));
#endif
      x = car(p);
      p = cdr(p);

      switch (type(x))
	{
	case T_INTEGER:
	  if (integer(x) == 0)
	    return(division_by_zero_error(sc, sc->DIVIDE, args));

	  /* to be consistent, I suppose we should search first for NaNs in the divisor list.
	   *   (* 0 0/0) is NaN, so (/ 1 0 0/0) should equal (/ 1 0/0) = NaN.  But the whole
	   *   thing is ridiculous.
	   */
	  if (is_null(p)) 
	    return(s7_make_ratio(sc, num_a, integer(x)));

	  den_a = integer(x);
	  if (reduce_fraction(&num_a, &den_a) == T_INTEGER)
	    goto DIVIDE_INTEGERS;
	  goto DIVIDE_RATIOS;
	  
	case T_RATIO:
	  den_a = denominator(x);
	  if ((integer_length(num_a) + integer_length(den_a)) > s7_int_bits)
	    {
	      if (is_null(p))
		return(make_real(sc, num_a * inverted_fraction(x)));
	      rl_a = (s7_Double)num_a * inverted_fraction(x);
	      goto DIVIDE_REALS;
	    }
	  if (is_null(p)) return(s7_make_ratio(sc, num_a * denominator(x), numerator(x)));
	  den_a = numerator(x);
	  num_a *= denominator(x);
	  if (reduce_fraction(&num_a, &den_a) == T_INTEGER)
	    goto DIVIDE_INTEGERS;
	  goto DIVIDE_RATIOS;
	  
	case T_REAL:
	  rl_a = (s7_Double)num_a;
	  if (real(x) == 0.0)
	    return(division_by_zero_error(sc, sc->DIVIDE, args));
	  if (is_null(p)) return(make_real(sc, rl_a / real(x)));
	  rl_a /= real(x);
	  goto DIVIDE_REALS;
	  
	case T_COMPLEX:
	  {
	    s7_Double i2, r2, den;
	    rl_a = (s7_Double)num_a;
	    r2 = real_part(x);
	    i2 = imag_part(x);
	    den = 1.0 / (r2 * r2 + i2 * i2);
	    /* we could avoid the squaring (see Knuth II p613 16)
	     *    not a big deal: (/ 1.0e308+1.0e308i 2.0e308+2.0e308i) => nan
	     *    (gmp case is ok here) 
	     */
	    if (is_null(p))
	      return(s7_make_complex(sc, rl_a * r2 * den, -(rl_a * i2 * den)));
	    im_a = -rl_a * i2 * den;
	    rl_a *= r2 * den;
	    goto DIVIDE_COMPLEX;
	  }

	default:
	  check_method(sc, x, sc->DIVIDE, cons(sc, s7_make_integer(sc, num_a), cons(sc, x, p)));
	  return(wrong_type_argument_n_with_type(sc, sc->DIVIDE, position_of(p, args) - 1, x, A_NUMBER)); /* p is 1 past the current arg */
	}
      break;

    case T_RATIO:
      num_a = numerator(x);
      den_a = denominator(x);
    DIVIDE_RATIOS:
#if WITH_GMP
      if ((num_a > S7_LONG_MAX) ||
	  (den_a > S7_LONG_MAX) ||
	  (num_a < S7_LONG_MIN))
	return(big_divide(sc, cons(sc, s7_ratio_to_big_ratio(sc, num_a, den_a), p)));
#endif
      x = car(p);
      p = cdr(p);

      switch (type(x))
	{
	case T_INTEGER:
	  if (integer(x) == 0)
	    return(division_by_zero_error(sc, sc->DIVIDE, args));

	  if ((integer_length(integer(x)) + integer_length(den_a)) > s7_int_bits)
	    {
	      if (is_null(p))
		return(make_real(sc, (long double)num_a / ((long double)den_a * (s7_Double)integer(x))));
	      rl_a = (long double)num_a / ((long double)den_a * (s7_Double)integer(x));
	      goto DIVIDE_REALS;
	    }
	  if (is_null(p)) return(s7_make_ratio(sc, num_a, den_a * integer(x)));
	  den_a *= integer(x);
	  if (reduce_fraction(&num_a, &den_a) == T_INTEGER)
	    goto DIVIDE_INTEGERS;
	  goto DIVIDE_RATIOS;

	case T_RATIO:
	  {
	    s7_Int d1, d2, n1, n2;
	    d1 = den_a;
	    n1 = num_a;
	    d2 = denominator(x);
	    n2 = numerator(x);
	    if (d1 == d2)  
	      {
		if (is_null(p))
		  return(s7_make_ratio(sc, n1, n2));
		den_a = n2;
	      }
	    else
	      {
#if (!WITH_GMP)
		if ((d1 > s7_int_max) || (d2 > s7_int_max) ||     /* before counting bits, check that overflow is possible */
		    (n1 > s7_int_max) || (n2 > s7_int_max) ||
		    (n1 < s7_int_min) || (n2 < s7_int_min))
		  {
		    if ((integer_length(d1) + integer_length(n2) > s7_int_bits) ||
			(integer_length(d2) + integer_length(n1) > s7_int_bits))
		      {
			s7_Double r1, r2;
			r1 = ((long double)num_a / (long double)den_a);
			r2 = inverted_fraction(x);
			if (is_null(p))
			  return(make_real(sc, r1 * r2));
			rl_a = r1 * r2;
			goto DIVIDE_REALS;
		      }
		  }
#endif
		if (is_null(p))
		  return(s7_make_ratio(sc, n1 * d2, d1 * n2));
		num_a *= d2;
		den_a *= n2;
	      }
	    if (reduce_fraction(&num_a, &den_a) == T_INTEGER)
	      goto DIVIDE_INTEGERS;
	    goto DIVIDE_RATIOS;
	  }

	case T_REAL:
	  {
	    s7_Double r1;
	    if (real(x) == 0.0)
	      return(division_by_zero_error(sc, sc->DIVIDE, args));
	    r1 = ((long double)num_a / (long double)den_a);
	    if (is_null(p)) return(make_real(sc, r1 / real(x)));
	    rl_a = r1 / real(x);
	    goto DIVIDE_REALS;
	  }
	  
	case T_COMPLEX:
	  {
	    s7_Double den, i2, r2;
	    rl_a = ((long double)num_a / (long double)den_a);
	    r2 = real_part(x);
	    i2 = imag_part(x);
	    den = 1.0 / (r2 * r2 + i2 * i2);
	    if (is_null(p))
	      return(s7_make_complex(sc, rl_a * r2 * den, -rl_a * i2 * den));
	    im_a = -rl_a * i2 * den;
	    rl_a *= r2 * den;
	    goto DIVIDE_COMPLEX;
	  }
	  
	default:
	  check_method(sc, x, sc->DIVIDE, cons(sc, s7_make_ratio(sc, num_a, den_a), cons(sc, x, p)));
	  return(wrong_type_argument_n_with_type(sc, sc->DIVIDE, position_of(p, args) - 1, x, A_NUMBER));
	}
      break;

    case T_REAL:
      rl_a = real(x);
      if (rl_a == 0)
	{
	  bool return_nan = false;
	  for (; is_pair(p); p = cdr(p))
	    {
	      s7_pointer n;
	      n = car(p);
	      if (!s7_is_number(n))
		{
		  n = check_values(sc, n, p);
		  if (!s7_is_number(n))
		    return(wrong_type_argument_n_with_type(sc, sc->DIVIDE, position_of(p, args), n, A_NUMBER));
		}
	      if (s7_is_zero(n))
		return(division_by_zero_error(sc, sc->DIVIDE, args));
	      if ((type(n) == T_REAL) && 
		  (is_NaN(real(n))))
		return_nan = true;
	    }
	  if (return_nan)
	    return(real_NaN);
	  return(real_zero);
	}

    DIVIDE_REALS:
      x = car(p);
      p = cdr(p);

      switch (type(x))
	{
	case T_INTEGER:
	  if (integer(x) == 0)
	    return(division_by_zero_error(sc, sc->DIVIDE, args));
	  if (is_null(p)) return(make_real(sc, rl_a / integer(x)));
	  rl_a /= (s7_Double)integer(x);
	  goto DIVIDE_REALS;

	case T_RATIO:
	  if (is_null(p)) return(make_real(sc, rl_a * inverted_fraction(x)));
	  rl_a *= (s7_Double)inverted_fraction(x);
	  goto DIVIDE_REALS;

	case T_REAL:
	  if (real(x) == 0.0)
	    return(division_by_zero_error(sc, sc->DIVIDE, args));
	  if (is_null(p)) return(make_real(sc, rl_a / real(x)));
	  rl_a /= real(x);
	  goto DIVIDE_REALS;
	  
	case T_COMPLEX:
	  {
	    s7_Double den, r2, i2;
	    r2 = real_part(x);
	    i2 = imag_part(x);
	    den = 1.0 / (r2 * r2 + i2 * i2);
	    if (is_null(p))
	      return(s7_make_complex(sc, rl_a * r2 * den, -rl_a * i2 * den));
	    im_a = -rl_a * i2 * den;
	    rl_a *= r2 * den;
	    goto DIVIDE_COMPLEX;
	  }
	  
	default:
	  check_method(sc, x, sc->DIVIDE, cons(sc, make_real(sc, rl_a), cons(sc, x, p)));
	  return(wrong_type_argument_n_with_type(sc, sc->DIVIDE, position_of(p, args) - 1, x, A_NUMBER));
	}
      break;

    case T_COMPLEX:
      rl_a = real_part(x);
      im_a = imag_part(x);

    DIVIDE_COMPLEX:
      x = car(p);
      p = cdr(p);

      switch (type(x))
	{
	case T_INTEGER:
	  {
	    s7_Double r1;
	    if (integer(x) == 0)
	      return(division_by_zero_error(sc, sc->DIVIDE, args));
	    r1 = 1.0 / (s7_Double)integer(x);
	    if (is_null(p)) return(s7_make_complex(sc, rl_a * r1, im_a * r1));
	    rl_a *= r1;
	    im_a *= r1;
	    goto DIVIDE_COMPLEX;
	  }

	case T_RATIO:
	  {
	    s7_Double frac;
	    frac = inverted_fraction(x);
	    if (is_null(p)) return(s7_make_complex(sc, rl_a * frac, im_a * frac));
	    rl_a *= frac;
	    im_a *= frac;
	    goto DIVIDE_COMPLEX;
	  }

	case T_REAL:
	  {
	    s7_Double r1;
	    if (real(x) == 0.0)
	      return(division_by_zero_error(sc, sc->DIVIDE, args));
	    r1 = 1.0 / real(x);
	    if (is_null(p)) return(s7_make_complex(sc, rl_a * r1, im_a * r1));
	    rl_a *= r1;
	    im_a *= r1;
	    goto DIVIDE_COMPLEX;
	  }

	case T_COMPLEX:
	  {
	    s7_Double r1, r2, i1, i2, den;
	    r1 = rl_a;
	    i1 = im_a;
	    r2 = real_part(x);
	    i2 = imag_part(x);
	    den = 1.0 / (r2 * r2 + i2 * i2);
	    if (is_null(p))
	      return(s7_make_complex(sc, (r1 * r2 + i1 * i2) * den, (r2 * i1 - r1 * i2) * den));
	    rl_a = (r1 * r2 + i1 * i2) * den;
	    im_a = (r2 * i1 - r1 * i2) * den;
	    goto DIVIDE_COMPLEX;
	  }
	  
	default:
	  check_method(sc, x, sc->DIVIDE, cons(sc, s7_make_complex(sc, rl_a, im_a), cons(sc, x, p)));
	  return(wrong_type_argument_n_with_type(sc, sc->DIVIDE, position_of(p, args) - 1, x, A_NUMBER));
	}
      break;

    default:
      check_method(sc, x, sc->DIVIDE, args);
      return(wrong_type_argument_with_type(sc, sc->DIVIDE, small_int(1), x, A_NUMBER));
    }
}


#if (!WITH_GMP)
static s7_pointer invert_1;
static s7_pointer g_invert_1(s7_scheme *sc, s7_pointer args)
{
  s7_pointer p;
  p = car(args);
  switch (type(p))
    {
    case T_INTEGER:
      if (integer(p) != 0)
	return(s7_make_ratio(sc, 1, integer(p)));      /* a already checked, not 0 */
      return(division_by_zero_error(sc, sc->DIVIDE, args));

    case T_RATIO:
      return(s7_make_ratio(sc, denominator(p), numerator(p)));

    case T_REAL:
      if (real(p) != 0.0)
	return(make_real(sc, 1.0 / real(p)));
      return(division_by_zero_error(sc, sc->DIVIDE, args));

    case T_COMPLEX:
      {
	s7_Double r2, i2, den;
	r2 = real_part(p);
	i2 = imag_part(p);
	den = (r2 * r2 + i2 * i2);
	return(s7_make_complex(sc, r2 / den, -i2 / den));
      }

    default:
      check_method(sc, p, sc->DIVIDE, args);
      return(wrong_type_argument_with_type(sc, sc->DIVIDE, small_int(1), p, A_NUMBER));
    }
}


static s7_pointer divide_1r;
static s7_pointer g_divide_1r(s7_scheme *sc, s7_pointer args)
{
  if (s7_is_real(cadr(args)))
    {
      s7_Double rl;
      rl = number_to_double(sc, cadr(args), "/");
      if (rl == 0.0)
	return(division_by_zero_error(sc, sc->DIVIDE, args));
      return(make_real(sc, 1.0 / rl));
    }
  return(g_divide(sc, args));
}


static s7_pointer divide_temp_s;
static s7_pointer g_divide_temp_s(s7_scheme *sc, s7_pointer args)
{
  s7_pointer arg1, arg2;

  arg1 = car(args);
  arg2 = cadr(args);
  if (is_simple_real(arg2))
    {
      if (real(arg2) == 0.0)
	return(division_by_zero_error(sc, sc->DIVIDE, args));
      return(remake_real(sc, arg1, real(arg1) / real(arg2)));
    }
  return(g_divide(sc, args));
}

static s7_pointer divide_s_temp;
static s7_pointer g_divide_s_temp(s7_scheme *sc, s7_pointer args)
{
  s7_pointer arg1, arg2;

  arg1 = car(args);
  arg2 = cadr(args);
  if (is_simple_real(arg1))
    {
      if (real(arg2) == 0.0)
	return(division_by_zero_error(sc, sc->DIVIDE, args));
      return(remake_real(sc, arg2, real(arg1) / real(arg2)));
    }
  return(g_divide(sc, args));
}
#endif


/* ---------------------------------------- max/min ---------------------------------------- */

static bool is_real_via_method(s7_scheme *sc, s7_pointer p)
{
  if (s7_is_real(p))
    return(true);
  if (has_methods(p))
    {
      s7_pointer f;
      f = find_method(sc, find_let(sc, p), sc->IS_REAL);
      if (f != sc->UNDEFINED)
	return(is_true(sc, s7_apply_function(sc, f, cons(sc, p, sc->NIL))));
    }
  return(false);
}

static s7_pointer g_max(s7_scheme *sc, s7_pointer args)
{
  #define H_max "(max ...) returns the maximum of its arguments"

  s7_pointer x, y, p;
  s7_Int num_a, num_b, den_a, den_b;

  x = car(args);
  p = cdr(args);
  
  switch (type(x))
    {
    case T_INTEGER:
    MAX_INTEGERS:
      if (is_null(p)) return(x);
      y = car(p);
      p = cdr(p);
      
      switch (type(y))
	{
	case T_INTEGER: 
	  if (integer(x) < integer(y)) x = y;
	  goto MAX_INTEGERS;

	case T_RATIO:
	  num_a = integer(x);
	  den_a = 1;
	  num_b = numerator(y);
	  den_b = denominator(y);
	  goto RATIO_MAX_RATIO;
	  
	case T_REAL:    
	  if (is_NaN(real(y)))
	    {
	      for (; is_not_null(p); p = cdr(p))
		if (!is_real_via_method(sc, car(p)))
		  return(wrong_type_argument_n(sc, sc->MAX, position_of(p, args), car(p), T_REAL));
	      return(y);
	    }
	  if (integer(x) < real(y)) 
	    {
	      x = y; 
	      goto MAX_REALS;
	    }
	  goto MAX_INTEGERS;

	default:        
	  check_method(sc, y, sc->MAX, cons(sc, x, cons(sc, y, p)));
	  return(wrong_type_argument_n(sc, sc->MAX, position_of(p, args) - 1, y, T_REAL));
	}


    case T_RATIO:
    MAX_RATIOS:
      if (is_null(p)) return(x);
      y = car(p);
      p = cdr(p);
      
      switch (type(y))
	{
	case T_INTEGER:
	  num_a = numerator(x);
	  den_a = denominator(x);
	  num_b = integer(y);
	  den_b = 1;
	  goto RATIO_MAX_RATIO;
	  
	case T_RATIO:
	  num_a = numerator(x);
	  den_a = denominator(x);
	  num_b = numerator(y);
	  den_b = denominator(y);
	  
	RATIO_MAX_RATIO:
	  /* there are tricky cases here where long ints outrun doubles:
	   *   (max 92233720368547758/9223372036854775807 92233720368547757/9223372036854775807)
	   * which should be 92233720368547758/9223372036854775807) but first the fraction gets reduced
	   * to 13176245766935394/1317624576693539401, so we fall into the double comparison, and
	   * there we should be comparing 
	   *    9.999999999999999992410584792601468961145E-3 and
	   *    9.999999999999999883990367544051025548645E-3
	   * but if using doubles we get 
	   *    0.010000000000000000208166817117 and
	   *    0.010000000000000000208166817117
	   * that is, we can't distinguish these two fractions once they're coerced to doubles.
	   *
	   * Even long doubles fail in innocuous-looking cases:
	   *     (min 21053343141/6701487259 3587785776203/1142027682075) -> 3587785776203/1142027682075
	   *     (max 21053343141/6701487259 3587785776203/1142027682075) -> 3587785776203/1142027682075
	   *
	   * Another consequence: outside gmp, we can't handle cases like
	   *    (max 9223372036854776/9223372036854775807 #i9223372036854775/9223372036854775000)
	   *    (max #i9223372036854776/9223372036854775807 9223372036854775/9223372036854775000)
	   * I guess if the user is using "inexact" numbers (#i...), he accepts their inexactness.
	   */
	  
	  if (den_a == den_b)
	    {
	      if (num_a < num_b)
		x = y;
	    }
	  else
	    {
	      if (num_a == num_b)
		{
		  if (((num_a >= 0) &&
		       (den_a > den_b)) ||
		      ((num_a < 0) &&
		       (den_a < den_b)))
		    x = y;
		}
	      else
		{
		  s7_Int vala, valb;
		  vala = num_a / den_a;
		  valb = num_b / den_b;
		  
		  if (!((vala > valb) ||
			((vala == valb) && (type(y) == T_INTEGER))))
		    {
		      if ((valb > vala) ||
			  ((vala == valb) && (type(x) == T_INTEGER)) ||
			  /* sigh -- both are ratios and the int parts are equal */
			  (((long double)(num_a % den_a) / (long double)den_a) <= ((long double)(num_b % den_b) / (long double)den_b)))
			x = y;
		    }
		}
	    }
	  if (type(x) == T_RATIO)
	    goto MAX_RATIOS;
	  goto MAX_INTEGERS;

	case T_REAL:
	  /* (max 3/4 nan.0) should probably return NaN */
	  if (is_NaN(real(y)))
	    {
	      for (; is_not_null(p); p = cdr(p))
		if (!is_real_via_method(sc, car(p)))
		  return(wrong_type_argument_n(sc, sc->MAX, position_of(p, args), car(p), T_REAL));
	      return(y);
	    }

	  if (fraction(x) < real(y))
	    {
	      x = y;
	      goto MAX_REALS;
	    }
	  goto MAX_RATIOS;

	default:        
	  check_method(sc, y, sc->MAX, cons(sc, x, cons(sc, y, p)));
	  return(wrong_type_argument_n(sc, sc->MAX, position_of(p, args) - 1, y, T_REAL));
	}


    case T_REAL:
      if (is_NaN(real(x)))
	{
	  for (; is_not_null(p); p = cdr(p))
	    if (!is_real_via_method(sc, car(p)))
	      return(wrong_type_argument_n(sc, sc->MAX, position_of(p, args), car(p), T_REAL));
	  return(x);
	}

    MAX_REALS:
      if (is_null(p)) return(x);
      y = car(p);
      p = cdr(p);
      
      switch (type(y))
	{
	case T_INTEGER: 
	  if (real(x) < integer(y))
	    {
	      x = y;
	      goto MAX_INTEGERS;
	    }
	  goto MAX_REALS;

	case T_RATIO:
	  if (real(x) < fraction(y))
	    {
	      x = y;
	      goto MAX_RATIOS;
	    }
	  goto MAX_REALS;

	case T_REAL:
	  if (is_NaN(real(y)))
	    {
	      for (; is_not_null(p); p = cdr(p))
		if (!is_real_via_method(sc, car(p)))
		  return(wrong_type_argument_n(sc, sc->MAX, position_of(p, args), car(p), T_REAL));
	      return(y);
	    }
	  if (real(x) < real(y)) x = y;
	  goto MAX_REALS;

	default:
	  check_method(sc, y, sc->MAX, cons(sc, x, cons(sc, y, p)));
	  return(wrong_type_argument_n(sc, sc->MAX, position_of(p, args) - 1, y, T_REAL));
	}

    default:
      check_method(sc, x, sc->MAX, cons(sc, x, p));
      return(wrong_type_argument(sc, sc->MAX, small_int(1), x, T_REAL));
    }
}


static s7_pointer max_f2;
static s7_pointer g_max_f2(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x, y;
  x = car(args);
  y = cadr(args);
  if (is_simple_real(y))
    return((real(x) >= real(y)) ? x : y);
  if (is_real(y))
    return((real(x) >= number_to_double(sc, y, "max")) ? x : y);
  check_method(sc, y, sc->MAX, args);
  return(wrong_type_argument(sc, sc->MAX, small_int(2), y, T_REAL));
}


static s7_pointer g_min(s7_scheme *sc, s7_pointer args)
{
  #define H_min "(min ...) returns the minimum of its arguments"

  s7_pointer x, y, p;
  s7_Int num_a, num_b, den_a, den_b;

  x = car(args);
  p = cdr(args);
  
  switch (type(x))
    {
    case T_INTEGER:
    MIN_INTEGERS:
      if (is_null(p)) return(x);
      y = car(p);
      p = cdr(p);
      
      switch (type(y))
	{
	case T_INTEGER: 
	  if (integer(x) > integer(y)) x = y;
	  goto MIN_INTEGERS;

	case T_RATIO:
	  num_a = integer(x);
	  den_a = 1;
	  num_b = numerator(y);
	  den_b = denominator(y);
	  goto RATIO_MIN_RATIO;
	  
	case T_REAL:    
	  if (is_NaN(real(y)))
	    {
	      for (; is_not_null(p); p = cdr(p))
		if (!is_real_via_method(sc, car(p)))
		  return(wrong_type_argument_n(sc, sc->MIN, position_of(p, args), car(p), T_REAL));
	      return(y);
	    }
	  if (integer(x) > real(y)) 
	    {
	      x = y; 
	      goto MIN_REALS;
	    }
	  goto MIN_INTEGERS;

	default:        
	  check_method(sc, y, sc->MIN, cons(sc, x, cons(sc, y, p)));
	  return(wrong_type_argument_n(sc, sc->MIN, position_of(p, args) - 1, y, T_REAL));
	}


    case T_RATIO:
    MIN_RATIOS:
      if (is_null(p)) return(x);
      y = car(p);
      p = cdr(p);
      
      switch (type(y))
	{
	case T_INTEGER:
	  num_a = numerator(x);
	  den_a = denominator(x);
	  num_b = integer(y);
	  den_b = 1;
	  goto RATIO_MIN_RATIO;
	  
	case T_RATIO:
	  num_a = numerator(x);
	  den_a = denominator(x);
	  num_b = numerator(y);
	  den_b = denominator(y);
	  
	RATIO_MIN_RATIO:
	    if (den_a == den_b)
	      {
		if (num_a > num_b)
		  x = y;
	      }
	    else
	      {
		if (num_a == num_b)
		  {
		    if (((num_a >= 0) &&
			 (den_a < den_b)) ||
			((num_a < 0) &&
			 (den_a > den_b)))
		      x = y;
		  }
		else
		  {
		    s7_Int vala, valb;
		    vala = num_a / den_a;
		    valb = num_b / den_b;

		    if (!((vala < valb) ||
			  ((vala == valb) && (type(x) == T_INTEGER))))
		      {
			if ((valb < vala) ||
			    ((vala == valb) && (type(y) == T_INTEGER)) ||
			    (((long double)(num_a % den_a) / (long double)den_a) >= ((long double)(num_b % den_b) / (long double)den_b)))
			  x = y;
		      }
		  }
	      }
	  if (type(x) == T_RATIO)
	    goto MIN_RATIOS;
	  goto MIN_INTEGERS;

	case T_REAL:
	  /* (min 3/4 nan.0) should probably return NaN */
	  if (is_NaN(real(y)))
	    {
	      for (; is_not_null(p); p = cdr(p))
		if (!is_real_via_method(sc, car(p)))
		  return(wrong_type_argument_n(sc, sc->MIN, position_of(p, args), car(p), T_REAL));
	      return(y);
	    }
	  if (fraction(x) > real(y))
	    {
	      x = y;
	      goto MIN_REALS;
	    }
	  goto MIN_RATIOS;

	default:        
	  check_method(sc, y, sc->MIN, cons(sc, x, cons(sc, y, p)));
	  return(wrong_type_argument_n(sc, sc->MIN, position_of(p, args) - 1, y, T_REAL));
	}


    case T_REAL:
      if (is_NaN(real(x)))
	{
	  for (; is_not_null(p); p = cdr(p))
	    if (!is_real_via_method(sc, car(p)))
	      return(wrong_type_argument_n(sc, sc->MIN, position_of(p, args), car(p), T_REAL));
	  return(x);
	}

    MIN_REALS:
      if (is_null(p)) return(x);
      y = car(p);
      p = cdr(p);
      
      switch (type(y))
	{
	case T_INTEGER: 
	  if (real(x) > integer(y))
	    {
	      x = y;
	      goto MIN_INTEGERS;
	    }
	  goto MIN_REALS;

	case T_RATIO:
	  if (real(x) > fraction(y))
	    {
	      x = y;
	      goto MIN_RATIOS;
	    }
	  goto MIN_REALS;

	case T_REAL:
	  if (is_NaN(real(y)))
	    {
	      for (; is_not_null(p); p = cdr(p))
		if (!is_real_via_method(sc, car(p)))
		  return(wrong_type_argument_n(sc, sc->MIN, position_of(p, args), car(p), T_REAL));
	      return(y);
	    }
	  if (real(x) > real(y)) x = y;
	  goto MIN_REALS;

	default:
	  check_method(sc, y, sc->MIN, cons(sc, x, cons(sc, y, p)));
	  return(wrong_type_argument_n(sc, sc->MIN, position_of(p, args) - 1, y, T_REAL));
	}

    default:
      check_method(sc, x, sc->MIN, cons(sc, x, p));
      return(wrong_type_argument(sc, sc->MIN, small_int(1), x, T_REAL));
    }
}


static s7_pointer min_f2;
static s7_pointer g_min_f2(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x, y;
  x = car(args);
  y = cadr(args);
  if (is_simple_real(y))
    return((real(x) <= real(y)) ? x : y);
  if (is_real(y))
    return((real(x) <= number_to_double(sc, y, "min")) ? x : y);
  check_method(sc, y, sc->MIN, args);
  return(wrong_type_argument(sc, sc->MIN, small_int(2), y, T_REAL));
}


/* ---------------------------------------- = > < >= <= ---------------------------------------- */

static s7_pointer g_equal(s7_scheme *sc, s7_pointer args)
{
  #define H_equal "(= z1 ...) returns #t if all its arguments are equal"
  s7_pointer x, p;
  s7_Int num_a, den_a;
  s7_Double rl_a, im_a;

  x = car(args);
  p = cdr(args);

  switch (type(x))
    {
    case T_INTEGER:
      num_a = integer(x);
      while (true)
	{
	  x = car(p);
	  p = cdr(p);
	  switch (type(x))
	    {
	    case T_INTEGER: 
	      if (num_a != integer(x)) goto NOT_EQUAL; 
	      break;

	    case T_RATIO:   
	    case T_COMPLEX: 
	      goto NOT_EQUAL;                          
	      break;
	      
	    case T_REAL:    
	      if (num_a != real(x)) goto NOT_EQUAL;    
	      break;

	    default:
	      check_method(sc, x, sc->EQ, cons(sc, make_integer(sc, num_a), cons(sc, x, p)));
	      return(wrong_type_argument_n_with_type(sc, sc->EQ, position_of(p, args) - 1, x, A_NUMBER));	      
	    }
	  if (is_null(p))
	    return(sc->T);
	}

    case T_RATIO:
      num_a = numerator(x);
      den_a = denominator(x);
      rl_a = 0.0;
      while (true)
	{
	  x = car(p);
	  p = cdr(p);
	  switch (type(x))
	    {
	    case T_INTEGER: 
	    case T_COMPLEX: 
	      goto NOT_EQUAL;
	      break;

	    case T_RATIO:   
	      if ((num_a != numerator(x)) || (den_a != denominator(x)))	goto NOT_EQUAL;
	      break;

	    case T_REAL:
	      if (rl_a == 0.0)
		rl_a = ((long double)num_a) / ((long double)den_a);
	      if (rl_a != real(x)) goto NOT_EQUAL;
	      break;

	    default:        
	      check_method(sc, x, sc->EQ, cons(sc, s7_make_ratio(sc, num_a, den_a), cons(sc, x, p)));
	      return(wrong_type_argument_n_with_type(sc, sc->EQ, position_of(p, args) - 1, x, A_NUMBER));	      
	    }
	  if (is_null(p))
	    return(sc->T);
	}

    case T_REAL:
      rl_a = real(x);
      while (true)
	{
	  x = car(p);
	  p = cdr(p);
	  switch (type(x))
	    {
	    case T_INTEGER: 
	      if (rl_a != integer(x)) goto NOT_EQUAL; 
	      break;

	    case T_RATIO:
	      if (rl_a != fraction(x)) goto NOT_EQUAL;
	      break;
	      
	    case T_REAL:    
	      if (rl_a != real(x)) goto NOT_EQUAL;    
	      break;

	    case T_COMPLEX: 
	      goto NOT_EQUAL;                          
	      break;

	    default:
	      check_method(sc, x, sc->EQ, cons(sc, make_real(sc, rl_a), cons(sc, x, p)));
	      return(wrong_type_argument_n_with_type(sc, sc->EQ, position_of(p, args) - 1, x, A_NUMBER));	      
	    }
	  if (is_null(p))
	    return(sc->T);
	}
      
    case T_COMPLEX:
      rl_a = real_part(x);
      im_a = imag_part(x);
      while (true)
	{
	  x = car(p);
	  p = cdr(p);
	  switch (type(x))
	    {
	    case T_INTEGER: 
	    case T_RATIO:
	    case T_REAL:
	      goto NOT_EQUAL;
	      break;

	    case T_COMPLEX: 
	      if ((rl_a != real_part(x)) || (im_a != imag_part(x)))
		goto NOT_EQUAL;
	      break;

	    default:
	      check_method(sc, x, sc->EQ, cons(sc, s7_make_complex(sc, rl_a, im_a), cons(sc, x, p)));
	      return(wrong_type_argument_n_with_type(sc, sc->EQ, position_of(p, args) - 1, x, A_NUMBER));	      
	    }
	  if (is_null(p))
	    return(sc->T);
	}
      
    default:
      check_method(sc, x, sc->EQ, args);
      return(wrong_type_argument_with_type(sc, sc->EQ, small_int(1), x, A_NUMBER));
    }

 NOT_EQUAL:
  for (; is_pair(p); p = cdr(p))
    if (!is_number_via_method(sc, car(p)))
      return(wrong_type_argument_n_with_type(sc, sc->EQ, position_of(p, args), car(p), A_NUMBER));

  return(sc->F);
}


static s7_pointer equal_s_ic, equal_length_ic, equal_2;
static s7_pointer g_equal_s_ic(s7_scheme *sc, s7_pointer args)
{
  s7_Int y;
  s7_pointer val;

  val = find_symbol_checked(sc, car(args));
  y = s7_integer(cadr(args));
  if (is_integer(val))
    return(make_boolean(sc, integer(val) == y));

  switch (type(val))
    {
    case T_INTEGER:
      return(make_boolean(sc, integer(val) == y));
      break;
      
    case T_RATIO:  
      return(sc->F);
      
    case T_REAL:    
      return(make_boolean(sc, real(val) == y));
      break;

    case T_COMPLEX:
      return(sc->F);

    default:
      check_method(sc, val, sc->EQ, list_2(sc, val, cadr(args)));
      return(wrong_type_argument_with_type(sc, sc->EQ, small_int(1), val, A_NUMBER));
    }
  return(sc->T);
}

static s7_Int object_length_to_int(s7_scheme *sc, s7_pointer obj);

static s7_pointer g_equal_length_ic(s7_scheme *sc, s7_pointer args)
{
  /* avoid make_integer (and telescope opts), we get here with car=length expr, cadr=int */
  s7_Int ilen;
  s7_pointer val;
  
  val = find_symbol_checked(sc, cadar(args));
  ilen = s7_integer(cadr(args));

  switch (type(val))
    {
    case T_PAIR:
      return(make_boolean(sc, s7_list_length(sc, val) == ilen));

    case T_NIL:
      return(make_boolean(sc, ilen == 0));

    case T_INT_VECTOR:
    case T_FLOAT_VECTOR:
    case T_VECTOR:
      return(make_boolean(sc, vector_length(val) == ilen));

    case T_STRING:
      return(make_boolean(sc, string_length(val) == ilen));

    case T_HASH_TABLE:
      return(make_boolean(sc, hash_table_length(val) == ilen));

    case T_C_OBJECT:
      return(make_boolean(sc, object_length_to_int(sc, val) == ilen));

    case T_ENVIRONMENT:
      return(make_boolean(sc, let_length(sc, val) == ilen));

    case T_CLOSURE:
    case T_CLOSURE_STAR:
      if (has_methods(val))
	return(make_boolean(sc, closure_length(sc, val) == ilen));

    default:
      /* here we've already lost because we've checked for the length above */
      return(simple_wrong_type_argument_with_type(sc, sc->LENGTH, val, A_SEQUENCE));
    }
  return(sc->F);
}


static s7_pointer g_equal_2(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x, y;

  x = car(args);
  y = cadr(args);

#if (!MS_WINDOWS)
  if (type(x) == type(y))
    {
      if (is_integer(x))
	return(make_boolean(sc, integer(x) == integer(y)));
      switch (type(x))
	{
	case T_INTEGER: return(make_boolean(sc, integer(x) == integer(y)));
	case T_RATIO:   return(make_boolean(sc, (numerator(x) == numerator(y)) && (denominator(x) == denominator(y))));
	case T_REAL:    return(make_boolean(sc, real(x) == real(y)));
	case T_COMPLEX: return(make_boolean(sc, (real_part(x) == real_part(y)) && (imag_part(x) == imag_part(y))));
	}
    }
#endif

  switch (type(x))
    {
    case T_INTEGER:
      switch (type(y))
	{
	case T_INTEGER: return(make_boolean(sc, integer(x) == integer(y)));
	case T_RATIO:   return(sc->F);
	case T_REAL:    return(make_boolean(sc, integer(x) == real(y)));
	case T_COMPLEX: return(sc->F);
	default:        
	  check_method(sc, y, sc->EQ, args);
	  return(wrong_type_argument_with_type(sc, sc->EQ, small_int(2), y, A_NUMBER));	  
	}
      break;
      
    case T_RATIO:  
      switch (type(y))
	{
	case T_INTEGER: return(sc->F);
	case T_RATIO:   return(make_boolean(sc, (numerator(x) == numerator(y)) && (denominator(x) == denominator(y))));
	case T_REAL:    return(make_boolean(sc, fraction(x) == real(y)));            /* this could avoid the divide via numerator == denominator * x */
	case T_COMPLEX: return(sc->F);
	default:        
	  check_method(sc, y, sc->EQ, args);
	  return(wrong_type_argument_with_type(sc, sc->EQ, small_int(2), y, A_NUMBER));	  
	}
      break;
      
    case T_REAL:    
      switch (type(y))
	{
	case T_INTEGER: return(make_boolean(sc, real(x) == integer(y)));
	case T_RATIO:   return(make_boolean(sc, real(x) == fraction(y)));
	case T_REAL:    return(make_boolean(sc, real(x) == real(y)));
	case T_COMPLEX: return(sc->F);
	default:        
	  check_method(sc, y, sc->EQ, args);
	  return(wrong_type_argument_with_type(sc, sc->EQ, small_int(2), y, A_NUMBER));	  
	}
      break;
      
    case T_COMPLEX:
      switch (type(y))
	{
	case T_INTEGER:
	case T_RATIO:
	case T_REAL:
	  return(sc->F);

#if (!MS_WINDOWS)
	case T_COMPLEX: 
	  return(make_boolean(sc, (real_part(x) == real_part(y)) && (imag_part(x) == imag_part(y))));
#else
	case T_COMPLEX: 
	  if ((real_part(x) == real_part(y)) && (imag_part(x) == imag_part(y))) return(sc->T); else return(sc->F);
#endif
	default:      
	  check_method(sc, y, sc->EQ, args);  
	  return(wrong_type_argument_with_type(sc, sc->EQ, small_int(2), y, A_NUMBER));	  
	}
      break;

    default:
      check_method(sc, x, sc->EQ, args);
      return(wrong_type_argument_with_type(sc, sc->EQ, small_int(1), x, A_NUMBER));
    }
  return(sc->F);
}


#if (!WITH_GMP)
static s7_pointer g_less(s7_scheme *sc, s7_pointer args)
{
  #define H_less "(< x1 ...) returns #t if its arguments are in increasing order"
  s7_pointer x, y, p;

  x = car(args);
  p = cdr(args);

  switch (type(x))
    {
    case T_INTEGER:
    INTEGER_LESS:
      y = car(p);
      p = cdr(p);
      switch (type(y))
	{
	case T_INTEGER:
	  if (integer(x) >= integer(y)) goto NOT_LESS;
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto INTEGER_LESS;

	case T_RATIO:
	  /* no gmp here, but this can overflow: (< 9223372036 1/9223372036), but conversion to real is also problematic
	   */
	  if ((integer(x) >= 0) && (numerator(y) < 0)) goto NOT_LESS;  /* (< 1 -1/2), ratio numerator can't be 0 */
	  if ((integer(x) <= 0) && (numerator(y) > 0))                 /* (< 0 1/2) */
	    {
	      if (is_null(p)) return(sc->T);
	      x = y;
	      goto RATIO_LESS;
	    }
	  if ((integer(x) < S7_LONG_MAX) && 
	      (integer(x) > S7_LONG_MIN) && 
	      (denominator(y) < S7_LONG_MAX))
	    {
	      if ((integer(x) * denominator(y)) >= numerator(y)) goto NOT_LESS;
	    }
	  else
	    {
	      if (integer(x) >= fraction(y)) goto NOT_LESS;
	    }
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto RATIO_LESS;

	case T_REAL:
	  if (is_NaN(real(y))) goto NOT_LESS;
	  if (integer(x) >= real(y)) goto NOT_LESS;
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto REAL_LESS;

	default:
	  check_method(sc, y, sc->LT, cons(sc, x, cons(sc, y, p)));
	  return(wrong_type_argument_n(sc, sc->LT, position_of(p, args) - 1, y, T_REAL));
	}


    case T_RATIO:
    RATIO_LESS:
      y = car(p);
      p = cdr(p);
      switch (type(y))
	{
	case T_INTEGER:
	  if ((numerator(x) > 0) && (integer(y) <= 0)) goto NOT_LESS;
	  if ((numerator(x) < 0) && (integer(y) >= 0)) 
	    {
	      if (is_null(p)) return(sc->T);
	      x = y;
	      goto INTEGER_LESS;
	    }
	  if ((integer(y) < S7_LONG_MAX) && 
	      (integer(y) > S7_LONG_MIN) &&
	      (denominator(x) < S7_LONG_MAX))
	    {
	      if (numerator(x) >= (integer(y) * denominator(x))) goto NOT_LESS;
	    }
	  else
	    {
	      if (fraction(x) >= integer(y)) goto NOT_LESS;
	    }
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto INTEGER_LESS;

	case T_RATIO:
	  /* conversion to real and >= is not safe here (see comment under g_greater) */
	  {
	    s7_Int d1, d2, n1, n2;
	    d1 = denominator(x);
	    n1 = numerator(x);
	    d2 = denominator(y);
	    n2 = numerator(y);
	    if (d1 == d2)       
	      {
		if (n1 >= n2) goto NOT_LESS;
	      }
	    else
	      {
		if ((d1 > s7_int_max) || (d2 > s7_int_max) ||     /* before counting bits, check that overflow is possible */
		    (n1 > s7_int_max) || (n2 > s7_int_max) ||
		    (n1 < s7_int_min) || (n2 < s7_int_min))
		  {
		    int d1bits, d2bits;
		    d1bits = integer_length(d1);
		    d2bits = integer_length(d2);
		    if (((d1bits + d2bits) > s7_int_bits) ||
			((d1bits + integer_length(n2)) > (s7_int_bits - 1)) ||
			((d2bits + integer_length(n1)) > (s7_int_bits - 1)))
		      {
			if (fraction(x) >= fraction(y)) goto NOT_LESS;
      
			/* (< 21053343141/6701487259 3587785776203/1142027682075) -> #f because even long doubles aren't enough here 
			 * (= 21053343141/6701487259 3587785776203/1142027682075) is #f because it checks the actual ints and
			 * (> 21053343141/6701487259 3587785776203/1142027682075) is #f just like the < case.
			 * similarly
			 * (min 21053343141/6701487259 3587785776203/1142027682075) -> 3587785776203/1142027682075
			 * (max 21053343141/6701487259 3587785776203/1142027682075) -> 3587785776203/1142027682075
			 *
			 * if we print the long double results as integers, both are -3958705157555305931
			 *    so there's not a lot I can do in the non-gmp case.
			 */
		      }
		    else 
		      {
			if ((n1 * d2) >= (n2 * d1)) goto NOT_LESS;
		      }
		  }
		else
		  {
		    if ((n1 * d2) >=  (n2 * d1)) goto NOT_LESS;
		  }
	      }
	  }
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto RATIO_LESS;

	case T_REAL:
	  if (is_NaN(real(y))) goto NOT_LESS;
	  if (fraction(x) >= real(y)) goto NOT_LESS;
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto REAL_LESS;
	  
	default:
	  check_method(sc, y, sc->LT, cons(sc, x, cons(sc, y, p)));
	  return(wrong_type_argument_n(sc, sc->LT, position_of(p, args) - 1, y, T_REAL));
	}


    case T_REAL:
      if (is_NaN(real(x))) goto NOT_LESS;

    REAL_LESS:
      y = car(p);
      p = cdr(p);
      switch (type(y))
	{
	case T_INTEGER:
	  if (real(x) >= integer(y)) goto NOT_LESS;
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto INTEGER_LESS;

	case T_RATIO:
	  if (real(x) >= fraction(y)) goto NOT_LESS;
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto RATIO_LESS;

	case T_REAL:
	  if (is_NaN(real(y))) goto NOT_LESS;
	  if (real(x) >= real(y)) goto NOT_LESS;
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto REAL_LESS;

	default:
	  check_method(sc, y, sc->LT, cons(sc, x, cons(sc, y, p)));
	  return(wrong_type_argument_n(sc, sc->LT, position_of(p, args) - 1, y, T_REAL));
	}

    default:
      check_method(sc, x, sc->LT, args);
      return(wrong_type_argument(sc, sc->LT, small_int(1), x, T_REAL));
    }

 NOT_LESS:
  for (; is_pair(p); p = cdr(p))
    if (!is_real_via_method(sc, car(p)))
      return(wrong_type_argument_n(sc, sc->LT, position_of(p, args), car(p), T_REAL));

  return(sc->F);
}


static s7_pointer g_less_or_equal(s7_scheme *sc, s7_pointer args)
{
  #define H_less_or_equal "(<= x1 ...) returns #t if its arguments are in increasing order"
  s7_pointer x, y, p;

  x = car(args);
  p = cdr(args);

  switch (type(x))
    {
    case T_INTEGER:
    INTEGER_LEQ:
      y = car(p);
      p = cdr(p);
      switch (type(y))
	{
	case T_INTEGER:
	  if (integer(x) > integer(y)) goto NOT_LEQ;
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto INTEGER_LEQ;

	case T_RATIO:
	  /* no gmp here, but this can overflow: (< 9223372036 1/9223372036), but conversion to real is also problematic
	   */
	  if ((integer(x) >= 0) && (numerator(y) < 0)) goto NOT_LEQ;  /* (< 1 -1/2), ratio numerator can't be 0 */
	  if ((integer(x) <= 0) && (numerator(y) > 0))                 /* (< 0 1/2) */
	    {
	      if (is_null(p)) return(sc->T);
	      x = y;
	      goto RATIO_LEQ;
	    }
	  if ((integer(x) < S7_LONG_MAX) && 
	      (integer(x) > S7_LONG_MIN) && 
	      (denominator(y) < S7_LONG_MAX))
	    {
	      if ((integer(x) * denominator(y)) > numerator(y)) goto NOT_LEQ;
	    }
	  else
	    {
	      if (integer(x) > fraction(y)) goto NOT_LEQ;
	    }
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto RATIO_LEQ;

	case T_REAL:
	  if (is_NaN(real(y))) goto NOT_LEQ;
	  if (integer(x) > real(y)) goto NOT_LEQ;
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto REAL_LEQ;

	default:
	  check_method(sc, y, sc->LEQ, cons(sc, x, cons(sc, y, p)));
	  return(wrong_type_argument_n(sc, sc->LEQ, position_of(p, args) - 1, y, T_REAL));
	}


    case T_RATIO:
    RATIO_LEQ:
      y = car(p);
      p = cdr(p);
      switch (type(y))
	{
	case T_INTEGER:
	  if ((numerator(x) > 0) && (integer(y) <= 0)) goto NOT_LEQ;
	  if ((numerator(x) < 0) && (integer(y) >= 0)) 
	    {
	      if (is_null(p)) return(sc->T);
	      x = y;
	      goto INTEGER_LEQ;
	    }
	  if ((integer(y) < S7_LONG_MAX) && 
	      (integer(y) > S7_LONG_MIN) && 
	      (denominator(x) < S7_LONG_MAX))
	    {
	      if (numerator(x) > (integer(y) * denominator(x))) goto NOT_LEQ;
	    }
	  else
	    {
	      if (fraction(x) > integer(y)) goto NOT_LEQ;
	    }
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto INTEGER_LEQ;

	case T_RATIO:
	  {
	    s7_Int d1, d2, n1, n2;
	    d1 = denominator(x);
	    n1 = numerator(x);
	    d2 = denominator(y);
	    n2 = numerator(y);
	    if (d1 == d2)       
	      {
		if (n1 > n2) goto NOT_LEQ;
	      }
	    else
	      {
		if ((d1 > s7_int_max) || (d2 > s7_int_max) ||     /* before counting bits, check that overflow is possible */
		    (n1 > s7_int_max) || (n2 > s7_int_max) ||
		    (n1 < s7_int_min) || (n2 < s7_int_min))
		  {
		    int d1bits, d2bits;
		    d1bits = integer_length(d1);
		    d2bits = integer_length(d2);
		    if (((d1bits + d2bits) > s7_int_bits) ||
			((d1bits + integer_length(n2)) > (s7_int_bits - 1)) ||
			((d2bits + integer_length(n1)) > (s7_int_bits - 1)))
		      {
			if (fraction(x) > fraction(y)) goto NOT_LEQ;
		      }
		    else 
		      {
			if ((n1 * d2) > (n2 * d1)) goto NOT_LEQ;
		      }
		  }
		else
		  {
		    if ((n1 * d2) >  (n2 * d1)) goto NOT_LEQ;
		  }
	      }
	  }
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto RATIO_LEQ;

	case T_REAL:
	  if (is_NaN(real(y))) goto NOT_LEQ;
	  if (fraction(x) > real(y)) goto NOT_LEQ;
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto REAL_LEQ;
	  
	default:
	  check_method(sc, y, sc->LEQ, cons(sc, x, cons(sc, y, p)));
	  return(wrong_type_argument_n(sc, sc->LEQ, position_of(p, args) - 1, y, T_REAL));
	}


    case T_REAL:
      if (is_NaN(real(x))) goto NOT_LEQ;

    REAL_LEQ:
      y = car(p);
      p = cdr(p);
      switch (type(y))
	{
	case T_INTEGER:
	  if (real(x) > integer(y)) goto NOT_LEQ;
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto INTEGER_LEQ;

	case T_RATIO:
	  if (real(x) > fraction(y)) goto NOT_LEQ;
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto RATIO_LEQ;

	case T_REAL:
	  if (is_NaN(real(y))) goto NOT_LEQ;
	  if (real(x) > real(y)) goto NOT_LEQ;
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto REAL_LEQ;

	default:
	  check_method(sc, y, sc->LEQ, cons(sc, x, cons(sc, y, p)));
	  return(wrong_type_argument_n(sc, sc->LEQ, position_of(p, args) - 1, y, T_REAL));
	}

    default:
      check_method(sc, x, sc->LEQ, args);
      return(wrong_type_argument(sc, sc->LEQ, small_int(1), x, T_REAL));
    }

 NOT_LEQ:
  for (; is_pair(p); p = cdr(p))
    if (!is_real_via_method(sc, car(p)))
      return(wrong_type_argument_n(sc, sc->LEQ, position_of(p, args), car(p), T_REAL));

  return(sc->F);
}


static s7_pointer g_greater(s7_scheme *sc, s7_pointer args)
{
  #define H_greater "(> x1 ...) returns #t if its arguments are in decreasing order"
  s7_pointer x, y, p;

  x = car(args);
  p = cdr(args);

  switch (type(x))
    {
    case T_INTEGER:
    INTEGER_GREATER:
      y = car(p);
      p = cdr(p);
      switch (type(y))
	{
	case T_INTEGER:
	  if (integer(x) <= integer(y)) goto NOT_GREATER;
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto INTEGER_GREATER;

	case T_RATIO:
	  /* no gmp here, but this can overflow: (< 9223372036 1/9223372036), but conversion to real is also problematic
	   */
	  if ((integer(x) <= 0) && (numerator(y) > 0)) goto NOT_GREATER; 
	  if ((integer(x) >= 0) && (numerator(y) < 0)) 
	    {
	      if (is_null(p)) return(sc->T);
	      x = y;
	      goto RATIO_GREATER;
	    }
	  if ((integer(x) < S7_LONG_MAX) && 
	      (integer(x) > S7_LONG_MIN) && 
	      (denominator(y) < S7_LONG_MAX))
	    {
	      if ((integer(x) * denominator(y)) <= numerator(y)) goto NOT_GREATER;
	    }
	  else
	    {
	      if (integer(x) <= fraction(y)) goto NOT_GREATER;
	    }
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto RATIO_GREATER;

	case T_REAL:
	  if (is_NaN(real(y))) goto NOT_GREATER;
	  if (integer(x) <= real(y)) goto NOT_GREATER;
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto REAL_GREATER;

	default:
	  check_method(sc, y, sc->GT, cons(sc, x, cons(sc, y, p)));
	  return(wrong_type_argument_n(sc, sc->GT, position_of(p, args) - 1, y, T_REAL));
	}


    case T_RATIO:
    RATIO_GREATER:
      y = car(p);
      p = cdr(p);
      switch (type(y))
	{
	case T_INTEGER:
	  if ((numerator(x) < 0) && (integer(y) >= 0)) goto NOT_GREATER;
	  if ((numerator(x) > 0) && (integer(y) <= 0))
	    {
	      if (is_null(p)) return(sc->T);
	      x = y;
	      goto INTEGER_GREATER;
	    }
	  if ((integer(y) < S7_LONG_MAX) && 
	      (integer(y) > S7_LONG_MIN) && 
	      (denominator(x) < S7_LONG_MAX))
	    {
	      if (numerator(x) <= (integer(y) * denominator(x))) goto NOT_GREATER;
	    }
	  else
	    {
	      if (fraction(x) <= integer(y)) goto NOT_GREATER;
	    }
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto INTEGER_GREATER;

	case T_RATIO:
	  {
	    s7_Int d1, d2, n1, n2;
	    d1 = denominator(x);
	    n1 = numerator(x);
	    d2 = denominator(y);
	    n2 = numerator(y);
	    if (d1 == d2)       
	      {
		if (n1 <= n2) goto NOT_GREATER;
	      }
	    else
	      {
		if ((d1 > s7_int_max) || (d2 > s7_int_max) ||     /* before counting bits, check that overflow is possible */
		    (n1 > s7_int_max) || (n2 > s7_int_max) ||
		    (n1 < s7_int_min) || (n2 < s7_int_min))
		  {
		    int d1bits, d2bits;
		    d1bits = integer_length(d1);
		    d2bits = integer_length(d2);
		    if (((d1bits + d2bits) > s7_int_bits) ||
			((d1bits + integer_length(n2)) > (s7_int_bits - 1)) ||
			((d2bits + integer_length(n1)) > (s7_int_bits - 1)))
		      {
			if (fraction(x) <= fraction(y)) goto NOT_GREATER;
      
			/* (< 21053343141/6701487259 3587785776203/1142027682075) -> #f because even long doubles aren't enough here 
			 * (= 21053343141/6701487259 3587785776203/1142027682075) is #f because it checks the actual ints and
			 * (> 21053343141/6701487259 3587785776203/1142027682075) is #f just like the < case.
			 * similarly
			 * (min 21053343141/6701487259 3587785776203/1142027682075) -> 3587785776203/1142027682075
			 * (max 21053343141/6701487259 3587785776203/1142027682075) -> 3587785776203/1142027682075
			 *
			 * if we print the long double results as integers, both are -3958705157555305931
			 *    so there's not a lot I can do in the non-gmp case.
			 */
		      }
		    else 
		      {
			if ((n1 * d2) <= (n2 * d1)) goto NOT_GREATER;
		      }
		  }
		else
		  {
		    if ((n1 * d2) <=  (n2 * d1)) goto NOT_GREATER;
		  }
	      }
	  }
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto RATIO_GREATER;

	case T_REAL:
	  if (is_NaN(real(y))) goto NOT_GREATER;
	  if (fraction(x) <= real(y)) goto NOT_GREATER;
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto REAL_GREATER;
	  
	default:
	  check_method(sc, y, sc->GT, cons(sc, x, cons(sc, y, p)));
	  return(wrong_type_argument_n(sc, sc->GT, position_of(p, args) - 1, y, T_REAL));
	}


    case T_REAL:
      if (is_NaN(real(x))) goto NOT_GREATER;

    REAL_GREATER:
      y = car(p);
      p = cdr(p);
      switch (type(y))
	{
	case T_INTEGER:
	  if (real(x) <= integer(y)) goto NOT_GREATER;
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto INTEGER_GREATER;

	case T_RATIO:
	  if (real(x) <= fraction(y)) goto NOT_GREATER;
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto RATIO_GREATER;

	case T_REAL:
	  if (is_NaN(real(y))) goto NOT_GREATER;
	  if (real(x) <= real(y)) goto NOT_GREATER;
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto REAL_GREATER;

	default:
	  check_method(sc, y, sc->GT, cons(sc, x, cons(sc, y, p)));
	  return(wrong_type_argument_n(sc, sc->GT, position_of(p, args) - 1, y, T_REAL));
	}

    default:
      check_method(sc, x, sc->GT, args);
      return(wrong_type_argument(sc, sc->GT, small_int(1), x, T_REAL));
    }

 NOT_GREATER:
  for (; is_pair(p); p = cdr(p))
    if (!is_real_via_method(sc, car(p)))
      return(wrong_type_argument_n(sc, sc->GT, position_of(p, args), car(p), T_REAL));

  return(sc->F);
}


static s7_pointer g_greater_or_equal(s7_scheme *sc, s7_pointer args)
{
  #define H_greater_or_equal "(>= x1 ...) returns #t if its arguments are in decreasing order"
  /* (>= 1+i 1+i) is an error which seems unfortunate
   */
  s7_pointer x, y, p;

  x = car(args);
  p = cdr(args);

  switch (type(x))
    {
    case T_INTEGER:
    INTEGER_GEQ:
      y = car(p);
      p = cdr(p);
      switch (type(y))
	{
	case T_INTEGER:
	  if (integer(x) < integer(y)) goto NOT_GEQ;
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto INTEGER_GEQ;

	case T_RATIO:
	  /* no gmp here, but this can overflow: (< 9223372036 1/9223372036), but conversion to real is also problematic
	   */
	  if ((integer(x) <= 0) && (numerator(y) > 0)) goto NOT_GEQ; 
	  if ((integer(x) >= 0) && (numerator(y) < 0)) 
	    {
	      if (is_null(p)) return(sc->T);
	      x = y;
	      goto RATIO_GEQ;
	    }
	  if ((integer(x) < S7_LONG_MAX) && 
	      (integer(x) > S7_LONG_MIN) && 
	      (denominator(y) < S7_LONG_MAX))
	    {
	      if ((integer(x) * denominator(y)) < numerator(y)) goto NOT_GEQ;
	    }
	  else
	    {
	      if (integer(x) < fraction(y)) goto NOT_GEQ;
	    }
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto RATIO_GEQ;

	case T_REAL:
	  if (is_NaN(real(y))) goto NOT_GEQ;
	  if (integer(x) < real(y)) goto NOT_GEQ;
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto REAL_GEQ;

	default:
	  check_method(sc, y, sc->GEQ, cons(sc, x, cons(sc, y, p)));
	  return(wrong_type_argument_n(sc, sc->GEQ, position_of(p, args) - 1, y, T_REAL));
	}


    case T_RATIO:
    RATIO_GEQ:
      y = car(p);
      p = cdr(p);
      switch (type(y))
	{
	case T_INTEGER:
	  if ((numerator(x) < 0) && (integer(y) >= 0)) goto NOT_GEQ;
	  if ((numerator(x) > 0) && (integer(y) <= 0))
	    {
	      if (is_null(p)) return(sc->T);
	      x = y;
	      goto INTEGER_GEQ;
	    }
	  if ((integer(y) < S7_LONG_MAX) && 
	      (integer(y) > S7_LONG_MIN) && 
	      (denominator(x) < S7_LONG_MAX))
	    {
	      if (numerator(x) < (integer(y) * denominator(x))) goto NOT_GEQ;
	    }
	  else
	    {
	      if (fraction(x) < integer(y)) goto NOT_GEQ;
	    }
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto INTEGER_GEQ;

	case T_RATIO:
	  {
	    s7_Int d1, d2, n1, n2;
	    d1 = denominator(x);
	    n1 = numerator(x);
	    d2 = denominator(y);
	    n2 = numerator(y);
	    if (d1 == d2)       
	      {
		if (n1 < n2) goto NOT_GEQ;
	      }
	    else
	      {
		if ((d1 > s7_int_max) || (d2 > s7_int_max) ||     /* before counting bits, check that overflow is possible */
		    (n1 > s7_int_max) || (n2 > s7_int_max) ||
		    (n1 < s7_int_min) || (n2 < s7_int_min))
		  {
		    int d1bits, d2bits;
		    d1bits = integer_length(d1);
		    d2bits = integer_length(d2);
		    if (((d1bits + d2bits) > s7_int_bits) ||
			((d1bits + integer_length(n2)) > (s7_int_bits - 1)) ||
			((d2bits + integer_length(n1)) > (s7_int_bits - 1)))
		      {
			if (fraction(x) < fraction(y)) goto NOT_GEQ;
		      }
		    else 
		      {
			if ((n1 * d2) < (n2 * d1)) goto NOT_GEQ;
		      }
		  }
		else
		  {
		    if ((n1 * d2) <  (n2 * d1)) goto NOT_GEQ;
		  }
	      }
	  }
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto RATIO_GEQ;

	case T_REAL:
	  if (is_NaN(real(y))) goto NOT_GEQ;
	  if (fraction(x) < real(y)) goto NOT_GEQ;
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto REAL_GEQ;
	  
	default:
	  check_method(sc, y, sc->GEQ, cons(sc, x, cons(sc, y, p)));
	  return(wrong_type_argument_n(sc, sc->GEQ, position_of(p, args) - 1, y, T_REAL));
	}


    case T_REAL:
      if (is_NaN(real(x))) goto NOT_GEQ;

    REAL_GEQ:
      y = car(p);
      p = cdr(p);
      switch (type(y))
	{
	case T_INTEGER:
	  if (real(x) < integer(y)) goto NOT_GEQ;
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto INTEGER_GEQ;

	case T_RATIO:
	  if (real(x) < fraction(y)) goto NOT_GEQ;
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto RATIO_GEQ;

	case T_REAL:
	  if (is_NaN(real(y))) goto NOT_GEQ;
	  if (real(x) < real(y)) goto NOT_GEQ;
	  if (is_null(p)) return(sc->T);
	  x = y;
	  goto REAL_GEQ;

	default:
	  check_method(sc, y, sc->GEQ, cons(sc, x, cons(sc, y, p)));
	  return(wrong_type_argument_n(sc, sc->GEQ, position_of(p, args) - 1, y, T_REAL));
	}

    default:
      check_method(sc, x, sc->GEQ, args);
      return(wrong_type_argument(sc, sc->GEQ, small_int(1), x, T_REAL));
    }

 NOT_GEQ:
  for (; is_pair(p); p = cdr(p))
    if (!is_real_via_method(sc, car(p)))
      return(wrong_type_argument_n(sc, sc->GEQ, position_of(p, args), car(p), T_REAL));

  return(sc->F);

}


static s7_pointer less_s_ic, less_s0;
static s7_pointer g_less_s0(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x;
  x = car(args);
  if (is_integer(x))
    return(make_boolean(sc, integer(x) < 0));
  if (is_real(x))
    return(make_boolean(sc, s7_is_negative(x)));
  check_method(sc, x, sc->LT, args);
  return(wrong_type_argument(sc, sc->LT, small_int(1), x, T_REAL));
}

static s7_pointer g_less_s_ic(s7_scheme *sc, s7_pointer args)
{
  s7_Int y;
  s7_pointer x;
  
  x = car(args);
  y = integer(cadr(args));
  if (is_integer(x))
    return(make_boolean(sc, integer(x) < y));

  switch (type(x))
    {
    case T_INTEGER:
      return(make_boolean(sc, integer(x) < y));
      break;
      
    case T_RATIO:  
      if ((y >= 0) && (numerator(x) < 0))
	return(sc->T);
      if ((y <= 0) && (numerator(x) > 0))
	return(sc->F);
      if (denominator(x) < S7_LONG_MAX)
	return(make_boolean(sc, (numerator(x) < (y * denominator(x)))));
      return(make_boolean(sc, fraction(x) < y));
      break;
      
    case T_REAL:    
      return(make_boolean(sc, real(x) < y));
      break;

    case T_COMPLEX:
    default:
      check_method(sc, x, sc->LT, args);
      return(wrong_type_argument(sc, sc->LT, small_int(1), x, T_REAL));
    }
  return(sc->T);
}

static s7_pointer less_length_ic;
static s7_pointer g_less_length_ic(s7_scheme *sc, s7_pointer args)
{
  s7_Int ilen;
  s7_pointer val;
  
  val = find_symbol_checked(sc, cadar(args));
  ilen = s7_integer(cadr(args));

  switch (type(val))
    {
    case T_PAIR:
      return(make_boolean(sc, s7_list_length(sc, val) < ilen));

    case T_NIL:
      return(make_boolean(sc, ilen > 0));

    case T_INT_VECTOR:
    case T_FLOAT_VECTOR:
    case T_VECTOR:
      return(make_boolean(sc, vector_length(val) < ilen));

    case T_STRING:
      return(make_boolean(sc, string_length(val) < ilen));

    case T_HASH_TABLE:
      return(make_boolean(sc, hash_table_length(val) < ilen));

    case T_C_OBJECT:
      return(make_boolean(sc, object_length_to_int(sc, val) < ilen));

    case T_ENVIRONMENT:
      /* this works because let_length handles the length method itself! */
      return(make_boolean(sc, let_length(sc, val) < ilen));

    case T_CLOSURE:
    case T_CLOSURE_STAR:
      if (has_methods(val))
	return(make_boolean(sc, closure_length(sc, val) < ilen));

    default:
      /* no check method here because we checked above */
      return(simple_wrong_type_argument_with_type(sc, sc->LENGTH, val, A_SEQUENCE));
    }
  return(sc->F);
}

static s7_pointer less_2;
static s7_pointer g_less_2(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x, y;

  x = car(args);
  y = cadr(args);

#if (!MS_WINDOWS)
  if (type(x) == type(y))
    {
      switch (type(x))
	{
	case T_INTEGER: return(make_boolean(sc, integer(x) < integer(y)));
	case T_RATIO:   return(make_boolean(sc, fraction(x) < fraction(y)));
	case T_REAL:    return(make_boolean(sc, real(x) < real(y)));
	}
    }
#endif

  switch (type(x))
    {
    case T_INTEGER:
      switch (type(y))
	{
	case T_INTEGER:
	  return(make_boolean(sc, integer(x) < integer(y)));
	  break;
	  
	case T_RATIO: 
	  return(g_less(sc, args));
	  break;
	  
	case T_REAL:
	  if (is_NaN(real(y))) return(sc->F);
	  return(make_boolean(sc, integer(x) < real(y)));
	  break;

	default:
	  check_method(sc, y, sc->LT, args);
	  return(wrong_type_argument(sc, sc->LT, small_int(2), y, T_REAL));	  
	}
      break;
      
    case T_RATIO:
      return(g_less(sc, args));
      break;
      
    case T_REAL:
      switch (type(y))
	{
	case T_INTEGER: 
	  if (is_NaN(real(x))) return(sc->F);
	  return(make_boolean(sc, real(x) < integer(y)));
	  break;
	  
	case T_RATIO:
	  if (is_NaN(real(x))) return(sc->F);
	  return(make_boolean(sc, real(x) < fraction(y)));
	  break;
	  
	case T_REAL:
	  if (is_NaN(real(x))) return(sc->F);
	  if (is_NaN(real(y))) return(sc->F);
	  return(make_boolean(sc, real(x) < real(y)));
	  break;

	default:
	  check_method(sc, y, sc->LT, args);
	  return(wrong_type_argument(sc, sc->LT, small_int(2), y, T_REAL));	  
	}
      break;

    default:
      check_method(sc, x, sc->LT, args);
      return(wrong_type_argument(sc, sc->LT, small_int(1), x, T_REAL));      
    }
  return(sc->T);
}

static s7_pointer leq_s_ic;
static s7_pointer g_leq_s_ic(s7_scheme *sc, s7_pointer args)
{
  s7_Int y;
  s7_pointer x;
  
  x = car(args);
  y = s7_integer(cadr(args));

  switch (type(x))
    {
    case T_INTEGER:
      return(make_boolean(sc, integer(x) <= y));
      break;
      
    case T_RATIO:  
      if ((y >= 0) && (numerator(x) <= 0))
	return(sc->T);
      if ((y <= 0) && (numerator(x) > 0))
	return(sc->F);
      if (denominator(x) < S7_LONG_MAX)
	return(make_boolean(sc, (numerator(x) <= (y * denominator(x)))));
      return(make_boolean(sc, fraction(x) <= y));
      break;
      
    case T_REAL:    
      return(make_boolean(sc, real(x) <= y));
      break;

    default:
      check_method(sc, x, sc->LEQ, args);
      return(wrong_type_argument(sc, sc->LEQ, small_int(1), x, T_REAL));  
    }
  return(sc->T);
}

static s7_pointer leq_2;
static s7_pointer g_leq_2(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x, y;

  x = car(args);
  y = cadr(args);

  switch (type(x))
    {
    case T_INTEGER:
      switch (type(y))
	{
	case T_INTEGER:
	  return(make_boolean(sc, integer(x) <= integer(y)));
	  break;
	  
	case T_RATIO: 
	  return(g_less_or_equal(sc, args));
	  break;
	  
	case T_REAL:
	  if (is_NaN(real(y))) return(sc->F);
	  return(make_boolean(sc, integer(x) <= real(y)));
	  break;

	default:
	  check_method(sc, y, sc->LEQ, args);
	  return(wrong_type_argument(sc, sc->LEQ, small_int(2), y, T_REAL));	  
	}
      break;
      
    case T_RATIO:
      return(g_less_or_equal(sc, args));
      break;
      
    case T_REAL:
      switch (type(y))
	{
	case T_INTEGER: 
	  if (is_NaN(real(x))) return(sc->F);
	  return(make_boolean(sc, real(x) <= integer(y)));
	  break;
	  
	case T_RATIO:
	  if (is_NaN(real(x))) return(sc->F);
	  return(make_boolean(sc, real(x) <= fraction(y)));
	  break;
	  
	case T_REAL:
	  if (is_NaN(real(x))) return(sc->F);
	  if (is_NaN(real(y))) return(sc->F);
	  return(make_boolean(sc, real(x) <= real(y)));
	  break;

	default:
	  check_method(sc, y, sc->LEQ, args);
	  return(wrong_type_argument(sc, sc->LEQ, small_int(2), y, T_REAL));	  
	}
      break;

    default:
      check_method(sc, x, sc->LEQ, args);
      return(wrong_type_argument(sc, sc->LEQ, small_int(1), x, T_REAL));
    }
  return(sc->T);
}

static s7_pointer greater_s_ic, greater_s_fc;
static s7_pointer g_greater_s_ic(s7_scheme *sc, s7_pointer args)
{
  s7_Int y;
  s7_pointer x;
  
  x = car(args);
  y = integer(cadr(args));
  switch (type(x))
    {
    case T_INTEGER:
      return(make_boolean(sc, integer(x) > y));
      break;
      
    case T_RATIO:  
      if (denominator(x) < S7_LONG_MAX) /* y has already been checked for range */
	return(make_boolean(sc, (numerator(x) > (y * denominator(x)))));
      return(make_boolean(sc, fraction(x) > y));
      break;
      
    case T_REAL:    
      return(make_boolean(sc, real(x) > y));
      break;

    default:
      check_method(sc, x, sc->GT, args);
      return(wrong_type_argument_with_type(sc, sc->GT, small_int(1), x, A_NUMBER));  
    }
  return(sc->T);
}

static s7_pointer g_greater_s_fc(s7_scheme *sc, s7_pointer args)
{
  s7_Double y;
  s7_pointer x;

  x = car(args);
  y = real(cadr(args));

  if (type(x) == T_REAL)
    return(make_boolean(sc, real(x) > y));

  switch (type(x))
    {
    case T_INTEGER:
      return(make_boolean(sc, integer(x) > y));
      break;
      
    case T_RATIO:  
      /* (> 9223372036854775807/9223372036854775806 1.0) */
      if (denominator(x) < S7_LONG_MAX) /* y range check was handled in greater_chooser */
	return(make_boolean(sc, (numerator(x) > (y * denominator(x)))));
      return(make_boolean(sc, fraction(x) > y));
      break;
      
    case T_REAL:    
      return(make_boolean(sc, real(x) > y));
      break;

    default:
      check_method(sc, x, sc->GT, args);
      return(wrong_type_argument_with_type(sc, sc->GT, small_int(1), x, A_NUMBER));  
    }
  return(sc->T);
}

static s7_pointer greater_2;
static s7_pointer g_greater_2(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x, y;

  x = car(args);
  y = cadr(args);

  switch (type(x))
    {
    case T_INTEGER:
      switch (type(y))
	{
	case T_INTEGER: 
	  return(make_boolean(sc, integer(x) > integer(y)));

	case T_RATIO:   
	  return(g_greater(sc, args));

	case T_REAL:
	  if (is_NaN(real(y))) return(sc->F);
	  return(make_boolean(sc, integer(x) > real(y)));
	  break;

	default:       
	  check_method(sc, y, sc->GT, args);
	  return(wrong_type_argument(sc, sc->GT, small_int(2), y, T_REAL));
	}
      break;
      
    case T_RATIO:
      return(g_greater(sc, args));
      break;
      
    case T_REAL:
      switch (type(y))
	{
	case T_INTEGER: 
	  if (is_NaN(real(x))) return(sc->F);
	  return(make_boolean(sc, real(x) > integer(y)));
	  break;
	  
	case T_RATIO:
	  if (is_NaN(real(x))) return(sc->F);
	  return(make_boolean(sc, real(x) > fraction(y)));
	  break;
	  
	case T_REAL:
	  if (is_NaN(real(x))) return(sc->F);
	  if (is_NaN(real(y))) return(sc->F);
	  return(make_boolean(sc, real(x) > real(y)));
	  break;

	default:
	  check_method(sc, y, sc->GT, args);
	  return(wrong_type_argument(sc, sc->GT, small_int(2), y, T_REAL));
	}
      break;

    default:
      check_method(sc, x, sc->GT, args);
      return(wrong_type_argument(sc, sc->GT, small_int(1), x, T_REAL));
    }
  return(sc->T);
}


static s7_pointer geq_2;
static s7_pointer g_geq_2(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x, y;

  x = car(args);
  y = cadr(args);

#if (!MS_WINDOWS)
  if (type(x) == type(y))
    {
      if (is_integer(x))
	return(make_boolean(sc, integer(x) >= integer(y)));
      switch (type(x))
	{
	case T_INTEGER: return(make_boolean(sc, integer(x) >= integer(y)));
	case T_RATIO:   return(make_boolean(sc, fraction(x) >= fraction(y)));
	case T_REAL:    return(make_boolean(sc, real(x) >= real(y)));
	}
    }
#endif

  switch (type(x))
    {
    case T_INTEGER:
      switch (type(y))
	{
	case T_INTEGER:
	  return(make_boolean(sc, integer(x) >= integer(y)));
	  break;
	  
	case T_RATIO: 
	  return(g_greater_or_equal(sc, args));
	  break;
	  
	case T_REAL:
	  if (is_NaN(real(y))) return(sc->F);
	  return(make_boolean(sc, integer(x) >= real(y)));
	  break;

	default:
	  check_method(sc, y, sc->GEQ, args);
	  return(wrong_type_argument(sc, sc->GEQ, small_int(2), y, T_REAL));	  
	}
      break;
      
    case T_RATIO:
      return(g_greater_or_equal(sc, args));
      break;
      
    case T_REAL:
      switch (type(y))
	{
	case T_INTEGER: 
	  if (is_NaN(real(x))) return(sc->F);
	  return(make_boolean(sc, real(x) >= integer(y)));
	  break;
	  
	case T_RATIO:
	  if (is_NaN(real(x))) return(sc->F);
	  return(make_boolean(sc, real(x) >= fraction(y)));
	  break;
	  
	case T_REAL:
	  if (is_NaN(real(x))) return(sc->F);
	  if (is_NaN(real(y))) return(sc->F);
	  return(make_boolean(sc, real(x) >= real(y)));
	  break;

	default:
	  check_method(sc, y, sc->GEQ, args);
	  return(wrong_type_argument(sc, sc->GEQ, small_int(2), y, T_REAL));	  
	}
      break;

    default:
      check_method(sc, x, sc->GEQ, args);
      return(wrong_type_argument(sc, sc->GEQ, small_int(1), x, T_REAL));
    }
  return(sc->T);
}

static s7_pointer geq_s_fc;
static s7_pointer g_geq_s_fc(s7_scheme *sc, s7_pointer args)
{
  s7_Double y;
  s7_pointer x;

  x = car(args);
  y = real(cadr(args));

  if (type(x) == T_REAL)
    return(make_boolean(sc, real(x) >= y));
  return(g_geq_2(sc, args));
}


static s7_pointer geq_length_ic;
static s7_pointer g_geq_length_ic(s7_scheme *sc, s7_pointer args)
{
  return(make_boolean(sc, is_false(sc, g_less_length_ic(sc, args))));
}


static s7_pointer geq_s_ic;
static s7_pointer g_geq_s_ic(s7_scheme *sc, s7_pointer args)
{
  s7_Int y;
  s7_pointer x;
  
  x = car(args);
  y = s7_integer(cadr(args));

  switch (type(x))
    {
    case T_INTEGER:
      return(make_boolean(sc, integer(x) >= y));
      break;
      
    case T_RATIO:  
      if ((y >= 0) && (numerator(x) < 0))
	return(sc->F);
      if ((y <= 0) && (numerator(x) >= 0))
	return(sc->T);
      if ((y < S7_LONG_MAX) && 
	  (y > S7_LONG_MIN) && 
	  (denominator(x) < S7_LONG_MAX))
	return(make_boolean(sc, (numerator(x) >= (y * denominator(x)))));
      return(make_boolean(sc, fraction(x) >= y));
      break;
      
    case T_REAL:    
      return(make_boolean(sc, real(x) >= y));
      break;

    default:
      check_method(sc, x, sc->GEQ, args);
      return(wrong_type_argument(sc, sc->GEQ, small_int(1), x, T_REAL));  
    }
  return(sc->T);
}
#endif
/* end (!WITH_GMP) */


/* ---------------------------------------- real-part imag-part ---------------------------------------- */

s7_Double s7_real_part(s7_pointer x)
{
  switch(type(x))
    {
    case T_INTEGER:     return((s7_Double)integer(x));
    case T_RATIO:       return(fraction(x));
    case T_REAL:        return(real(x));
    case T_COMPLEX:     return(real_part(x));
#if WITH_GMP
    case T_BIG_INTEGER: return((s7_Double)big_integer_to_s7_Int(big_integer(x)));
    case T_BIG_RATIO:   return((s7_Double)((long double)big_integer_to_s7_Int(mpq_numref(big_ratio(x))) / (long double)big_integer_to_s7_Int(mpq_denref(big_ratio(x)))));
    case T_BIG_REAL:    return((s7_Double)mpfr_get_d(big_real(x), GMP_RNDN));
    case T_BIG_COMPLEX: return((s7_Double)mpfr_get_d(mpc_realref(big_complex(x)), GMP_RNDN));
#endif
    }
  return(0.0);
}


s7_Double s7_imag_part(s7_pointer x)
{
  switch (type(x))
    {
    case T_COMPLEX:     return(imag_part(x));
#if WITH_GMP
    case T_BIG_COMPLEX: return((s7_Double)mpfr_get_d(mpc_imagref(big_complex(x)), GMP_RNDN));
#endif
    }
  return(0.0);
}


static s7_pointer g_real_part(s7_scheme *sc, s7_pointer args)
{
  #define H_real_part "(real-part num) returns the real part of num"
  s7_pointer p;

  p = car(args);
  switch (type(p))
    {
    case T_INTEGER:
    case T_RATIO:
    case T_REAL:
      return(p);

    case T_COMPLEX:
      return(make_real(sc, real_part(p)));

#if WITH_GMP
    case T_BIG_INTEGER:
    case T_BIG_RATIO:
    case T_BIG_REAL:
      return(p);

    case T_BIG_COMPLEX:
      {
	s7_pointer x;
       
	NEW_CELL(sc, x);
	set_type(x, T_BIG_REAL);
	mpfr_init(big_real(x));
	mpc_real(big_real(x), big_complex(p), GMP_RNDN);
	
	return(x);
      }
#endif

    default:
      check_method(sc, p, sc->REAL_PART, args);
      return(simple_wrong_type_argument_with_type(sc, sc->REAL_PART, p, A_NUMBER));
    }
}


static s7_pointer g_imag_part(s7_scheme *sc, s7_pointer args)
{
  #define H_imag_part "(imag-part num) returns the imaginary part of num"
  s7_pointer p;
  /* currently (imag-part nan.0) -> 0.0 ? it's true but maybe confusing */

  p = car(args);
  switch (type(p))
    {
    case T_INTEGER:   
    case T_RATIO:   
      return(small_int(0));

    case T_REAL:    
      return(real_zero);

    case T_COMPLEX: 
      return(make_real(sc, imag_part(p)));

#if WITH_GMP
    case T_BIG_INTEGER:   
    case T_BIG_RATIO:   
      return(small_int(0));

    case T_BIG_REAL:    
      return(real_zero);

    case T_BIG_COMPLEX:
      {
	s7_pointer x;
       
	NEW_CELL(sc, x);
	set_type(x, T_BIG_REAL);
	mpfr_init(big_real(x));
	mpc_imag(big_real(x), big_complex(p), GMP_RNDN);
	
	return(x);
      }
#endif

    default:       
      check_method(sc, p, sc->IMAG_PART, args);
      return(simple_wrong_type_argument_with_type(sc, sc->IMAG_PART, p, A_NUMBER));
    }
}


/* ---------------------------------------- numerator denominator ---------------------------------------- */

static s7_pointer g_numerator(s7_scheme *sc, s7_pointer args)
{
  #define H_numerator "(numerator rat) returns the numerator of the rational number rat"
  s7_pointer x;

  x = car(args);
  switch (type(x))
    {
    case T_RATIO:
      return(make_integer(sc, numerator(x)));
      
    case T_INTEGER:
      return(x);

#if WITH_GMP
    case T_BIG_INTEGER:
      return(x);

    case T_BIG_RATIO:
      return(mpz_to_big_integer(sc, mpq_numref(big_ratio(x))));
#endif

    case T_REAL:
    case T_COMPLEX:
    default:
      check_method(sc, x, sc->NUMERATOR, args);
      return(simple_wrong_type_argument_with_type(sc, sc->NUMERATOR, x, A_RATIONAL));
    }
}


static s7_pointer g_denominator(s7_scheme *sc, s7_pointer args)
{
  #define H_denominator "(denominator rat) returns the denominator of the rational number rat"
  s7_pointer x;

  x = car(args);
  switch (type(x))
    {
    case T_RATIO:
      return(make_integer(sc, denominator(x)));
      
    case T_INTEGER:
      return(small_int(1));

#if WITH_GMP
    case T_BIG_INTEGER:
      return(small_int(1));

    case T_BIG_RATIO:
      return(mpz_to_big_integer(sc, mpq_denref(big_ratio(x))));
#endif

    case T_REAL:
    case T_COMPLEX:
    default:
      check_method(sc, x, sc->DENOMINATOR, args);
      return(simple_wrong_type_argument_with_type(sc, sc->DENOMINATOR, x, A_RATIONAL));
    }
}


/* ---------------------------------------- nan? infinite? ---------------------------------------- */

static s7_pointer g_is_nan(s7_scheme *sc, s7_pointer args) 
{
  #define H_is_nan "(nan? obj) returns #t if obj is a NaN"
  /* s7 thinks a NaN is a real. */
  s7_pointer x;

  x = car(args);
  switch (type(x))
    {
    case T_INTEGER:
    case T_RATIO:
      return(sc->F);
	  
    case T_REAL:
      return(make_boolean(sc, is_NaN(real(x))));
	  
    case T_COMPLEX:
      return(make_boolean(sc, (is_NaN(real_part(x))) || (is_NaN(imag_part(x)))));

#if WITH_GMP
    case T_BIG_INTEGER:
    case T_BIG_RATIO:
      return(sc->F);

    case T_BIG_REAL:
      return(make_boolean(sc, is_NaN(s7_real_part(x))));

    case T_BIG_COMPLEX:
      return(make_boolean(sc, (is_NaN(s7_real_part(x))) || (is_NaN(s7_imag_part(x)))));
#endif

    default:
      check_method(sc, x, sc->IS_NAN, args);
      return(simple_wrong_type_argument_with_type(sc, sc->IS_NAN, x, A_NUMBER));
    }
}


static s7_pointer g_is_infinite(s7_scheme *sc, s7_pointer args) 
{
  #define H_is_infinite "(infinite? obj) returns #t if obj is an infinite real"
  s7_pointer x;
  x = car(args);
  switch (type(x))
    {
    case T_INTEGER:
    case T_RATIO:
      return(sc->F);

    case T_REAL:
      return(make_boolean(sc, is_inf(real(x))));

    case T_COMPLEX:
      return(make_boolean(sc, (is_inf(real_part(x))) || (is_inf(imag_part(x)))));

#if WITH_GMP
    case T_BIG_INTEGER:
    case T_BIG_RATIO:
      return(sc->F);

    case T_BIG_REAL:
      return(make_boolean(sc, mpfr_inf_p(big_real(x)) != 0));

    case T_BIG_COMPLEX:
      return(make_boolean(sc, 
			  (mpfr_inf_p(big_real(g_real_part(sc, args))) != 0) ||
			  (mpfr_inf_p(big_real(g_imag_part(sc, args))) != 0)));
#endif

    default:
      check_method(sc, x, sc->IS_INFINITE, args);
      return(simple_wrong_type_argument_with_type(sc, sc->IS_INFINITE, x, A_NUMBER));
    }
}


/* ---------------------------------------- number? complex? integer? rational? real?  ---------------------------------------- */

static s7_pointer g_is_number(s7_scheme *sc, s7_pointer args) 
{
  #define H_is_number "(number? obj) returns #t if obj is a number"
  check_boolean_method(sc, s7_is_number, sc->IS_NUMBER, args); /* we need the s7_* versions here for the GMP case */
}


static s7_pointer g_is_integer(s7_scheme *sc, s7_pointer args) 
{
  #define H_is_integer "(integer? obj) returns #t if obj is an integer"
  check_boolean_method(sc, s7_is_integer, sc->IS_INTEGER, args);
}


static s7_pointer g_is_real(s7_scheme *sc, s7_pointer args) 
{
  #define H_is_real "(real? obj) returns #t if obj is a real number"
  check_boolean_method(sc, s7_is_real, sc->IS_REAL, args);
}


static s7_pointer g_is_complex(s7_scheme *sc, s7_pointer args) 
{
  #define H_is_complex "(complex? obj) returns #t if obj is a number"
  check_boolean_method(sc, s7_is_number, sc->IS_COMPLEX, args);
}


static s7_pointer g_is_rational(s7_scheme *sc, s7_pointer args) 
{
  #define H_is_rational "(rational? obj) returns #t if obj is a rational number (either an integer or a ratio)"
  check_boolean_method(sc, s7_is_rational, sc->IS_RATIONAL, args);
  /* in the non-gmp case, (rational? 455702434782048082459/86885567283849955830) -> #f, not #t
   *  and similarly for exact? etc.
   */
}


/* ---------------------------------------- even? odd?---------------------------------------- */

static s7_pointer g_is_even(s7_scheme *sc, s7_pointer args)
{
  #define H_is_even "(even? int) returns #t if the integer int is even"
  s7_pointer p;

  p = car(args);
  switch (type(p))
    {
    case T_INTEGER:
      return(make_boolean(sc, ((integer(p) & 1) == 0)));

#if WITH_GMP
    case T_BIG_INTEGER:
      return(make_boolean(sc, mpz_even_p(big_integer(p))));
#endif

    default:
      check_method(sc, p, sc->IS_EVEN, args);
      return(simple_wrong_type_argument(sc, sc->IS_EVEN, p, T_INTEGER));
    }

  /* extension to gaussian integers: odd if a+b is odd? */
}


static s7_pointer g_is_odd(s7_scheme *sc, s7_pointer args)
{
  #define H_is_odd "(odd? int) returns #t if the integer int is odd"
  s7_pointer p;

  p = car(args);
  switch (type(p))
    {
    case T_INTEGER:
      return(make_boolean(sc, ((integer(p) & 1) == 1)));

#if WITH_GMP
    case T_BIG_INTEGER:
      return(make_boolean(sc, mpz_odd_p(big_integer(p))));
#endif  

    default:
      check_method(sc, p, sc->IS_ODD, args);
      return(simple_wrong_type_argument(sc, sc->IS_ODD, p, T_INTEGER));
    }
}


/* ---------------------------------------- zero? positive? negative? ---------------------------------------- */

static s7_pointer g_is_zero(s7_scheme *sc, s7_pointer args)
{
  #define H_is_zero "(zero? num) returns #t if the number num is zero"
  s7_pointer x;

  x = car(args);
  switch (type(x))
    {
    case T_INTEGER: 
      return(make_boolean(sc, integer(x) == 0));

    case T_REAL:    
      return(make_boolean(sc, real(x) == 0.0));

    case T_RATIO:
    case T_COMPLEX:
      /* ratios and complex numbers are already collapsed into integers and reals */
      return(sc->F);

#if WITH_GMP
    case T_BIG_INTEGER:
      return(make_boolean(sc, mpz_cmp_ui(big_integer(x), 0) == 0));

    case T_BIG_REAL:
      return(make_boolean(sc, mpfr_zero_p(big_real(x))));

    case T_BIG_RATIO:
    case T_BIG_COMPLEX:
      return(sc->F);
#endif

    default:
      check_method(sc, x, sc->IS_ZERO, args);
      return(simple_wrong_type_argument_with_type(sc, sc->IS_ZERO, x, A_NUMBER));
    }
}


static s7_pointer g_is_positive(s7_scheme *sc, s7_pointer args)
{
  #define H_is_positive "(positive? num) returns #t if the real number num is positive (greater than 0)"
  s7_pointer x;
  
  x = car(args);
  switch (type(x))
    {
    case T_INTEGER: 
      return(make_boolean(sc, integer(x) > 0));

    case T_RATIO:   
      return(make_boolean(sc, numerator(x) > 0));

    case T_REAL:    
      /* what about (positive? nan.0)? */
      return(make_boolean(sc, real(x) > 0.0));

#if WITH_GMP
    case T_BIG_INTEGER:
      return(make_boolean(sc, (mpz_cmp_ui(big_integer(x), 0) > 0)));

    case T_BIG_RATIO:
      return(make_boolean(sc, (mpq_cmp_ui(big_ratio(x), 0, 1) > 0)));

    case T_BIG_REAL:
      return(make_boolean(sc, (mpfr_cmp_ui(big_real(x), 0) > 0)));
#endif

    default:       
      check_method(sc, x, sc->IS_POSITIVE, args);
      return(simple_wrong_type_argument(sc, sc->IS_POSITIVE, x, T_REAL));
    }
}


#if (!WITH_GMP)
static s7_pointer is_negative_length;
static s7_pointer g_is_negative_length(s7_scheme *sc, s7_pointer args)
{
  s7_pointer val;
  val = find_symbol_checked(sc, cadar(args));

  switch (type(val))
    {
    case T_PAIR:
      return(make_boolean(sc, s7_list_length(sc, val) < 0));

    case T_INT_VECTOR:
    case T_FLOAT_VECTOR:
    case T_VECTOR:
    case T_NIL:
    case T_STRING:
    case T_HASH_TABLE:
      return(sc->F);

    case T_ENVIRONMENT:
    case T_CLOSURE:
    case T_CLOSURE_STAR:
      if (has_methods(val)) /* this is a special case */
	{
	  s7_pointer func;
	  func = find_method(sc, find_let(sc, val), sc->LENGTH);
	  if (func != sc->UNDEFINED)
	    return(make_boolean(sc, s7_integer(s7_apply_function(sc, func, list_1(sc, val))) < 0));
	}
      return(sc->F);

    case T_C_OBJECT:
      return(make_boolean(sc, object_length_to_int(sc, val) < 0));

    default:
      return(simple_wrong_type_argument_with_type(sc, sc->LENGTH, val, A_SEQUENCE));
    }
  return(sc->F);
}
#endif


static s7_pointer g_is_negative(s7_scheme *sc, s7_pointer args)
{
  #define H_is_negative "(negative? num) returns #t if the real number num is negative (less than 0)"
  s7_pointer x;
  
  x = car(args);
  switch (type(x))
    {
    case T_INTEGER: 
      return(make_boolean(sc, integer(x) < 0));

    case T_RATIO:   
      return(make_boolean(sc, numerator(x) < 0));

    case T_REAL:    
      /* what about (negative? -nan.0)? */
      return(make_boolean(sc, real(x) < 0.0));

#if WITH_GMP
    case T_BIG_INTEGER:
      return(make_boolean(sc, (mpz_cmp_ui(big_integer(x), 0) < 0)));

    case T_BIG_RATIO:
      return(make_boolean(sc, (mpq_cmp_ui(big_ratio(x), 0, 1) < 0)));

    case T_BIG_REAL:
      return(make_boolean(sc, (mpfr_cmp_ui(big_real(x), 0) < 0)));
#endif

    default:        
      check_method(sc, x, sc->IS_NEGATIVE, args);
      return(simple_wrong_type_argument(sc, sc->IS_NEGATIVE, x, T_REAL));
    }
}


#if (!WITH_GMP)
/* ---------------------------------------- exact<->inexact exact? inexact? ---------------------------------------- */

static s7_pointer g_exact_to_inexact(s7_scheme *sc, s7_pointer args)
{
  #define H_exact_to_inexact "(exact->inexact num) converts num to an inexact number; (exact->inexact 3/2) = 1.5"
  return(exact_to_inexact(sc, car(args)));
}


static s7_pointer g_inexact_to_exact(s7_scheme *sc, s7_pointer args)
{
  #define H_inexact_to_exact "(inexact->exact num) converts num to an exact number; (inexact->exact 1.5) = 3/2"
  return(inexact_to_exact(sc, car(args), WITH_OVERFLOW_ERROR));
}

#endif
/* (!WITH_GMP) */


static s7_pointer g_is_exact(s7_scheme *sc, s7_pointer args)
{
  #define H_is_exact "(exact? num) returns #t if num is exact (an integer or a ratio)"
  s7_pointer x;
  
  x = car(args);
  switch (type(x))
    {
    case T_INTEGER:
    case T_RATIO:
      return(sc->T);

    case T_REAL:
    case T_COMPLEX:
      return(sc->F);

#if WITH_GMP
    case T_BIG_INTEGER:
    case T_BIG_RATIO:
      return(sc->T);

    case T_BIG_REAL:
    case T_BIG_COMPLEX:
      return(sc->F);
#endif

    default:
      check_method(sc, x, sc->IS_EXACT, args);
      return(simple_wrong_type_argument_with_type(sc, sc->IS_EXACT, x, A_NUMBER));
    }
}


static s7_pointer g_is_inexact(s7_scheme *sc, s7_pointer args)
{
  #define H_is_inexact "(inexact? num) returns #t if num is inexact (neither an integer nor a ratio)"
  s7_pointer x;
  
  x = car(args);
  switch (type(x))
    {
    case T_INTEGER:
    case T_RATIO:
      return(sc->F);

    case T_REAL:
    case T_COMPLEX:
      return(sc->T);

#if WITH_GMP
    case T_BIG_INTEGER:
    case T_BIG_RATIO:
      return(sc->F);

    case T_BIG_REAL:
    case T_BIG_COMPLEX:
      return(sc->T);
#endif

    default:
      check_method(sc, x, sc->IS_INEXACT, args);
      return(simple_wrong_type_argument_with_type(sc, sc->IS_INEXACT, x, A_NUMBER));
    }
}


bool s7_is_ulong(s7_pointer arg) 
{
  return(is_integer(arg));
}


unsigned long s7_ulong(s7_pointer p) 
{
  return(p->object.number.ul_value);
}


s7_pointer s7_make_ulong(s7_scheme *sc, unsigned long n) 
{
  s7_pointer x;
  NEW_CELL(sc, x);
  set_type(x, T_INTEGER);
  x->object.number.ul_value = n;
  return(x);
}


bool s7_is_ulong_long(s7_pointer arg) 
{
  return(is_integer(arg));
}


unsigned long long s7_ulong_long(s7_pointer p) 
{
  return(p->object.number.ull_value);
}


s7_pointer s7_make_ulong_long(s7_scheme *sc, unsigned long long n) 
{
  s7_pointer x;
  NEW_CELL(sc, x);
  set_type(x, T_INTEGER);
  x->object.number.ull_value = n;
  return(x);
}


/* ---------------------------------------- integer-length, integer-decode-float ---------------------------------------- */

static s7_pointer g_integer_length(s7_scheme *sc, s7_pointer args)
{
  #define H_integer_length "(integer-length arg) returns the number of bits required to represent the integer 'arg': (ceiling (log (abs arg) 2))"
  s7_Int x;
  s7_pointer p;

  p = car(args);
  if (!s7_is_integer(p))
    {
      check_method(sc, p, sc->INTEGER_LENGTH, args);
      return(simple_wrong_type_argument(sc, sc->INTEGER_LENGTH, p, T_INTEGER));
    }

  x = s7_integer(p);
  if (x < 0)
    return(make_integer(sc, integer_length(-(x + 1))));
  return(make_integer(sc, integer_length(x)));
}


static s7_pointer g_integer_decode_float(s7_scheme *sc, s7_pointer args)
{
  #define H_integer_decode_float "(integer-decode-float x) returns a list containing the significand, exponent, and \
sign of 'x' (1 = positive, -1 = negative).  (integer-decode-float 0.0): (0 0 1)"

  /* no matter what s7_Double is, integer-decode-float acts as if the real it is a C double */

  typedef struct decode_float_t {
    union {
      long long int ix;
      double fx;
    } value;
  } decode_float_t;

 decode_float_t num;
  s7_pointer x;
  x = car(args);

  switch (type(x))
    {
    case T_REAL:
      num.value.fx = (double)real(x);
      break;

#if WITH_GMP
    case T_BIG_REAL:
      num.value.fx = (double)number_to_double(sc, x, "integer-decode-float");
      break;
#endif

    default:
      check_method(sc, x, sc->INTEGER_DECODE_FLOAT, args);
      return(simple_wrong_type_argument_with_type(sc, sc->INTEGER_DECODE_FLOAT, x, make_string_wrapper(sc, "a non-rational real")));
    }
  
  if (num.value.fx == 0.0)
    return(list_3(sc, small_int(0), small_int(0), small_int(1)));

  return(list_3(sc,
		make_integer(sc, (s7_Int)((num.value.ix & 0xfffffffffffffLL) | 0x10000000000000LL)),
		make_integer(sc, (s7_Int)(((num.value.ix & 0x7fffffffffffffffLL) >> 52) - 1023 - 52)),
		make_integer(sc, ((num.value.ix & 0x8000000000000000LL) != 0) ? -1 : 1)));
}


/* ---------------------------------------- logand, logior, logxor, lognot, logbit?, ash ---------------------------------------- */

static s7_pointer g_logior(s7_scheme *sc, s7_pointer args)
{
  #define H_logior "(logior int ...) returns the bitwise OR of its integer arguments (the bits that are on in any of the arguments)"
  s7_Int result = 0;
  s7_pointer x;

  for (x = args; is_not_null(x); x = cdr(x))
    if (!s7_is_integer(car(x)))
      {
	check_method(sc, car(x), sc->LOGIOR, cons(sc, make_integer(sc, result), x));
	return(wrong_type_argument_n(sc, sc->LOGIOR, position_of(x, args), car(x), T_INTEGER));
      }
    else result |= s7_integer(car(x));

  return(make_integer(sc, result));
}


static s7_pointer g_logxor(s7_scheme *sc, s7_pointer args)
{
  #define H_logxor "(logxor int ...) returns the bitwise XOR of its integer arguments (the bits that are on in an odd number of the arguments)"
  s7_Int result = 0;
  s7_pointer x;

  for (x = args; is_not_null(x); x = cdr(x))
    if (!s7_is_integer(car(x)))
      {
	check_method(sc, car(x), sc->LOGXOR, cons(sc, make_integer(sc, result), x));
	return(wrong_type_argument_n(sc, sc->LOGXOR, position_of(x, args), car(x), T_INTEGER));
      }
    else result ^= s7_integer(car(x));

  return(make_integer(sc, result));
}


static s7_pointer g_logand(s7_scheme *sc, s7_pointer args)
{
  #define H_logand "(logand int ...) returns the bitwise AND of its integer arguments (the bits that are on in every argument)"
  s7_Int result = -1;
  s7_pointer x;

  for (x = args; is_not_null(x); x = cdr(x))
    if (!s7_is_integer(car(x)))
      {
	check_method(sc, car(x), sc->LOGAND, cons(sc, make_integer(sc, result), x));
	return(wrong_type_argument_n(sc, sc->LOGAND, position_of(x, args), car(x), T_INTEGER));
      }
    else result &= s7_integer(car(x));

  return(make_integer(sc, result));
}


static s7_pointer g_lognot(s7_scheme *sc, s7_pointer args)
{
  #define H_lognot "(lognot num) returns the bitwise negation (the complement, the bits that are not on) in num: (lognot 0) -> -1"
  if (!s7_is_integer(car(args)))
    {
      check_method(sc, car(args), sc->LOGNOT, args);
      return(simple_wrong_type_argument(sc, sc->LOGNOT, car(args), T_INTEGER));
    }
  return(make_integer(sc, ~s7_integer(car(args))));
}


/* logbit?  CL is (logbitp index int) using 2^index, but that order strikes me as backwards
 *   at least gmp got the arg order right!
 */

static s7_pointer g_logbit(s7_scheme *sc, s7_pointer args)
{
  #define H_logbit "(logbit? int index) returns #t if the index-th bit is on in int, otherwise #f. The argument \
order here follows gmp, and is the opposite of the CL convention.  (logbit? int bit) is the same as (not (zero? (logand int (ash 1 bit))))."

  s7_pointer x, y;
  s7_Int index;      /* index in gmp is mp_bitcnt which is an unsigned long int */

  x = car(args);
  y = cadr(args);

  if (!s7_is_integer(x))
    {
      check_method(sc, x, sc->LOGBIT, args);
      return(wrong_type_argument(sc, sc->LOGBIT, small_int(1), x, T_INTEGER));
    }

  if (!s7_is_integer(y))
    {
      check_method(sc, y, sc->LOGBIT, args);
      return(wrong_type_argument(sc, sc->LOGBIT, small_int(2), y, T_INTEGER));
    }
  index = s7_integer(y);
  if (index < 0)
    return(out_of_range(sc, sc->LOGBIT, small_int(2), y, ITS_NEGATIVE));

#if WITH_GMP
  if (type(x) == T_BIG_INTEGER)
    return(make_boolean(sc, (mpz_tstbit(big_integer(x), index) != 0)));
#endif
  
  if (index >= s7_int_bits)           /* not sure about the >: (logbit? -1 64) ?? */
    return(make_boolean(sc, integer(x) < 0));

  /* :(zero? (logand most-positive-fixnum (ash 1 63)))
   *   -> ash argument 2, 63, is out of range (shift is too large) 
   *   so logbit? has a wider range than the logand/ash shuffle above.
   */

  /* all these long long ints are necessary, else C turns it into an int, gets confused about signs etc */
  return(make_boolean(sc, ((((long long int)(1LL << (long long int)index)) & (long long int)integer(x)) != 0)));
}


static s7_pointer g_ash(s7_scheme *sc, s7_pointer args)
{
  #define H_ash "(ash i1 i2) returns i1 shifted right or left i2 times, i1 << i2, (ash 1 3) -> 8, (ash 8 -3) -> 1"
  s7_Int arg1, arg2;
  s7_pointer x, y;

  x = car(args);
  if (!s7_is_integer(x))
    {
      check_method(sc, x, sc->ASH, args);
      return(wrong_type_argument(sc, sc->ASH, small_int(1), x, T_INTEGER));
    }
  y = cadr(args);
  if (!s7_is_integer(y))
    {
      check_method(sc, y, sc->ASH, args);
      return(wrong_type_argument(sc, sc->ASH, small_int(2), y, T_INTEGER));
    }
  arg1 = s7_integer(x);
  if (arg1 == 0) return(small_int(0));

  arg2 = s7_integer(y);
  if (arg2 >= s7_int_bits)
    return(out_of_range(sc, sc->ASH, small_int(2), cadr(args), ITS_TOO_LARGE));

  if (arg2 < -s7_int_bits)
    {
      if (arg1 < 0)                      /* (ash -31 -100) */
	return(make_integer(sc, -1));
      return(small_int(0));
    }

  /* I can't see any point in protecting this:
   *    (ash 9223372036854775807 1) -> -2
   * but anyone using ash must know something about bits...
   */
  if (arg2 >= 0)
    return(make_integer(sc, arg1 << arg2));
  return(make_integer(sc, arg1 >> -arg2));
}


/* ---------------------------------------- random ---------------------------------------- */

/* random numbers.  The simple version used in clm.c is probably adequate,
 *   but here I'll use Marsaglia's MWC algorithm.
 *     (random num) -> a number (0..num), if num == 0 return 0, use global default state
 *     (random num state) -> same but use this state
 *     (make-random-state seed) -> make a new state
 *     (make-random-state seed type) ??
 *   to save the current seed, use copy
 *   to save it across load, random-state->list and list->random-state.
 *   random-state? returns #t if its arg is one of these guys
 */

static char *print_rng(s7_scheme *sc, void *val)
{
  char *buf;
  s7_rng_t *r = (s7_rng_t *)val;
  buf = (char *)malloc(64 * sizeof(char));
  snprintf(buf, 64, "#<rng %d %d>", (unsigned int)(r->ran_seed), (unsigned int)(r->ran_carry));
  return(buf);
}


static char *print_rng_readably(s7_scheme *sc, void *val)
{
  char *buf;
  s7_rng_t *r = (s7_rng_t *)val;
  buf = (char *)malloc(64 * sizeof(char));
  snprintf(buf, 64, "(make-random-state %d %d)", (unsigned int)(r->ran_seed), (unsigned int)(r->ran_carry));
  return(buf);
}


static void free_rng(void *val)
{
  free(val);
}


static bool equal_rng(void *val1, void *val2)
{
  s7_rng_t *r1 = (s7_rng_t *)val1;
  s7_rng_t *r2 = (s7_rng_t *)val2;

  return((val1 == val2) ||
	 ((r1->ran_seed == r2->ran_seed) &&
	  (r1->ran_carry == r2->ran_carry)));
}


s7_pointer s7_make_random_state(s7_scheme *sc, s7_pointer args)
{
  #define H_make_random_state "(make-random-state seed (carry plausible-default)) returns a new random number state initialized with 'seed'. \
Pass this as the second argument to 'random' to get a repeatable random number sequence:\n\
    (let ((seed (make-random-state 1234))) (random 1.0 seed))"

  s7_rng_t *r;

  if (!(s7_is_integer(car(args))))
    {
      check_method(sc, car(args), sc->MAKE_RANDOM_STATE, args);
      return(wrong_type_argument(sc, sc->MAKE_RANDOM_STATE, small_int(1), car(args), T_INTEGER));
    }
  if (is_null(cdr(args)))
    {
      r = (s7_rng_t *)calloc(1, sizeof(s7_rng_t));
      r->ran_seed = s7_integer(car(args));
      r->ran_carry = 1675393560;  /* should this be dependent on the seed? */
      return(s7_make_object(sc, sc->rng_tag, (void *)r));
    }

  if (!(s7_is_integer(cadr(args))))
    {
      check_method(sc, cadr(args), sc->MAKE_RANDOM_STATE, args);
      return(wrong_type_argument(sc, sc->MAKE_RANDOM_STATE, small_int(2), cadr(args), T_INTEGER));
    }
  r = (s7_rng_t *)calloc(1, sizeof(s7_rng_t));
  r->ran_seed = s7_integer(car(args));
  r->ran_carry = s7_integer(cadr(args));
  return(s7_make_object(sc, sc->rng_tag, (void *)r));
}


static s7_pointer copy_random_state(s7_scheme *sc, s7_pointer obj)
{
  if (c_object_type(obj) == sc->rng_tag)
    {
      s7_rng_t *r, *new_r;
      r = (s7_rng_t *)s7_object_value(obj);
      new_r = (s7_rng_t *)calloc(1, sizeof(s7_rng_t));
      new_r->ran_seed = r->ran_seed;
      new_r->ran_carry = r->ran_carry;
      return(s7_make_object(sc, sc->rng_tag, (void *)new_r));
    }
  /* I can't find a way to copy a gmp random generator */
  return(sc->F);
}


static s7_pointer g_is_random_state(s7_scheme *sc, s7_pointer args)
{
  #define H_is_random_state "(random-state? obj) returns #t if obj is a random-state object (from make-random-state)."
  s7_pointer obj;
  obj = car(args);
  if (is_c_object(obj))
    {
      if (c_object_type(obj) == sc->rng_tag) 
	return(sc->T);
#if WITH_GMP
      if (c_object_type(obj) == sc->big_rng_tag)
	return(sc->T);
#endif      
    }
  check_method(sc, obj, sc->IS_RANDOM_STATE, args);
  return(sc->F);
}


static double next_random(s7_rng_t *r)
{
  /* The multiply-with-carry generator for 32-bit integers: 
   *        x(n)=a*x(n-1) + carry mod 2^32 
   * Choose multiplier a from this list: 
   *   1791398085 1929682203 1683268614 1965537969 1675393560 
   *   1967773755 1517746329 1447497129 1655692410 1606218150 
   *   2051013963 1075433238 1557985959 1781943330 1893513180 
   *   1631296680 2131995753 2083801278 1873196400 1554115554 
   * ( or any 'a' for which both a*2^32-1 and a*2^31-1 are prime) 
   */
  double result;
  unsigned long long int temp;
  #define RAN_MULT 2131995753UL

  temp = r->ran_seed * RAN_MULT + r->ran_carry;
  r->ran_seed = (temp & 0xffffffffUL);
  r->ran_carry = (temp >> 32);
  result = (double)((unsigned int)(r->ran_seed)) / 4294967295.5;
  /* divisor was 2^32-1 = 4294967295.0, but somehow this can round up once in a billion tries? 
   *   do we want the double just less than 2^32?
   */

  /* (let ((mx 0) (mn 1000)) (do ((i 0 (+ i 1))) ((= i 10000)) (let ((val (random 123))) (set! mx (max mx val)) (set! mn (min mn val)))) (list mn mx)) */
  return(result);
}


s7_pointer s7_random_state_to_list(s7_scheme *sc, s7_pointer args)
{
  #define H_random_state_to_list "(random-state->list r) returns the random state object as a list.\
You can later apply make-random-state to this list to continue a random number sequence from any point."

  s7_rng_t *r = NULL;
  if (is_null(args))
    r = sc->default_rng;
  else
    {
      s7_pointer obj;
      obj = car(args);
      if ((!is_c_object(obj)) ||
	  (c_object_type(obj) != sc->rng_tag))
	{
	  check_method(sc, obj, sc->RANDOM_STATE_TO_LIST, args);
	  return(wrong_type_argument_with_type(sc, sc->RANDOM_STATE_TO_LIST, small_int(1), obj, 
					       make_string_wrapper(sc, "a random state as returned by make-random-state")));
	}
	r = (s7_rng_t *)s7_object_value(obj);
    }
  
  if (r)
    return(list_2(sc, make_integer(sc, r->ran_seed), make_integer(sc, r->ran_carry)));
  return(sc->F);
}


void s7_set_default_random_state(s7_scheme *sc, s7_Int seed, s7_Int carry)
{
  if (sc->default_rng) free(sc->default_rng);
  sc->default_rng = (s7_rng_t *)calloc(1, sizeof(s7_rng_t));
  (sc->default_rng)->ran_seed = (unsigned long long)seed;
  (sc->default_rng)->ran_carry = (unsigned long long)carry;
}


s7_Double s7_random(s7_scheme *sc, s7_pointer state)
{
  if (!state)
    return(next_random(sc->default_rng));
  return(next_random((s7_rng_t *)s7_object_value(state)));
}


static s7_pointer g_random(s7_scheme *sc, s7_pointer args)
{
  #define H_random "(random num (state #f)) returns a random number between 0 and num (0 if num=0)."
  s7_pointer num;
  s7_rng_t *r;

  num = car(args);
  if (!s7_is_number(num))
    {
      check_method(sc, num, sc->RANDOM, args);
      return(wrong_type_argument_with_type(sc, sc->RANDOM, small_int(1), num, A_NUMBER));
    }
  if (is_not_null(cdr(args)))
    {
      s7_pointer state;
      state = cadr(args);
      if (!is_c_object(state))
	{
	  check_method(sc, state, sc->RANDOM, args);
	  return(wrong_type_argument_with_type(sc, sc->RANDOM, small_int(2), state, 
					       make_string_wrapper(sc, "a random state as returned by make-random-state")));
	}
      if (c_object_type(state) == sc->rng_tag)
	r = (s7_rng_t *)s7_object_value(state);
      else
	{
#if WITH_GMP
	  if (c_object_type(state) == sc->big_rng_tag)
	    return(big_random(sc, args));
#endif
	  return(wrong_type_argument_with_type(sc, sc->RANDOM, small_int(2), state, 
					       make_string_wrapper(sc, "a random state as returned by make-random-state")));
	}
    }
  else r = sc->default_rng;

  switch (type(num))
    {
    case T_INTEGER:
      return(make_integer(sc, (s7_Int)(integer(num) * next_random(r))));

    case T_RATIO:
      {
	s7_Double x, error;
	s7_Int numer = 0, denom = 1;
	/* the error here needs to take the size of the fraction into account.  Otherwise, if
	 *    error is (say) 1e-6 and the fraction is (say) 9000000/9223372036854775807,
	 *    c_rationalize will always return 0.  But even that isn't foolproof:
	 *    (random 1/562949953421312) -> 1/376367230475000
	 */
	x = fraction(num);
	if ((x < 1.0e-10) && (x > -1.0e-10))
	  {
	    /* 1e-12 is not tight enough: 
	     *    (random 1/2251799813685248) -> 1/2250240579436280
	     *    (random -1/4503599627370496) -> -1/4492889778435526
	     *    (random 1/140737488355328) -> 1/140730223985746
	     *    (random -1/35184372088832) -> -1/35183145492420
	     *    (random -1/70368744177664) -> -1/70366866392738
	     *    (random 1/4398046511104) -> 1/4398033095756
	     *    (random 1/137438953472) -> 1/137438941127
	     */
	    if (numerator(num) < -10)
	      numer = -(s7_Int)(floor(-numerator(num) * next_random(r)));
	    else
	      {
		if (numerator(num) > 10)
		  numer = (s7_Int)floor(numerator(num) * next_random(r));
		else 
		  {
		    long long int diff;
		    numer = numerator(num);
		    diff = S7_LLONG_MAX - denominator(num);
		    if (diff < 100)
		      return(s7_make_ratio(sc, numer, denominator(num)));
		    denom = denominator(num) + (s7_Int)floor(diff * next_random(r));
		    return(s7_make_ratio(sc, numer, denom));
		  }
	      }
	    return(s7_make_ratio(sc, numer, denominator(num)));
	  }
	if ((x < 1e-6) && (x > -1e-6))
	  error = 1e-18;
	else error = 1e-12;
	c_rationalize(x * next_random(r), error, &numer, &denom);
	return(s7_make_ratio(sc, numer, denom));
      }

    case T_REAL:
      return(make_real(sc, real(num) * next_random(r)));

    case T_COMPLEX:
      return(s7_make_complex(sc, real_part(num) * next_random(r), imag_part(num) * next_random(r)));

#if WITH_GMP
    case T_BIG_INTEGER:
    case T_BIG_RATIO:
    case T_BIG_REAL:
    case T_BIG_COMPLEX:
      return(big_random(sc, args));
#endif
    }
  return(sc->F);
}


#if (!WITH_GMP)
static s7_pointer random_ic, random_rc;

static s7_pointer g_random_ic(s7_scheme *sc, s7_pointer args)
{
  return(make_integer(sc, (s7_Int)(integer(car(args)) * next_random(sc->default_rng))));
}

static s7_pointer g_random_rc(s7_scheme *sc, s7_pointer args)
{
  return(make_real(sc, real(car(args)) * next_random(sc->default_rng)));
}

static s7_pointer random_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if (args == 1)
    {
      if (s7_is_integer(cadr(expr)))
	{
	  set_optimize_data(expr, HOP_SAFE_C_C);
	  return(random_ic);
	}
      if ((is_real(cadr(expr))) &&
	  (!is_rational(cadr(expr))))
	{
	  set_optimize_data(expr, HOP_SAFE_C_C);
	  return(random_rc);
	}
    }
  return(f);
}
#endif



/* -------------------------------- characters -------------------------------- */

static s7_pointer g_char_to_integer(s7_scheme *sc, s7_pointer args)
{
  #define H_char_to_integer "(char->integer c) converts the character c to an integer"
  if (!s7_is_character(car(args)))
    {
      check_method(sc, car(args), sc->CHAR_TO_INTEGER, args);
      return(simple_wrong_type_argument(sc, sc->CHAR_TO_INTEGER, car(args), T_CHARACTER));
    }
  return(small_int(character(car(args))));
}


static s7_pointer g_integer_to_char(s7_scheme *sc, s7_pointer args)
{
  #define H_integer_to_char "(integer->char i) converts the non-negative integer i to a character"
  s7_pointer x;
  s7_Int ind;                   /* not int here!  (integer->char (expt 2 32)) -> #\null */
  x = car(args);

  if (!s7_is_integer(x))
    {
      check_method(sc, x, sc->INTEGER_TO_CHAR, args);
      return(simple_wrong_type_argument(sc, sc->INTEGER_TO_CHAR, x, T_INTEGER));
    }
  ind = s7_integer(x);
  if ((ind < 0) || (ind >= NUM_CHARS))
    return(simple_wrong_type_argument_with_type(sc, sc->INTEGER_TO_CHAR, x, make_string_wrapper(sc, "an integer that can represent a character")));

  return(s7_make_character(sc, (unsigned char)ind));
}


static s7_pointer g_char_upcase(s7_scheme *sc, s7_pointer args)
{
  #define H_char_upcase "(char-upcase c) converts the character c to upper case"
  if (!s7_is_character(car(args)))
    {
      check_method(sc, car(args), sc->CHAR_UPCASE, args);
      return(simple_wrong_type_argument(sc, sc->CHAR_UPCASE, car(args), T_CHARACTER));
    }

  return(s7_make_character(sc, upper_character(car(args))));
}


static s7_pointer g_char_downcase(s7_scheme *sc, s7_pointer args)
{
  #define H_char_downcase "(char-downcase c) converts the character c to lower case"
  if (!s7_is_character(car(args)))
    {
      check_method(sc, car(args), sc->CHAR_DOWNCASE, args);
      return(simple_wrong_type_argument(sc, sc->CHAR_DOWNCASE, car(args), T_CHARACTER));
    }
  return(s7_make_character(sc, lower_character(car(args))));
}


static s7_pointer g_is_char_alphabetic(s7_scheme *sc, s7_pointer args)
{
  #define H_is_char_alphabetic "(char-alphabetic? c) returns #t if the character c is alphabetic"
  if (!s7_is_character(car(args)))
    {
      check_method(sc, car(args), sc->IS_CHAR_ALPHABETIC, args);
      return(simple_wrong_type_argument(sc, sc->IS_CHAR_ALPHABETIC, car(args), T_CHARACTER));
    }
  return(make_boolean(sc, is_char_alphabetic(car(args))));

  /* isalpha returns #t for (integer->char 226) and others in that range */
}


static s7_pointer g_is_char_numeric(s7_scheme *sc, s7_pointer args)
{
  #define H_is_char_numeric "(char-numeric? c) returns #t if the character c is a digit"
  if (!s7_is_character(car(args)))
    {
      check_method(sc, car(args), sc->IS_CHAR_NUMERIC, args);
      return(simple_wrong_type_argument(sc, sc->IS_CHAR_NUMERIC, car(args), T_CHARACTER));
    }
  return(make_boolean(sc, is_char_numeric(car(args))));
}


static s7_pointer g_is_char_whitespace(s7_scheme *sc, s7_pointer args)
{
  #define H_is_char_whitespace "(char-whitespace? c) returns #t if the character c is non-printing character"
  if (!s7_is_character(car(args)))
    {
      check_method(sc, car(args), sc->IS_CHAR_WHITESPACE, args);
      return(simple_wrong_type_argument(sc, sc->IS_CHAR_WHITESPACE, car(args), T_CHARACTER));
    }
  return(make_boolean(sc, is_char_whitespace(car(args))));
}


static s7_pointer g_is_char_upper_case(s7_scheme *sc, s7_pointer args)
{
  #define H_is_char_upper_case "(char-upper-case? c) returns #t if the character c is in upper case"
  if (!s7_is_character(car(args)))
    {
      check_method(sc, car(args), sc->IS_CHAR_UPPER_CASE, args);
      return(simple_wrong_type_argument(sc, sc->IS_CHAR_UPPER_CASE, car(args), T_CHARACTER));
    }
  return(make_boolean(sc, is_char_uppercase(car(args))));
}


static s7_pointer g_is_char_lower_case(s7_scheme *sc, s7_pointer args)
{
  #define H_is_char_lower_case "(char-lower-case? c) returns #t if the character c is in lower case"
  if (!s7_is_character(car(args)))
    {
      check_method(sc, car(args), sc->IS_CHAR_LOWER_CASE, args);
      return(simple_wrong_type_argument(sc, sc->IS_CHAR_LOWER_CASE, car(args), T_CHARACTER));
    }
  return(make_boolean(sc, is_char_lowercase(car(args))));
}


static s7_pointer g_is_char(s7_scheme *sc, s7_pointer args)
{
  #define H_is_char "(char? obj) returns #t if obj is a character"
  check_boolean_method(sc, s7_is_character, sc->IS_CHAR, args);
}


s7_pointer s7_make_character(s7_scheme *sc, unsigned int c) 
{
  return(chars[c]);
}


bool s7_is_character(s7_pointer p) 
{ 
  return(type(p) == T_CHARACTER);
}


char s7_character(s7_pointer p)  
{ 
  return(character(p));
}


static int charcmp(unsigned char c1, unsigned char c2)
{
  return((c1 == c2) ? 0 : (c1 < c2) ? -1 : 1);
  /* not tolower here -- the single case is apparently supposed to be upper case
   *   this matters in a case like (char-ci<? #\_ #\e) which Guile and Gauche say is #f
   *   although (char<? #\_ #\e) is #t -- the spec does not say how to interpret this!
   */
}


static bool is_character_via_method(s7_scheme *sc, s7_pointer p)
{
  if (s7_is_character(p))
    return(true);
  if (has_methods(p))
    {
      s7_pointer f;
      f = find_method(sc, find_let(sc, p), sc->IS_CHAR);
      if (f != sc->UNDEFINED)
	return(is_true(sc, s7_apply_function(sc, f, cons(sc, p, sc->NIL))));
    }
  return(false);
}


static s7_pointer g_char_cmp(s7_scheme *sc, s7_pointer args, int val, s7_pointer sym)
{
  s7_pointer x, y;

  y = car(args);
  if (!s7_is_character(y))
    {
      check_method(sc, y, sym, args);
      return(wrong_type_argument_n(sc, sym, 1, y, T_CHARACTER));
    }
  for (x = cdr(args); is_pair(x); x = cdr(x))
    {
      if (!s7_is_character(car(x)))
	{
	  check_method(sc, car(x), sym, cons(sc, y, x));
	  return(wrong_type_argument_n(sc, sym, position_of(x, args), car(x), T_CHARACTER));
	}
      if (charcmp(character(y), character(car(x))) != val)
	{
	  for (y = cdr(x); is_pair(y); y = cdr(y))
	    if (!is_character_via_method(sc, car(y)))
	      return(wrong_type_argument_n(sc, sym, position_of(y, args), car(y), T_CHARACTER));
	  return(sc->F);
	}
      y = car(x);
    }
  return(sc->T);
}


static s7_pointer g_char_cmp_not(s7_scheme *sc, s7_pointer args, int val, s7_pointer sym)
{
  s7_pointer x, y;

  y = car(args);
  if (!s7_is_character(y))
    {
      check_method(sc, y, sym, args);
      return(wrong_type_argument_n(sc, sym, 1, y, T_CHARACTER));
    }
  for (x = cdr(args); is_pair(x); x = cdr(x))
    {
      if (!s7_is_character(car(x)))
	{
	  check_method(sc, car(x), sym, cons(sc, y, x));
	  return(wrong_type_argument_n(sc, sym, position_of(x, args), car(x), T_CHARACTER));
	}
      if (charcmp(character(y), character(car(x))) == val)
	{
	  for (y = cdr(x); is_pair(y); y = cdr(y))
	    if (!is_character_via_method(sc, car(y)))
	      return(wrong_type_argument_n(sc, sym, position_of(y, args), car(y), T_CHARACTER));
	  return(sc->F);
	}
      y = car(x);
    }
  return(sc->T);
}


static s7_pointer g_chars_are_equal(s7_scheme *sc, s7_pointer args)
{
  #define H_chars_are_equal "(char=? char ...) returns #t if all the character arguments are equal"
  s7_pointer x, y;
  
  y = car(args);
  if (!s7_is_character(y))
    {
      check_method(sc, y, sc->CHAR_EQ, args);
      return(wrong_type_argument_n(sc, sc->CHAR_EQ, 1, y, T_CHARACTER));
    }
  for (x = cdr(args); is_pair(x); x = cdr(x))
    {
      if (!s7_is_character(car(x)))
	{
	  check_method(sc, car(x), sc->CHAR_EQ, cons(sc, y, x));
	  return(wrong_type_argument_n(sc, sc->CHAR_EQ, position_of(x, args), car(x), T_CHARACTER));
	}
      if (car(x) != y)
	{
	  for (y = cdr(x); is_pair(y); y = cdr(y))
	    if (!is_character_via_method(sc, car(y)))
	      return(wrong_type_argument_n(sc, sc->CHAR_EQ, position_of(y, args), car(y), T_CHARACTER));
	  return(sc->F);
	}
    }
  return(sc->T);
}	


static s7_pointer g_chars_are_less(s7_scheme *sc, s7_pointer args)
{
  #define H_chars_are_less "(char<? char ...) returns #t if all the character arguments are increasing"
  return(g_char_cmp(sc, args, -1, sc->CHAR_LT));
}	


static s7_pointer g_chars_are_greater(s7_scheme *sc, s7_pointer args)
{
  #define H_chars_are_greater "(char>? char ...) returns #t if all the character arguments are decreasing"
  return(g_char_cmp(sc, args, 1, sc->CHAR_GT));
}


static s7_pointer g_chars_are_geq(s7_scheme *sc, s7_pointer args)
{
  #define H_chars_are_geq "(char>=? char ...) returns #t if all the character arguments are equal or decreasing"
  return(g_char_cmp_not(sc, args, -1, sc->CHAR_GEQ));
}	


static s7_pointer g_chars_are_leq(s7_scheme *sc, s7_pointer args)
{
  #define H_chars_are_leq "(char<=? char ...) returns #t if all the character arguments are equal or increasing"
  return(g_char_cmp_not(sc, args, 1, sc->CHAR_LEQ));
}


static s7_pointer char_equal_s_ic, char_equal_2;
static s7_pointer g_char_equal_s_ic(s7_scheme *sc, s7_pointer args)
{
  s7_pointer c;
  c = find_symbol_checked(sc, car(args));
  if (c == cadr(args))
    return(sc->T);
  if (!s7_is_character(c))
    {
      check_method(sc, c, sc->CHAR_EQ, list_2(sc, c, cadr(args)));
      return(wrong_type_argument(sc, sc->CHAR_EQ, small_int(1), c, T_CHARACTER));
    }
  return(sc->F);
}

static s7_pointer g_char_equal_2(s7_scheme *sc, s7_pointer args)
{
  if (!s7_is_character(car(args)))
    {
      check_method(sc, car(args), sc->CHAR_EQ, args);
      return(wrong_type_argument(sc, sc->CHAR_EQ, small_int(1), car(args), T_CHARACTER));
    }
  if (car(args) == cadr(args))
    return(sc->T);
  if (!s7_is_character(cadr(args)))
    {
      check_method(sc, cadr(args), sc->CHAR_EQ, args);
      return(wrong_type_argument(sc, sc->CHAR_EQ, small_int(2), cadr(args), T_CHARACTER));
    }
  return(sc->F);
}


static s7_pointer char_less_s_ic, char_less_2;
static s7_pointer g_char_less_s_ic(s7_scheme *sc, s7_pointer args)
{
  if (!s7_is_character(car(args)))
    {
      check_method(sc, car(args), sc->CHAR_LT, args);
      return(wrong_type_argument(sc, sc->CHAR_LT, small_int(1), car(args), T_CHARACTER));
    }
  return(make_boolean(sc, character(car(args)) < character(cadr(args))));
}

static s7_pointer g_char_less_2(s7_scheme *sc, s7_pointer args)
{
  if (!s7_is_character(car(args)))
    {
      check_method(sc, car(args), sc->CHAR_LT, args);
      return(wrong_type_argument(sc, sc->CHAR_LT, small_int(1), car(args), T_CHARACTER));
    }
  if (!s7_is_character(cadr(args)))
    {
      check_method(sc, cadr(args), sc->CHAR_LT, args);
      return(wrong_type_argument(sc, sc->CHAR_LT, small_int(2), cadr(args), T_CHARACTER));
    }
  return(make_boolean(sc, character(car(args)) < character(cadr(args))));
}


static s7_pointer char_greater_s_ic, char_greater_2;
static s7_pointer g_char_greater_s_ic(s7_scheme *sc, s7_pointer args)
{
  if (!s7_is_character(car(args)))
    {
      check_method(sc, car(args), sc->CHAR_GT, args);
      return(wrong_type_argument(sc, sc->CHAR_GT, small_int(1), car(args), T_CHARACTER));
    }
  return(make_boolean(sc, character(car(args)) > character(cadr(args))));
}

static s7_pointer g_char_greater_2(s7_scheme *sc, s7_pointer args)
{
  if (!s7_is_character(car(args)))
    {
      check_method(sc, car(args), sc->CHAR_GT, args);
      return(wrong_type_argument(sc, sc->CHAR_GT, small_int(1), car(args), T_CHARACTER));
    }
  if (!s7_is_character(cadr(args)))
    {
      check_method(sc, cadr(args), sc->CHAR_GT, args);
      return(wrong_type_argument(sc, sc->CHAR_GT, small_int(2), cadr(args), T_CHARACTER));
    }
  return(make_boolean(sc, character(car(args)) > character(cadr(args))));
}


static s7_pointer char_geq_s_ic, char_geq_2;
static s7_pointer g_char_geq_s_ic(s7_scheme *sc, s7_pointer args)
{
  if (!s7_is_character(car(args)))
    {
      check_method(sc, car(args), sc->CHAR_GEQ, args);
      return(wrong_type_argument(sc, sc->CHAR_GEQ, small_int(1), car(args), T_CHARACTER));
    }
  return(make_boolean(sc, character(car(args)) >= character(cadr(args))));
}

static s7_pointer g_char_geq_2(s7_scheme *sc, s7_pointer args)
{
  if (!s7_is_character(car(args)))
    {
      check_method(sc, car(args), sc->CHAR_GEQ, args);
      return(wrong_type_argument(sc, sc->CHAR_GEQ, small_int(1), car(args), T_CHARACTER));
    }
  if (!s7_is_character(cadr(args)))
    {
      check_method(sc, cadr(args), sc->CHAR_GEQ, args);
      return(wrong_type_argument(sc, sc->CHAR_GEQ, small_int(2), cadr(args), T_CHARACTER));
    }
  return(make_boolean(sc, character(car(args)) >= character(cadr(args))));
}


static s7_pointer char_leq_s_ic, char_leq_2;
static s7_pointer g_char_leq_s_ic(s7_scheme *sc, s7_pointer args)
{
  if (!s7_is_character(car(args)))
    {
      check_method(sc, car(args), sc->CHAR_LEQ, args);
      return(wrong_type_argument(sc, sc->CHAR_LEQ, small_int(1), car(args), T_CHARACTER));
    }
  return(make_boolean(sc, character(car(args)) <= character(cadr(args))));
}

static s7_pointer g_char_leq_2(s7_scheme *sc, s7_pointer args)
{
  if (!s7_is_character(car(args)))
    {
      check_method(sc, car(args), sc->CHAR_LEQ, args);
      return(wrong_type_argument(sc, sc->CHAR_LEQ, small_int(1), car(args), T_CHARACTER));
    }
  if (!s7_is_character(cadr(args)))
    {
      check_method(sc, cadr(args), sc->CHAR_LEQ, args);
      return(wrong_type_argument(sc, sc->CHAR_LEQ, small_int(2), cadr(args), T_CHARACTER));
    }
  return(make_boolean(sc, character(car(args)) <= character(cadr(args))));
}


#if (!WITH_PURE_S7)
static s7_pointer g_char_cmp_ci(s7_scheme *sc, s7_pointer args, int val, s7_pointer sym)
{
  s7_pointer x, y;

  y = car(args);
  if (!s7_is_character(y))
    {
      check_method(sc, y, sym, args);
      return(wrong_type_argument_n(sc, sym, 1, y, T_CHARACTER));
    }
  for (x = cdr(args); is_pair(x); x = cdr(x))
    {
      if (!s7_is_character(car(x)))
	{
	  check_method(sc, car(x), sym, cons(sc, y, x));
	  return(wrong_type_argument_n(sc, sym, position_of(x, args), car(x), T_CHARACTER));
	}
      if (charcmp(upper_character(y), upper_character(car(x))) != val)
	{
	  for (y = cdr(x); is_pair(y); y = cdr(y))
	    if (!is_character_via_method(sc, car(y)))
	      return(wrong_type_argument_n(sc, sym, position_of(y, args), car(y), T_CHARACTER));
	  return(sc->F);
	}
      y = car(x);
    }
  return(sc->T);
}


static s7_pointer g_char_cmp_ci_not(s7_scheme *sc, s7_pointer args, int val, s7_pointer sym)
{
  s7_pointer x, y;

  y = car(args);
  if (!s7_is_character(y))
    {
      check_method(sc, y, sym, args);
      return(wrong_type_argument_n(sc, sym, 1, y, T_CHARACTER));
    }
  for (x = cdr(args); is_pair(x); x = cdr(x))
    {
      if (!s7_is_character(car(x)))
	{
	  check_method(sc, car(x), sym, cons(sc, y, x));
	  return(wrong_type_argument_n(sc, sym, position_of(x, args), car(x), T_CHARACTER));
	}
      if (charcmp(upper_character(y), upper_character(car(x))) == val)
	{
	  for (y = cdr(x); is_pair(y); y = cdr(y))
	    if (!is_character_via_method(sc, car(y)))
	      return(wrong_type_argument_n(sc, sym, position_of(y, args), car(y), T_CHARACTER));
	  return(sc->F);
	}
      y = car(x);
    }
  return(sc->T);
}


static s7_pointer g_chars_are_ci_equal(s7_scheme *sc, s7_pointer args)
{
  #define H_chars_are_ci_equal "(char-ci=? char ...) returns #t if all the character arguments are equal, ignoring case"
  return(g_char_cmp_ci(sc, args, 0, sc->CHAR_CI_EQ));
}


static s7_pointer g_chars_are_ci_less(s7_scheme *sc, s7_pointer args)
{
  #define H_chars_are_ci_less "(char-ci<? char ...) returns #t if all the character arguments are increasing, ignoring case"
  return(g_char_cmp_ci(sc, args, -1, sc->CHAR_CI_LT));
}	


static s7_pointer g_chars_are_ci_greater(s7_scheme *sc, s7_pointer args)
{
  #define H_chars_are_ci_greater "(char-ci>? char ...) returns #t if all the character arguments are decreasing, ignoring case"
  return(g_char_cmp_ci(sc, args, 1, sc->CHAR_CI_GT));
}	


static s7_pointer g_chars_are_ci_geq(s7_scheme *sc, s7_pointer args)
{
  #define H_chars_are_ci_geq "(char-ci>=? char ...) returns #t if all the character arguments are equal or decreasing, ignoring case"
  return(g_char_cmp_ci_not(sc, args, -1, sc->CHAR_CI_GEQ));
}


static s7_pointer g_chars_are_ci_leq(s7_scheme *sc, s7_pointer args)
{
  #define H_chars_are_ci_leq "(char-ci<=? char ...) returns #t if all the character arguments are equal or increasing, ignoring case"
  return(g_char_cmp_ci_not(sc, args, 1, sc->CHAR_CI_LEQ));
}


static s7_pointer char_ci_equal_s_ic, char_ci_equal_2;
static s7_pointer g_char_ci_equal_s_ic(s7_scheme *sc, s7_pointer args)
{
  if (!s7_is_character(car(args)))
    {
      check_method(sc, car(args), sc->CHAR_CI_EQ, args);
      return(wrong_type_argument(sc, sc->CHAR_CI_EQ, small_int(1), car(args), T_CHARACTER));
    }
  return(make_boolean(sc, upper_character(car(args)) == upper_character(cadr(args))));
}

static s7_pointer g_char_ci_equal_2(s7_scheme *sc, s7_pointer args)
{
  if (!s7_is_character(car(args)))
    {
      check_method(sc, car(args), sc->CHAR_CI_EQ, args);
      return(wrong_type_argument(sc, sc->CHAR_CI_EQ, small_int(1), car(args), T_CHARACTER));
    }
  if (!s7_is_character(cadr(args)))
    {
      check_method(sc, cadr(args), sc->CHAR_CI_EQ, args);
      return(wrong_type_argument(sc, sc->CHAR_CI_EQ, small_int(2), cadr(args), T_CHARACTER));
    }
  return(make_boolean(sc, upper_character(car(args)) == upper_character(cadr(args))));
}


static s7_pointer char_ci_less_s_ic, char_ci_less_2;
static s7_pointer g_char_ci_less_s_ic(s7_scheme *sc, s7_pointer args)
{
  if (!s7_is_character(car(args)))
    {
      check_method(sc, car(args), sc->CHAR_CI_LT, args);
      return(wrong_type_argument(sc, sc->CHAR_CI_LT, small_int(1), car(args), T_CHARACTER));
    }
  return(make_boolean(sc, upper_character(car(args)) < upper_character(cadr(args))));
}

static s7_pointer g_char_ci_less_2(s7_scheme *sc, s7_pointer args)
{
  if (!s7_is_character(car(args)))
    {
      check_method(sc, car(args), sc->CHAR_CI_LT, args);
      return(wrong_type_argument(sc, sc->CHAR_CI_LT, small_int(1), car(args), T_CHARACTER));
    }
  if (!s7_is_character(cadr(args)))
    {
      check_method(sc, cadr(args), sc->CHAR_CI_LT, args);
      return(wrong_type_argument(sc, sc->CHAR_CI_LT, small_int(2), cadr(args), T_CHARACTER));
    }
  return(make_boolean(sc, upper_character(car(args)) < upper_character(cadr(args))));
}


static s7_pointer char_ci_greater_s_ic, char_ci_greater_2;
static s7_pointer g_char_ci_greater_s_ic(s7_scheme *sc, s7_pointer args)
{
  if (!s7_is_character(car(args)))
    {
      check_method(sc, car(args), sc->CHAR_CI_GT, args);
      return(wrong_type_argument(sc, sc->CHAR_CI_GT, small_int(1), car(args), T_CHARACTER));
    }
  return(make_boolean(sc, upper_character(car(args)) > upper_character(cadr(args))));
}

static s7_pointer g_char_ci_greater_2(s7_scheme *sc, s7_pointer args)
{
  if (!s7_is_character(car(args)))
    {
      check_method(sc, car(args), sc->CHAR_CI_GT, args);
      return(wrong_type_argument(sc, sc->CHAR_CI_GT, small_int(1), car(args), T_CHARACTER));
    }
  if (!s7_is_character(cadr(args)))
    {
      check_method(sc, cadr(args), sc->CHAR_CI_GT, args);
      return(wrong_type_argument(sc, sc->CHAR_CI_GT, small_int(2), cadr(args), T_CHARACTER));
    }
  return(make_boolean(sc, upper_character(car(args)) > upper_character(cadr(args))));
}


static s7_pointer char_ci_geq_s_ic, char_ci_geq_2;
static s7_pointer g_char_ci_geq_s_ic(s7_scheme *sc, s7_pointer args)
{
  if (!s7_is_character(car(args)))
    {
      check_method(sc, car(args), sc->CHAR_CI_GEQ, args);
      return(wrong_type_argument(sc, sc->CHAR_CI_GEQ, small_int(1), car(args), T_CHARACTER));
    }
  return(make_boolean(sc, upper_character(car(args)) >= upper_character(cadr(args))));
}

static s7_pointer g_char_ci_geq_2(s7_scheme *sc, s7_pointer args)
{
  if (!s7_is_character(car(args)))
    {
      check_method(sc, car(args), sc->CHAR_CI_GEQ, args);
      return(wrong_type_argument(sc, sc->CHAR_CI_GEQ, small_int(1), car(args), T_CHARACTER));
    }
  if (!s7_is_character(cadr(args)))
    {
      check_method(sc, cadr(args), sc->CHAR_CI_GEQ, args);
      return(wrong_type_argument(sc, sc->CHAR_CI_GEQ, small_int(2), cadr(args), T_CHARACTER));
    }
  return(make_boolean(sc, upper_character(car(args)) >= upper_character(cadr(args))));
}


static s7_pointer char_ci_leq_s_ic, char_ci_leq_2;
static s7_pointer g_char_ci_leq_s_ic(s7_scheme *sc, s7_pointer args)
{
  if (!s7_is_character(car(args)))
    {
      check_method(sc, car(args), sc->CHAR_CI_LEQ, args);
      return(wrong_type_argument(sc, sc->CHAR_CI_LEQ, small_int(1), car(args), T_CHARACTER));
    }
  return(make_boolean(sc, upper_character(car(args)) <= upper_character(cadr(args))));
}

static s7_pointer g_char_ci_leq_2(s7_scheme *sc, s7_pointer args)
{
  if (!s7_is_character(car(args)))
    {
      check_method(sc, car(args), sc->CHAR_CI_LEQ, args);
      return(wrong_type_argument(sc, sc->CHAR_CI_LEQ, small_int(1), car(args), T_CHARACTER));
    }
  if (!s7_is_character(cadr(args)))
    {
      check_method(sc, cadr(args), sc->CHAR_CI_LEQ, args);
      return(wrong_type_argument(sc, sc->CHAR_CI_LEQ, small_int(2), cadr(args), T_CHARACTER));
    }
  return(make_boolean(sc, upper_character(car(args)) <= upper_character(cadr(args))));
}
#endif /* not pure s7 */


static s7_pointer g_char_position(s7_scheme *sc, s7_pointer args)
{
  #define H_char_position "(char-position char-or-str str (start 0)) returns the position of the first occurrence of char in str, or #f"
  const char *porig, *p, *pset;
  s7_Int start, pos, len; /* not "int" because start arg might be most-negative-fixnum */
  s7_pointer arg1, arg2;

  arg1 = car(args);
  if ((!s7_is_character(arg1)) &&
      (!is_string(arg1)))
    {
      check_method(sc, arg1, sc->CHAR_POSITION, args);
      return(wrong_type_argument(sc, sc->CHAR_POSITION, small_int(1), arg1, T_CHARACTER));
    }
    
  arg2 = cadr(args);
  if (!is_string(arg2))
    {
      check_method(sc, arg2, sc->CHAR_POSITION, args);
      return(wrong_type_argument(sc, sc->CHAR_POSITION, small_int(2), arg2, T_STRING));
    }
  porig = string_value(arg2);
  len = string_length(arg2);

  if (is_pair(cddr(args)))
    {
      s7_pointer arg3;
      arg3 = caddr(args);
      if (!s7_is_integer(arg3))
	{
	  s7_pointer p;
	  if (!s7_is_integer(p = check_values(sc, arg3, cddr(args))))
	    {
	      check_method(sc, arg3, sc->CHAR_POSITION, args);
	      return(wrong_type_argument(sc, sc->CHAR_POSITION, small_int(3), arg3, T_INTEGER));
	    }
	  else arg3 = p;
	}
      start = s7_integer(arg3);
      if (start < 0)
	return(wrong_type_argument_with_type(sc, sc->CHAR_POSITION, small_int(3), arg3, A_NON_NEGATIVE_INTEGER));
    }
  else start = 0;
  if (start >= len) return(sc->F);

  if (s7_is_character(arg1))
    {
      char c;
      c = character(arg1);
      p = strchr((const char *)(porig + start), (int)c); /* use strchrnul in Gnu C to catch embedded null case */
      if (p)
	return(make_integer(sc, p - porig));
      return(sc->F);
    }
  
  if (string_length(arg1) == 0)
    return(sc->F);
  pset = string_value(arg1);

  pos = strcspn((const char *)(porig + start), (const char *)pset);
  if ((pos + start) < len)
    return(make_integer(sc, pos + start));

  /* but if the string has an embedded null, we can get erroneous results here --
   *   perhaps check for null at pos+start?  What about a searched-for string that
   *   also has embedded nulls?  
   *
   * The embedded nulls are for bytevector usages, where presumably you're not talking
   *   about chars and strings, so I think I'll ignore these cases.  In unicode, you'd
   *   want to use unicode-aware searchers, so that also is irrelevant.
   */
  return(sc->F);
}


static s7_pointer char_position_cs;
static s7_pointer g_char_position_cs(s7_scheme *sc, s7_pointer args)
{
  /* assume char arg1, no start */
  const char *porig, *p;
  char c;
  s7_pointer arg2;

  c = character(car(args));
  arg2 = cadr(args);

  if (!is_string(arg2))
    {
      check_method(sc, arg2, sc->CHAR_POSITION, args);
      return(wrong_type_argument(sc, sc->CHAR_POSITION, small_int(2), arg2, T_STRING));
    }
  if (string_length(arg2) == 0) return(sc->F);
  porig = string_value(arg2);

  p = strchr(porig, (int)c);
  if (p)
    return(make_integer(sc, p - porig));
  return(sc->F);
}


static s7_pointer g_string_position(s7_scheme *sc, s7_pointer args)
{
  #define H_string_position "(string-position str1 str2 (start 0)) returns the starting position of str1 in str2 or #f"
  const char *s1, *s2, *p2;
  s7_Int start = 0;
  s7_pointer s1p, s2p;

  s1p = car(args);
  if (!is_string(s1p))
    {
      check_method(sc, s1p, sc->STRING_POSITION, args);
      return(wrong_type_argument(sc, sc->STRING_POSITION, small_int(1), s1p, T_STRING));
    }

  s2p = cadr(args);
  if (!is_string(s2p))
    {
      check_method(sc, s2p, sc->STRING_POSITION, args);
      return(wrong_type_argument(sc, sc->STRING_POSITION, small_int(2), s2p, T_STRING));
    }

  if (is_pair(cddr(args)))
    {
      s7_pointer arg3;
      arg3 = caddr(args);
      if (!s7_is_integer(arg3))
	{
	  s7_pointer p;
	  if (!s7_is_integer(p = check_values(sc, arg3, cddr(args))))
	    {
	      check_method(sc, arg3, sc->STRING_POSITION, args);
	      return(wrong_type_argument(sc, sc->STRING_POSITION, small_int(3), arg3, T_INTEGER));
	    }
	  else arg3 = p;
	}      
      start = s7_integer(arg3);
      if (start < 0)
	return(wrong_type_argument_with_type(sc, sc->STRING_POSITION, small_int(3), arg3, A_NON_NEGATIVE_INTEGER));
    }
  
  if (string_length(s1p) == 0)
    return(sc->F);
  s1 = string_value(s1p);
  s2 = string_value(s2p);
  if (start >= string_length(s2p))
    return(sc->F);

  p2 = strstr((const char *)(s2 + start), s1);
  if (!p2) return(sc->F);
  return(make_integer(sc, p2 - s2));
}



/* -------------------------------- strings -------------------------------- */

s7_pointer s7_make_string_with_length(s7_scheme *sc, const char *str, int len) 
{
  s7_pointer x;
  NEW_CELL(sc, x);
  set_type(x, T_STRING | T_SAFE_PROCEDURE); /* should this follow the malloc? */
  string_value(x) = (char *)malloc((len + 1) * sizeof(char)); 
  if (len != 0)                                             /* memcpy can segfault if string_value(x) is NULL */
    memcpy((void *)string_value(x), (void *)str, len + 1);
  else string_value(x)[0] = 0;
  string_length(x) = len;
  string_hash(x) = 0;
  string_needs_free(x) = true;
  add_string(sc, x);
  return(x);
}


static s7_pointer s7_make_terminated_string_with_length(s7_scheme *sc, const char *str, int len) 
{
  s7_pointer x;
  NEW_CELL(sc, x);
  set_type(x, T_STRING | T_SAFE_PROCEDURE); /* should this follow the malloc? */
  string_value(x) = (char *)malloc((len + 1) * sizeof(char)); 
  if (len != 0)                                             /* memcpy can segfault if string_value(x) is NULL */
    memcpy((void *)string_value(x), (void *)str, len);
  string_value(x)[len] = 0;
  string_length(x) = len;
  string_hash(x) = 0;
  string_needs_free(x) = true;
  add_string(sc, x);
  return(x);
}


static s7_pointer make_string_uncopied_with_length(s7_scheme *sc, char *str, int len) 
{
  s7_pointer x;
  NEW_CELL(sc, x);
  set_type(x, T_STRING | T_SAFE_PROCEDURE);
  string_value(x) = str;
  string_length(x) = len;
  string_hash(x) = 0;
  string_needs_free(x) = true;
  add_string(sc, x);
  return(x);
}


static s7_pointer make_string_wrapper(s7_scheme *sc, const char *str) /* here "protected" = immutable, const char*, I think */
{
  s7_pointer x;
  NEW_CELL(sc, x);
  set_type(x, T_STRING | T_IMMUTABLE | T_SAFE_PROCEDURE);
  string_value(x) = (char *)str;
  string_length(x) = safe_strlen(str);
  string_hash(x) = 0;
  string_needs_free(x) = false;
  return(x);
}


static s7_pointer make_empty_string(s7_scheme *sc, int len, char fill) 
{
  s7_pointer x;
  NEW_CELL(sc, x);
  set_type(x, T_STRING);
  
  if (fill == 0)
    string_value(x) = (char *)calloc((len + 1), sizeof(char));
  else
    {
      string_value(x) = (char *)malloc((len + 1) * sizeof(char));
      memset((void *)(string_value(x)), fill, len);
    }
  string_value(x)[len] = 0;
  string_hash(x) = 0;
  string_length(x) = len;
  string_needs_free(x) = true;
  add_string(sc, x);
  return(x);
}


s7_pointer s7_make_string(s7_scheme *sc, const char *str) 
{
  if (str)
    return(s7_make_string_with_length(sc, str, safe_strlen(str)));
  return(make_empty_string(sc, 0, 0));
}


static char *make_permanent_string(const char *str)
{
  char *x;
  int len;
  len = safe_strlen(str);
  x = (char *)malloc((len + 1) * sizeof(char)); 
  memcpy((void *)x, (void *)str, len);
  x[len] = 0;
  return(x);
}


s7_pointer s7_make_permanent_string(const char *str) 
{
  /* for the symbol table which is never GC'd */
  s7_pointer x;
  x = alloc_pointer();
  heap_location(x) = NOT_IN_HEAP;
  set_type(x, T_STRING | T_IMMUTABLE);
  if (str)
    {
      unsigned int len;
      len = safe_strlen(str);
      string_length(x) = len;
      string_value(x) = (char *)malloc((len + 1) * sizeof(char)); 
      memcpy((void *)string_value(x), (void *)str, len);
      string_value(x)[len] = 0;
    }
  else 
    {
      string_value(x) = NULL;
      string_length(x) = 0;
    }
  string_hash(x) = 0;
  string_needs_free(x) = false;
  return(x);
}


static s7_pointer make_permanent_string_with_length_and_hash(const char *str, unsigned int len, unsigned int hash)
{
  /* for the symbol table which is never GC'd */
  s7_pointer x;
  x = alloc_pointer();
  heap_location(x) = NOT_IN_HEAP;
  set_type(x, T_STRING | T_IMMUTABLE);
  string_length(x) = len;
  string_value(x) = (char *)malloc((len + 1) * sizeof(char)); 
  memcpy((void *)string_value(x), (void *)str, len);
  string_value(x)[len] = 0;
  string_hash(x) = hash;
  string_needs_free(x) = false;
  return(x);
}


static s7_pointer make_temporary_string(s7_scheme *sc, const char *str, int len) 
{
  if (!sc->tmp_str)
    {
      sc->tmp_str = alloc_pointer();
      heap_location(sc->tmp_str) = NOT_IN_HEAP;
      set_type(sc->tmp_str, T_STRING | T_SAFE_PROCEDURE);
      string_hash(sc->tmp_str) = 0;
      string_needs_free(sc->tmp_str) = false;
    }
  if (len >= sc->tmp_str_size)
    {
      if (len == 0)
	sc->tmp_str_size = 8;
      else sc->tmp_str_size = 2 * len;
      if (!sc->tmp_str_chars)
	sc->tmp_str_chars = (char *)malloc(sc->tmp_str_size * sizeof(char));
      else sc->tmp_str_chars = (char *)realloc(sc->tmp_str_chars, sc->tmp_str_size * sizeof(char));
      string_value(sc->tmp_str) = sc->tmp_str_chars;
    }

  string_length(sc->tmp_str) = len;
  if (len > 0)
    memmove((void *)sc->tmp_str_chars, (void *)str, len); /* not memcpy because str might be a temp string (i.e. sc->tmp_str_chars -> itself) */
  sc->tmp_str_chars[len] = 0;
  return(sc->tmp_str);
}


bool s7_is_string(s7_pointer p)
{
  return(is_string(p));
}


const char *s7_string(s7_pointer p) 
{ 
  return(string_value(p));
}


static s7_pointer g_is_string(s7_scheme *sc, s7_pointer args)
{
  #define H_is_string "(string? obj) returns #t if obj is a string"
  check_boolean_method(sc, is_string, sc->IS_STRING, args);
}


#define MAX_STRING_LENGTH 1073741824

static s7_pointer g_make_string(s7_scheme *sc, s7_pointer args)
{
  #define H_make_string "(make-string len (val #\\space)) makes a string of length len filled with the character val (default: space)"
  s7_pointer n;
  s7_Int len;
  char fill = ' ';
  
  n = car(args);
  if (!s7_is_integer(n))
    {
      check_two_methods(sc, n, sc->MAKE_STRING, sc->MAKE_BYTEVECTOR, args);
      return(wrong_type_argument(sc, sc->MAKE_STRING, small_int(1), n, T_INTEGER));
    }

  len = s7_integer(n);
  if (len < 0)
    return(out_of_range(sc, sc->MAKE_STRING, small_int(1), n, ITS_NEGATIVE));
  if (len > MAX_STRING_LENGTH)
    return(out_of_range(sc, sc->MAKE_STRING, small_int(1), n, ITS_TOO_LARGE));

  if (is_not_null(cdr(args))) 
    {
      if (!s7_is_character(cadr(args)))
	{
	  check_method(sc, cadr(args), sc->MAKE_STRING, args);
	  return(wrong_type_argument(sc, sc->MAKE_STRING, small_int(2), cadr(args), T_CHARACTER));
	}
      fill = s7_character(cadr(args));
    }
  return(make_empty_string(sc, (int)len, fill));
}


static s7_pointer g_string_length(s7_scheme *sc, s7_pointer args)
{
  #define H_string_length "(string-length str) returns the length of the string str"
  s7_pointer p;
  p = car(args);
  if (!is_string(p))
    {
      check_method(sc, p, sc->STRING_LENGTH, args);
      return(simple_wrong_type_argument(sc, sc->STRING_LENGTH, p, T_STRING));
    }
  return(make_integer(sc, string_length(p)));
}


static s7_pointer g_string_downcase(s7_scheme *sc, s7_pointer args)
{
  #define H_string_downcase "(string-downcase str) returns the lower case version of str."
  s7_pointer p, newstr;
  int i, len;
  unsigned char *nstr, *ostr;

  p = car(args);
  if (!is_string(p))
    {
      check_method(sc, p, sc->STRING_DOWNCASE, args);
      return(simple_wrong_type_argument(sc, sc->STRING_DOWNCASE, p, T_STRING));
    }

  len = string_length(p);
  newstr = make_empty_string(sc, len + 1, 0); 
  string_length(newstr) = len;

  ostr = (unsigned char *)string_value(p);
  nstr = (unsigned char *)string_value(newstr);
  for (i = 0; i < len; i++)
    nstr[i] = tolower((int)ostr[i]);

  return(newstr);
}


static s7_pointer g_string_upcase(s7_scheme *sc, s7_pointer args)
{
  #define H_string_upcase "(string-upcase str) returns the upper case version of str."
  s7_pointer p, newstr;
  int i, len;
  unsigned char *nstr, *ostr;

  p = car(args);
  if (!is_string(p))
    {
      check_method(sc, p, sc->STRING_UPCASE, args);
      return(simple_wrong_type_argument(sc, sc->STRING_UPCASE, p, T_STRING));
    }

  len = string_length(p);
  newstr = make_empty_string(sc, len + 1, 0); 
  string_length(newstr) = len;

  ostr = (unsigned char *)string_value(p);
  nstr = (unsigned char *)string_value(newstr);
  for (i = 0; i < len; i++)
    nstr[i] = toupper((int)ostr[i]);

  return(newstr);
}


unsigned int s7_string_length(s7_pointer str)
{
  return(string_length(str));
}


static s7_pointer string_ref_1(s7_scheme *sc, s7_pointer strng, s7_pointer index)
{
  char *str;
  s7_Int ind;

  if (!s7_is_integer(index))
    {
      s7_pointer p;
      if (!s7_is_integer(p = check_values(sc, index, cons(sc, index, sc->NIL))))
	{
	  check_method(sc, index, sc->STRING_REF, list_2(sc, strng, index));
	  return(wrong_type_argument(sc, sc->STRING_REF, small_int(2), index, T_INTEGER));
	}
      else index = p;
    }
  ind = s7_integer(index);
  if (ind < 0)
    return(wrong_type_argument_with_type(sc, sc->STRING_REF, small_int(2), index, A_NON_NEGATIVE_INTEGER));
  if (ind >= string_length(strng))
    return(out_of_range(sc, sc->STRING_REF, small_int(2), index, ITS_TOO_LARGE));

  str = string_value(strng);
  return(s7_make_character(sc, ((unsigned char *)str)[ind]));
}


static s7_pointer g_string_ref(s7_scheme *sc, s7_pointer args)
{
  s7_pointer strng, index, p;
  char *str;
  s7_Int ind;

  #define H_string_ref "(string-ref str index) returns the character at the index-th element of the string str"

  strng = car(args);
  if (!is_string(strng))
    {
      check_method(sc, strng, sc->STRING_REF, args);
      return(wrong_type_argument(sc, sc->STRING_REF, small_int(1), strng, T_STRING));
    }
  
  index = cadr(args);
  if (!s7_is_integer(index))
    {
      if (!s7_is_integer(p = check_values(sc, index, cdr(args))))
	{
	  check_method(sc, index, sc->STRING_REF, args); 
	  return(wrong_type_argument(sc, sc->STRING_REF, small_int(2), index, T_INTEGER));
	}
      else index = p;
    }
  ind = s7_integer(index);
  if (ind < 0)
    return(wrong_type_argument_with_type(sc, sc->STRING_REF, small_int(2), index, A_NON_NEGATIVE_INTEGER));
  if (ind >= string_length(strng))
    return(out_of_range(sc, sc->STRING_REF, small_int(2), index, ITS_TOO_LARGE));

  str = string_value(strng);
  return(s7_make_character(sc, ((unsigned char *)str)[ind]));
}


static s7_pointer g_string_set(s7_scheme *sc, s7_pointer args)
{
  #define H_string_set "(string-set! str index chr) sets the index-th element of the string str to the character chr"
  
  s7_pointer x, c, index;
  char *str;
  s7_Int ind;

  x = car(args);
  if (!is_string(x))
    {
      check_method(sc, x, sc->STRING_SET, args);
      return(wrong_type_argument(sc, sc->STRING_SET, small_int(1), x, T_STRING));
    }

  index = cadr(args);
  if (!s7_is_integer(index))
    {
      s7_pointer p;
      if (!s7_is_integer(p = check_values(sc, index, cdr(args))))
	{
	  check_method(sc, index, sc->STRING_SET, args);
	  return(wrong_type_argument(sc, sc->STRING_SET, small_int(2), index, T_INTEGER));
	}
      else index = p;
    }
  ind = s7_integer(index);
  if (ind < 0)
    return(wrong_type_argument_with_type(sc, sc->STRING_SET, small_int(2), index, A_NON_NEGATIVE_INTEGER));
  if (ind >= string_length(x))
    return(out_of_range(sc, sc->STRING_SET, small_int(2), index, ITS_TOO_LARGE));
  str = string_value(x);

  c = caddr(args);
  if (!s7_is_character(c))
    {
      if ((is_bytevector(x)) &&
	  (s7_is_integer(c)))
	{
	  int ic;
	  ic = s7_integer(c);
	  if ((ic < 0) || (ic > 255))
	    return(wrong_type_argument_with_type(sc, sc->STRING_SET, small_int(3), c, AN_UNSIGNED_BYTE));
	  str[ind] = (char)ic;
	  return(c);
	}
      check_method(sc, c, sc->STRING_SET, list_3(sc, x, index, c));
      return(wrong_type_argument(sc, sc->STRING_SET, small_int(3), c, T_CHARACTER));
    }

  str[ind] = (char)s7_character(c);
  return(c);
}


static s7_pointer g_string_append(s7_scheme *sc, s7_pointer args)
{
  #define H_string_append "(string-append str1 ...) appends all its string arguments into one string"
  int len = 0;
  s7_pointer x, newstr;
  char *pos;
  
  if (is_null(args))
    return(s7_make_string_with_length(sc, "", 0));
  
  /* get length for new string */
  for (x = args; is_not_null(x); x = cdr(x)) 
    {
      s7_pointer p;
      p = car(x);
      if (!is_string(p))
	{
	  /* look for string-append and if found, cobble up a plausible intermediate call */
	  if (has_methods(p))
	    {
	      s7_pointer func;
	      func = find_method(sc, find_let(sc, p), sc->STRING_APPEND);
	      if (func != sc->UNDEFINED)
		{
		  s7_pointer y;
		  if (len == 0)
		    return(s7_apply_function(sc, func, args));
		  newstr = make_empty_string(sc, len + 1, 0); 
		  string_length(newstr) = len;
		  for (pos = string_value(newstr), y = args; y != x; pos += string_length(car(y)), y = cdr(y)) 
		    memcpy(pos, string_value(car(y)), string_length(car(y)));
		  return(s7_apply_function(sc, func, cons(sc, newstr, x)));
		}
	    }
	  return(wrong_type_argument_n(sc, sc->STRING_APPEND, position_of(x, args), p, T_STRING));
	}
      len += string_length(p);
    }
  
  /* store the contents of the argument strings into the new string */
  newstr = make_empty_string(sc, len + 1, 0); 
  string_length(newstr) = len;
  for (pos = string_value(newstr), x = args; is_not_null(x); pos += string_length(car(x)), x = cdr(x)) 
    memcpy(pos, string_value(car(x)), string_length(car(x)));
  
  return(newstr);
}


static s7_pointer g_string_copy(s7_scheme *sc, s7_pointer args)
{
  #define H_string_copy "(string-copy str) returns a copy of its string argument"
  s7_pointer p;
  p = car(args);
  if (!is_string(p))
    {
      check_method(sc, p, sc->STRING_COPY, args);
      return(wrong_type_argument(sc, sc->STRING_COPY, small_int(1), p, T_STRING));
    }
  return(s7_make_terminated_string_with_length(sc, string_value(p), string_length(p)));
}


static s7_pointer start_and_end(s7_scheme *sc, s7_pointer caller, s7_pointer fallback,
				s7_pointer start_and_end_args, s7_pointer args, int position, s7_Int *start, s7_Int *end)
{
  /* we assume that *start=0 and *end=length, that end is "exclusive"
   *   return true if the start/end points are not changed.
   */
  s7_pointer pstart, pend, p;
  s7_Int index;

  if (is_null(start_and_end_args))
    return(sc->GC_NIL);

  pstart = car(start_and_end_args);
  if (!s7_is_integer(pstart))
    {
      if (!s7_is_integer(p = check_values(sc, pstart, start_and_end_args)))
	{
	  check_two_methods(sc, pstart, caller, fallback, args);
	  return(wrong_type_argument_n(sc, caller, position, pstart, T_INTEGER));
	}
      else pstart = p;
    }
      
  index = s7_integer(pstart);
  if ((index < 0) ||
      (index > *end)) /* *end == length here */
    return(out_of_range(sc, caller, small_int(position), pstart, (index < 0) ? ITS_NEGATIVE : ITS_TOO_LARGE));
  *start = index;

  if (is_null(cdr(start_and_end_args)))
    return(sc->GC_NIL);

  pend = cadr(start_and_end_args);
  /* if (pend == sc->F) return(sc->GC_NIL); */
  if (!s7_is_integer(pend))
    {
      if (!s7_is_integer(p = check_values(sc, pend, cdr(start_and_end_args))))
	{
	  check_two_methods(sc, pend, caller, fallback, 
			    (position == 2) ? list_3(sc, car(args), pstart, pend) : list_4(sc, car(args), cadr(args), pstart, pend));
	  return(wrong_type_argument_n(sc, caller, position + 1, pend, T_INTEGER));
	}
      else pend = p;
    }
  index = s7_integer(pend);
  if ((index < *start) ||
      (index > *end))
    return(out_of_range(sc, caller, small_int(position + 1), pend, 
			    (index < *start) ? make_string_wrapper(sc, "it is less than the start point") : ITS_TOO_LARGE));
  *end = index;
  return(sc->GC_NIL);
}


static s7_pointer g_substring(s7_scheme *sc, s7_pointer args)
{
  #define H_substring "(substring str start (end (length str))) returns the portion of the string str between start and \
end: (substring \"01234\" 1 2) -> \"1\""
  
  s7_pointer x, str;
  s7_Int start = 0, end;
  int len;
  char *s;
  
  str = car(args);
  if (!is_string(str))
    {
      check_method(sc, str, sc->SUBSTRING, args);
      return(wrong_type_argument(sc, sc->SUBSTRING, small_int(1), str, T_STRING));
    }

  end = string_length(str);
  x = start_and_end(sc, sc->SUBSTRING, NULL, cdr(args), args, 2, &start, &end);
  if (x != sc->GC_NIL) return(x);

  s = string_value(str);
  len = (int)(end - start);
  x = s7_make_string_with_length(sc, (char *)(s + start), len);
  string_value(x)[len] = 0;
  return(x);
}


static s7_pointer substring_to_temp;
static s7_pointer g_substring_to_temp(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x, str;
  s7_Int start = 0, end;
  
  str = car(args);
  if (!is_string(str))
    {
      check_method(sc, str, sc->SUBSTRING, args);
      return(wrong_type_argument(sc, sc->SUBSTRING, small_int(1), str, T_STRING));
    }
  end = string_length(str);
  x = start_and_end(sc, sc->SUBSTRING, NULL, cdr(args), args, 2, &start, &end);
  if (x != sc->GC_NIL) return(x);

  return(make_temporary_string(sc, (const char *)(string_value(str) + start), (int)(end - start)));
}

/* (set! (substring...) ...)? -- might require allocation
 *   to implement substring_uncopied we'd need to make sure the lack of a trailing null can't confuse anyone
 *   this is also the case for read-line.
 *
 * (string=? (substring...) str) or reverse is very common 
 */


static use_write_t write_choice(s7_scheme *sc, s7_pointer arg)
{
  if (arg == sc->F) return(USE_DISPLAY);
  if (arg == sc->T) return(USE_WRITE);
  if (arg == sc->KEY_READABLE) return(USE_READABLE_WRITE);
  return(USE_WRITE_WRONG);
}

#define DONT_USE_DISPLAY(Choice) ((Choice == USE_DISPLAY) ? USE_WRITE : Choice)

static char *s7_object_to_c_string_1(s7_scheme *sc, s7_pointer obj, use_write_t use_write, int *nlen);

static s7_pointer g_object_to_string(s7_scheme *sc, s7_pointer args)
{
  #define H_object_to_string "(object->string obj (write #t)) returns a string representation of obj."
  use_write_t choice;
  char *str;
  s7_pointer obj;
  int len = 0;

  if (is_not_null(cdr(args)))
    {
      choice = write_choice(sc, cadr(args));
      if (choice == USE_WRITE_WRONG)
	{
	  check_method(sc, cadr(args), sc->OBJECT_TO_STRING, args);
	  return(wrong_type_argument(sc, sc->OBJECT_TO_STRING, small_int(2), cadr(args), T_BOOLEAN));
	}
    }
  else choice = USE_WRITE;
  /* can't use s7_object_to_string here anymore because it assumes use_write arg is a boolean
   */
  
  obj = car(args);
  check_method(sc, obj, sc->OBJECT_TO_STRING, args);
  str = s7_object_to_c_string_1(sc, obj, choice, &len);
  if (str)
    return(make_string_uncopied_with_length(sc, str, len));
  return(s7_make_string_with_length(sc, "", 0)); 
}


static int scheme_strcmp(s7_pointer s1, s7_pointer s2)
{
  /* tricky here because str[i] must be treated as unsigned
   *   (string<? (string (integer->char #xf0)) (string (integer->char #x70)))
   * also null or lack thereof does not say anything about the string end
   *   so we have to go by its length.
   */
  int i, len, len1, len2;
  char *str1, *str2;

  len1 = string_length(s1);
  len2 = string_length(s2);
  if (len1 > len2)
    len = len2;
  else len = len1;

  str1 = string_value(s1);
  str2 = string_value(s2);

  for (i = 0; i < len; i++)
    if ((unsigned char)(str1[i]) < (unsigned char )(str2[i]))
      return(-1);
    else
      {
	if ((unsigned char)(str1[i]) > (unsigned char)(str2[i]))
	  return(1);
      }

  if (len1 < len2) 
    return(-1);
  if (len1 > len2)
    return(1);
  return(0);
}


static bool is_string_via_method(s7_scheme *sc, s7_pointer p)
{
  if (s7_is_string(p))
    return(true);
  if (has_methods(p))
    {
      s7_pointer f;
      f = find_method(sc, find_let(sc, p), sc->IS_STRING);
      if (f != sc->UNDEFINED)
	return(is_true(sc, s7_apply_function(sc, f, cons(sc, p, sc->NIL))));
    }
  return(false);
}

static s7_pointer g_string_cmp(s7_scheme *sc, s7_pointer args, int val, s7_pointer sym)
{
  s7_pointer x, y;

  y = car(args);
  if (!is_string(y))
    {
      check_method(sc, y, sym, args);
      return(wrong_type_argument(sc, sym, small_int(1), y, T_STRING));
    }
  
  for (x = cdr(args); is_not_null(x); x = cdr(x))
    {
      if (!is_string(car(x)))
	{
	  check_method(sc, car(x), sym, cons(sc, y, x));
	  return(wrong_type_argument_n(sc, sym, position_of(x, args), car(x), T_STRING));
	}
      if (scheme_strcmp(y, car(x)) != val)
	{
	  for (y = cdr(x); is_pair(y); y = cdr(y))
	    if (!is_string_via_method(sc, car(y)))
	      return(wrong_type_argument_n(sc, sym, position_of(y, args), car(y), T_STRING));
	  return(sc->F);
	}
      y = car(x);
    }
  return(sc->T);
}


static s7_pointer g_string_cmp_not(s7_scheme *sc, s7_pointer args, int val, s7_pointer sym)
{
  s7_pointer x, y;
  
  y = car(args);
  if (!is_string(y))
    {
      check_method(sc, y, sym, args);
      return(wrong_type_argument(sc, sym, small_int(1), y, T_STRING));
    }
  
  for (x = cdr(args); is_not_null(x); x = cdr(x))
    {
      if (!is_string(car(x)))
	{
	  check_method(sc, car(x), sym, cons(sc, y, x));
	  return(wrong_type_argument_n(sc, sym, position_of(x, args), car(x), T_STRING));
	}
      if (scheme_strcmp(y, car(x)) == val)
	{
	  for (y = cdr(x); is_pair(y); y = cdr(y))
	    if (!is_string_via_method(sc, car(y)))
	      return(wrong_type_argument_n(sc, sym, position_of(y, args), car(y), T_STRING));
	  return(sc->F);
	}
      y = car(x);
    }
  return(sc->T);
}


static bool scheme_strings_are_equal(s7_pointer x, s7_pointer y)
{
  char *xstr, *ystr;
  unsigned int i, len;

  len = string_length(x);
  if (string_length(y) != len) 
    return(false);

  xstr = string_value(x);
  ystr = string_value(y);

  for (i = 0; i < len; i++)
    if (xstr[i] != ystr[i])
      return(false);
  return(true);
}


static s7_pointer g_strings_are_equal(s7_scheme *sc, s7_pointer args)
{
  #define H_strings_are_equal "(string=? str ...) returns #t if all the string arguments are equal"

  /* C-based check stops at null, but we can have embedded nulls.
   *   (let ((s1 "1234") (s2 "1245")) (string-set! s1 1 #\null) (string-set! s2 1 #\null) (string=? s1 s2))
   */
  
  s7_pointer x, y;
  unsigned int len;
  bool happy = true;
  char *ystr;
  
  y = car(args);
  if (!is_string(y))
    {
      check_method(sc, y, sc->STRING_EQ, args);
      return(wrong_type_argument(sc, sc->STRING_EQ, small_int(1), y, T_STRING));
    }
  len = string_length(y);
  ystr = string_value(y);

  for (x = cdr(args); is_not_null(x); x = cdr(x)) 
    {
      s7_pointer p;
      p = car(x);
      if (y != p)
	{
	  if (!is_string(p))
	    {
	      check_method(sc, p, sc->STRING_EQ, cons(sc, y, x));
	      return(wrong_type_argument_n(sc, sc->STRING_EQ, position_of(x, args), p, T_STRING));
	    }
	  if (happy)
	    {
	      if (string_length(p) != len)
		happy = false;
	      else
		{
		  unsigned int k;
		  char *xstr;
		  xstr = string_value(p);
		  for (k = 0; k < len; k++)
		    if (ystr[k] != xstr[k])
		      {
			happy = false;
			break;
		      }
		}
	    }
	}
    }

  if (!happy)
    return(sc->F);
  return(sc->T);
}	


static s7_pointer g_strings_are_less(s7_scheme *sc, s7_pointer args)
{
  #define H_strings_are_less "(string<? str ...) returns #t if all the string arguments are increasing"
  return(g_string_cmp(sc, args, -1, sc->STRING_LT));
}	


static s7_pointer g_strings_are_greater(s7_scheme *sc, s7_pointer args)
{
  #define H_strings_are_greater "(string>? str ...) returns #t if all the string arguments are decreasing"
  return(g_string_cmp(sc, args, 1, sc->STRING_GT));
}	


static s7_pointer g_strings_are_geq(s7_scheme *sc, s7_pointer args)
{
  #define H_strings_are_geq "(string>=? str ...) returns #t if all the string arguments are equal or decreasing"
  return(g_string_cmp_not(sc, args, -1, sc->STRING_GEQ));
}	


static s7_pointer g_strings_are_leq(s7_scheme *sc, s7_pointer args)
{
  #define H_strings_are_leq "(string<=? str ...) returns #t if all the string arguments are equal or increasing"
  return(g_string_cmp_not(sc, args, 1, sc->STRING_LEQ));
}	


static s7_pointer string_equal_s_ic, string_equal_2;
static s7_pointer g_string_equal_s_ic(s7_scheme *sc, s7_pointer args)
{
  if (!is_string(car(args)))
    {
      check_method(sc, car(args), sc->STRING_EQ, args);
      return(wrong_type_argument(sc, sc->STRING_EQ, small_int(1), car(args), T_STRING));
    }
  return(make_boolean(sc, scheme_strings_are_equal(car(args), cadr(args))));
}

static s7_pointer g_string_equal_2(s7_scheme *sc, s7_pointer args)
{
  if (!is_string(car(args)))
    {
      check_method(sc, car(args), sc->STRING_EQ, args);
      return(wrong_type_argument(sc, sc->STRING_EQ, small_int(1), car(args), T_STRING));
    }
  if (!is_string(cadr(args)))
    {
      check_method(sc, cadr(args), sc->STRING_EQ, args);
      return(wrong_type_argument(sc, sc->STRING_EQ, small_int(2), cadr(args), T_STRING));
    }
  return(make_boolean(sc, scheme_strings_are_equal(car(args), cadr(args))));
}


static s7_pointer string_less_s_ic, string_less_2;
static s7_pointer g_string_less_s_ic(s7_scheme *sc, s7_pointer args)
{
  if (!is_string(car(args)))
    {
      check_method(sc, car(args), sc->STRING_LT, args);
      return(wrong_type_argument(sc, sc->STRING_LT, small_int(1), car(args), T_STRING));
    }
  return(make_boolean(sc, scheme_strcmp(car(args), cadr(args)) == -1));
}

static s7_pointer g_string_less_2(s7_scheme *sc, s7_pointer args)
{
  if (!is_string(car(args)))
    {
      check_method(sc, car(args), sc->STRING_LT, args);
      return(wrong_type_argument(sc, sc->STRING_LT, small_int(1), car(args), T_STRING));
    }
  if (!is_string(cadr(args)))
    {
      check_method(sc, cadr(args), sc->STRING_LT, args);
      return(wrong_type_argument(sc, sc->STRING_LT, small_int(2), cadr(args), T_STRING));
    }
  return(make_boolean(sc, scheme_strcmp(car(args), cadr(args)) == -1));
}


static s7_pointer string_greater_s_ic, string_greater_2;
static s7_pointer g_string_greater_s_ic(s7_scheme *sc, s7_pointer args)
{
  if (!is_string(car(args)))
    {
      check_method(sc, car(args), sc->STRING_GT, args);
      return(wrong_type_argument(sc, sc->STRING_GT, small_int(1), car(args), T_STRING));
    }
  return(make_boolean(sc, scheme_strcmp(car(args), cadr(args)) == 1));
}

static s7_pointer g_string_greater_2(s7_scheme *sc, s7_pointer args)
{
  if (!is_string(car(args)))
    {
      check_method(sc, car(args), sc->STRING_GT, args);
      return(wrong_type_argument(sc, sc->STRING_GT, small_int(1), car(args), T_STRING));
    }
  if (!is_string(cadr(args)))
    {
      check_method(sc, cadr(args), sc->STRING_GT, args);
      return(wrong_type_argument(sc, sc->STRING_GT, small_int(2), cadr(args), T_STRING));
    }
  return(make_boolean(sc, scheme_strcmp(car(args), cadr(args)) == 1));
}


static s7_pointer string_geq_s_ic, string_geq_2;
static s7_pointer g_string_geq_s_ic(s7_scheme *sc, s7_pointer args)
{
  if (!is_string(car(args)))
    {
      check_method(sc, car(args), sc->STRING_GEQ, args);
      return(wrong_type_argument(sc, sc->STRING_GEQ, small_int(1), car(args), T_STRING));
    }
  return(make_boolean(sc, scheme_strcmp(car(args), cadr(args)) != -1));
}

static s7_pointer g_string_geq_2(s7_scheme *sc, s7_pointer args)
{
  if (!is_string(car(args)))
    {
      check_method(sc, car(args), sc->STRING_GEQ, args);
      return(wrong_type_argument(sc, sc->STRING_GEQ, small_int(1), car(args), T_STRING));
    }
  if (!is_string(cadr(args)))
    {
      check_method(sc, cadr(args), sc->STRING_GEQ, args);
      return(wrong_type_argument(sc, sc->STRING_GEQ, small_int(2), cadr(args), T_STRING));
    }
  return(make_boolean(sc, scheme_strcmp(car(args), cadr(args)) != -1));
}


static s7_pointer string_leq_s_ic, string_leq_2;
static s7_pointer g_string_leq_s_ic(s7_scheme *sc, s7_pointer args)
{
  if (!is_string(car(args)))
    {
      check_method(sc, car(args), sc->STRING_LEQ, args);
      return(wrong_type_argument(sc, sc->STRING_LEQ, small_int(1), car(args), T_STRING));
    }
  return(make_boolean(sc, scheme_strcmp(car(args), cadr(args)) != 1));
}

static s7_pointer g_string_leq_2(s7_scheme *sc, s7_pointer args)
{
  if (!is_string(car(args)))
    {
      check_method(sc, car(args), sc->STRING_LEQ, args);
      return(wrong_type_argument(sc, sc->STRING_LEQ, small_int(1), car(args), T_STRING));
    }
  if (!is_string(cadr(args)))
    {
      check_method(sc, cadr(args), sc->STRING_LEQ, args);
      return(wrong_type_argument(sc, sc->STRING_LEQ, small_int(2), cadr(args), T_STRING));
    }
  return(make_boolean(sc, scheme_strcmp(car(args), cadr(args)) != 1));
}


#if (!WITH_PURE_S7)
static unsigned char uppers[256];
static void init_uppers(void)
{
  int i;
  for (i = 0; i < 256; i++)
    uppers[i] = (unsigned char)toupper(i);
}

static int scheme_strcasecmp(s7_pointer s1, s7_pointer s2)
{
  /* same as scheme_strcmp -- watch out for unwanted sign! and lack of trailing null (length sets string end).
   */
  int i, len, len1, len2;
  char *str1, *str2;

  len1 = string_length(s1);
  len2 = string_length(s2);
  if (len1 > len2)
    len = len2;
  else len = len1;

  str1 = string_value(s1);
  str2 = string_value(s2);

  for (i = 0; i < len; i++)
    if (uppers[(int)str1[i]] < uppers[(int)str2[i]])
      return(-1);
    else
      {
	if (uppers[(int)str1[i]] > uppers[(int)str2[i]])
	  return(1);
      }

  if (len1 < len2) 
    return(-1);
  if (len1 > len2)
    return(1);
  return(0);
}


static bool scheme_strequal_ci(s7_pointer s1, s7_pointer s2)
{
  /* same as scheme_strcmp -- watch out for unwanted sign! */
  int i, len, len2;
  char *str1, *str2;

  len = string_length(s1);
  len2 = string_length(s2);
  if (len != len2)
    return(false);

  str1 = string_value(s1);
  str2 = string_value(s2);

  for (i = 0; i < len; i++)
    if (uppers[(int)str1[i]] != uppers[(int)str2[i]])
      return(false);
  return(true);
}


static s7_pointer g_string_ci_cmp(s7_scheme *sc, s7_pointer args, int val, s7_pointer sym)
{
  s7_pointer x, y;
  
  y = car(args);
  if (!is_string(y))
    {
      check_method(sc, y, sym, args);
      return(wrong_type_argument(sc, sym, small_int(1), y, T_STRING));
    }
  
  for (x = cdr(args); is_not_null(x); x = cdr(x))
    {
      if (!is_string(car(x)))
	{
	  check_method(sc, car(x), sym, cons(sc, y, x));
	  return(wrong_type_argument_n(sc, sym, position_of(x, args), car(x), T_STRING));
	}
      if (val == 0)
	{
	  if (!scheme_strequal_ci(y, car(x)))
	    {
	      for (y = cdr(x); is_pair(y); y = cdr(y))
		if (!is_string_via_method(sc, car(y)))
		  return(wrong_type_argument_n(sc, sym, position_of(y, args), car(y), T_STRING));
	      return(sc->F);
	    }
	}
      else
	{
	  if (scheme_strcasecmp(y, car(x)) != val)
	    {
	      for (y = cdr(x); is_pair(y); y = cdr(y))
		if (!is_string_via_method(sc, car(y)))
		  return(wrong_type_argument_n(sc, sym, position_of(y, args), car(y), T_STRING));
	      return(sc->F);
	    }
	}
      y = car(x);
    }
  return(sc->T);
}


static s7_pointer g_string_ci_cmp_not(s7_scheme *sc, s7_pointer args, int val, s7_pointer sym)
{
  s7_pointer x, y;
  
  y = car(args);
  if (!is_string(y))
    {
      check_method(sc, y, sym, args);
      return(wrong_type_argument(sc, sym, small_int(1), y, T_STRING));
    }
  
  for (x = cdr(args); is_not_null(x); x = cdr(x))
    {
      if (!is_string(car(x)))
	{
	  check_method(sc, car(x), sym, cons(sc, y, x));
	  return(wrong_type_argument_n(sc, sym, position_of(x, args), car(x), T_STRING));
	}
      if (scheme_strcasecmp(y, car(x)) == val)
	{
	  for (y = cdr(x); is_pair(y); y = cdr(y))
	    if (!is_string_via_method(sc, car(y)))
	      return(wrong_type_argument_n(sc, sym, position_of(y, args), car(y), T_STRING));
	  return(sc->F);
	}
      y = car(x);
    }
  return(sc->T);
}


static s7_pointer g_strings_are_ci_equal(s7_scheme *sc, s7_pointer args)
{
  #define H_strings_are_ci_equal "(string-ci=? str ...) returns #t if all the string arguments are equal, ignoring case"
  return(g_string_ci_cmp(sc, args, 0, sc->STRING_CI_EQ));
}	


static s7_pointer g_strings_are_ci_less(s7_scheme *sc, s7_pointer args)
{
  #define H_strings_are_ci_less "(string-ci<? str ...) returns #t if all the string arguments are increasing, ignoring case"
  return(g_string_ci_cmp(sc, args, -1, sc->STRING_CI_LT));
}	


static s7_pointer g_strings_are_ci_greater(s7_scheme *sc, s7_pointer args)
{
  #define H_strings_are_ci_greater "(string-ci>? str ...) returns #t if all the string arguments are decreasing, ignoring case"
  return(g_string_ci_cmp(sc, args, 1, sc->STRING_CI_GT));
}	


static s7_pointer g_strings_are_ci_geq(s7_scheme *sc, s7_pointer args)
{
  #define H_strings_are_ci_geq "(string-ci>=? str ...) returns #t if all the string arguments are equal or decreasing, ignoring case"
  return(g_string_ci_cmp_not(sc, args, -1, sc->STRING_CI_GEQ));
}	


static s7_pointer g_strings_are_ci_leq(s7_scheme *sc, s7_pointer args)
{
  #define H_strings_are_ci_leq "(string-ci<=? str ...) returns #t if all the string arguments are equal or increasing, ignoring case"
  return(g_string_ci_cmp_not(sc, args, 1, sc->STRING_CI_LEQ));
}	


static s7_pointer string_ci_equal_s_ic, string_ci_equal_2;
static s7_pointer g_string_ci_equal_s_ic(s7_scheme *sc, s7_pointer args)
{
  if (!is_string(car(args)))
    {
      check_method(sc, car(args), sc->STRING_CI_EQ, args);
      return(wrong_type_argument(sc, sc->STRING_CI_EQ, small_int(1), car(args), T_STRING));
    }
  return(make_boolean(sc, scheme_strequal_ci(car(args), cadr(args))));
}

static s7_pointer g_string_ci_equal_2(s7_scheme *sc, s7_pointer args)
{
  if (!is_string(car(args)))
    {
      check_method(sc, car(args), sc->STRING_CI_EQ, args);
      return(wrong_type_argument(sc, sc->STRING_CI_EQ, small_int(1), car(args), T_STRING));
    }
  if (!is_string(cadr(args)))
    {
      check_method(sc, cadr(args), sc->STRING_CI_EQ, args);
      return(wrong_type_argument(sc, sc->STRING_CI_EQ, small_int(2), cadr(args), T_STRING));
    }
  return(make_boolean(sc, scheme_strequal_ci(car(args), cadr(args))));
}


static s7_pointer string_ci_less_s_ic, string_ci_less_2;
static s7_pointer g_string_ci_less_s_ic(s7_scheme *sc, s7_pointer args)
{
  if (!is_string(car(args)))
    {
      check_method(sc, car(args), sc->STRING_CI_LT, args);
      return(wrong_type_argument(sc, sc->STRING_CI_LT, small_int(1), car(args), T_STRING));
    }
  return(make_boolean(sc, scheme_strcasecmp(car(args), cadr(args)) == -1));
}

static s7_pointer g_string_ci_less_2(s7_scheme *sc, s7_pointer args)
{
  if (!is_string(car(args)))
    {
      check_method(sc, car(args), sc->STRING_CI_LT, args);
      return(wrong_type_argument(sc, sc->STRING_CI_LT, small_int(1), car(args), T_STRING));
    }
  if (!is_string(cadr(args)))
    {
      check_method(sc, cadr(args), sc->STRING_CI_LT, args);
      return(wrong_type_argument(sc, sc->STRING_CI_LT, small_int(2), cadr(args), T_STRING));
    }
  return(make_boolean(sc, scheme_strcasecmp(car(args), cadr(args)) == -1));
}


static s7_pointer string_ci_greater_s_ic, string_ci_greater_2;
static s7_pointer g_string_ci_greater_s_ic(s7_scheme *sc, s7_pointer args)
{
  if (!is_string(car(args)))
    {
      check_method(sc, car(args), sc->STRING_CI_GT, args);
      return(wrong_type_argument(sc, sc->STRING_CI_GT, small_int(1), car(args), T_STRING));
    }
  return(make_boolean(sc, scheme_strcasecmp(car(args), cadr(args)) == 1));
}

static s7_pointer g_string_ci_greater_2(s7_scheme *sc, s7_pointer args)
{
  if (!is_string(car(args)))
    {
      check_method(sc, car(args), sc->STRING_CI_GT, args);
      return(wrong_type_argument(sc, sc->STRING_CI_GT, small_int(1), car(args), T_STRING));
    }
  if (!is_string(cadr(args)))
    {
      check_method(sc, cadr(args), sc->STRING_CI_GT, args);
      return(wrong_type_argument(sc, sc->STRING_CI_GT, small_int(2), cadr(args), T_STRING));
    }
  return(make_boolean(sc, scheme_strcasecmp(car(args), cadr(args)) == 1));
}


static s7_pointer string_ci_geq_s_ic, string_ci_geq_2;
static s7_pointer g_string_ci_geq_s_ic(s7_scheme *sc, s7_pointer args)
{
  if (!is_string(car(args)))
    {
      check_method(sc, car(args), sc->STRING_CI_GEQ, args);
      return(wrong_type_argument(sc, sc->STRING_CI_GEQ, small_int(1), car(args), T_STRING));
    }
  return(make_boolean(sc, scheme_strcasecmp(car(args), cadr(args)) != -1));
}

static s7_pointer g_string_ci_geq_2(s7_scheme *sc, s7_pointer args)
{
  if (!is_string(car(args)))
    {
      check_method(sc, car(args), sc->STRING_CI_GEQ, args);
      return(wrong_type_argument(sc, sc->STRING_CI_GEQ, small_int(1), car(args), T_STRING));
    }
  if (!is_string(cadr(args)))
    {
      check_method(sc, cadr(args), sc->STRING_CI_GEQ, args);
      return(wrong_type_argument(sc, sc->STRING_CI_GEQ, small_int(2), cadr(args), T_STRING));
    }
  return(make_boolean(sc, scheme_strcasecmp(car(args), cadr(args)) != -1));
}


static s7_pointer string_ci_leq_s_ic, string_ci_leq_2;
static s7_pointer g_string_ci_leq_s_ic(s7_scheme *sc, s7_pointer args)
{
  if (!is_string(car(args)))
    {
      check_method(sc, car(args), sc->STRING_CI_LEQ, args);
      return(wrong_type_argument(sc, sc->STRING_CI_LEQ, small_int(1), car(args), T_STRING));
    }
  return(make_boolean(sc, scheme_strcasecmp(car(args), cadr(args)) != 1));
}

static s7_pointer g_string_ci_leq_2(s7_scheme *sc, s7_pointer args)
{
  if (!is_string(car(args)))
    {
      check_method(sc, car(args), sc->STRING_CI_LEQ, args);
      return(wrong_type_argument(sc, sc->STRING_CI_LEQ, small_int(1), car(args), T_STRING));
    }
  if (!is_string(cadr(args)))
    {
      check_method(sc, cadr(args), sc->STRING_CI_LEQ, args);
      return(wrong_type_argument(sc, sc->STRING_CI_LEQ, small_int(2), cadr(args), T_STRING));
    }
  return(make_boolean(sc, scheme_strcasecmp(car(args), cadr(args)) != 1));
}
#endif /* not pure s7 */


static s7_pointer g_string_fill(s7_scheme *sc, s7_pointer args)
{
  #define H_string_fill "(string-fill! str chr start end) fills the string str with the character chr"
  s7_pointer x, chr;
  s7_Int start = 0, end;
  x = car(args);

  if (!is_string(x))
    {
      check_method(sc, x, sc->STRING_FILL, args); /* not two_methods here */
      return(wrong_type_argument(sc, sc->STRING_FILL, small_int(1), x, T_STRING));
    }

  chr = cadr(args);
  if (!s7_is_character(chr))
    {
      check_two_methods(sc, chr, sc->STRING_FILL, sc->FILL, args);
      return(wrong_type_argument(sc, sc->STRING_FILL, small_int(2), chr, T_CHARACTER));
    }

  end = string_length(x);
  if (end == 0) return(chr);

  if (!is_null(cddr(args)))
    {
      s7_pointer p;
      p = start_and_end(sc, sc->STRING_FILL, sc->FILL, cddr(args), args, 3, &start, &end);
      if (p != sc->GC_NIL) return(p);
      if (start == end) return(chr);
    }
  memset((void *)(string_value(x) + start), (int)character(chr), end - start);
  return(chr);
}


static s7_pointer g_string_1(s7_scheme *sc, s7_pointer args, s7_pointer sym)
{
  int i, len;
  s7_pointer x, newstr;
  
  /* get length for new string and check arg types */
  for (len = 0, x = args; is_not_null(x); len++, x = cdr(x)) 
    {
      s7_pointer p;
      p = car(x);
      if (!s7_is_character(p))
	{
	  if (has_methods(p))
	    {
	      s7_pointer func;
	      func = find_method(sc, find_let(sc, p), sym);
	      if (func != sc->UNDEFINED)
		{
		  s7_pointer y;
		  if (len == 0)
		    return(s7_apply_function(sc, func, args));
		  newstr = make_empty_string(sc, len, 0); 
		  for (i = 0, y = args; y != x; i++, y = cdr(y)) 
		    string_value(newstr)[i] = character(car(y));
		  return(g_string_append(sc, list_2(sc, newstr, s7_apply_function(sc, func, x))));
		}
	    }
	  return(wrong_type_argument_n(sc, sym, len + 1, car(x), T_CHARACTER));
	}
    }
  newstr = make_empty_string(sc, len, 0);
  for (i = 0, x = args; is_not_null(x); i++, x = cdr(x)) 
    string_value(newstr)[i] = character(car(x));
  
  return(newstr);
}


static s7_pointer g_string(s7_scheme *sc, s7_pointer args)
{
  #define H_string "(string chr...) appends all its character arguments into one string"
  if (is_null(args))                                /* (string) but not (string ()) */
    return(s7_make_string_with_length(sc, "", 0));
  return(g_string_1(sc, args, sc->STRING));
}


static s7_pointer g_list_to_string(s7_scheme *sc, s7_pointer args)
{
  #define H_list_to_string "(list->string lst) appends all the list's characters into one string; (apply string lst)"
  if (is_null(car(args)))
    return(s7_make_string_with_length(sc, "", 0));
  
  if (!is_proper_list(sc, car(args)))
    {
      check_method(sc, car(args), sc->LIST_TO_STRING, args);
      return(simple_wrong_type_argument_with_type(sc, sc->LIST_TO_STRING, car(args), make_string_wrapper(sc, "a (proper, non-circular) list of characters")));
    }
  return(g_string_1(sc, car(args), sc->LIST_TO_STRING));
}


static s7_pointer s7_string_to_list(s7_scheme *sc, const char *str, int len)
{
  int i;
  s7_pointer p;

  if (len == 0)
    return(sc->NIL);

  if (len < (sc->free_heap_top - sc->free_heap))
    {
      sc->w = s7_cons(sc, s7_make_character(sc, ((unsigned char)str[len - 1])), sc->NIL);
      for (i = len - 2; i >= 0; i--)
	sc->w = cons_unchecked(sc, s7_make_character(sc, ((unsigned char)str[i])), sc->w);
    }
  else
    {
      sc->w = sc->NIL;
      for (i = len - 1; i >= 0; i--)
	sc->w = cons(sc, s7_make_character(sc, ((unsigned char)str[i])), sc->w);
    }
  p = sc->w;
  sc->w = sc->NIL;
  return(p);
}


static s7_pointer g_string_to_list(s7_scheme *sc, s7_pointer args)
{
  #define H_string_to_list "(string->list str start end) returns the elements of the string str in a list; (map values str)"
  s7_Int i, start = 0, end;
  s7_pointer p, str;

  str = car(args);
  if (!is_string(str))
    {
      check_method(sc, str, sc->STRING_TO_LIST, args);
      return(simple_wrong_type_argument(sc, sc->STRING_TO_LIST, str, T_STRING));
    }

  end = string_length(str);
  if (!is_null(cdr(args)))
    {
      p = start_and_end(sc, sc->STRING_TO_LIST, NULL, cdr(args), args, 2, &start, &end);
      if (p != sc->GC_NIL) return(p);
      if (start == end) return(sc->NIL);
    }
  else
    {
      if (end == 0) return(sc->NIL);
    }
  if ((start == 0) && (end == string_length(str)))
    return(s7_string_to_list(sc, string_value(str), string_length(str)));
  
  sc->w = sc->NIL;
  for (i = end - 1; i >= start; i--)
    sc->w = cons(sc, s7_make_character(sc, ((unsigned char)string_value(str)[i])), sc->w);

  p = sc->w;
  sc->w = sc->NIL;
  return(p);
}


/* -------------------------------- bytevectors -------------------------------- 
 *
 * these are just strings with the T_BYTEVECTOR bit set.
 */

static bool s7_is_bytevector(s7_pointer b) {return((is_string(b)) && (is_bytevector(b)));}

static s7_pointer g_is_bytevector(s7_scheme *sc, s7_pointer args)
{
  #define H_is_bytevector "(bytevector? obj) returns #t if obj is a bytevector"
  check_boolean_method(sc, s7_is_bytevector, sc->IS_BYTEVECTOR, args);
}


static s7_pointer g_to_bytevector(s7_scheme *sc, s7_pointer args)
{
  #define H_to_bytevector "(->bytevector obj) turns a string into a bytevector."
  s7_pointer str;
  str = car(args);
  if (!is_string(str))
    {
      check_method(sc, str, sc->TO_BYTEVECTOR, args);
      return(wrong_type_argument(sc, sc->TO_BYTEVECTOR, small_int(1), str, T_STRING));
    }
  set_bytevector(str);
  return(str);
}


static s7_pointer g_make_bytevector(s7_scheme *sc, s7_pointer args)
{
  #define H_make_bytevector "(make-bytevector len (byte 0)) makes a bytevector of length len filled with byte."
  s7_pointer str;
  if (is_null(cdr(args)))
    {
      str = g_make_string(sc, args);
      if (is_string(str))
	memset((void *)(string_value(str)), 0, string_length(str));
    }
  else 
    {
      s7_pointer len, byte;
      s7_Int b;
      len = car(args);
      if (!is_integer(len))
	{
	  check_method(sc, len, sc->MAKE_BYTEVECTOR, args);
	  return(wrong_type_argument(sc, sc->MAKE_BYTEVECTOR, small_int(1), len, T_INTEGER));
	}
      byte = cadr(args);
      if (!s7_is_integer(byte))
	{
	  check_method(sc, byte, sc->MAKE_BYTEVECTOR, args);
	  return(wrong_type_argument(sc, sc->MAKE_BYTEVECTOR, small_int(2), byte, T_INTEGER));
	}
      b = s7_integer(byte);
      if ((b < 0) || (b > 255))
	return(simple_wrong_type_argument_with_type(sc, sc->MAKE_BYTEVECTOR, byte, AN_UNSIGNED_BYTE));
      str = g_make_string(sc, list_2(sc, len, chars[b]));
    }
  set_bytevector(str);
  return(str);
}


static s7_pointer g_bytevector(s7_scheme *sc, s7_pointer args)
{
  #define H_bytevector "(bytevector ...) returns a bytevector whose elements are the arguments"
  s7_Int i, len;
  s7_pointer vec, x;
  
  len = s7_list_length(sc, args);
  vec = make_empty_string(sc, len, 0);

  for (i = 0, x = args; is_pair(x); i++, x = cdr(x)) 
    {
      s7_pointer byte;
      s7_Int b;
      byte = car(x);
      if (!s7_is_integer(byte))
	{
	  if (has_methods(byte))
	    {
	      s7_pointer func;
	      func = find_method(sc, find_let(sc, byte), sc->BYTEVECTOR);
	      if (func != sc->UNDEFINED)
		{
		  if (i == 0)
		    return(s7_apply_function(sc, func, args));
		  string_length(vec) = i;
		  vec = g_string_append(sc, list_2(sc, vec, s7_apply_function(sc, func, x)));
		  set_bytevector(vec);
		  return(vec);
		}
	    }
	  return(wrong_type_argument(sc, sc->BYTEVECTOR, make_integer(sc, i + 1), byte, T_INTEGER));
	}
      b = s7_integer(byte);
      if ((b < 0) || (b > 255))
	return(simple_wrong_type_argument_with_type(sc, sc->BYTEVECTOR, byte, AN_UNSIGNED_BYTE));
      string_value(vec)[i] = (unsigned char)b;
    }
  set_bytevector(vec);
  return(vec);
}

static s7_pointer bytevector_to_list(s7_scheme *sc, const char *str, int len)
{
  int i;
  s7_pointer p;
  if (len == 0) return(sc->NIL);
  sc->w = sc->NIL;
  for (i = len - 1; i >= 0; i--)
    sc->w = cons(sc, small_int((unsigned int)(str[i])), sc->w);
  p = sc->w;
  sc->w = sc->NIL;
  return(p);
}


  
/* -------------------------------- ports -------------------------------- 
 *
 * originally nil served as stdin and friends, but that made it impossible to catch an error
 *   like (read-line (current-output-port)) when the latter was stdout.  So we now have
 *   the built-in constant ports *stdin*, *stdout*, and *stderr*.  Some way is needed to
 *   refer to these directly so that (read-line *stdin*) for example can insist on reading
 *   from the terminal, or whatever stdin is.
 */

static s7_pointer g_is_port_closed(s7_scheme *sc, s7_pointer args)
{
  #define H_is_port_closed "(port-closed? p) returns #t if the port p is closed."
  s7_pointer x;

  x = car(args);
  if ((is_input_port(x)) || (is_output_port(x)))
    return(make_boolean(sc, port_is_closed(x)));

  check_method(sc, x, sc->IS_PORT_CLOSED, args);
  return(simple_wrong_type_argument_with_type(sc, sc->IS_PORT_CLOSED, x, make_string_wrapper(sc, "a port")));      
}


static s7_pointer g_port_line_number(s7_scheme *sc, s7_pointer args)
{
  #define H_port_line_number "(port-line-number input-file-port) returns the current read line number of port"
  s7_pointer x;

  if (is_null(args))
    x = sc->input_port;
  else x = car(args);

  if ((!(is_input_port(x))) ||
      (port_is_closed(x)))
    {
      check_method(sc, x, sc->PORT_LINE_NUMBER, args);
      return(simple_wrong_type_argument_with_type(sc, sc->PORT_LINE_NUMBER, x, AN_INPUT_PORT));
    }
  return(make_integer(sc, port_line_number(x)));
}


int s7_port_line_number(s7_pointer p)
{
  if (is_input_port(p))
    return(port_line_number(p));
  return(0);
}


const char *s7_port_filename(s7_pointer x)
{
  if (((is_input_port(x)) || 
       (is_output_port(x))) &&
      (!port_is_closed(x)))
    return(port_filename(x));
  return(NULL);
}


static s7_pointer g_port_filename(s7_scheme *sc, s7_pointer args)
{
  #define H_port_filename "(port-filename file-port) returns the filename associated with port"
  s7_pointer x;

  if (is_null(args))
    x = sc->input_port;
  else x = car(args);

  if (((is_input_port(x)) ||
       (is_output_port(x))) &&
      (!port_is_closed(x)))
    {
      if (port_filename(x))
	return(make_string_wrapper(sc, port_filename(x)));
      return(s7_make_string_with_length(sc, "", 0));   
      /* otherwise (eval-string (port-filename)) and (string->symbol (port-filename)) segfault */
    }
  check_method(sc, x, sc->PORT_FILENAME, args);
  return(simple_wrong_type_argument_with_type(sc, sc->PORT_FILENAME, x, AN_OPEN_PORT));
}


bool s7_is_input_port(s7_scheme *sc, s7_pointer p)   
{ 
  return(is_input_port(p));
}


static s7_pointer g_is_input_port(s7_scheme *sc, s7_pointer args)
{
  #define H_is_input_port "(input-port? p) returns #t if p is an input port"
  check_boolean_method(sc, is_input_port, sc->IS_INPUT_PORT, args);
}


bool s7_is_output_port(s7_scheme *sc, s7_pointer p)     
{ 
  return(is_output_port(p));
}


static s7_pointer g_is_output_port(s7_scheme *sc, s7_pointer args)
{
  #define H_is_output_port "(output-port? p) returns #t if p is an output port"
  check_boolean_method(sc, is_output_port, sc->IS_OUTPUT_PORT, args);
}


s7_pointer s7_current_input_port(s7_scheme *sc)
{
  return(sc->input_port);
}


static s7_pointer g_current_input_port(s7_scheme *sc, s7_pointer args)
{
  #define H_current_input_port "(current-input-port) returns the current input port"
  return(sc->input_port);
}


static s7_pointer g_set_current_input_port(s7_scheme *sc, s7_pointer args)
{
  #define H_set_current_input_port "(set-current-input-port port) sets the current-input port to port and returns the previous value of the input port"
  s7_pointer old_port, port;

  old_port = sc->input_port;
  port = car(args);
  if ((is_input_port(port)) &&
      (!port_is_closed(port)))
    sc->input_port = port;
  else
    {
      check_method(sc, port, s7_make_symbol(sc, "set-current-input-port"), args);
      return(s7_wrong_type_arg_error(sc, "set-current-input-port", 0, port, "an open input port"));
    }
  return(old_port);
}


s7_pointer s7_set_current_input_port(s7_scheme *sc, s7_pointer port)
{
  s7_pointer old_port;
  old_port = sc->input_port;
  sc->input_port = port;
  return(old_port);
}


s7_pointer s7_current_output_port(s7_scheme *sc)
{
  return(sc->output_port);
}


s7_pointer s7_set_current_output_port(s7_scheme *sc, s7_pointer port)
{
  s7_pointer old_port;
  old_port = sc->output_port;
  sc->output_port = port;
  return(old_port);
}


static s7_pointer g_current_output_port(s7_scheme *sc, s7_pointer args)
{
  #define H_current_output_port "(current-output-port) returns the current output port"
  return(sc->output_port);
}


static s7_pointer g_set_current_output_port(s7_scheme *sc, s7_pointer args)
{
  #define H_set_current_output_port "(set-current-output-port port) sets the current-output port to port and returns the previous value of the output port"
  s7_pointer old_port, port;

  old_port = sc->output_port;
  port = car(args);
  if (((is_output_port(port)) &&
       (!port_is_closed(port))) ||
      (port == sc->F))
    sc->output_port = port;
  else 
    {
      check_method(sc, port, s7_make_symbol(sc, "set-current-output-port"), args);
      return(s7_wrong_type_arg_error(sc, "set-current-output-port", 0, port, "an open output port"));
    }
  return(old_port);
}


s7_pointer s7_current_error_port(s7_scheme *sc)
{
  return(sc->error_port);
}


s7_pointer s7_set_current_error_port(s7_scheme *sc, s7_pointer port)
{
  s7_pointer old_port;
  old_port = sc->error_port;
  sc->error_port = port;
  return(old_port);
}


static s7_pointer g_current_error_port(s7_scheme *sc, s7_pointer args)
{
  #define H_current_error_port "(current-error-port) returns the current error port"
  return(sc->error_port);
}


static s7_pointer g_set_current_error_port(s7_scheme *sc, s7_pointer args)
{
  #define H_set_current_error_port "(set-current-error-port port) sets the current-error port to port and returns the previous value of the error port"
  s7_pointer old_port, port;

  old_port = sc->error_port;
  port = car(args);
  if (((is_output_port(port)) &&
       (!port_is_closed(port))) ||
      (port == sc->F))
    sc->error_port = port;
  else 
    {
      check_method(sc, port, s7_make_symbol(sc, "set-current-error-port"), args);
      return(s7_wrong_type_arg_error(sc, "set-current-error-port", 0, port, "an open output port"));
    }
  return(old_port);
}


#if (!WITH_PURE_S7)
static s7_pointer g_is_char_ready(s7_scheme *sc, s7_pointer args)
{
  #define H_is_char_ready "(char-ready? (port (current-input-port))) returns #t if a character is ready for input on the given port"
  if (is_not_null(args))
    {
      s7_pointer pt = car(args);
      if (!is_input_port(pt))
	{
	  check_method(sc, pt, sc->IS_CHAR_READY, args);
	  return(simple_wrong_type_argument_with_type(sc, sc->IS_CHAR_READY, pt, AN_INPUT_PORT));
	}
      if (port_is_closed(pt))
	return(simple_wrong_type_argument_with_type(sc, sc->IS_CHAR_READY, pt, AN_OPEN_PORT));

      if (is_function_port(pt))
	return((*(port_input_function(pt)))(sc, S7_IS_CHAR_READY, pt));
      return(make_boolean(sc, is_string_port(pt)));
    }
  return(make_boolean(sc, (is_input_port(sc->input_port)) && (is_string_port(sc->input_port))));
}      
#endif


static s7_pointer g_is_eof_object(s7_scheme *sc, s7_pointer args)
{
  #define H_is_eof_object "(eof-object? val) returns #t if val is the end-of-file object"
  s7_pointer p;
  p = car(args);
  if (is_eof(p)) return(sc->T);
  check_method(sc, p, sc->IS_EOF_OBJECT, args);
  return(sc->F);
}


static int closed_port_read_char(s7_scheme *sc, s7_pointer port);
static s7_pointer closed_port_read_line(s7_scheme *sc, s7_pointer port, bool with_eol);
static void closed_port_write_char(s7_scheme *sc, int c, s7_pointer port);
static void closed_port_write_string(s7_scheme *sc, const char *str, int len, s7_pointer port);
static void closed_port_display(s7_scheme *sc, const char *s, s7_pointer port);

void s7_close_input_port(s7_scheme *sc, s7_pointer p)
{
  if ((is_immutable(p)) ||
      ((is_input_port(p)) && (port_is_closed(p))))
    return;

  if (port_filename(p))
    {
      free(port_filename(p));
      port_filename(p) = NULL;
    }
  
  if (is_file_port(p))
    {
      if (port_file(p))
	{
	  fclose(port_file(p));
	  port_file(p) = NULL;
	}
    }
  else
    {
      if ((is_string_port(p)) &&
	  (port_gc_loc(p) != -1))
	s7_gc_unprotect_at(sc, port_gc_loc(p));
    }
  if (port_needs_free(p))
    {
      if (port_data(p))
	{
	  free(port_data(p));
	  port_data(p) = NULL;
	  port_data_size(p) = 0;
	}
      port_needs_free(p) = false;
    }

  port_read_character(p) = closed_port_read_char;
  port_read_line(p) = closed_port_read_line;
  port_write_character(p) = closed_port_write_char;
  port_write_string(p) = closed_port_write_string;
  port_display(p) = closed_port_display;
  port_is_closed(p) = true;
}


static s7_pointer g_close_input_port(s7_scheme *sc, s7_pointer args)
{
  #define H_close_input_port "(close-input-port port) closes the port"
  s7_pointer pt;

  pt = car(args);
  if (!is_input_port(pt))
    {
      check_method(sc, pt, sc->CLOSE_INPUT_PORT, args);
      return(simple_wrong_type_argument_with_type(sc, sc->CLOSE_INPUT_PORT, pt, AN_INPUT_PORT));
    }
  if (!is_immutable(pt))
    s7_close_input_port(sc, pt);

  return(sc->UNSPECIFIED);
}


void s7_flush_output_port(s7_scheme *sc, s7_pointer p)
{
  /* what does it mean to "flush" a string port? Surely
   *    (with-output-to-string (lambda () (display "123") (flush-output-port))) 
   * should return "123" not ""?
   */
  if ((!is_output_port(p)) || 
      (!is_file_port(p)) || 
      (port_is_closed(p)) ||
      (p == sc->F))
    return;

  if (port_file(p))
    {
      if (port_position(p) > 0)
	{
	  if (fwrite((void *)(port_data(p)), 1, port_position(p), port_file(p)) != port_position(p))
	    s7_warn(sc, 64, "fwrite trouble in flush-output-port\n");
	}
      port_position(p) = 0;
      fflush(port_file(p));
    }
}


static s7_pointer g_flush_output_port(s7_scheme *sc, s7_pointer args)
{
  #define H_flush_output_port "(flush-output-port port) flushes the port"
  s7_pointer pt;
  
  if (is_null(args))
    pt = sc->output_port;
  else pt = car(args);

  if (!is_output_port(pt))
    {
      if (pt == sc->F) return(pt);
      check_method(sc, pt, sc->FLUSH_OUTPUT_PORT, args);
      return(simple_wrong_type_argument_with_type(sc, sc->FLUSH_OUTPUT_PORT, pt, AN_OUTPUT_PORT));
    }
  s7_flush_output_port(sc, pt);
  return(pt);
}


void s7_close_output_port(s7_scheme *sc, s7_pointer p)
{
  if ((is_immutable(p)) ||
      ((is_output_port(p)) && (port_is_closed(p))) ||
      (p == sc->F))
    return;
  
  if (is_file_port(p))
    {
      if (port_filename(p)) /* only a file port has a filename */
	{
	  free(port_filename(p));
	  port_filename(p) = NULL;
	  port_filename_length(p) = 0;
	}
  
      if (port_file(p))
	{
	  if (port_position(p) > 0)
	    {
	      if (fwrite((void *)(port_data(p)), 1, port_position(p), port_file(p)) != port_position(p))
		s7_warn(sc, 64, "fwrite trouble in close-output-port\n");
	    }
	  port_position(p) = 0;
	  free(port_data(p));
	  fflush(port_file(p));
	  fclose(port_file(p));
	  port_file(p) = NULL;
	}
    }
  else
    {
      if ((is_string_port(p)) && 
	  (port_data(p)))
	{
	  free(port_data(p));
	  port_data(p) = NULL;
	  port_data_size(p) = 0;
	  port_needs_free(p) = false;
	}
    }
  port_read_character(p) = closed_port_read_char;
  port_read_line(p) = closed_port_read_line;
  port_write_character(p) = closed_port_write_char;
  port_write_string(p) = closed_port_write_string;
  port_display(p) = closed_port_display;
  port_is_closed(p) = true;
}


static s7_pointer g_close_output_port(s7_scheme *sc, s7_pointer args)
{
  #define H_close_output_port "(close-output-port port) closes the port"
  s7_pointer pt;
  pt = car(args);
  if (!is_output_port(pt))
    {
      if (pt == sc->F) return(sc->UNSPECIFIED);
      check_method(sc, pt, sc->CLOSE_OUTPUT_PORT, args);
      return(simple_wrong_type_argument_with_type(sc, sc->CLOSE_OUTPUT_PORT, pt, AN_OUTPUT_PORT));
    }
  if (!(is_immutable(pt)))
    s7_close_output_port(sc, pt);
  return(sc->UNSPECIFIED);
}


/* -------- read character functions -------- */

static int file_read_char(s7_scheme *sc, s7_pointer port)
{
  return(fgetc(port_file(port)));
}


static int function_read_char(s7_scheme *sc, s7_pointer port)
{
  return(character((*(port_input_function(port)))(sc, S7_READ_CHAR, port)));
}


static int string_read_char(s7_scheme *sc, s7_pointer port)
{
  if (port_data_size(port) <= port_position(port)) /* port_string_length is 0 if no port string */
    return(EOF);
  return((unsigned char)port_data(port)[port_position(port)++]);
}


static int output_read_char(s7_scheme *sc, s7_pointer port)
{
  simple_wrong_type_argument_with_type(sc, sc->READ_CHAR, port, AN_INPUT_PORT);
  return(0);
}


static int closed_port_read_char(s7_scheme *sc, s7_pointer port)
{
  simple_wrong_type_argument_with_type(sc, sc->READ_CHAR, port, AN_OPEN_PORT);
  return(0);
}



/* -------- read line functions -------- */

static s7_pointer output_read_line(s7_scheme *sc, s7_pointer port, bool with_eol)
{
  return(simple_wrong_type_argument_with_type(sc, sc->READ_LINE, port, AN_INPUT_PORT));
}


static s7_pointer closed_port_read_line(s7_scheme *sc, s7_pointer port, bool with_eol)
{
  return(simple_wrong_type_argument_with_type(sc, sc->READ_LINE, port, AN_OPEN_PORT));
}


static s7_pointer function_read_line(s7_scheme *sc, s7_pointer port, bool with_eol)
{
  return((*(port_input_function(port)))(sc, S7_READ_LINE, port));
}


static s7_pointer stdin_read_line(s7_scheme *sc, s7_pointer port, bool with_eol)
{
  if (sc->read_line_buf == NULL)
    {
      sc->read_line_buf_size = 1024;
      sc->read_line_buf = (char *)malloc(sc->read_line_buf_size * sizeof(char));
    }

  if (fgets(sc->read_line_buf, sc->read_line_buf_size, stdin) != NULL)
    return(s7_make_string(sc, sc->read_line_buf)); /* fgets adds the trailing '\0' */
  return(s7_make_string_with_length(sc, NULL, 0));
}


static s7_pointer file_read_line(s7_scheme *sc, s7_pointer port, bool with_eol)
{
  char *buf;
  int read_size, previous_size = 0;

  if (sc->read_line_buf == NULL)
    {
      sc->read_line_buf_size = 1024;
      sc->read_line_buf = (char *)malloc(sc->read_line_buf_size * sizeof(char));
    }
  
  buf = sc->read_line_buf;
  read_size = sc->read_line_buf_size;

  while (true)
    {
      char *p, *rtn;
      size_t len;

      p = fgets(buf, read_size, port_file(port));
      if (!p)
	return(sc->EOF_OBJECT);

      rtn = strchr(buf, (int)'\n');
      if (rtn)
	{
	  port_line_number(port)++;
	  return(s7_make_terminated_string_with_length(sc, sc->read_line_buf, (with_eol) ? (previous_size + rtn - p + 1) : (previous_size + rtn - p)));
	}
      /* if no newline, then either at eof or need bigger buffer */
      len = strlen(sc->read_line_buf);

      if ((len + 1) < sc->read_line_buf_size)
	return(s7_make_terminated_string_with_length(sc, sc->read_line_buf, len));
      
      previous_size = sc->read_line_buf_size;
      sc->read_line_buf_size *= 2;
      sc->read_line_buf = (char *)realloc(sc->read_line_buf, sc->read_line_buf_size * sizeof(char));
      read_size = previous_size;
      previous_size -= 1;
      buf = (char *)(sc->read_line_buf + previous_size);
    }
  return(sc->EOF_OBJECT);
}


static s7_pointer string_read_line(s7_scheme *sc, s7_pointer port, bool with_eol)
{
  unsigned int i, port_start;
  unsigned char *port_str, *cur, *start;

  /* this string can be left uncopied if port_needs_free(port), then the sc->strings
   *   array checked when the port is closed, and any held strings copied.  But 
   *   even in a best case (make-index.scm), the savings is small.
   *
   * perhaps dont free port string until GC sweep, sweep strings ahead of ports, do the holder check just before free
   *   but this fails because FFI code (snd-xen.c) assumes strings end in null.  This will mess up uncopied substring too...
   */

  port_start = port_position(port);
  port_str = port_data(port);
  start = (unsigned char *)(port_str + port_start);

  cur = (unsigned char *)strchr((const char *)start, (int)'\n'); /* this can run off the end making valgrind unhappy, but I think it's innocuous */
  if (cur)
      {
	port_line_number(port)++;
	i = cur - port_str;
	port_position(port) = i + 1;
	return(s7_make_terminated_string_with_length(sc, (const char *)start, ((with_eol) ? i + 1 : i) - port_start));
      }
  i = port_data_size(port);
  port_position(port) = i;
  if (i <= port_start)         /* the < part can happen -- if not caught we try to create a string of length -1 -> segfault */
    return(sc->EOF_OBJECT);

  return(s7_make_terminated_string_with_length(sc, (const char *)start, i - port_start));
}


/* -------- write character functions -------- */

static void string_write_char(s7_scheme *sc, int c, s7_pointer pt)
{
  if (port_position(pt) >= port_data_size(pt))
    {
      int loc;
      loc = port_data_size(pt);
      port_data_size(pt) *= 2;
      port_data(pt) = (unsigned char *)realloc(port_data(pt), port_data_size(pt) * sizeof(unsigned char));
      memset((void *)(port_data(pt) + loc), 0, loc);
    }
  port_data(pt)[port_position(pt)++] = c;
}

static void stdout_write_char(s7_scheme *sc, int c, s7_pointer port)
{
  fputc(c, stdout);
}

static void stderr_write_char(s7_scheme *sc, int c, s7_pointer port)
{
  fputc(c, stderr);
}

static void function_write_char(s7_scheme *sc, int c, s7_pointer port)
{
  (*(port_output_function(port)))(sc, c, port);
}


#define PORT_DATA_SIZE 256
static void file_write_char(s7_scheme *sc, int c, s7_pointer port)
{
  if (port_position(port) == PORT_DATA_SIZE)
    {
      if (fwrite((void *)(port_data(port)), 1, PORT_DATA_SIZE, port_file(port)) != PORT_DATA_SIZE)
	s7_warn(sc, 64, "fwrite trouble during write-char\n");
      port_position(port) = 0;
    }
  port_data(port)[port_position(port)++] = (unsigned char)c;
}


static void input_write_char(s7_scheme *sc, int c, s7_pointer port)
{
  simple_wrong_type_argument_with_type(sc, sc->WRITE_CHAR, port, AN_OUTPUT_PORT);
}


static void closed_port_write_char(s7_scheme *sc, int c, s7_pointer port)
{
  simple_wrong_type_argument_with_type(sc, sc->WRITE_CHAR, port, AN_OPEN_PORT);
}



/* -------- write string functions -------- */

static void input_write_string(s7_scheme *sc, const char *str, int len, s7_pointer port)
{
  simple_wrong_type_argument_with_type(sc, sc->WRITE, port, AN_OUTPUT_PORT);
}


static void closed_port_write_string(s7_scheme *sc, const char *str, int len, s7_pointer port)
{
  simple_wrong_type_argument_with_type(sc, sc->WRITE, port, AN_OPEN_PORT);
}


static void input_display(s7_scheme *sc, const char *s, s7_pointer port)
{
  simple_wrong_type_argument_with_type(sc, sc->WRITE, port, AN_OUTPUT_PORT);
}

static void closed_port_display(s7_scheme *sc, const char *s, s7_pointer port)
{
  simple_wrong_type_argument_with_type(sc, sc->WRITE, port, AN_OPEN_PORT);
}

static void stdout_write_string(s7_scheme *sc, const char *str, int len, s7_pointer port)
{
  if (str[len] == '\0')
    fputs(str, stdout);
  else
    {
      int i;
      for (i = 0; i < len; i++)
	fputc(str[i], stdout);
    }
}

static void stderr_write_string(s7_scheme *sc, const char *str, int len, s7_pointer port)
{
  if (str[len] == '\0')
    fputs(str, stderr);
  else
    {
      int i;
      for (i = 0; i < len; i++)
	fputc(str[i], stderr);
    }
}

static void string_write_string(s7_scheme *sc, const char *str, int len, s7_pointer pt)
{
  int new_len;
  /* len is known to be non-zero */

  new_len = port_position(pt) + len;
  if (new_len >= (int)port_data_size(pt))
    {
      int loc;
      loc = port_data_size(pt);
      port_data_size(pt) = new_len * 2;
      port_data(pt) = (unsigned char *)realloc(port_data(pt), port_data_size(pt) * sizeof(unsigned char) + 8);
      memset((void *)(port_data(pt) + loc), 0, port_data_size(pt) + 8 - loc); 
    }

  memcpy((void *)(port_data(pt) + port_position(pt)), (void *)str, len);
  /* memcpy is much faster than the equivalent while loop */
  port_position(pt) = new_len;
}

static void file_display(s7_scheme *sc, const char *s, s7_pointer port)
{
  if (s)
    {
      if (port_position(port) > 0)
	{
	  if (fwrite((void *)(port_data(port)), 1, port_position(port), port_file(port)) != port_position(port))
	    s7_warn(sc, 64, "fwrite trouble in display\n");
	  port_position(port) = 0;
	}
      if (fputs(s, port_file(port)) == EOF)
	s7_warn(sc, 64, "write to %s: %s\n", port_filename(port), strerror(errno));
    }
}

static void file_write_string(s7_scheme *sc, const char *str, int len, s7_pointer pt)
{
  int new_len;
  new_len = port_position(pt) + len;
  if (new_len >= PORT_DATA_SIZE)
    {
      if (port_position(pt) > 0)
	{
	  if (fwrite((void *)(port_data(pt)), 1, port_position(pt), port_file(pt)) != port_position(pt))
	    s7_warn(sc, 64, "fwrite trouble in write-string\n");
	  port_position(pt) = 0;
	}
      if (fwrite((void *)str, 1, len, port_file(pt)) != (size_t)len)
	s7_warn(sc, 64, "fwrite trouble in write-string\n");
    }
  else
    {
      memcpy((void *)(port_data(pt) + port_position(pt)), (void *)str, len);
      port_position(pt) = new_len;
    }
}

static void string_display(s7_scheme *sc, const char *s, s7_pointer port)
{
  if (s)
    string_write_string(sc, s, safe_strlen(s), port);
}


static void function_display(s7_scheme *sc, const char *s, s7_pointer port)
{
  if (s)
    {
      for (; *s; s++)
	(*(port_output_function(port)))(sc, *s, port);
    }
}

static void function_write_string(s7_scheme *sc, const char *str, int len, s7_pointer pt)
{
  int i;
  for (i = 0; i < len; i++)
    (*(port_output_function(pt)))(sc, str[i], pt);
}

static void stdout_display(s7_scheme *sc, const char *s, s7_pointer port)
{
  if (s) fputs(s, stdout);
}


static void stderr_display(s7_scheme *sc, const char *s, s7_pointer port)
{
  if (s) fputs(s, stderr);
}


static s7_pointer g_write_string(s7_scheme *sc, s7_pointer args)
{
  #define H_write_string "(write-string str port start end) writes str to port."
  s7_pointer str, port;
  s7_Int start = 0, end;

  str = car(args);
  if (!is_string(str))
    {
      check_method(sc, str, sc->WRITE_STRING, args);
      return(wrong_type_argument(sc, sc->WRITE_STRING, small_int(1), str, T_STRING));
    }
  end = string_length(str);
  if (!is_null(cdr(args)))
    {
      s7_pointer p;
      port = cadr(args);
      p = start_and_end(sc, sc->WRITE_STRING, NULL, cddr(args), args, 3, &start, &end);
      if (p != sc->GC_NIL) return(p);
    }
  else port = sc->output_port;
  if (!is_output_port(port))
    {
      if (port == sc->F) return(sc->UNSPECIFIED);
      check_method(sc, port, sc->WRITE_STRING, args);
      return(wrong_type_argument_with_type(sc, sc->WRITE_STRING, small_int(2), port, AN_OUTPUT_PORT));
    }

  if (start == 0)
    port_write_string(port)(sc, string_value(str), end, port);
  else port_write_string(port)(sc, (char *)(string_value(str) + start), (end - start), port);
  return(sc->UNSPECIFIED);
}


/* -------- skip to semicolon readers -------- */

static token_t file_read_semicolon(s7_scheme *sc, s7_pointer pt)
{
  int c;
  do (c = fgetc(port_file(pt))); while ((c != '\n') && (c != EOF));
  port_line_number(pt)++;
  if (c == EOF)
    return(TOKEN_EOF);
  return(token(sc));
}


static token_t string_read_semicolon(s7_scheme *sc, s7_pointer pt)
{
  const char *orig_str, *str;      
  str = (const char *)(port_data(pt) + port_position(pt));
  orig_str = strchr(str, (int)'\n');
  if (!orig_str)
    {
      port_position(pt) = port_data_size(pt);
      return(TOKEN_EOF);
    }
  port_position(pt) += (orig_str - str + 1); /* + 1 because strchr leaves orig_str pointing at the newline */
  port_line_number(pt)++;
  return(token(sc));
}


/* -------- white space readers -------- */

static int file_read_white_space(s7_scheme *sc, s7_pointer port)
{
  int c;
  while (is_white_space(c = fgetc(port_file(port))))
    if (c == '\n')
      port_line_number(port)++;
  return(c);
}


static int terminated_string_read_white_space(s7_scheme *sc, s7_pointer pt)
{
  const unsigned char *str;
  unsigned char c;
  /* here we know we have null termination and white_space[#\null] is false.
   */
  str = (const unsigned char *)(port_data(pt) + port_position(pt));

  while (white_space[c = *str++]) /* (let ((ÿa 1)) ÿa) -- 255 is not -1 = EOF */
    if (c == '\n')
      port_line_number(pt)++;
  if (c)
    port_position(pt) = str - port_data(pt);
  else port_position(pt) = port_data_size(pt);
  return((int)c);
}


/* name (alphanumeric token) readers */

static void resize_strbuf(s7_scheme *sc, unsigned int needed_size)
{
  unsigned int i, old_size;
  old_size = sc->strbuf_size;
  while (sc->strbuf_size <= needed_size) sc->strbuf_size *= 2;
  sc->strbuf = (char *)realloc(sc->strbuf, sc->strbuf_size * sizeof(char));
  for (i = old_size; i < sc->strbuf_size; i++) sc->strbuf[i] = '\0';
}


static s7_pointer file_read_name_or_sharp(s7_scheme *sc, s7_pointer pt, bool atom_case)
{
  int c;
  unsigned i = 1;
  /* sc->strbuf[0] has the first char of the string we're reading */

  do {
    c = fgetc(port_file(pt)); /* might return EOF */
    if (c == '\n')
      port_line_number(pt)++;
    
    sc->strbuf[i++] = c;
    if (i >= sc->strbuf_size)
      resize_strbuf(sc, i);
  } while ((c != EOF) && (char_ok_in_a_name[c]));
  
  if ((i == 2) && 
      (sc->strbuf[0] == '\\'))
    sc->strbuf[2] = '\0';
  else 
    {
      if (c != EOF)
	{
	  if (c == '\n')
	    port_line_number(pt)--;
	  ungetc(c, port_file(pt));
	}
      sc->strbuf[i - 1] = '\0';
    }

  if (atom_case)
    return(make_atom(sc, sc->strbuf, BASE_10, SYMBOL_OK, WITH_OVERFLOW_ERROR));
  
  return(make_sharp_constant(sc, sc->strbuf, UNNESTED_SHARP, BASE_10, WITH_OVERFLOW_ERROR));
}

static s7_pointer file_read_name(s7_scheme *sc, s7_pointer pt)
{
  return(file_read_name_or_sharp(sc, pt, true));
}

static s7_pointer file_read_sharp(s7_scheme *sc, s7_pointer pt)
{
  return(file_read_name_or_sharp(sc, pt, false));
}


static s7_pointer string_read_name_no_free(s7_scheme *sc, s7_pointer pt)
{
  /* sc->strbuf[0] has the first char of the string we're reading */
  unsigned int k;
  char *str, *orig_str;

  str = (char *)(port_data(pt) + port_position(pt));

  if (!char_ok_in_a_name[(unsigned char)*str])
    {
      s7_pointer result;
      result = sc->singletons[(unsigned char)(sc->strbuf[0])];
      if (!result)
	{
	  sc->strbuf[1] = '\0';
	  result = make_symbol_with_length(sc, sc->strbuf, 1);
	  sc->singletons[(unsigned char)(sc->strbuf[0])] = result;
	}
      return(result);
    }
  
  orig_str = (char *)(str - 1);
  str++;
  while (char_ok_in_a_name[(unsigned char)(*str)]) {str++;}
  k = str - orig_str;
  if (*str != 0) 
    port_position(pt) += (k - 1);
  else port_position(pt) += k;

  /* this is equivalent to:
   *    str = strpbrk(str, "(); \"\t\r\n");
   *    if (!str) 
   *      {
   *        k = strlen(orig_str);
   *        str = (char *)(orig_str + k);
   *      }
   *    else k = str - orig_str;
   * but slightly faster.
   */

  if (!number_table[(unsigned char)(*orig_str)])
    return(make_symbol_with_length(sc, orig_str, k));

  /* eval_c_string string is a constant so we can't set and unset the token's end char 
   */
  if ((k + 1) >= sc->strbuf_size)
    resize_strbuf(sc, k + 1);
  
  memcpy((void *)(sc->strbuf), (void *)orig_str, k);
  sc->strbuf[k] = '\0';
  return(make_atom(sc, sc->strbuf, BASE_10, SYMBOL_OK, WITH_OVERFLOW_ERROR));
}


static s7_pointer string_read_sharp(s7_scheme *sc, s7_pointer pt)
{
  /* sc->strbuf[0] has the first char of the string we're reading.
   *   since a *#readers* function might want to get further input, we can't mess with the input even when it is otherwise safe
   */
  unsigned int k;
  char *orig_str, *str;

  str = (char *)(port_data(pt) + port_position(pt));

  if (!char_ok_in_a_name[(unsigned char)*str])
    {
      if (sc->strbuf[0] == 'f')
	return(sc->F);
      if (sc->strbuf[0] == 't')
	return(sc->T);
      if (sc->strbuf[0] == '\\')
	{
	  /* must be from #\( and friends -- a character that happens to be not ok-in-a-name */
	  sc->strbuf[1] = str[0];
	  sc->strbuf[2] = '\0';
	  port_position(pt)++;
	}
      else sc->strbuf[1] = '\0';
      return(make_sharp_constant(sc, sc->strbuf, UNNESTED_SHARP, BASE_10, WITH_OVERFLOW_ERROR));
    }

  orig_str = (char *)(str - 1);
  str++;
  while (char_ok_in_a_name[(unsigned char)(*str)]) {str++;}
  k = str - orig_str;
  if (*str != 0) 
    port_position(pt) += (k - 1);
  else port_position(pt) += k;

  if ((k + 1) >= sc->strbuf_size)
    resize_strbuf(sc, k + 1);
  
  memcpy((void *)(sc->strbuf), (void *)orig_str, k);
  sc->strbuf[k] = '\0';
  return(make_sharp_constant(sc, sc->strbuf, UNNESTED_SHARP, BASE_10, WITH_OVERFLOW_ERROR));
}


static s7_pointer string_read_name(s7_scheme *sc, s7_pointer pt)
{
  /* port_string was allocated (and read from a file) so we can mess with it directly */
  s7_pointer result;
  unsigned int k;
  char *orig_str, *str;
  char endc;
  
  str = (char *)(port_data(pt) + port_position(pt));
  if (!char_ok_in_a_name[(unsigned char)*str])
    {
      s7_pointer result;
      result = sc->singletons[(unsigned char)(sc->strbuf[0])];
      if (!result)
	{
	  sc->strbuf[1] = '\0';
	  result = make_symbol_with_length(sc, sc->strbuf, 1);
	  sc->singletons[(unsigned char)(sc->strbuf[0])] = result;
	}
      return(result);
    }

  orig_str = (char *)(str - 1);
  str++;
  while (char_ok_in_a_name[(unsigned char)(*str)]) {str++;}
  k = str - orig_str;
  if (*str != 0) 
    port_position(pt) += (k - 1);
  else port_position(pt) += k;

  if (!number_table[(unsigned char)(*orig_str)])
    return(make_symbol_with_length(sc, orig_str, k));

  endc = (*str);
  (*str) = '\0';
  result = make_atom(sc, orig_str, BASE_10, SYMBOL_OK, WITH_OVERFLOW_ERROR);
  (*str) = endc;
  return(result);
}


static s7_pointer read_file(s7_scheme *sc, FILE *fp, const char *name, long max_size, const char *caller)
{
  s7_pointer port;
#ifndef _MSC_VER
  long size;
#endif
  int port_loc;

  NEW_CELL(sc, port);
  port_loc = s7_gc_protect(sc, port);
  set_type(port, T_INPUT_PORT);
  port_port(port) = alloc_port(sc);
  port_is_closed(port) = false;
  port_original_input_string(port) = sc->NIL;
  port_write_character(port) = input_write_char;
  port_write_string(port) = input_write_string;

  /* if we're constantly opening files, and each open saves the file name in permanent
   *   memory, we gradually core-up.  
   */
  port_filename_length(port) = safe_strlen(name);
  port_filename(port) = copy_string_with_length(name, port_filename_length(port));
  port_line_number(port) = 1;  /* first line is numbered 1 */
  add_input_port(sc, port);

#ifndef _MSC_VER
  /* this doesn't work in MS C */
  fseek(fp, 0, SEEK_END);
  size = ftell(fp);
  rewind(fp);

  /* pseudo files (under /proc for example) have size=0, but we can read them, so don't assume a 0 length file is empty 
   */

  if ((size > 0) &&                /* if (size != 0) we get (open-input-file "/dev/tty") -> (open "/dev/tty") read 0 bytes of an expected -1? */
      ((max_size < 0) || (size < max_size)))
    {
      size_t bytes;
      unsigned char *content;

      content = (unsigned char *)malloc((size + 2) * sizeof(unsigned char));
      bytes = fread(content, sizeof(unsigned char), size, fp);
      if (bytes != (size_t)size)
	{
	  char tmp[256];
	  int len;
	  len = snprintf(tmp, 256, "(%s \"%s\") read %ld bytes of an expected %ld?", caller, name, (long)bytes, size);
	  port_write_string(sc->output_port)(sc, tmp, len, sc->output_port);
	  size = bytes;
	}
      content[size] = '\0';
      content[size + 1] = '\0';
      fclose(fp);

      port_type(port) = STRING_PORT;
      port_data(port) = content;
      port_data_size(port) = size;
      port_position(port) = 0;
      port_needs_free(port) = true;
      port_gc_loc(port) = -1;
      port_read_character(port) = string_read_char;
      port_read_line(port) = string_read_line;
      port_display(port) = input_display;
      port_read_semicolon(port) = string_read_semicolon;
      port_read_white_space(port) = terminated_string_read_white_space;
      port_read_name(port) = string_read_name;
      port_read_sharp(port) = string_read_sharp;
    }
  else
    {
      port_file(port) = fp;
      port_type(port) = FILE_PORT;
      port_needs_free(port) = false;
      port_read_character(port) = file_read_char;
      port_read_line(port) = file_read_line;
      port_display(port) = input_display;
      port_read_semicolon(port) = file_read_semicolon;
      port_read_white_space(port) = file_read_white_space;
      port_read_name(port) = file_read_name;
      port_read_sharp(port) = file_read_sharp; /* was string_read_sharp?? */
    }
#else
  /* _stat64 is no better than the fseek/ftell route, and 
   *    GetFileSizeEx and friends requires Windows.h which makes hash of everything else.
   *    fread until done takes too long on big files, so use a file port
   */
  port_file(port) = fp;
  port_type(port) = FILE_PORT;
  port_needs_free(port) = false;
  port_read_character(port) = file_read_char;
  port_read_line(port) = file_read_line;
  port_display(port) = input_display;
  port_read_semicolon(port) = file_read_semicolon;
  port_read_white_space(port) = file_read_white_space;
  port_read_name(port) = file_read_name;
  port_read_sharp(port) = file_read_sharp;
#endif

  s7_gc_unprotect_at(sc, port_loc);
  return(port);
}


static s7_pointer make_input_file(s7_scheme *sc, const char *name, FILE *fp)
{
  #define MAX_SIZE_FOR_STRING_PORT 5000000
  return(read_file(sc, fp, name, MAX_SIZE_FOR_STRING_PORT, "open"));
}

#if (!MS_WINDOWS)
#include <sys/stat.h>
#endif

static bool is_directory(const char *filename)
{
#if (!MS_WINDOWS)
  #ifdef S_ISDIR
    struct stat statbuf;
    return((stat(filename, &statbuf) >= 0) &&
	   (S_ISDIR(statbuf.st_mode)));
  #endif
#endif
  return(false);
}


static s7_pointer open_input_file_1(s7_scheme *sc, const char *name, const char *mode, const char *caller)
{
  FILE *fp;
  /* see if we can open this file before allocating a port */

  if (is_directory(name))
    return(file_error(sc, caller, "is a directory", name));

  errno = 0;
  fp = fopen(name, mode);
  if (!fp)
    {
#if (!MS_WINDOWS)
      if (errno == EINVAL)
	return(file_error(sc, caller, "invalid mode", mode));

#if WITH_GCC
      /* catch one special case, "~/..." */
      if ((name[0] == '~') &&
	  (name[1] == '/'))
	{
	  char *home;
	  home = getenv("HOME");
	  if (home)
	    {
	      char *filename;
	      int len;
	      len = safe_strlen(name) + safe_strlen(home) + 1;
	      filename = (char *)malloc(len * sizeof(char));
	      snprintf(filename, len, "%s%s", home, (char *)(name + 1));
	      fp = fopen(filename, "r");
	      free(filename);
	      if (fp)
		return(make_input_file(sc, name, fp));
	    }
	}
#endif
#endif
      return(file_error(sc, caller, strerror(errno), name));
    }
  return(make_input_file(sc, name, fp));
}


s7_pointer s7_open_input_file(s7_scheme *sc, const char *name, const char *mode)
{
  return(open_input_file_1(sc, name, mode, "open-input-file"));
}


static s7_pointer g_open_input_file(s7_scheme *sc, s7_pointer args)
{
  #define H_open_input_file "(open-input-file filename (mode \"r\")) opens filename for reading"
  s7_pointer name = car(args);

  if (!is_string(name))
    {
      check_method(sc, name, sc->OPEN_INPUT_FILE, args);
      return(wrong_type_argument(sc, sc->OPEN_INPUT_FILE, small_int(1), name, T_STRING));
    }
  if (is_pair(cdr(args)))
    {
      if (!is_string(cadr(args)))
	{
	  check_method(sc, cadr(args), sc->OPEN_INPUT_FILE, args);
	  return(wrong_type_argument_with_type(sc, sc->OPEN_INPUT_FILE, small_int(2), cadr(args), 
					       make_string_wrapper(sc, "a string (a mode such as \"r\")")));
	}
      return(open_input_file_1(sc, string_value(name), string_value(cadr(args)), "open-input-file"));
    }
  return(open_input_file_1(sc, string_value(name), "r", "open-input-file"));
}


static void make_standard_ports(s7_scheme *sc)
{
  s7_pointer x;

  /* standard output */
  x = alloc_pointer();
  heap_location(x) = NOT_IN_HEAP;
  set_type(x, T_OUTPUT_PORT | T_IMMUTABLE);
  port_port(x) = (port_t *)calloc(1, sizeof(port_t));
  port_type(x) = FILE_PORT;
  port_data(x) = NULL;
  port_is_closed(x) = false;
  port_filename_length(x) = 8;
  port_filename(x) = copy_string_with_length("*stdout*", 8);
  port_file_number(x) = remember_file_name(sc, port_filename(x)); /* these numbers need to be correct for the evaluator (__FUNC__ data) */
  port_line_number(x) = 0;
  port_file(x) = stdout;
  port_needs_free(x) = false;
  port_read_character(x) = output_read_char;
  port_read_line(x) = output_read_line;
  port_display(x) = stdout_display;
  port_write_character(x) = stdout_write_char;
  port_write_string(x) = stdout_write_string;
  sc->standard_output = x;

  /* standard error */
  x = alloc_pointer();
  heap_location(x) = NOT_IN_HEAP;
  set_type(x, T_OUTPUT_PORT | T_IMMUTABLE);
  port_port(x) = (port_t *)calloc(1, sizeof(port_t));
  port_type(x) = FILE_PORT;
  port_data(x) = NULL;
  port_is_closed(x) = false;
  port_filename_length(x) = 8;
  port_filename(x) = copy_string_with_length("*stderr*", 8);
  port_file_number(x) = remember_file_name(sc, port_filename(x));
  port_line_number(x) = 0;
  port_file(x) = stderr;
  port_needs_free(x) = false;
  port_read_character(x) = output_read_char;
  port_read_line(x) = output_read_line;
  port_display(x) = stderr_display;
  port_write_character(x) = stderr_write_char;
  port_write_string(x) = stderr_write_string;
  sc->standard_error = x;

  /* standard input */
  x = alloc_pointer();
  heap_location(x) = NOT_IN_HEAP;
  set_type(x, T_INPUT_PORT | T_IMMUTABLE);
  port_port(x) = (port_t *)calloc(1, sizeof(port_t));
  port_type(x) = FILE_PORT;
  port_is_closed(x) = false;
  port_original_input_string(x) = sc->NIL;
  port_filename_length(x) = 7;
  port_filename(x) = copy_string_with_length("*stdin*", 7);
  port_file_number(x) = remember_file_name(sc, port_filename(x));
  port_line_number(x) = 0;
  port_file(x) = stdin;
  port_needs_free(x) = false;
  port_read_character(x) = file_read_char;
  port_read_line(x) = stdin_read_line;
  port_display(x) = input_display;
  port_read_semicolon(x) = file_read_semicolon;
  port_read_white_space(x) = file_read_white_space;
  port_read_name(x) = file_read_name;
  port_read_sharp(x) = file_read_sharp;
  port_write_character(x) = input_write_char;
  port_write_string(x) = input_write_string;
  sc->standard_input = x;

  s7_define_constant(sc, "*stdin*", sc->standard_input);
  s7_define_constant(sc, "*stdout*", sc->standard_output);
  s7_define_constant(sc, "*stderr*", sc->standard_error);

  sc->input_port = sc->standard_input;
  sc->output_port = sc->standard_output;
  sc->error_port = sc->standard_error;
  sc->current_file = NULL;
  sc->current_line = -1;
}


s7_pointer s7_open_output_file(s7_scheme *sc, const char *name, const char *mode)
{
  FILE *fp;
  s7_pointer x;
  /* see if we can open this file before allocating a port */
  
  errno = 0;
  fp = fopen(name, mode);
  if (!fp)
    {
#if (!MS_WINDOWS)
      if (errno == EINVAL)
	return(file_error(sc, "open-output-file", "invalid mode", mode));
#endif
      return(file_error(sc, "open-output-file", strerror(errno), name));
    }

  NEW_CELL(sc, x);
  set_type(x, T_OUTPUT_PORT);
  
  port_port(x) = alloc_port(sc);
  port_type(x) = FILE_PORT;
  port_is_closed(x) = false;
  port_filename_length(x) = safe_strlen(name);
  port_filename(x) = copy_string_with_length(name, port_filename_length(x));
  port_line_number(x) = 1;
  port_file(x) = fp;
  port_needs_free(x) = false;
  port_read_character(x) = output_read_char;
  port_read_line(x) = output_read_line;
  port_display(x) = file_display;
  port_write_character(x) = file_write_char;
  port_write_string(x) = file_write_string;
  port_position(x) = 0;
  port_data_size(x) = PORT_DATA_SIZE;
  port_data(x) = (unsigned char *)malloc(PORT_DATA_SIZE + 8);
  add_output_port(sc, x);
  return(x);
}


static s7_pointer g_open_output_file(s7_scheme *sc, s7_pointer args)
{
  #define H_open_output_file "(open-output-file filename (mode \"w\")) opens filename for writing"
  s7_pointer name = car(args);

  if (!is_string(name))
    {
      check_method(sc, name, sc->OPEN_OUTPUT_FILE, args);
      return(wrong_type_argument(sc, sc->OPEN_OUTPUT_FILE, small_int(1), name, T_STRING));
    }
  if (is_pair(cdr(args)))
    {
      if (!is_string(cadr(args)))
	{
	  check_method(sc, cadr(args), sc->OPEN_OUTPUT_FILE, args);
	  return(wrong_type_argument_with_type(sc, sc->OPEN_OUTPUT_FILE, small_int(2), cadr(args), 
					       make_string_wrapper(sc, "a string (a mode such as \"w\")")));
	}
      return(s7_open_output_file(sc, string_value(name), string_value(cadr(args))));
    }
  return(s7_open_output_file(sc, string_value(name), "w"));
}


static s7_pointer open_input_string(s7_scheme *sc, const char *input_string, int len)
{
  s7_pointer x;
  NEW_CELL(sc, x);
  set_type(x, T_INPUT_PORT);
  
  port_port(x) = alloc_port(sc);
  port_type(x) = STRING_PORT;
  port_is_closed(x) = false;
  port_original_input_string(x) = sc->NIL;
  port_data(x) = (unsigned char *)input_string;
  port_data_size(x) = len;
  port_position(x) = 0;
  port_filename_length(x) = 0;
  port_filename(x) = NULL;
  port_file_number(x) = -1;
  port_needs_free(x) = false;
  port_gc_loc(x) = -1;
  port_read_character(x) = string_read_char;
  port_read_line(x) = string_read_line;
  port_display(x) = input_display;
  port_read_semicolon(x) = string_read_semicolon;
#if DEBUGGING
  if (input_string[len] != '\0')
    fprintf(stderr, "read_white_space string is not terminated: %s", input_string);
#endif
  port_read_white_space(x) = terminated_string_read_white_space;
  port_read_name(x) = string_read_name_no_free;
  port_read_sharp(x) = string_read_sharp;
  port_write_character(x) = input_write_char;
  port_write_string(x) = input_write_string;
  add_input_port(sc, x);
  return(x);
}


static s7_pointer open_and_protect_input_string(s7_scheme *sc, s7_pointer str)
{
  s7_pointer p;
  p = open_input_string(sc, string_value(str), string_length(str));
  port_gc_loc(p) = s7_gc_protect(sc, str);
  return(p);
}


s7_pointer s7_open_input_string(s7_scheme *sc, const char *input_string)
{
  return(open_input_string(sc, input_string, safe_strlen(input_string)));
}


static s7_pointer g_open_input_string(s7_scheme *sc, s7_pointer args)
{
  #define H_open_input_string "(open-input-string str) opens an input port reading str"
  s7_pointer input_string, port;

  input_string = car(args);
  if (!is_string(input_string))
    {
      check_method(sc, input_string, sc->OPEN_INPUT_STRING, args);
      return(simple_wrong_type_argument(sc, sc->OPEN_INPUT_STRING, input_string, T_STRING));
    }
  port = open_and_protect_input_string(sc, input_string);
  return(port);
}


#define FORMAT_PORT_LENGTH 128
/* the large majority (> 99% in my tests) of the output strings have less than 128 chars when the port is finally closed 
 *   256 is slightly slower (the calloc time below dominates the realloc time in string_write_string)
 *   64 is much slower (realloc dominates)  so sc->initial_string_port_length defaults to 128
 */

static s7_pointer open_output_string(s7_scheme *sc, int len)
{
  s7_pointer x;
  NEW_CELL(sc, x);
  set_type(x, T_OUTPUT_PORT);
  
  port_port(x) = alloc_port(sc);
  port_type(x) = STRING_PORT;
  port_is_closed(x) = false;
  port_data_size(x) = len;
  port_data(x) = (unsigned char *)calloc(len + 8, sizeof(unsigned char));
  port_position(x) = 0;
  port_needs_free(x) = true;
  port_read_character(x) = output_read_char;
  port_read_line(x) = output_read_line;
  port_display(x) = string_display;
  port_write_character(x) = string_write_char;
  port_write_string(x) = string_write_string;
  add_output_port(sc, x);
  return(x);
}

s7_pointer s7_open_output_string(s7_scheme *sc)
{
  return(open_output_string(sc, sc->initial_string_port_length));
}


static s7_pointer g_open_output_string(s7_scheme *sc, s7_pointer args)
{
  #define H_open_output_string "(open-output-string) opens an output string port"
  return(s7_open_output_string(sc));
}


const char *s7_get_output_string(s7_scheme *sc, s7_pointer p)
{
  return((const char *)port_data(p));
}


static s7_pointer g_get_output_string(s7_scheme *sc, s7_pointer args)
{
  #define H_get_output_string "(get-output-string port) returns the output accumulated in port"
  s7_pointer p = car(args);

  if ((!is_output_port(p)) ||
      (!is_string_port(p)))
    {
      if (p == sc->F) return(make_empty_string(sc, 0, 0));
      check_method(sc, p, sc->GET_OUTPUT_STRING, args);
      return(simple_wrong_type_argument_with_type(sc, sc->GET_OUTPUT_STRING, p, make_string_wrapper(sc, "an output string port")));
    }
  if (port_is_closed(p))
    return(simple_wrong_type_argument_with_type(sc, sc->GET_OUTPUT_STRING, p, make_string_wrapper(sc, "an active (open) string port")));

  return(s7_make_string_with_length(sc, (const char *)port_data(p), port_position(p)));
}


s7_pointer s7_open_input_function(s7_scheme *sc, s7_pointer (*function)(s7_scheme *sc, s7_read_t read_choice, s7_pointer port))
{
  s7_pointer x;
  NEW_CELL(sc, x);
  set_type(x, T_INPUT_PORT);
  
  port_port(x) = alloc_port(sc);
  port_type(x) = FUNCTION_PORT;
  port_is_closed(x) = false;
  port_original_input_string(x) = sc->NIL;
  port_needs_free(x) = false;
  port_input_function(x) = function;
  port_read_character(x) = function_read_char;
  port_read_line(x) = function_read_line;
  port_display(x) = input_display;
  port_write_character(x) = input_write_char;
  port_write_string(x) = input_write_string;
  add_input_port(sc, x);
  return(x);
}


s7_pointer s7_open_output_function(s7_scheme *sc, void (*function)(s7_scheme *sc, unsigned char c, s7_pointer port))
{
  s7_pointer x;
  NEW_CELL(sc, x);
  set_type(x, T_OUTPUT_PORT);
  
  port_port(x) = alloc_port(sc);
  port_type(x) = FUNCTION_PORT;
  port_data(x) = NULL;
  port_is_closed(x) = false;
  port_needs_free(x) = false;
  port_output_function(x) = function;
  port_read_character(x) = output_read_char;
  port_read_line(x) = output_read_line;
  port_display(x) = function_display;
  port_write_character(x) = function_write_char;
  port_write_string(x) = function_write_string;
  add_output_port(sc, x);
  return(x);
}


static void push_input_port(s7_scheme *sc, s7_pointer new_port)
{
  sc->input_port_stack = cons(sc, sc->input_port, sc->input_port_stack);
  sc->input_port = new_port;
}


static void pop_input_port(s7_scheme *sc)
{
  if (is_pair(sc->input_port_stack))
    {
      sc->input_port = car(sc->input_port_stack);

      /* is this safe? */
      clear_type(sc->input_port_stack);
      (*(sc->free_heap_top++)) = sc->input_port_stack;

      sc->input_port_stack = cdr(sc->input_port_stack);
    }
  else sc->input_port = sc->standard_input;
}


static int inchar(s7_pointer pt)
{
  int c;
  if (is_file_port(pt))
    c = fgetc(port_file(pt)); /* not unsigned char! -- could be EOF */
  else 
    {
      if (port_data_size(pt) <= port_position(pt))
	return(EOF);
      c = (unsigned char)port_data(pt)[port_position(pt)++];
    }

  if (c == '\n')
    port_line_number(pt)++;

  return(c);
}


static void backchar(char c, s7_pointer pt) 
{
  if (c == '\n')
    port_line_number(pt)--;

  if (is_file_port(pt))
    ungetc(c, port_file(pt));
  else 
    {
      if (port_position(pt) > 0)
	port_position(pt)--;
    }
}


int s7_read_char(s7_scheme *sc, s7_pointer port)
{
  /* needs to be int return value so EOF=-1, but not 255 */
  return(port_read_character(port)(sc, port));
}


int s7_peek_char(s7_scheme *sc, s7_pointer port)
{
  int c;              /* needs to be an int so EOF=-1, but not 255 */
  c = port_read_character(port)(sc, port);
  if (c != EOF)
    backchar(c, port);
  return(c);
}


void s7_write_char(s7_scheme *sc, int c, s7_pointer pt) 
{
  if (pt != sc->F)
    port_write_character(pt)(sc, c, pt);
}


static s7_pointer g_read_char(s7_scheme *sc, s7_pointer args)
{
  #define H_read_char "(read-char (port (current-input-port))) returns the next character in the input port"

  s7_pointer port;

  if (is_not_null(args))
    port = car(args);
  else 
    {
      port = sc->input_port;
      if (is_loader_port(port))
	return(sc->EOF_OBJECT);
    }
  if (!is_input_port(port))
    {
      check_method(sc, port, sc->READ_CHAR, args);
      return(simple_wrong_type_argument_with_type(sc, sc->READ_CHAR, port, AN_INPUT_PORT));
    }
  return(chars[port_read_character(port)(sc, port)]);
}


static s7_pointer read_char_0, read_char_1;
static s7_pointer g_read_char_0(s7_scheme *sc, s7_pointer args)
{
  return(chars[port_read_character(sc->input_port)(sc, sc->input_port)]);
}


static s7_pointer g_read_char_1(s7_scheme *sc, s7_pointer args)
{
  s7_pointer port;
  port = car(args);
  if (!is_input_port(port))
    {
      check_method(sc, port, sc->READ_CHAR, args);
      return(simple_wrong_type_argument_with_type(sc, sc->READ_CHAR, port, AN_INPUT_PORT));
    }
  return(chars[port_read_character(port)(sc, port)]);
}


static s7_pointer read_char_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if (args == 0)
    return(read_char_0);
  if (args == 1)
    return(read_char_1);
  return(f);
}


static s7_pointer g_write_char(s7_scheme *sc, s7_pointer args)
{
  #define H_write_char "(write-char char (port (current-output-port))) writes char to the output port"
  s7_pointer port, chr;
  
  chr = car(args);
  if (!s7_is_character(chr))
    {
      check_method(sc, chr, sc->WRITE_CHAR, args);
      return(wrong_type_argument(sc, sc->WRITE_CHAR, small_int(1), chr, T_CHARACTER));
    }
  if (is_pair(cdr(args)))
    port = cadr(args);
  else port = sc->output_port;
  if (port == sc->F) return(sc->UNSPECIFIED);
  if (!is_output_port(port))
    {
      check_method(sc, port, sc->WRITE_CHAR, args);
      return(wrong_type_argument_with_type(sc, sc->WRITE_CHAR, small_int(2), port, AN_OUTPUT_PORT));
    }

  port_write_character(port)(sc, s7_character(chr), port);
  return(sc->UNSPECIFIED);
}

static s7_pointer write_char_1;
static s7_pointer g_write_char_1(s7_scheme *sc, s7_pointer args)
{
  s7_pointer chr;
  
  chr = car(args);
  if (!s7_is_character(chr))
    {
      check_method(sc, chr, sc->WRITE_CHAR, args);
      return(wrong_type_argument(sc, sc->WRITE_CHAR, small_int(1), chr, T_CHARACTER));
    }
  port_write_character(sc->output_port)(sc, s7_character(chr), sc->output_port);
  return(sc->UNSPECIFIED);
}


static s7_pointer write_char_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if (args == 1)
    return(write_char_1);
  return(f);
}

/* (with-output-to-string (lambda () (write-char #\space))) -> " "
 * (with-output-to-string (lambda () (write #\space))) -> "#\\space"
 * (with-output-to-string (lambda () (display #\space))) -> " "
 * is this correct?  It's what Guile does.  write-char is actually display-char.
 */


static s7_pointer g_peek_char(s7_scheme *sc, s7_pointer args)
{
  #define H_peek_char "(peek-char (port (current-input-port))) returns the next character in the input port, but does not remove it from the input stream"
  int c;
  s7_pointer port;

  if (is_not_null(args))
    port = car(args);
  else port = sc->input_port;

  if (!is_input_port(port))
    {
      check_method(sc, port, sc->PEEK_CHAR, args);
      return(simple_wrong_type_argument_with_type(sc, sc->PEEK_CHAR, port, AN_INPUT_PORT));
    }
  if (port_is_closed(port))
    return(simple_wrong_type_argument_with_type(sc, sc->PEEK_CHAR, port, AN_OPEN_PORT));

  if (is_function_port(port))
    return((*(port_input_function(port)))(sc, S7_PEEK_CHAR, port));
      
  c = s7_peek_char(sc, port);
  return(chars[c]);
}


static s7_pointer g_read_byte(s7_scheme *sc, s7_pointer args)
{
  #define H_read_byte "(read-byte (port (current-input-port))): reads a byte from the input port"
  s7_pointer port;
  int c;
  
  if (is_not_null(args))
    port = car(args);
  else 
    {
      port = sc->input_port;
      if (is_loader_port(port))
	return(sc->EOF_OBJECT);
    }

  if (!is_input_port(port))
    {
      check_method(sc, port, sc->READ_BYTE, args);
      return(simple_wrong_type_argument_with_type(sc, sc->READ_BYTE, port, AN_INPUT_PORT));
    }
  c = port_read_character(port)(sc, port);
  if (c == EOF)
    return(sc->EOF_OBJECT);
  return(small_int(c));
}


static s7_pointer g_write_byte(s7_scheme *sc, s7_pointer args)
{
  #define H_write_byte "(write-byte byte (port (current-output-port))): writes byte to the output port"
  s7_pointer port, b;
  s7_Int val;

  b = car(args);
  if (!s7_is_integer(b))
    {
      check_method(sc, car(args), sc->WRITE_BYTE, args);
      return(wrong_type_argument(sc, sc->WRITE_BYTE, small_int(1), b, T_INTEGER));
    }
  if (is_pair(cdr(args)))
    port = cadr(args);
  else port = sc->output_port;

  if (!is_output_port(port))
    {
      if (port == sc->F) return(car(args));
      check_method(sc, port, sc->WRITE_BYTE, args);
      return(simple_wrong_type_argument_with_type(sc, sc->WRITE_BYTE, port, AN_OUTPUT_PORT));
    }

  val = s7_integer(b);
  if ((val < 0) || (val > 255))
    return(wrong_type_argument_with_type(sc, sc->WRITE_BYTE, small_int(1), b, AN_UNSIGNED_BYTE));
  s7_write_char(sc, (int)(s7_integer(b)), port);
  return(car(args));
}


static s7_pointer g_read_line(s7_scheme *sc, s7_pointer args)
{
  #define H_read_line "(read-line port (with-eol #f)) returns the next line from port, or #<eof>.\
If 'with-eol' is not #f, read-line includes the trailing end-of-line character."

  s7_pointer port;
  bool with_eol = false;

  if (is_not_null(args))
    {
      port = car(args);
      if (!is_input_port(port))
	{
	  check_method(sc, port, sc->READ_LINE, args);
	  return(wrong_type_argument_with_type(sc, sc->READ_LINE, small_int(1), port, AN_INPUT_PORT));
	}
      if (is_not_null(cdr(args)))
	{
	  /* support (read-line fp 'concat) for compatibility with guile */
	  if ((!s7_is_boolean(cadr(args))) &&
	      (!is_symbol(cadr(args))))
	    return(wrong_type_argument(sc, sc->READ_LINE, small_int(2), cadr(args), T_BOOLEAN));
	  if (cadr(args) != sc->F)
	    with_eol = true;
	}
    }
  else 
    {
      port = sc->input_port;
      if (is_loader_port(port))
	return(sc->EOF_OBJECT);
    }
  return(port_read_line(port)(sc, port, with_eol));
}


static s7_pointer g_read_string(s7_scheme *sc, s7_pointer args)
{
  #define H_read_string "(read-string k port) reads k characters from port into a new string and returns it."
  s7_pointer k, port, str;
  s7_Int i, chars;

  k = car(args);
  if (!s7_is_integer(k))
    {
      check_method(sc, k, sc->READ_STRING, args);
      return(wrong_type_argument(sc, sc->READ_STRING, small_int(1), k, T_INTEGER));
    }
  chars = s7_integer(k);
  if (chars < 0)
    return(wrong_type_argument_with_type(sc, sc->READ_STRING, small_int(1), k, A_NON_NEGATIVE_INTEGER));
  if (chars > MAX_STRING_LENGTH)
    return(out_of_range(sc, sc->READ_STRING, small_int(1), k, ITS_TOO_LARGE));

  if (chars == 0)
    return(make_empty_string(sc, 0, 0));

  if (!is_null(cdr(args)))
    {
      port = cadr(args);
      if (!is_input_port(port))
	{
	  check_method(sc, port, sc->READ_STRING, args);
	  return(wrong_type_argument_with_type(sc, sc->READ_STRING, small_int(2), port, AN_INPUT_PORT));
	}
    }
  else 
    {
      port = sc->input_port;
      if (is_loader_port(port))
	return(sc->EOF_OBJECT);
    }

  str = make_empty_string(sc, chars, 0);
  for (i = 0; i < chars; i++)
    {
      int c;
      c = port_read_character(port)(sc, port);
      if (c == EOF)
	{
	  if (i == 0)
	    return(sc->EOF_OBJECT);
	  string_length(str) = i;
	  return(str);
	}
      string_value(str)[i] = (unsigned int)c;
    }
  return(str);
}


s7_pointer s7_read(s7_scheme *sc, s7_pointer port)
{
  if (is_input_port(port))
    {
      bool old_longjmp;
      s7_pointer old_envir;
      old_envir = sc->envir;
      sc->envir = sc->NIL; 
      if (sc->longjmp_ok)
	{
	  push_input_port(sc, port);
	  push_stack(sc, OP_BARRIER, port, sc->NIL);
	  push_stack(sc, OP_EVAL_DONE, sc->args, sc->code);  
	  eval(sc, OP_READ_INTERNAL);
	  pop_input_port(sc);
	  sc->envir = old_envir;
	  return(sc->value);
	}
      stack_reset(sc); 
      push_stack(sc, OP_EVAL_DONE, old_envir, sc->NIL); /* GC protect envir */
      push_input_port(sc, port);
      old_longjmp = sc->longjmp_ok;
      if (!sc->longjmp_ok)
	{
	  sc->longjmp_ok = true;
	  if (setjmp(sc->goto_start) != 0)
	    eval(sc, sc->op);
	  else eval(sc, OP_READ_INTERNAL);
	}
      sc->longjmp_ok = old_longjmp;
      pop_input_port(sc);
      sc->envir = old_envir;  
      return(sc->value);
    }
  return(simple_wrong_type_argument_with_type(sc, sc->READ, port, AN_INPUT_PORT));
}


static s7_pointer g_read(s7_scheme *sc, s7_pointer args)
{
  /* would it be useful to add an environment arg here?  (just set sc->envir at the end?)
   *    except for expansions, nothing is evaluated at read time, unless...
   *    say we set up a dot reader:
   *        (set! *#readers* (cons (cons #\. (lambda (str) (if (string=? str ".") (eval (read)) #f))) *#readers*))
   *    then 
   *        (call-with-input-string "(+ 1 #.(+ 1 hiho))" (lambda (p) (read p)))
   *    evaluates hiho in the rootlet, but how to pass the env to the inner eval or read?
   * (eval, eval-string and load already have an env arg)
   */
  #define H_read "(read (port (current-input-port))) returns the next object in the input port, or #<eof> at the end"
  s7_pointer port;
  
  if (is_not_null(args))
    port = car(args);
  else 
    {
      port = sc->input_port;
      if (is_loader_port(port))
	return(sc->EOF_OBJECT);
    }
  
  if (!is_input_port(port)) /* was also not stdin */
    {
      check_method(sc, port, sc->READ, args);
      return(simple_wrong_type_argument_with_type(sc, sc->READ, port, AN_INPUT_PORT));
    }

  if (is_function_port(port))
    return((*(port_input_function(port)))(sc, S7_READ, port));
  
  if ((is_string_port(port)) &&
      (port_data_size(port) <= port_position(port)))
    return(sc->EOF_OBJECT);

  push_input_port(sc, port);
  push_stack(sc, OP_READ_DONE, sc->NIL, sc->NIL); /* this stops the internal read process so we only get one form */
  push_stack(sc, OP_READ_INTERNAL, sc->NIL, sc->NIL);

  return(port);
}


/* -------------------------------- load -------------------------------- */

static FILE *search_load_path(s7_scheme *sc, const char *name)
{
  int i, len, name_len;
  s7_pointer lst;

  lst = s7_load_path(sc);
  len = s7_list_length(sc, lst);
  name_len = safe_strlen(name);

  for (i = 0; i < len; i++)
    {
      const char *new_dir;
      new_dir = string_value(s7_list_ref(sc, lst, i));
      if (new_dir)
	{
	  int size;
	  char *new_name;
	  FILE *fp;
	  size = name_len + safe_strlen(new_dir) + 2;
	  new_name = (char *)malloc(size * sizeof(char));
	  snprintf(new_name, size, "%s/%s", new_dir, name);
	  fp = fopen(new_name, "r");
	  free(new_name);
	  if (fp) return(fp);
	}
    }
  return(NULL);
}


static s7_pointer s7_load_1(s7_scheme *sc, const char *filename, s7_pointer e)
{
  s7_pointer port;
  FILE *fp;

  fp = fopen(filename, "r");
  if (!fp)
    fp = search_load_path(sc, filename);
  if (!fp)
    return(file_error(sc, "load", "can't open", filename));

  if (is_pair(s7_hook_functions(sc, sc->load_hook)))
    s7_call(sc, sc->load_hook, list_1(sc, s7_make_string(sc, filename)));

  port = read_file(sc, fp, filename, -1, "load");   /* -1 means always read its contents into a local string */
  port_file_number(port) = remember_file_name(sc, filename);
  set_loader_port(port);
  push_input_port(sc, port);
  
  /* it's possible to call this recursively (s7_load is Xen_load_file which can be invoked via s7_call)
   *   but in that case, we actually want it to behave like g_load and continue the evaluation upon completion
   */
  sc->envir = e;
  
  if (!sc->longjmp_ok)
    {
      bool old_longjmp;
      push_stack(sc, OP_LOAD_RETURN_IF_EOF, port, sc->code);
      
      old_longjmp = sc->longjmp_ok;
      if (!sc->longjmp_ok)
	{
	  sc->longjmp_ok = true;
	  if (setjmp(sc->goto_start) != 0)
	    eval(sc, sc->op);
	  else eval(sc, OP_READ_INTERNAL);
	}
      sc->longjmp_ok = old_longjmp;  
      pop_input_port(sc);
      s7_close_input_port(sc, port);
    }
  else
    {
      /* caller here is assuming the load will be complete before this function returns */
      push_stack(sc, OP_LOAD_RETURN_IF_EOF, sc->args, sc->code);

      eval(sc, OP_READ_INTERNAL);
      pop_input_port(sc);
      s7_close_input_port(sc, port);
    }
  return(sc->value);
}


s7_pointer s7_load(s7_scheme *sc, const char *filename)
{
  return(s7_load_1(sc, filename, sc->NIL));
}


#if WITH_C_LOADER
#include <dlfcn.h>

static char *full_filename(const char *filename)
{
  int len;
  char *pwd, *rtn;
  pwd = getcwd(NULL, 0); /* docs say this means it will return a new string of the right size */
  len = safe_strlen(pwd) + safe_strlen(filename) + 8;
  rtn = (char *)malloc(len * sizeof(char));
  if (pwd)
    {
      snprintf(rtn, len, "%s/%s", pwd, filename);
      free(pwd);
    }
  else snprintf(rtn, len, "%s", filename);
  return(rtn);
}
#endif


static s7_pointer g_load(s7_scheme *sc, s7_pointer args)
{
  #define H_load "(load file (env (rootlet))) loads the scheme file 'file'. The 'env' argument \
defaults to the rootlet.  To load into the current environment instead, pass (curlet)."

  FILE *fp = NULL;
  s7_pointer name, port;
  const char *fname;
  
  name = car(args);
  if (!is_string(name))
    {
      check_method(sc, name, sc->LOAD, args);
      return(wrong_type_argument(sc, sc->LOAD, small_int(1), name, T_STRING));
    }
  if (is_not_null(cdr(args))) 
    {
      s7_pointer e;
      e = cadr(args);
      if (!is_let(e))
	return(wrong_type_argument_with_type(sc, sc->LOAD, small_int(2), e, AN_ENVIRONMENT));
      if (e == sc->rootlet)
	sc->envir = sc->NIL;
      else sc->envir = e; 
    }
  else sc->envir = sc->NIL;
  
  fname = string_value(name);
  if ((!fname) || (!(*fname)))                 /* fopen("", "r") returns a file pointer?? */
    return(s7_error(sc, sc->OUT_OF_RANGE, 
		    list_2(sc, make_string_wrapper(sc, "load's first argument, ~S, should be a filename"), name)));

  if (is_directory(fname))
    return(s7_error(sc, sc->WRONG_TYPE_ARG, 
		    list_2(sc, make_string_wrapper(sc, "load argument, ~S, is a directory"), name)));

#if WITH_C_LOADER
  /* if fname ends in .so, try loading it as a c shared object
   *   (load "/home/bil/cl/m_j0.so" (inlet (cons 'init_func 'init_m_j0)))
   */
  {
    int fname_len;
    fname_len = safe_strlen(fname);
    if ((fname_len > 3) &&
	(is_pair(cdr(args))) &&
	(strcmp((const char *)(fname + (fname_len - 3)), ".so") == 0))
      {
	s7_pointer init;
	init = let_ref_1(sc, sc->envir, s7_make_symbol(sc, "init_func"));
	if (is_symbol(init))
	  {
	    void *library;
	    const char *init_name = NULL;
	    char *pwd_name = NULL;
	    if (fname[0] != '/')
	      pwd_name = full_filename(fname);
	    init_name = symbol_name(init);
	    library = dlopen((pwd_name) ? pwd_name : fname, RTLD_NOW);
	    if (library)
	      {
		void *init_func;
		init_func = dlsym(library, init_name);
		if (init_func)
		  {
		    typedef void *(*dl_func)(s7_scheme *sc);
		    ((dl_func)init_func)(sc); 
		    if (pwd_name) free(pwd_name);
		    return(sc->T);
		  }
		else 
		  {
		    s7_warn(sc, 512, "loaded %s, but can't find %s (%s)?\n", fname, init_name, dlerror());
		    dlclose(library);
		  }
	      }
	    else s7_warn(sc, 512, "load %s failed: %s\n", (pwd_name) ? pwd_name : fname, dlerror());
	    if (pwd_name) free(pwd_name);
	  }
	else s7_warn(sc, 512, "can't load %s: no init function\n", fname);
	return(sc->F);
      }
  }
#endif

  fp = fopen(fname, "r");

#if WITH_GCC
  if (!fp)
    {
      /* catch one special case, "~/..." since it causes 99.9% of the "can't load ..." errors
       */
      if ((fname[0] == '~') &&
	  (fname[1] == '/'))
	{
	  char *home;
	  home = getenv("HOME");
	  if (home)
	    {
	      char *filename;
	      int len;
	      len = safe_strlen(fname) + safe_strlen(home) + 1;
	      filename = (char *)malloc(len * sizeof(char));
	      snprintf(filename, len, "%s%s", home, (char *)(fname + 1));
	      fp = fopen(filename, "r");
	      free(filename);
	    }
	}
    }
#endif

  if (!fp)
    {
      fp = search_load_path(sc, fname);
      if (!fp)
	return(file_error(sc, "load", "can't open", fname));
    }

  port = read_file(sc, fp, fname, -1, "load");
  port_file_number(port) = remember_file_name(sc, fname);
  set_loader_port(port);
  push_input_port(sc, port); 

  push_stack(sc, OP_LOAD_CLOSE_AND_POP_IF_EOF, sc->NIL, sc->NIL);  /* was pushing args and code, but I don't think they're used later */
  push_stack(sc, OP_READ_INTERNAL, sc->NIL, sc->NIL);
  
  /* now we've opened and moved to the file to be loaded, and set up the stack to return
   *   to where we were when it is read.  Call *load-hook* if it is a procedure.
   */
  
  if (is_not_null(s7_hook_functions(sc, sc->load_hook)))
    s7_apply_function(sc, sc->load_hook, list_1(sc, s7_make_string(sc, fname)));

  return(sc->UNSPECIFIED);
}


s7_pointer s7_load_path(s7_scheme *sc)
{
  return(s7_symbol_value(sc, sc->LOAD_PATH));
}


s7_pointer s7_add_to_load_path(s7_scheme *sc, const char *dir)
{
  s7_symbol_set_value(sc, 
		      sc->LOAD_PATH,
		      cons(sc, 
			   s7_make_string(sc, dir), 
			   s7_symbol_value(sc, sc->LOAD_PATH)));
  return(s7_symbol_value(sc, sc->LOAD_PATH));
}


static s7_pointer g_load_path_set(s7_scheme *sc, s7_pointer args)
{
  /* new value must be either () or a proper list of strings */
  if (is_null(cadr(args))) return(cadr(args));
  if (is_pair(cadr(args)))
    {
      s7_pointer x;
      for (x = cadr(args); is_pair(x); x = cdr(x))
	if (!is_string(car(x)))
	  return(sc->ERROR);
      if (is_null(x))
	return(cadr(args));
    }
  return(sc->ERROR);
}


/* ---------------- autoload ---------------- */

void s7_autoload_set_names(s7_scheme *sc, const char **names, int size)
{
  /* the idea here is that by sticking to string constants we can handle 90% of the work at compile-time,
   *   with less start-up memory.  Then eventually we'll add C libraries a la xg (gtk) as environments
   *   and every name in that library will come as an import once dlopen has picked up the library.
   *   So, hopefully, we can pre-declare as many names as we want from as many libraries as we want,
   *   without a bloated mess of a run-time image.  And new libraries are easy to accommodate --
   *   add the names to be auto-exported to this list with the name of the scheme file that cloads
   *   the library and exports the given name. So, we'll need a separate such file for each library?
   *
   * the environment variable could use the library base name in *: *libm* or *libgtk*
   *   (*libm* 'j0)
   * why not just predeclare these libraries?  The caller could import what he wants via require.
   * So the autoloader need only know which libraries, but this doesn't fit the current use of gtk in xg
   * In fact, we only need to see *libm* -> libm.so etc, but we still need the arg/return types of each function, etc
   * And libgtk is enormous -- seems too bad to tie-in everything via the FFI when we need less than 1% of it.
   * Perhaps each module as an environment within the main one: ((*libgtk* *gtkwidget*) 'gtk_widget_new)?
   * But that requires inside knowlege of the library, and changes without notice.
   *
   * Also we need to decide how to handle name collisions (by order of autoload lib setup)
   * And (lastly?) how to handle different library versions?
   *
   *
   * so autoload known libs here in s7 so we're indepentdent of snd
   *   (currently these are included in make-index.scm[line 575] -> snd-xref.c)
   * for each module, include an env in the lib env (*libgtk* 'gtkwidget.h) or whatever that has the names in that header
   * in autoload below, don't sort! -- just build a list of autoload tables and check each in order at autoload time (we want startup to be fast)
   * for versions, include wrapper macro at end of each c-define choice
   * in the xg case, there's no savings in delaying the defines
   *
   */

  if (sc->autoload_names == NULL)
    {
      sc->autoload_names = (const char ***)calloc(INITIAL_AUTOLOAD_NAMES_SIZE, sizeof(const char **));
      sc->autoload_names_sizes = (int *)calloc(INITIAL_AUTOLOAD_NAMES_SIZE, sizeof(int));
      sc->autoloaded_already = (bool **)calloc(INITIAL_AUTOLOAD_NAMES_SIZE, sizeof(bool *));
      sc->autoload_names_top = INITIAL_AUTOLOAD_NAMES_SIZE;
      sc->autoload_names_loc = 0;
    }
  else
    {
      if (sc->autoload_names_loc >= sc->autoload_names_top)
	{
	  int i;
	  sc->autoload_names_top *= 2;
	  sc->autoload_names = (const char ***)realloc(sc->autoload_names, sc->autoload_names_top * sizeof(const char **));
	  sc->autoload_names_sizes = (int *)realloc(sc->autoload_names_sizes, sc->autoload_names_top * sizeof(int));
	  sc->autoloaded_already = (bool **)realloc(sc->autoloaded_already, sc->autoload_names_top * sizeof(bool *));
	  for (i = sc->autoload_names_loc; i < sc->autoload_names_top; i++)
	    {
	      sc->autoload_names[i] = NULL;
	      sc->autoload_names_sizes[i] = 0;
	      sc->autoloaded_already[i] = NULL;
	    }
	}
    }
     
  sc->autoload_names[sc->autoload_names_loc] = names;
  sc->autoload_names_sizes[sc->autoload_names_loc] = size;
  sc->autoloaded_already[sc->autoload_names_loc] = (bool *)calloc(size, sizeof(bool));
  sc->autoload_names_loc++;
}


static const char *find_autoload_name(s7_scheme *sc, s7_pointer symbol, bool *already_loaded, bool loading)
{
  int l = 0, pos = -1, lib, libs;
  const char *name, *this_name;

  name = symbol_name(symbol);
  libs = sc->autoload_names_loc;

  for (lib = 0; lib < libs; lib++)
    {
      const char **names;
      int u;
      u = sc->autoload_names_sizes[lib] - 1;
      names = sc->autoload_names[lib];
      
      while (true)
	{
	  int comp;
	  if (u < l) break;
	  pos = (l + u) / 2;
	  this_name = names[pos * 2];
	  comp = strcmp(this_name, name);
	  if (comp == 0)
	    {
	      *already_loaded = sc->autoloaded_already[lib][pos];
	      if (loading) sc->autoloaded_already[lib][pos] = true;
	      return(names[pos * 2 + 1]);             /* file name given func name */
	    }
	  if (comp < 0) 
	    l = pos + 1;
	  else u = pos - 1;
	}
    }
  return(NULL);
}


s7_pointer s7_autoload(s7_scheme *sc, s7_pointer symbol, s7_pointer file_or_function)
{
  /* add '(symbol . file) to s7's autoload table */
  if (is_null(sc->autoload_table))
    {
      sc->autoload_table = s7_make_hash_table(sc, 511);
      s7_gc_protect(sc, sc->autoload_table);
    }
  
  s7_hash_table_set(sc, sc->autoload_table, symbol, file_or_function);
  return(file_or_function);
}


static s7_pointer g_autoload(s7_scheme *sc, s7_pointer args)
{
  #define H_autoload "(autoload symbol file-or-function) adds the symbol to its table of autoloadable symbols. \
If that symbol is encountered as an unbound variable, s7 either loads the file (following *load-path*), or calls \
the function.  The function takes one argument, the calling environment.  Presumably the symbol is defined \
in the file, or by the function."

  s7_pointer sym, value;

  sym = car(args);
  if (is_string(sym))
    {
      if (string_length(sym) == 0)                   /* (autoload "" ...) */
	return(s7_wrong_type_arg_error(sc, "autoload", 1, sym, "a symbol-name or a symbol"));
      sym = make_symbol_with_length(sc, string_value(sym), string_length(sym));
    }
  if (!is_symbol(sym))
    {
      check_method(sc, sym, sc->AUTOLOAD, args);
      return(s7_wrong_type_arg_error(sc, "autoload", 1, sym, "a string (symbol-name) or a symbol"));
    }
  
  value = cadr(args);
  if ((is_string(value)) ||
      (is_closure(value)))
    return(s7_autoload(sc, sym, value));

  check_method(sc, value, sc->AUTOLOAD, args);
  return(s7_wrong_type_arg_error(sc, "autoload", 2, value, "a string (file-name) or a thunk"));
}


static s7_pointer g_autoloader(s7_scheme *sc, s7_pointer args)
{
  #define H_autoloader "(*autoload* sym) returns the autoload info for the symbol sym, or #f."
  s7_pointer sym;

  sym = car(args);
  if (!is_symbol(sym))
    {
      check_method(sc, sym, sc->AUTOLOADER, args);
      return(s7_wrong_type_arg_error(sc, "*autoload*", 1, sym, "a symbol"));
    }
  if (sc->autoload_names)
    {
      const char *file;
      bool loaded = false;
      file = find_autoload_name(sc, sym, &loaded, false);
      if (file)
	return(s7_make_string(sc, file));
    }
  if (is_hash_table(sc->autoload_table))
    return(s7_hash_table_ref(sc, sc->autoload_table, sym));

  return(sc->F);
}


static s7_pointer g_require(s7_scheme *sc, s7_pointer args)
{
  #define H_require "(require . symbols) loads each file associated with each symbol if it has not been loaded already.\
The symbols refer to the argument to \"provide\"."

  s7_pointer p;
  for (p = args; is_pair(p); p = cdr(p))
    {
      if (is_symbol(car(p)))
	{
	  if (!is_slot(find_symbol(sc, car(p))))
	    {
	      s7_pointer f;
	      f = g_autoloader(sc, p);
	      if (is_string(f))
		s7_load_1(sc, string_value(f), sc->envir);
	      else return(s7_error(sc, sc->READ_ERROR, 
				   list_2(sc, make_string_wrapper(sc, "require: no autoload info for ~S"), car(p)))); /* read-error?? */
	    }
	}
      else 
	{
	  if ((is_pair(car(p))) && (caar(p) == sc->QUOTE))
	    return(s7_error(sc, sc->WRONG_TYPE_ARG, 
			    list_2(sc, make_string_wrapper(sc, "require: don't quote ~S"), car(p))));
	  return(s7_error(sc, sc->WRONG_TYPE_ARG, 
			  list_2(sc, make_string_wrapper(sc, "require: ~S is not a symbol"), car(p))));
	}
    }
  return(sc->T);
}


/* -------------------------------- eval-string -------------------------------- */

static s7_pointer eval_string_1(s7_scheme *sc, const char *str)
{
  s7_pointer port;

  port = s7_open_input_string(sc, str);
  push_input_port(sc, port);

  push_stack(sc, OP_BARRIER, port, sc->NIL);
  /* we're being called directly from C here, not as part of a scheme program.
   *    Use this op to protect the port, I guess.
   */
  push_stack(sc, OP_EVAL_STRING, sc->args, sc->code);  
  /* eval-string is not tail-recursive because it pushes markers in eval to catch
   *    multiple statements in one eval-string call.
   */
  eval(sc, OP_READ_INTERNAL);

  pop_input_port(sc);
  s7_close_input_port(sc, port);
  if (is_multiple_value(sc->value))                    /* (+ 1 (eval-string "(values 2 3)")) */
    sc->value = splice_in_values(sc, multiple_value(sc->value));

  return(sc->value);
}


s7_pointer s7_eval_c_string(s7_scheme *sc, const char *str)
{
  bool old_longjmp;
  s7_pointer port, old_envir;
  /* this can be called recursively via s7_call */

  sc->v = sc->envir;          /* old envir needs GC protection even given the push_stack below */
  old_envir = sc->envir;
  sc->envir = sc->NIL; 
  if (sc->longjmp_ok)
    {
      s7_pointer result;
      result = eval_string_1(sc, str);
      sc->envir = old_envir;
      return(result);
    }
  
  stack_reset(sc); 
  push_stack(sc, OP_EVAL_STRING, old_envir, sc->NIL); /* GC protect envir */

  port = s7_open_input_string(sc, str);
  push_input_port(sc, port);
  
  old_longjmp = sc->longjmp_ok;
  if (!sc->longjmp_ok)
    {
      sc->longjmp_ok = true;
      if (setjmp(sc->goto_start) != 0)
	eval(sc, sc->op);
      else eval(sc, OP_READ_INTERNAL);
    }
  
  sc->longjmp_ok = old_longjmp;
  pop_input_port(sc);
  s7_close_input_port(sc, port);
  sc->envir = old_envir;  
  return(sc->value);
}


#if DEBUGGING
static const char *last_eval_string;
#endif

static s7_pointer g_eval_string(s7_scheme *sc, s7_pointer args)
{
  #define H_eval_string "(eval-string str (env (curlet))) returns the result of evaluating the string str as Scheme code"
  s7_pointer port, str;
  
  str = car(args);
#if DEBUGGING
  last_eval_string = string_value(str);
#endif
  if (!is_string(str))
    {
      check_method(sc, str, sc->EVAL_STRING, args);
      return(wrong_type_argument(sc, sc->EVAL_STRING, small_int(1), str, T_STRING));
    }
  if (is_not_null(cdr(args)))
    {
      s7_pointer e;
      e = cadr(args);
      if (!is_let(e))
 	return(wrong_type_argument_with_type(sc, sc->EVAL_STRING, small_int(2), e, AN_ENVIRONMENT));
      if (e == sc->rootlet)
	sc->envir = sc->NIL;
      else sc->envir = e;
    }

  port = open_and_protect_input_string(sc, str);
  push_input_port(sc, port);
  
  sc->temp3 = sc->args;
  push_stack(sc, OP_EVAL_STRING_1, args, sc->code); /* was sc->args */
  push_stack(sc, OP_READ_INTERNAL, sc->NIL, sc->NIL);
  
  return(sc->F);
}


static s7_pointer call_with_input(s7_scheme *sc, s7_pointer port, s7_pointer args)
{
  s7_pointer p;
  p = cadr(args);
  port_original_input_string(port) = car(args);
  push_stack(sc, OP_UNWIND_INPUT, sc->input_port, port);
  if (is_any_macro(p))
    {
      push_stack(sc, OP_EVAL_MACRO, sc->NIL, args);
      push_stack(sc, OP_APPLY, list_1(sc, list_2(sc, p, port)), p);
    }
  else push_stack(sc, OP_APPLY, list_1(sc, port), p);
  return(sc->F);
}


static s7_pointer g_call_with_input_string(s7_scheme *sc, s7_pointer args)
{
  s7_pointer str, proc;
  #define H_call_with_input_string "(call-with-input-string str proc) opens a string port for str and applies proc to it"
  
  /* (call-with-input-string "44" (lambda (p) (+ 1 (read p)))) -> 45
   */
  
  str = car(args);
  if (!is_string(str))
    {
      check_method(sc, str, sc->CALL_WITH_INPUT_STRING, args);
      return(wrong_type_argument(sc, sc->CALL_WITH_INPUT_STRING, small_int(1), str, T_STRING));
    }

  proc = cadr(args);
  if (is_let(proc))
    check_method(sc, proc, sc->CALL_WITH_INPUT_STRING, args);
  if (!s7_is_aritable(sc, proc, 1))
    return(wrong_type_argument_with_type(sc, sc->CALL_WITH_INPUT_STRING, small_int(2), proc, 
					 make_string_wrapper(sc, "a procedure of one argument (the port)")));
  if ((is_continuation(proc)) || (is_goto(proc)))
    return(wrong_type_argument_with_type(sc, sc->CALL_WITH_INPUT_STRING, small_int(2), proc,
					 make_string_wrapper(sc, "a normal procedure (not a continuation)")));
  
  return(call_with_input(sc, open_and_protect_input_string(sc, str), args));
}


static s7_pointer g_call_with_input_file(s7_scheme *sc, s7_pointer args)
{
  #define H_call_with_input_file "(call-with-input-file filename proc) opens filename and calls proc with the input port as its argument"
  s7_pointer str, proc;
  
  str = car(args);
  if (!is_string(str))
    {
      check_method(sc, str, sc->CALL_WITH_INPUT_FILE, args);
      return(wrong_type_argument(sc, sc->CALL_WITH_INPUT_FILE, small_int(1), str, T_STRING));
    }

  proc = cadr(args);
  if (!s7_is_aritable(sc, proc, 1))
    return(wrong_type_argument_with_type(sc, sc->CALL_WITH_INPUT_FILE, small_int(2), proc, 
					 make_string_wrapper(sc, "a procedure of one argument (the port)")));
  if ((is_continuation(proc)) || (is_goto(proc)))
    return(wrong_type_argument_with_type(sc, sc->CALL_WITH_INPUT_FILE, small_int(2), proc,
					 make_string_wrapper(sc, "a normal procedure (not a continuation)")));
  
  return(call_with_input(sc, open_input_file_1(sc, string_value(str), "r", "call-with-input-file"), args));
}


static s7_pointer with_input(s7_scheme *sc, s7_pointer port, s7_pointer args)
{
  s7_pointer old_input_port, p;
  old_input_port = sc->input_port;
  sc->input_port = port;
  port_original_input_string(port) = car(args);
  push_stack(sc, OP_UNWIND_INPUT, old_input_port, port);
  p = cadr(args);
  if (is_any_macro(p))
    {
      push_stack(sc, OP_EVAL_MACRO, sc->NIL, cdr(args));
      push_stack(sc, OP_APPLY, list_1(sc, cdr(args)), p);
    }
  else push_stack(sc, OP_APPLY, sc->NIL, p);
  return(sc->F);
}


static s7_pointer g_with_input_from_string(s7_scheme *sc, s7_pointer args)
{
  #define H_with_input_from_string "(with-input-from-string str thunk) opens str as the temporary current-input-port and calls thunk"
  s7_pointer str;

  str = car(args);
  if (!is_string(str))
    {
      check_method(sc, str, sc->WITH_INPUT_FROM_STRING, args);
      return(wrong_type_argument(sc, sc->WITH_INPUT_FROM_STRING, small_int(1), str, T_STRING));
    }
  if (!s7_is_aritable(sc, cadr(args), 0))
    {
      check_method(sc, cadr(args), sc->WITH_INPUT_FROM_STRING, args);
      return(wrong_type_argument_with_type(sc, sc->WITH_INPUT_FROM_STRING, small_int(2), cadr(args), A_THUNK));
    }
  /* since the arguments are evaluated before we get here, we can get some confusing situations:
   *   (with-input-from-string "#x2.1" (read))
   *   (read) -> whatever it can get from the current input port!
   *   ";with-input-from-string argument 2, #<eof>, is untyped but should be a thunk"
   */
  
  return(with_input(sc, open_and_protect_input_string(sc, str), args));
}


static s7_pointer g_with_input_from_file(s7_scheme *sc, s7_pointer args)
{
  #define H_with_input_from_file "(with-input-from-file filename thunk) opens filename as the temporary current-input-port and calls thunk"
  
  if (!is_string(car(args)))
    {
      check_method(sc, car(args), sc->WITH_INPUT_FROM_FILE, args);
      return(wrong_type_argument(sc, sc->WITH_INPUT_FROM_FILE, small_int(1), car(args), T_STRING));
    }
  if (!s7_is_aritable(sc, cadr(args), 0))
    {
      check_method(sc, cadr(args), sc->WITH_INPUT_FROM_FILE, args);
      return(wrong_type_argument_with_type(sc, sc->WITH_INPUT_FROM_FILE, small_int(2), cadr(args), A_THUNK));
    }
  return(with_input(sc, open_input_file_1(sc, string_value(car(args)), "r", "with-input-from-file"), args));
}


static bool needs_slashification(const char *str, int len)
{
  /* we have to go by len (str len) not *s==0 because s7 strings can have embedded nulls */
  unsigned char *p, *pend;
  pend = (unsigned char *)(str + len);
  for (p = (unsigned char *)str; p < pend; p++)
    if (slashify_table[*p])
      return(true);
  return(false);
}


#define IN_QUOTES true
#define NOT_IN_QUOTES false

static char *slashify_string(s7_scheme *sc, const char *p, int len, bool quoted, int *nlen) /* do not free result */
{
  int j = 0, cur_size, size;
  char *s;
  unsigned char *pcur, *pend;

  pend = (unsigned char *)(p + len);
  cur_size = len + 256;
  size = cur_size + 2;
  if (size > sc->slash_str_size)
    {
      if (sc->slash_str) free(sc->slash_str);
      sc->slash_str_size = size;
      sc->slash_str = (char *)malloc(size);
    }
  /* memset((void *)sc->slash_str, 0, size); */
  s = sc->slash_str;

  if (quoted) s[j++] = '"';

  /* what about the trailing nulls? Guile writes them out (as does s7 currently)
   *    but that is not ideal.  I'd like to use ~S for error messages, so that
   *    strings are clearly identified via the double-quotes, but this way of
   *    writing them is ugly:
   *
   *    :(let ((str (make-string 8 #\null))) (set! (str 0) #\a) str)
   *    "a\x00\x00\x00\x00\x00\x00\x00"
   *
   * but it would be misleading to omit them because:
   *
   *    :(let ((str (make-string 8 #\null))) (set! (str 0) #\a) (string-append str "bc"))
   *    "a\x00\x00\x00\x00\x00\x00\x00bc"
   */

  for (pcur = (unsigned char *)p; pcur < pend; pcur++) 
    {
      if (slashify_table[*pcur])
	{
	  s[j++] = '\\';
	  switch (*pcur)
	    {
	    case '"':
	      s[j++] = '"';
	      break;
	      
	    case '\\':
	      s[j++] = '\\';
	      break;
	      
	    default:               /* this is the "\x01" stuff */
	      { 
		unsigned int n;
		static char dignum[] = "0123456789abcdef";
		s[j++] = 'x';
		n = (unsigned int)(*pcur);
		if (n < 16)
		  s[j++] = '0';
		else s[j++] = dignum[(n / 16) % 16];
		s[j++] = dignum[n % 16];
	      }
	      break;
	    }
	}
      else s[j++] = *pcur;
      if (j >= cur_size) /* even with 256 extra, we can overflow (for example, inordinately many tabs in ALSA output) */
	{
	  /* int k; */
	  cur_size *= 2;
	  size = cur_size + 2;
	  sc->slash_str = (char *)realloc(sc->slash_str, size * sizeof(char));
	  s = sc->slash_str;
	  /* for (k = j; k < size; k++) s[k] = 0; */
	}
    }
  if (quoted) s[j++] = '"';
  s[j] = '\0';
  (*nlen) = j;
  return(s);
}


/* (let () (define (hi a) (+ a 1)) (object->string hi))
 * (let () (define (hi a) (asd a 1)) (let->list (funclet hi)))
 */
static s7_pointer find_closure_let(s7_scheme *sc, s7_pointer cur_env)
{
  s7_pointer e;
  for (e = cur_env; is_let(e); e = outlet(e))
    if (is_function_env(e))
      return(e);
  return(sc->NIL);
}


static s7_pointer find_closure(s7_scheme *sc, s7_pointer closure, s7_pointer cur_env)
{
  s7_pointer e, y;
  for (e = cur_env; is_let(e); e = outlet(e))
    {
      if ((is_function_env(e)) &&
	  (is_symbol(funclet_function(e))) &&
	  (is_global(funclet_function(e))) && /* (define (f1) (lambda () 1)) shouldn't say the returned closure is named f1 */
	  (slot_value(global_slot(funclet_function(e))) == closure))
	return(funclet_function(e));

      for (y = let_slots(e); is_slot(y); y = next_slot(y))
	if (slot_value(y) == closure)
	  return(slot_symbol(y));
    }
  return(sc->NIL);
}


static const char *c_closure_name(s7_scheme *sc, s7_pointer closure, int *nlen)
{
  s7_pointer x;
  x = find_closure(sc, closure, closure_let(closure));

  /* this can be confusing!  In some cases, the function is in its environment, and in other very similar-looking cases it isn't:
   * (let ((a (lambda () 1))) a)
   * #<lambda ()>
   * (letrec ((a (lambda () 1))) a)
   * a
   * (let () (define (a) 1) a)
   * a
   */

  if (is_symbol(x))
    {
      (*nlen) = symbol_name_length(x);
      return(symbol_name(x));
    }

  /* names like #<closure> and #<macro> are useless -- try to be a bit more informative
   *   #<lambda|* args> #<m|bacro|* args> (args truncated if long)
   * despite the name "c_closure..." this function refers to T_CLOSURE and T_CLOSURE_STAR objects only
   */
  if (is_null(closure_args(closure)))
    (*nlen) = snprintf(sc->strbuf, sc->strbuf_size, "#<lambda%s ()>", (type(closure) == T_CLOSURE) ? "" : "*");
  else
    {
      if (is_symbol(closure_args(closure)))
	(*nlen) = snprintf(sc->strbuf, sc->strbuf_size, "#<lambda%s %s>", (type(closure) == T_CLOSURE) ? "" : "*", symbol_name(closure_args(closure)));
      else
	{
	  x = closure_args(closure);
	  if (is_null(cdr(x)))
	    (*nlen) = snprintf(sc->strbuf, sc->strbuf_size, "#<lambda%s (%s)>", 
			       (type(closure) == T_CLOSURE) ? "" : "*", 
			       (is_pair(car(x))) ? symbol_name(caar(x)) : symbol_name(car(x)));
	  else (*nlen) = snprintf(sc->strbuf, sc->strbuf_size, "#<lambda%s (%s %s%s%s>", 
				  (type(closure) == T_CLOSURE) ? "" : "*", 
				  (is_pair(car(x))) ? symbol_name(caar(x)) : symbol_name(car(x)),
				  (is_pair(cdr(x))) ? "" : ". ",
				  (is_pair(cdr(x))) ? ((is_pair(cadr(x))) ? symbol_name(caadr(x)) : symbol_name(cadr(x))) : symbol_name(cdr(x)),
				  ((is_pair(cdr(x))) && (!is_null(cddr(x)))) ? " ..." : ")");
	}
    }
  return(sc->strbuf);
}


enum {NO_ARTICLE, DEFINITE_ARTICLE, INDEFINITE_ARTICLE};

static char *describe_type_bits(s7_scheme *sc, s7_pointer obj)
{
  char *buf;
  buf = (char *)malloc(512 * sizeof(char));

  snprintf(buf, 512, "type: %d (%s), flags: #x%x%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", 
	   type(obj), 
	   type_name(sc, obj, NO_ARTICLE),
	   typeflag(obj),
	   is_procedure(obj) ?          " procedure" : "",
	   is_marked(obj) ?             " gc-marked" : "",
	   is_immutable(obj) ?          " immutable" : "",
	   is_global(obj) ?             ((is_pair(obj)) ? " unsafe-do" : " global") : "",
	   has_line_number(obj) ?       ((is_symbol(obj)) ? " optimized-let" : ((is_input_port(obj)) ? " loader-port" : " line-number")) : "",
	   is_expansion(obj) ?          " expansion" : "",
	   is_multiple_value(obj) ?     " values" : "",
	   is_keyword(obj) ?            " keyword" : "",
	   dont_eval_args(obj) ?        " dont-eval-args" : "",
	   is_syntactic(obj) ?          " syntactic" : "",
	   is_overlaid(obj) ?           " overlay" : "",
	   is_checked(obj) ?            " checked" : "",
	   is_unsafe(obj) ?             ((is_procedure(obj)) ? " step-safe" : " unsafe") : "",
	   is_optimized(obj) ?          " optimized" : "",
	   is_safe_closure(obj) ?       " safe-closure" : "",
	   is_safe_procedure(obj) ?     " safe-procedure" : "",
	   is_setter(obj) ?             ((is_pair(obj)) ? " annotated" : " setter") : "",
	   is_mutable(obj) ?            ((is_string(obj)) ? " bytevector" : ((is_let(obj)) ? " let-ref-fallback" : " mutable")) : "",
	   has_print_name(obj) ?        ((is_let(obj)) ? " let-set!-fallback" : " print-name") : "",
	   needs_copied_args(obj) ?     " copy-args" : "",
	   is_gensym(obj) ?             ((is_let(obj)) ? " closure-env" : ((is_unspecified(obj)) ? " no-value" : ((is_pair(obj)) ? " list-in-use" : 
					   ((is_closure_star(obj)) ? " simple-args" : ((is_string(obj)) ? " documented" : " gensym"))))) : "",
	   has_methods(obj) ?           ((is_slot(obj) || (is_symbol(obj))) ? " has-accessor" : " has methods") : "",
	   returns_temp(obj) ?          " returns temp cell" : "",
	   ((typeflag(obj) & UNUSED_BITS) != 0) ? " bad bits!" : "");
  return(buf);
}


#if TRAP_SEGFAULT
#include <signal.h>
static sigjmp_buf senv; /* global here is not a problem -- it is used only to protect s7_is_valid */
static volatile sig_atomic_t can_jump = 0;
static void segv(int ignored) {if (can_jump) siglongjmp(senv, 1);}
#endif

bool s7_is_valid(s7_scheme *sc, s7_pointer arg)
{
  bool result;
  /* new name because the function above might be in use 
   */
  if (!arg) return(false);
  /* we might also check that (arg & 0x7) == 0, but see unaligned access above */

#if TRAP_SEGFAULT
  if (sigsetjmp(senv, 1) == 0)
    {
      void (*old_segv)(int sig);
      can_jump = 1;
      old_segv = signal(SIGSEGV, segv);
#endif

      result = ((!is_free(arg)) && 
		(type(arg) < NUM_TYPES) &&
		((arg->hloc == -1) ||
		 ((arg->hloc >= 0) && 
		  (arg->hloc < (int)sc->heap_size) && (sc->heap[arg->hloc] == arg))));

#if TRAP_SEGFAULT
  signal(SIGSEGV, old_segv);
    }
  can_jump = 0;
#endif

  return(result);
}


#define INITIAL_SHARED_INFO_SIZE 8

static int shared_ref(shared_info *ci, s7_pointer p)
{
  /* from print after collecting refs, not called by equality check
   */
  int i;
  s7_pointer *objs;

  if (!is_collected(p)) return(0);

  objs = ci->objs;
  for (i = 0; i < ci->top; i++)
    if (objs[i] == p)
      {
	int val;
	val = ci->refs[i];
	if (val > 0)
	  ci->refs[i] = -ci->refs[i];
	return(val);
      }
  return(0);
}


static int peek_shared_ref(shared_info *ci, s7_pointer p)
{
  /* returns 0 if not found, otherwise the ref value for p */
  int i;
  s7_pointer *objs;
  objs = ci->objs;

  if (!is_collected(p)) return(0);
  for (i = 0; i < ci->top; i++)
    if (objs[i] == p) return(ci->refs[i]);

  return(0);
}


static void enlarge_shared_info(shared_info *ci)
{
  int i;
  ci->size *= 2;
  ci->objs = (s7_pointer *)realloc(ci->objs, ci->size * sizeof(s7_pointer));
  ci->refs = (int *)realloc(ci->refs, ci->size * sizeof(int));
  for (i = ci->top; i < ci->size; i++)
    {
      ci->refs[i] = 0;
      ci->objs[i] = NULL;
    }
}


static void add_equal_ref(shared_info *ci, s7_pointer x, s7_pointer y)
{
  /* assume neither x nor y is in the table, and that they should share a ref value,
   *   called only in equality check, not printer.
   */

  if ((ci->top + 2) >= ci->size)
    enlarge_shared_info(ci);

  set_collected(x);
  set_collected(y);

  ci->ref++;
  ci->objs[ci->top] = x;
  ci->refs[ci->top++] = ci->ref;
  ci->objs[ci->top] = y;
  ci->refs[ci->top++] = ci->ref;
}


static void add_shared_ref(shared_info *ci, s7_pointer x, int ref_x)
{
  /* called only in equality check, not printer */

  if (ci->top == ci->size)
    enlarge_shared_info(ci);

  set_collected(x);

  ci->objs[ci->top] = x;
  ci->refs[ci->top++] = ref_x;
}

static shared_info *collect_shared_info(s7_scheme *sc, shared_info *ci, s7_pointer top, bool stop_at_print_length);
static s7_pointer hash_equal(s7_scheme *sc, s7_pointer table, s7_pointer key);
static s7_pointer hash_eq_func(s7_scheme *sc, s7_pointer table, s7_pointer key);

static void collect_vector_info(s7_scheme *sc, shared_info *ci, s7_pointer top, bool stop_at_print_length)
{
  int i, plen;

  if (stop_at_print_length)
    {
      plen = s7_vector_print_length(sc); 
      if (plen > vector_length(top))
	plen = vector_length(top);
    }
  else plen = vector_length(top);

  for (i = 0; i < plen; i++)
    if (has_structure(vector_element(top, i)))
      collect_shared_info(sc, ci, vector_element(top, i), stop_at_print_length);
}


static shared_info *collect_shared_info(s7_scheme *sc, shared_info *ci, s7_pointer top, bool stop_at_print_length)
{
  /* look for top in current list.
   * 
   * As we collect objects (guaranteed to have structure) we set the collected bit.  If we ever
   *   encounter an object with that bit on, we've seen it before so we have a possible cycle.
   *   Once the collection pass is done, we run through our list, and clear all these bits.
   */
  if (is_collected(top))
    {
      s7_pointer *p, *objs_end;
      int i;
      objs_end = (s7_pointer *)(ci->objs + ci->top);

      for (p = ci->objs; p < objs_end; p++)
	if ((*p) == top)
	  {
	    i = (int)(p - ci->objs);
	    if (ci->refs[i] == 0)
	      {
		ci->has_hits = true;
		ci->refs[i] = ++ci->ref;  /* if found, set the ref number */
	      }
	    break;
	  }
    }
  else
    {
      /* top not seen before -- add it to the list */
      
      set_collected(top);

      if (ci->top == ci->size)
	enlarge_shared_info(ci);
      ci->objs[ci->top++] = top;

      /* now search the rest of this structure */
      switch (type(top))
	{
	case T_PAIR:
	  if (has_structure(car(top)))
	    collect_shared_info(sc, ci, car(top), stop_at_print_length);
	  if (has_structure(cdr(top)))
	    collect_shared_info(sc, ci, cdr(top), stop_at_print_length);
	  break;
	  
	case T_VECTOR:
	  collect_vector_info(sc, ci, top, stop_at_print_length);
	  break;
	  
	case T_HASH_TABLE:
	  if (hash_table_entries(top) > 0)
	    {
	      s7_pointer iterator;
	      int gc_iter;
	      bool keys_safe;
	      
	      keys_safe = ((hash_table_function(top) != hash_equal) &&
			   (hash_table_function(top) != hash_eq_func));
	      iterator = g_make_hash_table_iterator(sc, list_1(sc, top));
	      gc_iter = s7_gc_protect(sc, iterator);
	      
	      while (true)
		{
		  s7_pointer x;
		  x = hash_table_iterate(sc, iterator);
		  if (is_null(x)) break;
		  /* x is a cons, if hash_table_function(top) != hash_equal, we know car is ok */
		  if ((!keys_safe) &&
		      (has_structure(car(x))))
		    collect_shared_info(sc, ci, car(x), stop_at_print_length);
		  if (has_structure(cdr(x)))
		    collect_shared_info(sc, ci, cdr(x), stop_at_print_length);
		}
	      s7_gc_unprotect_at(sc, gc_iter);
	    }
	  break;

	case T_SLOT:
	  if (has_structure(slot_value(top)))
	    collect_shared_info(sc, ci, slot_value(top), stop_at_print_length);
	  break;

	case T_ENVIRONMENT:
	  if (top == sc->rootlet)
	    collect_vector_info(sc, ci, top, stop_at_print_length);
	  else
	    {
	      s7_pointer p;
	      for (p = let_slots(top); is_slot(p); p = next_slot(p))
		if (has_structure(slot_value(p)))
		  collect_shared_info(sc, ci, slot_value(p), stop_at_print_length);
	    }
	  break;
	}	  
    }
  return(ci);
}


static shared_info *new_shared_info(s7_scheme *sc)
{
  shared_info *ci;
  if (sc->circle_info == NULL)
    {
      ci = (shared_info *)calloc(1, sizeof(shared_info));
      ci->size = INITIAL_SHARED_INFO_SIZE;
      ci->objs = (s7_pointer *)malloc(ci->size * sizeof(s7_pointer));
      ci->refs = (int *)calloc(ci->size, sizeof(int));   /* finder expects 0 = unseen previously */
      sc->circle_info = ci;
    }
  else 
    {
      int i;
      ci = sc->circle_info;
      memset((void *)(ci->refs), 0, ci->top * sizeof(int));
      for (i = 0; i < ci->top; i++)
	{
	  s7_pointer p;
	  p = ci->objs[i];
	  clear_collected(p);
	}
    }
  ci->top = 0;
  ci->ref = 0;
  ci->has_hits = false;
  return(ci);
}


static shared_info *make_shared_info(s7_scheme *sc, s7_pointer top, bool stop_at_print_length)
{
  /* for the printer */
  shared_info *ci;
  int i, refs;
  s7_pointer *ci_objs;
  int *ci_refs;
  bool no_problem = true;

  /* check for simple cases first */
  if (is_pair(top))
    {
      if (s7_list_length(sc, top) != 0) /* it is not circular at the top level (following cdr), so we can check each car(x) */
	{
	  s7_pointer x;
	  for (x = top; is_pair(x); x = cdr(x))
	    if (has_structure(car(x)))
	      {
		/* it can help a little in some cases to scan vectors here (and slots):
		 *   if no element has structure, it's ok (maybe also hash_table_entries == 0)
		 */
		no_problem = false;
		break;
	      }
	  if ((no_problem) &&
	      (!is_null(x)) &&
	      (has_structure(x)))
	    no_problem = false;
	    
	  if (no_problem)
	    return(NULL);
	}
    }
  else
    {
      if (s7_is_vector(top))
	{
	  if (type(top) != T_VECTOR)
	    return(NULL);

	  for (i = 0; i < vector_length(top); i++)
	    if (has_structure(vector_element(top, i)))
	      {
		no_problem = false;
		break;
	      }
	  if (no_problem)
	    return(NULL);
	}
    }

  ci = new_shared_info(sc);

  /* collect all pointers associated with top */
  collect_shared_info(sc, ci, top, stop_at_print_length);

  for (i = 0; i < ci->top; i++)
    {
      s7_pointer p;
      p = ci->objs[i];
      clear_collected(p);
    }

  if (!(ci->has_hits))
    return(NULL);

  ci_objs = ci->objs;
  ci_refs = ci->refs;

  /* find if any were referenced twice (once for just being there, so twice=shared)
   *   we know there's at least one such reference because has_hits is true.
   */
  for (i = 0, refs = 0; i < ci->top; i++)
    if (ci_refs[i] > 0)
      {
	set_collected(ci_objs[i]);
	if (i == refs)
	  refs++;
	else
	  {
	    ci_objs[refs] = ci_objs[i];
	    ci_refs[refs++] = ci_refs[i];
	    ci_refs[i] = 0;
	    ci_objs[i] = NULL;
	  }
      }
  ci->top = refs;
  return(ci);
}


static s7_pointer cyclic_sequences(s7_scheme *sc, s7_pointer obj, bool return_list)
{
  if (has_structure(obj))
    {
      shared_info *ci;
      ci = make_shared_info(sc, obj, false); /* false=don't stop at print length (vectors etc) */
      if (ci)
	{
	  int i, num;
	  s7_pointer *objects;
	  s7_pointer **obj_cis;
	  int *obj_top;

	  /* save list of current shared pointers, set cyclic bit in each
	   *   for each one, get its shared pointers
	   *     if none, clear cyclic bit, and look back clearing if possible
	   *   if all cleared, no cycles, else return list of those with cyclic bit still set
	   *
	   * ci->objs are the objects, ci->top is end pointer of list
	   */
	  
	  num = ci->top;
	  objects = (s7_pointer *)malloc(num * sizeof(s7_pointer));
	  memcpy((void *)objects, (void *)(ci->objs), num * sizeof(s7_pointer));
	  obj_cis = (s7_pointer **)calloc(num, sizeof(s7_pointer *));
	  obj_top = (int *)calloc(num, sizeof(int));

	  for (i = 0; i < num; i++)
	    set_cyclic(objects[i]);

	  for (i = 0; i < num; i++)
	    {
	      ci = make_shared_info(sc, objects[i], false);
	      if (!ci) 
		clear_cyclic(objects[i]);
	      else 
		{
		  obj_top[i] = ci->top;
		  obj_cis[i] = (s7_pointer *)malloc(obj_top[i] * sizeof(s7_pointer));
		  memcpy((void *)(obj_cis[i]), (void *)(ci->objs), obj_top[i] * sizeof(s7_pointer));
		}
	    }

	  while (true)
	    {
	      bool cleared_one = false;
	      for (i = 0; i < num; i++)
		{
		  if (is_cyclic(objects[i]))
		    {
		      int k;
		      bool empty = true;
		      for (k = 0; k < obj_top[i]; k++)
			if (is_cyclic(obj_cis[i][k]))
			  {
			    empty = false;
			    break;
			  }
		      if (empty)
			{
			  cleared_one = true;
			  clear_cyclic(objects[i]);
			}
		    }
		}
	      if (!cleared_one) break;
	    }
	  
	  for (i =0; i < num; i++)
	    if (obj_cis[i]) free(obj_cis[i]);
	  free(obj_cis);
	  free(obj_top);

	  if (return_list)
	    {
	      s7_pointer lst;
	      sc->w = sc->NIL;
	      for (i = 0; i < num; i++)
		if (is_cyclic(objects[i]))
		  sc->w = cons(sc, objects[i], sc->w);
	      lst = sc->w;
	      sc->w = sc->NIL;
	      free(objects);
	      return(lst);
	    }
	  else
	    {
	      for (i = 0; i < num; i++)
		if (is_cyclic(objects[i]))
		  {
		    free(objects);
		    return(sc->T);
		  }
	      free(objects);
	    }
	}
    }
  return(sc->NIL);
}

static s7_pointer g_cyclic_sequences(s7_scheme *sc, s7_pointer args)
{
  #define H_cyclic_sequences "(cyclic-sequences obj) returns a list of elements that are cyclic."
  return(cyclic_sequences(sc, car(args), true));
}

static bool has_cyclic_sequence(s7_scheme *sc, s7_pointer obj)
{
  return(cyclic_sequences(sc, obj, false) == sc->T);
}


#define WITH_ELLIPSES false

static int circular_list_entries(s7_pointer lst)
{
  int i;
  s7_pointer x;
  for (i = 1, x = cdr(lst); ; i++, x = cdr(x))
    {
      int j;
      s7_pointer y;
      for (y = lst, j = 0; j < i; y = cdr(y), j++)
	if (x == y)
	  return(i);
    }
}


static void object_to_port_with_circle_check(s7_scheme *sc, s7_pointer vr, s7_pointer port, use_write_t use_write, bool to_file, shared_info *ci);
static void object_to_port(s7_scheme *sc, s7_pointer obj, s7_pointer port, use_write_t use_write, bool to_file, shared_info *ci);
static s7_pointer write_or_display(s7_scheme *sc, s7_pointer obj, s7_pointer port, use_write_t use_write);

static char *multivector_indices_to_string(s7_scheme *sc, s7_Int index, s7_pointer vect, char *str, int cur_dim)
{
  s7_Int size, ind;
  char buf[64];

  size = vector_dimension(vect, cur_dim);
  ind = index % size;
  if (cur_dim > 0)
    multivector_indices_to_string(sc, (index - ind) / size, vect, str, cur_dim - 1);

  snprintf(buf, 64, " %lld", ind);
  strcat(str, buf);
  return(str);
}


static int multivector_to_port(s7_scheme *sc, s7_pointer vec, s7_pointer port, 
			       int out_len, int flat_ref, int dimension, int dimensions, bool *last, 
			       use_write_t use_write, bool to_file, shared_info *ci)
{
  int i;

  if (use_write != USE_READABLE_WRITE)
    {
      if (*last)
	port_write_string(port)(sc, " (", 2, port);
      else port_write_character(port)(sc, '(', port);
      (*last) = false;
    }

  for (i = 0; i < vector_dimension(vec, dimension); i++)
    {
      if (dimension == (dimensions - 1))
	{
	  if (flat_ref < out_len)
	    {
	      int plen;
	      char buf[128];
	      if (use_write == USE_READABLE_WRITE)
		{
		  char *indices;
		  /* need to translate flat_ref into a set of indices
		   */
		  indices = (char *)calloc(128, sizeof(char));
		  plen = snprintf(buf, 128, "(set! ({v}%s) ", multivector_indices_to_string(sc, flat_ref, vec, indices, dimension));
		  port_write_string(port)(sc, buf, plen, port);
		  free(indices);
		}

	      if (is_int_vector(vec))
		{
		  plen = snprintf(buf, 128, "%lld", int_vector_element(vec, flat_ref));
		  port_write_string(port)(sc, buf, plen, port);
		}
	      else
		{
		  if (is_float_vector(vec))
		    {
		      plen = snprintf(buf, 128, float_format_g, WRITE_REAL_PRECISION, float_vector_element(vec, flat_ref));
		      floatify(buf, &plen);
		      port_write_string(port)(sc, buf, plen, port);
		    }
		  else object_to_port_with_circle_check(sc, vector_element(vec, flat_ref), port, DONT_USE_DISPLAY(use_write), to_file, ci); 
		}

	      if (use_write == USE_READABLE_WRITE)
		port_write_string(port)(sc, ") ", 2, port);
	      flat_ref++;
	    }
	  else
	    {
	      port_write_string(port)(sc, "...)", 4, port);
	      return(flat_ref);
	    }
	  if ((use_write != USE_READABLE_WRITE) &&
	      (i < (vector_dimension(vec, dimension) - 1)))
	    port_write_character(port)(sc, ' ', port);
	}
      else 
	{
	  if (flat_ref < out_len)
	    flat_ref = multivector_to_port(sc, vec, port, out_len, flat_ref, dimension + 1, dimensions, last, DONT_USE_DISPLAY(use_write), to_file, ci);
	  else 
	    {
	      port_write_string(port)(sc, "...)", 4, port);
	      return(flat_ref);
	    }
	}
    }
  if (use_write != USE_READABLE_WRITE)
    port_write_character(port)(sc, ')', port);
  (*last) = true;
  return(flat_ref);
}


static void vector_to_port(s7_scheme *sc, s7_pointer vect, s7_pointer port, use_write_t use_write, bool to_file, shared_info *ci)
{
  s7_Int i, len;
  int plen;
  bool too_long = false;
  char buf[128];
  
  len = vector_length(vect);
  if (len == 0)
    {
      if (vector_rank(vect) > 1)
	{
	  plen = snprintf(buf, 32, "#%dD()", vector_ndims(vect));
	  port_write_string(port)(sc, buf, plen, port);
	}
      else port_write_string(port)(sc, "#()", 3, port);
      return;
    }
  
  if ((use_write != USE_READABLE_WRITE) &&
      ((!to_file) || (port == sc->standard_output) || (port == sc->standard_error)))
    {
      /* if to_file we ignore *vector-print-length* so a subsequent read will be ok
       *
       * (with-output-to-file "test.test" 
       *   (lambda () 
       *     (let ((vect (make-vector (+ *vector-print-length* 2) 1.0))) 
       *       (write vect))))
       */

      plen = s7_vector_print_length(sc);
      if (plen <= 0)
	{
	  if (vector_rank(vect) > 1)
	    {
	      plen = snprintf(buf, 32, "#%dD(...)", vector_ndims(vect));
	      port_write_string(port)(sc, buf, plen, port);
	    }
	  else port_write_string(port)(sc, "#(...)", 6, port);
	  return;
	}

      if (len > plen)
	{
	  too_long = true;
	  len = plen;
	}
    }

  if (use_write == USE_READABLE_WRITE)
    {
      port_write_string(port)(sc, "(let (({v} (make-vector ", 24, port);
      if (vector_rank(vect) > 1)
	{
	  unsigned int dim;
	  port_write_string(port)(sc, "'(", 2, port);
	  for (dim = 0; dim < vector_ndims(vect); dim++)
	    {
	      plen = snprintf(buf, 128, "%lld ", vector_dimension(vect, dim));
	      port_write_string(port)(sc, buf, plen, port);
	    }
	  if (is_int_vector(vect))
	    port_write_string(port)(sc, ") 0 #t))) ", 10, port);
	  else
	    {
	      if (is_float_vector(vect))
		port_write_string(port)(sc, ") 0.0 #t))) ", 12, port);
	      else port_write_string(port)(sc, ")))) ", 5, port);
	    }
	}
      else
	{
	  if (is_int_vector(vect))
	    plen = snprintf(buf, 128, "%lld 0 #t))) ", vector_length(vect));
	  else
	    {
	      if (is_float_vector(vect))
		plen = snprintf(buf, 128, "%lld 0.0 #t))) ", vector_length(vect));
	      else plen = snprintf(buf, 128, "%lld))) ", vector_length(vect));
	    }
	  port_write_string(port)(sc, buf, plen, port);
	}

      if ((ci) &&
	  (shared_ref(ci, vect) < 0))
	{
	  plen = snprintf(buf, 128, "(set! {%d} {v}) ", -shared_ref(ci, vect));
	  port_write_string(port)(sc, buf, plen, port);
	}

      if (vector_rank(vect) > 1)
	{
	  bool last = false;
	  multivector_to_port(sc, vect, port, len, 0, 0, vector_ndims(vect), &last, use_write, to_file, ci);
	}
      else
	{
	  for (i = 0; i < len; i++)
	    {
	      port_write_string(port)(sc, "(set! ({v} ", 11, port);
	      plen = snprintf(buf, 128, "%lld) ", i);
	      port_write_string(port)(sc, buf, plen, port);

	      if (is_int_vector(vect))
		{
		  plen = snprintf(buf, 128, "%lld", int_vector_element(vect, i));
		  port_write_string(port)(sc, buf, plen, port);
		}
	      else
		{
		  if (is_float_vector(vect))
		    {
		      plen = snprintf(buf, 128, float_format_g, WRITE_REAL_PRECISION, float_vector_element(vect, i));
		      floatify(buf, &plen);
		      port_write_string(port)(sc, buf, plen, port);
		    }
		  else object_to_port_with_circle_check(sc, vector_element(vect, i), port, use_write, to_file, ci);
		}

	      port_write_string(port)(sc, ") ", 2, port);
	    }
	}
      port_write_string(port)(sc, "{v})", 4, port);
    }
  else
    {
      if (vector_rank(vect) > 1)
	{
	  bool last = false;
	  if (vector_ndims(vect) > 1)
	    {
	      plen = snprintf(buf, 32, "#%dD", vector_ndims(vect));
	      port_write_string(port)(sc, buf, plen, port);
	    }
	  else port_write_character(port)(sc, '#', port);
	  multivector_to_port(sc, vect, port, len, 0, 0, vector_ndims(vect), &last, use_write, to_file, ci);
	}
      else
	{
	  if (is_float_vector(vect))
	    port_write_string(port)(sc, "(float-vector ", 14, port);
	  else port_write_string(port)(sc, "#(", 2, port);
	  if (is_int_vector(vect))
	    {
	      for (i = 0; i < len - 1; i++)
		{
		  plen = snprintf(buf, 128, "%lld ", int_vector_element(vect, i));
		  port_write_string(port)(sc, buf, plen, port);
		}
	      plen = snprintf(buf, 128, "%lld", int_vector_element(vect, i));
	      port_write_string(port)(sc, buf, plen, port);
	    }
	  else
	    {
	      if (is_float_vector(vect))
		{
		  plen = snprintf(buf, 128, float_format_g, WRITE_REAL_PRECISION, float_vector_element(vect, 0));
		  floatify(buf, &plen);
		  port_write_string(port)(sc, buf, plen, port);
		  for (i = 1; i < len; i++)
		    {
		      port_write_character(port)(sc, ' ', port);
		      plen = snprintf(buf, 128, float_format_g, WRITE_REAL_PRECISION, float_vector_element(vect, i));
		      floatify(buf, &plen);
		      port_write_string(port)(sc, buf, plen, port);
		    }
		}
	      else
		{
		  for (i = 0; i < len - 1; i++)
		    {
		      object_to_port_with_circle_check(sc, vector_element(vect, i), port, DONT_USE_DISPLAY(use_write), to_file, ci);
		      port_write_character(port)(sc, ' ', port);
		    }
		  object_to_port_with_circle_check(sc, vector_element(vect, i), port, DONT_USE_DISPLAY(use_write), to_file, ci);
		}
	    }

	  if (too_long)
	    port_write_string(port)(sc, " ...)", 5, port);
	  else port_write_character(port)(sc, ')', port);
	}
    }
}


static void bytevector_to_port(s7_scheme *sc, s7_pointer vect, s7_pointer port, use_write_t use_write, bool to_file)
{
  s7_Int i, len, plen;
  bool too_long = false;

  len = string_length(vect);
  if (use_write == USE_READABLE_WRITE)
    plen = len;
  else plen = s7_vector_print_length(sc);

  if (len == 0)
    port_write_string(port)(sc, "#u8()", 5, port);
  else
    {
      if (plen <= 0)
	port_write_string(port)(sc, "#u8(...)", 8, port);
      else
	{
	  int nlen;
          #define BYTE_TO_STR_SIZE 8
	  char byte_to_str[BYTE_TO_STR_SIZE];

	  if (len > plen)
	    {
	      too_long = true;
	      len = plen;
	    }
	  port_write_string(port)(sc, "#u8(", 4, port);
	  for (i = 0; i < len - 1; i++)
	    {
	      nlen = snprintf(byte_to_str, BYTE_TO_STR_SIZE, "%d", (unsigned int)((unsigned char)string_value(vect)[i])); /* casts are not redundant */
	      port_write_string(port)(sc, byte_to_str, nlen, port);
	      port_write_character(port)(sc, ' ', port);
	    }
	  nlen = snprintf(byte_to_str, BYTE_TO_STR_SIZE, "%d", (unsigned int)((unsigned char)string_value(vect)[i]));
	  port_write_string(port)(sc, byte_to_str, nlen, port);

	  if (too_long)
	    port_write_string(port)(sc, " ...)", 5, port);
	  else port_write_character(port)(sc, ')', port);
	}
    }
}


static void list_to_port(s7_scheme *sc, s7_pointer lst, s7_pointer port, use_write_t use_write, bool to_file, shared_info *ci)
{
  /* we need list_to_starboard... */
  s7_pointer x;
  int i, len, true_len;

  true_len = s7_list_length(sc, lst);
  if (true_len < 0)                    /* a dotted list -- handle cars, then final cdr */
    len = (-true_len + 1);
  else
    {
      if (true_len == 0)               /* either () or a circular list */
	{
	  if (is_not_null(lst))
	    len = circular_list_entries(lst);
	  else
	    {
	      port_write_string(port)(sc, "()", 2, port);
	      return;
	    }
	}
      else len = true_len;
    }

  if (((car(lst) == sc->QUOTE) || 
       (car(lst) == sc->QUOTE_UNCHECKED)) && /* this can happen (see lint.scm) */
      (true_len == 2))
    {
      /* len == 1 is important, otherwise (list 'quote 1 2) -> '1 2 which looks weird 
       *   or (object->string (apply . `''1)) -> "'quote 1"
       * so (quote x) = 'x but (quote x y z) should be left alone (if evaluated, it's an error)
       *
       * in CL:
       *    [2]> (list 'quote 1 2)
       *    (QUOTE 1 2)
       *    [3]> (list 'quote 1)
       *    '1
       *    [4]> (cons 'quote 1)
       *    (QUOTE . 1)
       *
       * in s7:
       *    :(list 'quote 1 2)
       *    (quote 1 2)
       *    :(list 'quote 1)
       *    '1
       *    :(cons 'quote 1)
       *    (quote . 1)
       *    (write (list 'quote 1))
       *    -> '1
       */

      port_write_character(port)(sc, '\'', port);
      object_to_port_with_circle_check(sc, cadr(lst), port, DONT_USE_DISPLAY(use_write), to_file, ci);
      return;
    }
  else port_write_character(port)(sc, '(', port);

  if (is_multiple_value(lst))
    port_write_string(port)(sc, "values ", 7, port);

  if (use_write == USE_READABLE_WRITE)
    {
      if (ci) 
	{
	  int plen;
	  char buf[128];

	  port_write_string(port)(sc, "let (({lst} (make-list ", 23, port);
	  plen = snprintf(buf, 128, "%d))) ", len);
	  port_write_string(port)(sc, buf, plen, port);

	  if ((shared_ref(ci, lst) < 0))
	    {
	      plen = snprintf(buf, 128, "(set! {%d} {lst}) ", -shared_ref(ci, lst));
	      port_write_string(port)(sc, buf, plen, port);
	    }
	  
	  port_write_string(port)(sc, "(let (({x} {lst})) ", 19, port);
	  for (i = 0, x = lst; (i < len) && (is_pair(x)); i++, x = cdr(x))
	    {
	      port_write_string(port)(sc, "(set-car! {x} ", 14, port);
	      object_to_port_with_circle_check(sc, car(x), port, use_write, to_file, ci);
	      port_write_string(port)(sc, ") ", 2, port);
	      if (i < len - 1)
		port_write_string(port)(sc, "(set! {x} (cdr {x})) ", 21, port);
	    }
	  if (!is_null(x))
	    {
	      port_write_string(port)(sc, "(set-cdr! {x} ", 14, port);
	      object_to_port_with_circle_check(sc, x, port, use_write, to_file, ci);
	      port_write_string(port)(sc, ") ", 2, port);
	    }
	  port_write_string(port)(sc, ") {lst})", 8, port);
	}
      else
	{
	  /* the easier cases: no circles or shared refs to patch up */
	  if (true_len > 0)
	    {
	      port_write_string(port)(sc, "list", 4, port);
	      for (x = lst; is_pair(x); x = cdr(x))
		{
		  port_write_character(port)(sc, ' ', port);
		  object_to_port_with_circle_check(sc, car(x), port, use_write, to_file, ci);
		}
	      port_write_character(port)(sc, ')', port);
	    }
	  else
	    {
	      port_write_string(port)(sc, "cons ", 5, port);
	      object_to_port_with_circle_check(sc, car(lst), port, use_write, to_file, ci);
	      for (x = cdr(lst); is_pair(x); x = cdr(x))
		{
		  port_write_character(port)(sc, ' ', port);
		  port_write_string(port)(sc, "(cons ", 6, port);
		  object_to_port_with_circle_check(sc, car(x), port, use_write, to_file, ci);
		}
	      port_write_character(port)(sc, ' ', port);
	      object_to_port_with_circle_check(sc, x, port, use_write, to_file, ci);
	      for (i = 1; i < len; i++)
		port_write_character(port)(sc, ')', port);
	    }
	}
    }
  else
    {
      for (x = lst, i = 0; (is_pair(x)) && (i < len) && ((!ci) || (i == 0) || (peek_shared_ref(ci, x) == 0)); i++, x = cdr(x))
	{
	  object_to_port_with_circle_check(sc, car(x), port, DONT_USE_DISPLAY(use_write), to_file, ci);
	  if (i < (len - 1)) 
	    port_write_character(port)(sc, ' ', port);
	}
      if (is_not_null(x))
	{
	  if ((true_len == 0) &&
	      (i == len))
	    port_write_string(port)(sc, " . ", 3, port);
	  else port_write_string(port)(sc, ". ", 2, port);
	  object_to_port_with_circle_check(sc, x, port, DONT_USE_DISPLAY(use_write), to_file, ci);
	}
      port_write_character(port)(sc, ')', port);
    }
}


static void hash_table_to_port(s7_scheme *sc, s7_pointer hash, s7_pointer port, use_write_t use_write, bool to_file, shared_info *ci)
{
  int i, len, gc_iter;
  bool too_long = false;
  s7_pointer iterator;
  
  /* if hash is a member of ci, just print its number
   * (let ((ht (hash-table '(a . 1)))) (hash-table-set! ht 'b ht))
   *
   * since equal? does not care about the hash-table lengths, we can ignore that complication in the :readable case
   */

  len = hash_table_entries(hash);
  if (len == 0)
    {
      if (use_write == USE_READABLE_WRITE)
	port_write_string(port)(sc, "(hash-table)", 12, port);
      else port_write_string(port)(sc, "#<hash-table>", 13, port);
      return;
    }

  if ((use_write != USE_READABLE_WRITE) &&
      ((!to_file) || (port == sc->standard_output) || (port == sc->standard_error)))
    {
      int plen;
      plen = s7_vector_print_length(sc);
      if (plen <= 0)
	{
	  port_write_string(port)(sc, "#<hash-table ...>", 17, port);
	  return;
	}
      if (len > plen)
	{
	  too_long = true;
	  len = plen;
	}
    }

  iterator = g_make_hash_table_iterator(sc, list_1(sc, hash));
  gc_iter = s7_gc_protect(sc, iterator);

  if (use_write == USE_READABLE_WRITE)
    {
      port_write_string(port)(sc, "(let (({ht} (make-hash-table)))", 31, port);
      if ((ci) &&
	  (shared_ref(ci, hash) < 0))
	{
	  int plen;
	  char buf[64];
	  plen = snprintf(buf, 64, "(set! {%d} {ht}) ", -shared_ref(ci, hash));
	  port_write_string(port)(sc, buf, plen, port);
	}
      for (i = 0; i < len; i++)
	{
	  s7_pointer key_val, key, val;

	  key_val = hash_table_iterate(sc, iterator);
	  key = car(key_val);
	  val = cdr(key_val);

	  port_write_string(port)(sc, " (set! ({ht} ", 13, port);	  
	  if (key == hash)
	    port_write_string(port)(sc, "{ht}", 4, port);
	  else object_to_port_with_circle_check(sc, key, port, USE_READABLE_WRITE, to_file, ci);
	  port_write_string(port)(sc, ") ", 2, port);
	  if (val == hash)
	    port_write_string(port)(sc, "{ht}", 4, port);
	  else object_to_port_with_circle_check(sc, val, port, USE_READABLE_WRITE, to_file, ci);
	  port_write_character(port)(sc, ')', port);
	}
      port_write_string(port)(sc, " {ht})", 6, port);
    }
  else 
    {
      port_write_string(port)(sc, "#<hash-table ", 13, port);
      for (i = 0; i < len - 1; i++)
	{
	  object_to_port_with_circle_check(sc, hash_table_iterate(sc, iterator), port, DONT_USE_DISPLAY(use_write), to_file, ci);
	  port_write_character(port)(sc, ' ', port);
	}
      object_to_port_with_circle_check(sc, hash_table_iterate(sc, iterator), port, DONT_USE_DISPLAY(use_write), to_file, ci);
      if (too_long)
	port_write_string(port)(sc, " ...>", 5, port);
      else port_write_character(port)(sc, '>', port);
    }

  s7_gc_unprotect_at(sc, gc_iter);
}


static void write_readably_error(s7_scheme *sc, const char *type)
{
  s7_error(sc, sc->IO_ERROR, list_2(sc, make_string_wrapper(sc, "can't write ~A readably"), make_string_wrapper(sc, type)));
}


static void e_to_p(s7_scheme *sc, s7_pointer x, s7_pointer port, use_write_t use_write, bool to_file, shared_info *ci)
{
  if (is_slot(x))
    {
      e_to_p(sc, next_slot(x), port, use_write, to_file, ci);
      port_write_character(port)(sc, ' ', port);
      object_to_port_with_circle_check(sc, x, port, use_write, to_file, ci);
    }
}

static void environment_to_port(s7_scheme *sc, s7_pointer obj, s7_pointer port, use_write_t use_write, bool to_file, shared_info *ci)
{
  /* if outer env points to (say) method list, the object needs to specialize object->string itself
   */
  if (has_methods(obj))
    {
      s7_pointer print_func;
      print_func = find_method(sc, obj, sc->OBJECT_TO_STRING);
      if (print_func != sc->UNDEFINED)
	{
	  s7_pointer p;
	  /* what needs to be protected here? for one, the function might not return a string! */
	  push_stack(sc, OP_NO_OP, sc->temp_cell_2, sc->temp_cell_3);
	  if (use_write == USE_WRITE)
	    p = s7_apply_function(sc, print_func, list_1(sc, obj));
	  else p = s7_apply_function(sc, print_func, list_2(sc, obj, (use_write == USE_DISPLAY) ? sc->F : sc->KEY_READABLE));
	  sc->temp_cell_2 = main_stack_args(sc);
	  sc->temp_cell_3 = main_stack_code(sc);
	  pop_main_stack(sc);
	  
	  if ((is_string(p)) && (string_length(p) > 0))
	    port_write_string(port)(sc, string_value(p), string_length(p), port);
	  return;
	}
    }
  if (obj == sc->rootlet)
    {
      if (use_write == USE_READABLE_WRITE)
	port_write_string(port)(sc, "(rootlet)", 9, port);
      else port_write_string(port)(sc, "#<rootlet>", 10, port);
    }
  else
    {
      /* circles can happen here: 
       *    (let () (let ((b (curlet))) (curlet)))
       *    #<let 'b #<let>>
       * or (let ((b #f)) (set! b (curlet)) (curlet))
       *    #1=#<let 'b #1#>
       */
      if (use_write == USE_READABLE_WRITE)
	{
	  if (obj == sc->stacktrace_env)
	    port_write_string(port)(sc, "*stacktrace*", 12, port);
	  else
	    {
	      s7_pointer x;
	      port_write_string(port)(sc, "(let (({e} (inlet))) ", 21, port);
	      if ((ci) &&
		  (shared_ref(ci, obj) < 0))
		{
		  int plen;
		  char buf[64];
		  plen = snprintf(buf, 64, "(set! {%d} {e}) ", -shared_ref(ci, obj));
		  port_write_string(port)(sc, buf, plen, port);
		}
	      
	      port_write_string(port)(sc, "(apply varlet {e} (reverse (list ", 33, port);
	      for (x = let_slots(obj); is_slot(x); x = next_slot(x))
		{
		  port_write_string(port)(sc, "(cons ", 6, port);
		  object_to_port(sc, slot_symbol(x), port, use_write, to_file, ci);
		  port_write_character(port)(sc, ' ', port);
		  object_to_port_with_circle_check(sc, slot_value(x), port, use_write, to_file, ci);
		  port_write_character(port)(sc, ')', port);
		}
	      port_write_string(port)(sc, "))) {e})", 8, port);
	    }
	}
      else
	{
	  port_write_string(port)(sc, "#<let", 5, port);
	  e_to_p(sc, let_slots(obj), port, use_write, to_file, ci);
	  port_write_character(port)(sc, '>', port);
	}
    }
}


static void write_macro_readably(s7_scheme *sc, s7_pointer obj, s7_pointer port)
{
  s7_pointer arglist, body, expr;
  bool starred = false;

  body = cadar(closure_body(obj));
  arglist = cadr(body);
  if ((car(body) == sc->LAMBDA_STAR) || (car(body) == sc->LAMBDA_STAR_UNCHECKED))
    starred = true;
  body = cddr(body);

  port_write_string(port)(sc, "(define-", 8, port);
  port_write_string(port)(sc, (type(obj) == T_MACRO) ? "macro" : "bacro", 5, port); 
  if (starred)
    port_write_character(port)(sc, '*', port);
  port_write_string(port)(sc, " (_m_", 5, port);
  if (is_symbol(arglist))
    {
      port_write_string(port)(sc, " . ", 3, port);
      port_write_string(port)(sc, symbol_name(arglist), symbol_name_length(arglist), port);
    }
  else
    {
      if (is_pair(arglist))
	{
	  for (expr = arglist; is_pair(expr); expr = cdr(expr))
	    {
	      port_write_character(port)(sc, ' ', port);
	      object_to_port(sc, car(expr), port, USE_WRITE, false, NULL);
	    }
	  if (!is_null(expr))
	    {
	      port_write_string(port)(sc, " . ", 3, port);
	      object_to_port(sc, expr, port, USE_WRITE, false, NULL);
	    }
	}
    }
  port_write_string(port)(sc, ") ", 2, port);
  for (expr = body; is_pair(expr); expr = cdr(expr))
    object_to_port(sc, car(expr), port, USE_WRITE, false, NULL);
  port_write_character(port)(sc, ')', port);
}


static s7_pointer match_symbol(s7_scheme *sc, s7_pointer symbol, s7_pointer e)
{
  s7_pointer y, le;
  for (le = e; is_let(le) && (le != sc->rootlet); le = outlet(le))
    for (y = let_slots(le); is_slot(y); y = next_slot(y))
      if (slot_symbol(y) == symbol)
	return(y);
  return(NULL);
}

static bool slot_memq(s7_pointer symbol, s7_pointer symbols)
{
  s7_pointer x;
  for (x = symbols; is_pair(x); x = cdr(x))
    if (slot_symbol(x) == symbol)
      return(x);
  return(false);
}


static void collect_locals(s7_scheme *sc, s7_pointer body, s7_pointer e, int gc_loc)
{
  if (is_pair(body))
    {
      collect_locals(sc, car(body), e, gc_loc);
      collect_locals(sc, cdr(body), e, gc_loc);
    }
  else
    {
      if (is_symbol(body))
	{
	  if (!slot_memq(body, gc_protected_at(sc, gc_loc)))
	    {
	      s7_pointer slot;
	      slot = match_symbol(sc, body, e);
	      if (slot)
		gc_protected_at(sc, gc_loc) = cons(sc, slot, gc_protected_at(sc, gc_loc));
	    }
	}
    }
}


static void write_closure_readably(s7_scheme *sc, s7_pointer obj, s7_pointer port)
{
  s7_pointer body, arglist, pe, local_slots, p;
  int gc_loc;

  body = closure_body(obj);
  arglist = closure_args(obj);
  pe = closure_let(obj);
  gc_loc = s7_gc_protect(sc, sc->NIL);
  collect_locals(sc, body, pe, gc_loc);
  local_slots = gc_protected_at(sc, gc_loc);

  if (!is_null(local_slots))
    {
      s7_pointer x;
      port_write_string(port)(sc, "(let (", 6, port);
      for (x = local_slots; is_pair(x); x = cdr(x))
	{
	  s7_pointer slot;
	  slot = car(x);
	  port_write_character(port)(sc, '(', port);
	  port_write_string(port)(sc, symbol_name(slot_symbol(slot)), symbol_name_length(slot_symbol(slot)), port);
	  port_write_character(port)(sc, ' ', port);
	  write_or_display(sc, slot_value(slot), port, USE_WRITE);
	  port_write_character(port)(sc, ')', port);
	}
      port_write_string(port)(sc, ") ", 2, port);
    }

  port_write_string(port)(sc, "(lambda", 7, port);
  if (type(obj) == T_CLOSURE_STAR)
    port_write_character(port)(sc, '*', port);
  port_write_character(port)(sc, ' ', port);
  write_or_display(sc, arglist, port, USE_WRITE); /* here we just want the straight output (a b) not (list 'a 'b) */
  
  for (p = body; is_pair(p); p = cdr(p))
    {
      port_write_character(port)(sc, ' ', port);
      write_or_display(sc, car(p), port, USE_WRITE);
    }
  port_write_character(port)(sc, ')', port);
  
  if (!is_null(local_slots))
    port_write_character(port)(sc, ')', port);
  s7_gc_unprotect_at(sc, gc_loc);
}


static void object_to_port(s7_scheme *sc, s7_pointer obj, s7_pointer port, use_write_t use_write, bool to_file, shared_info *ci)
{
  int nlen = 0;
  char *str;
  char buf[64];

  switch (type(obj))
    {
    case T_INT_VECTOR:
    case T_FLOAT_VECTOR:
    case T_VECTOR:
      vector_to_port(sc, obj, port, use_write, to_file, ci);
      break;

    case T_PAIR:
      list_to_port(sc, obj, port, use_write, to_file, ci);
      break;

    case T_HASH_TABLE:
      hash_table_to_port(sc, obj, port, use_write, to_file, ci);
      break;

    case T_ENVIRONMENT:
      environment_to_port(sc, obj, port, use_write, to_file, ci);
      break;

    case T_BOOLEAN:
    case T_NIL:
    case T_UNIQUE:
    case T_UNSPECIFIED:
      port_write_string(port)(sc, unique_name(obj), unique_name_length(obj), port);
      break;

    case T_INPUT_PORT:
      if (obj == sc->standard_input)
	port_write_string(port)(sc, port_filename(obj), port_filename_length(obj), port);
      else
	{
	  if (use_write == USE_READABLE_WRITE)
	    {
	      if (port_is_closed(obj))
		port_write_string(port)(sc, "(call-with-input-string \"\" (lambda (p) p))", 42, port);
	      else
		{
		  if (is_function_port(obj))
		    write_readably_error(sc, "a function input port");
		  else
		    {
		      if (port_read_character(port) == file_read_char)
			write_readably_error(sc, "a very large file input port");
		      else
			{
			  if (port_position(obj) != 0)
			    port_write_string(port)(sc, "(let ((p ", 9, port);
			  if (port_filename(obj) != NULL)
			    {
			      /* try to avoid storing enormous in-core strings */
			      port_write_string(port)(sc, "(open-input-file \"", 18, port);
			      port_write_string(port)(sc, port_filename(obj), port_filename_length(obj), port);
			      port_write_string(port)(sc, "\")", 2, port);
			    }
			  else
			    {
			      char *str;
			      int nlen = 0;
			      port_write_string(port)(sc, "(open-input-string ", 19, port);
			      /* not port_write_string here because there might be embedded double-quotes */
			      str = slashify_string(sc, (const char *)port_data(obj), port_data_size(obj), IN_QUOTES, &nlen);
			      port_write_string(port)(sc, str, nlen, port);
			      port_write_character(port)(sc, ')', port);
			    }
			  if (port_position(obj) != 0)
			    {
			      char buf[64];
			      int plen;
			      port_write_string(port)(sc, ")) (do ((i 0 (+ i 1))) ((= i ", 29, port);
			      plen = snprintf(buf, 64, "%u) p) (read-char p)))", port_position(obj));
			      port_write_string(port)(sc, buf, plen, port);
			    }
			}
		    }
		}
	    }
	  else
	    {
	      nlen = snprintf(buf, 64, "<input-%s-port%s>", 
			      (is_file_port(obj)) ? "file" : ((is_string_port(obj)) ? "string" : "function"), 
			      (port_is_closed(obj)) ? " (closed)" : "");
	    }
	  port_write_string(port)(sc, buf, nlen, port);
	}
      break;

    case T_OUTPUT_PORT:
      if ((obj == sc->standard_output) || 
	  (obj == sc->standard_error))
	port_write_string(port)(sc, port_filename(obj), port_filename_length(obj), port);
      else
	{
	  if (use_write == USE_READABLE_WRITE)
	    {
	      if (port_is_closed(obj))
		port_write_string(port)(sc, "(let ((p (open-output-string))) (close-output-port p) p)", 56, port);
	      else write_readably_error(sc, "an output port");
	      /* there's nothing safe or even modestly foolproof that we can do here */
	    }
	  else
	    {
	      nlen = snprintf(buf, 64, "<output-%s-port%s>", 
			      (is_file_port(obj)) ? "file" : ((is_string_port(obj)) ? "string" : "function"), 
			      (port_is_closed(obj)) ? " (closed)" : "");
	      port_write_string(port)(sc, buf, nlen, port);
	    }
	}
      break;

    case T_COUNTER:
      if (use_write == USE_READABLE_WRITE)
	write_readably_error(sc, "an internal for-each descriptor");
      nlen = snprintf(buf, 64, "#<counter: %d>", counter_count(obj));
      port_write_string(port)(sc, buf, nlen, port);
      break;

    case T_BAFFLE:
      if (use_write == USE_READABLE_WRITE)
	write_readably_error(sc, "an internal with-baffle descriptor");
      nlen = snprintf(buf, 64, "#<baffle: %d>", baffle_key(obj));
      port_write_string(port)(sc, buf, nlen, port);
      break;

    case T_INTEGER:
      if (has_print_name(obj))
	port_write_string(port)(sc, print_name(obj), print_name_length(obj), port);
      else
	{
	  nlen = 0;
	  str = integer_to_string_base_10_no_width(obj, &nlen);
	  if (nlen > 0)
	    {
	      set_print_name(obj, str, nlen);
	      port_write_string(port)(sc, str, nlen, port);
	    }
	  else port_display(port)(sc, str, port);
	}
      break;

    case T_REAL:
    case T_RATIO:
    case T_COMPLEX:
      if (has_print_name(obj))
	port_write_string(port)(sc, print_name(obj), print_name_length(obj), port);
      else
	{
	  nlen = 0;
	  str = number_to_string_base_10(obj, 0, WRITE_REAL_PRECISION, 'g', &nlen, use_write); /* was 14 */
	  set_print_name(obj, str, nlen);
	  port_write_string(port)(sc, str, nlen, port);
	}
      break;

#if WITH_GMP
    case T_BIG_INTEGER:
    case T_BIG_RATIO:
    case T_BIG_REAL:
    case T_BIG_COMPLEX:
      nlen = 0;
      str = big_number_to_string_with_radix(obj, BASE_10, 0, &nlen, use_write);
      port_write_string(port)(sc, str, nlen, port);
      free(str);
      break;
#endif

    case T_SYMBOL:
      /* I think this is the only place we print a symbol's name
       *   but in the readable case, what about (symbol "1;3")? it actually seems ok!
       */
      if ((symbol_name(obj)[0] == '#') ||                 /* otherwise the reader thinks there's a #-reader problem */
	  (needs_slashification(symbol_name(obj), symbol_name_length(obj))))
	{
	  char *symstr;
	  str = slashify_string(sc, symbol_name(obj), symbol_name_length(obj), NOT_IN_QUOTES, &nlen);
	  nlen += 16;
	  symstr = (char *)malloc(nlen * sizeof(char));
	  nlen = snprintf(symstr, nlen, "(symbol \"%s\")", str);
	  port_write_string(port)(sc, symstr, nlen, port);
	  free(symstr);
	}
      else 
	{
	  if ((use_write == USE_READABLE_WRITE) &&
	      (!is_keyword(obj)))
	    port_write_character(port)(sc, '\'', port);
	  port_write_string(port)(sc, symbol_name(obj), symbol_name_length(obj), port);
	}
      break;

    case T_SYNTAX:
      port_display(port)(sc, op_names[(int)syntax_opcode(obj)], port);
      break;

    case T_STRING:
      if (is_bytevector(obj))
	bytevector_to_port(sc, obj, port, use_write, to_file);
      else
	{
	  if (string_length(obj) > 0)
	    {
	      /* this used to check for length > 1<<24 -- is that still necessary?
	       * since string_length is a scheme length, not C, this write can embed nulls from C's point of view 
	       */
	      if (use_write == USE_DISPLAY)
		port_write_string(port)(sc, string_value(obj), string_length(obj), port);
	      else
		{
		  if (!needs_slashification(string_value(obj), string_length(obj)))
		    {
		      port_write_character(port)(sc, '"', port);
		      port_write_string(port)(sc, string_value(obj), string_length(obj), port);
		      port_write_character(port)(sc, '"', port);
		    }
		  else
		    {
		      str = slashify_string(sc, string_value(obj), string_length(obj), IN_QUOTES, &nlen);
		      port_write_string(port)(sc, str, nlen, port);
		    }
		}
	    }
	  else
	    {
	      if (use_write != USE_DISPLAY)
		port_write_string(port)(sc, "\"\"", 2, port);
	    }
	}
      break;

    case T_CHARACTER:
      if (use_write == USE_DISPLAY) 
	port_write_character(port)(sc, character(obj), port);
      else port_write_string(port)(sc, character_name(obj), character_name_length(obj), port);
      break;

    case T_MACRO:
      if (use_write == USE_READABLE_WRITE)
	write_macro_readably(sc, obj, port);
      else port_write_string(port)(sc, "#<macro>", 8, port);
      break;

    case T_BACRO:
      if (use_write == USE_READABLE_WRITE)
	write_macro_readably(sc, obj, port);
      else port_write_string(port)(sc, "#<bacro>", 8, port);
      break;

    case T_CLOSURE:
    case T_CLOSURE_STAR:
      if (has_methods(obj))
	{
	  /* look for object->string method else fallback on ordinary case.
	   * can't use recursion on closure_let here because then the fallback name is #<let>.
	   */
	  s7_pointer print_func;
	  print_func = find_method(sc, closure_let(obj), sc->OBJECT_TO_STRING);
	  if (print_func != sc->UNDEFINED)
	    {
	      s7_pointer p;
	      p = s7_apply_function(sc, print_func, list_1(sc, obj));
	      if (string_length(p) > 0)
		port_write_string(port)(sc, string_value(p), string_length(p), port);
	      break;
	    }
	}
      if (use_write == USE_READABLE_WRITE)
	write_closure_readably(sc, obj, port);
      else
	{
	  const char *p;
	  p = c_closure_name(sc, obj, &nlen);
	  if (nlen > 0)
	    port_write_string(port)(sc, p, nlen, port);
	}
      break;
  
    case T_C_OPT_ARGS_FUNCTION:
    case T_C_RST_ARGS_FUNCTION:
    case T_C_ANY_ARGS_FUNCTION:
    case T_C_FUNCTION:
    case T_C_FUNCTION_STAR:
      port_write_string(port)(sc, c_function_name(obj), c_function_name_length(obj), port);
      break;

    case T_C_MACRO:
      port_write_string(port)(sc, c_macro_name(obj), c_macro_name_length(obj), port);
      break;
  
    case T_C_POINTER:
      if (use_write == USE_READABLE_WRITE)
	{
#if (SIZEOF_VOID_P == 4)
	  if ((long)raw_pointer(obj) < 2) /* 0 = NULL is the main case, -1 is used in some libraries */
	    nlen = snprintf(buf, 64, "(c-pointer %ld)", (long)raw_pointer(obj));
#else
	  if ((long long int)raw_pointer(obj) < 2) /* 0 = NULL is the main case, -1 is used in some libraries */
	    nlen = snprintf(buf, 64, "(c-pointer %lld)", (long long int)raw_pointer(obj));
#endif
	  else write_readably_error(sc, "raw c pointer");
	}
      else nlen = snprintf(buf, 64, "#<c_pointer %p>", raw_pointer(obj));
      port_write_string(port)(sc, buf, nlen, port);
      break;
  
    case T_CONTINUATION:
      if (use_write == USE_READABLE_WRITE)
	write_readably_error(sc, "a continuation");
      port_write_string(port)(sc, "#<continuation>", 15, port);
      break;
  
    case T_GOTO:
      if (use_write == USE_READABLE_WRITE)
	write_readably_error(sc, "a call-with-exit goto");
      port_write_string(port)(sc, "#<goto>", 7, port);
      break;
  
    case T_CATCH:
      if (use_write == USE_READABLE_WRITE)
	write_readably_error(sc, "an internal catch descriptor");
      port_write_string(port)(sc, "#<catch>", 8, port);
      break;

    case T_DYNAMIC_WIND:
      /* this can happen (or could while *stack* was tied in) because *stack* can involve dynamic-wind markers */
      if (use_write == USE_READABLE_WRITE)
	write_readably_error(sc, "an internal dynamic-wind descriptor");
      port_write_string(port)(sc, "#<dynamic-wind>", 15, port);
      break;
  
    case T_C_OBJECT:
      if (use_write == USE_READABLE_WRITE)
	str = ((*(c_object_print_readably(obj)))(sc, c_object_value(obj)));
      else str = ((*(c_object_print(obj)))(sc, c_object_value(obj)));
      port_display(port)(sc, str, port);
      free(str);
      break;

    case T_SLOT:
      if (use_write == USE_READABLE_WRITE)
	write_readably_error(sc, "an internal slot");

      port_write_character(port)(sc, '\'', port);
      port_write_string(port)(sc, symbol_name(slot_symbol(obj)), symbol_name_length(slot_symbol(obj)), port);
      port_write_character(port)(sc, ' ', port);
      object_to_port_with_circle_check(sc, slot_value(obj), port, use_write, to_file, ci);
      break;

    default:
#if DEBUGGING
      {
	/* show current state, current allocated state, and previous allocated state.
	 */
	char *current_bits, *allocated_bits, *previous_bits, *str;
	int save_typeflag, len;
	const char *excl_name;
	
	if (is_free(obj))
	  excl_name = "free cell!";
	else excl_name = "unknown object!";
	
	current_bits = describe_type_bits(sc, obj);
	save_typeflag = typeflag(obj);
	typeflag(obj) = obj->current_alloc_type;
	allocated_bits = describe_type_bits(sc, obj);
	typeflag(obj) = obj->previous_alloc_type;
	previous_bits = describe_type_bits(sc, obj);
	typeflag(obj) = save_typeflag;
	
	len = safe_strlen(excl_name) + 
          safe_strlen(current_bits) + safe_strlen(allocated_bits) + safe_strlen(previous_bits) + 
          safe_strlen(obj->previous_alloc_func) + safe_strlen(obj->current_alloc_func) + 512;
	str = (char *)malloc(len * sizeof(char));
	
	nlen = snprintf(str, len, 
			"\n<%s %s,\n  current: %s[%d] %s,\n  previous: %s[%d] %s\n  hloc: %d (%d uses), last gc: %s[%d], %s[%d]>", 
			excl_name, current_bits, 
			obj->current_alloc_func, obj->current_alloc_line, allocated_bits,
			obj->previous_alloc_func, obj->previous_alloc_line, previous_bits,
			heap_location(obj), obj->uses,
			gc_last_func, gc_last_line, obj->gc_func, obj->gc_line);

	free(current_bits);
	free(allocated_bits);
	free(previous_bits);
	port_write_string(port)(sc, str, nlen, port);
	free(str);
      }
#else
      {
	char *str, *tmp;
	int len;
	tmp = describe_type_bits(sc, obj);
	len = 32 + safe_strlen(tmp);
	str = (char *)malloc(len * sizeof(char));
	if (is_free(obj))
	  nlen = snprintf(str, len, "<free cell! %s>", tmp);
	else nlen = snprintf(str, len, "<unknown object! %s>", tmp);
	free(tmp);
	port_write_string(port)(sc, str, nlen, port);
	free(str);
      }
#endif
      break;
    }
}


static void object_to_port_with_circle_check(s7_scheme *sc, s7_pointer vr, s7_pointer port, use_write_t use_write, bool to_file, shared_info *ci)
{
  if ((ci) &&
      (has_structure(vr)))
    {
      int ref;
      ref = shared_ref(ci, vr);
      if (ref != 0)
	{
	  char buf[32];
	  int nlen;
	  if (ref > 0)
	    {
	      if (use_write == USE_READABLE_WRITE)
		{
		  nlen = snprintf(buf, 32, "(set! {%d} ", ref);
		  port_write_string(port)(sc, buf, nlen, port);
		  object_to_port(sc, vr, port, USE_READABLE_WRITE, to_file, ci);
		  port_write_character(port)(sc, ')', port);
		}
	      else
		{
		  nlen = snprintf(buf, 32, "#%d=", ref);
		  port_write_string(port)(sc, buf, nlen, port);
		  object_to_port(sc, vr, port, DONT_USE_DISPLAY(use_write), WITH_ELLIPSES, ci);
		}
	    }
	  else
	    {
	      if (use_write == USE_READABLE_WRITE)
		nlen = snprintf(buf, 32, "{%d}", -ref);
	      else nlen = snprintf(buf, 32, "#%d#", -ref); 
	      port_write_string(port)(sc, buf, nlen, port);
	    }
	  return;
	}
    }
  object_to_port(sc, vr, port, use_write, to_file, ci);
}


static void setup_shared_reads(s7_scheme *sc, s7_pointer port, shared_info *ci)
{
  int i;
  char buf[64];

  port_write_string(port)(sc, "(let (", 6, port);
  for (i = 1; i <= ci->top; i++)
    {
      int len;
      len = snprintf(buf, 64, "({%d} #f)", i);
      port_write_string(port)(sc, buf, len, port);
    }
  port_write_string(port)(sc, ") ", 2, port);
}

static void finish_shared_reads(s7_scheme *sc, s7_pointer port, shared_info *ci)
{
  port_write_character(port)(sc, ')', port);
}

static s7_pointer write_or_display(s7_scheme *sc, s7_pointer obj, s7_pointer port, use_write_t use_write)
{
  shared_info *ci = NULL;

  if (port_is_closed(port))
    return(s7_wrong_type_arg_error(sc, (use_write != USE_DISPLAY) ? "write" : "display", 2, port, "an open input port"));

  if (has_structure(obj))
    {
      if (obj != sc->rootlet)
	ci = make_shared_info(sc, obj, use_write != USE_READABLE_WRITE);
      if ((ci) &&
	  (use_write == USE_READABLE_WRITE))
	{
	  setup_shared_reads(sc, port, ci);
	  object_to_port_with_circle_check(sc, obj, port, use_write, is_file_port(port), ci);
	  finish_shared_reads(sc, port, ci);
	}
      else object_to_port_with_circle_check(sc, obj, port, use_write, is_file_port(port), ci);
    }
  else object_to_port(sc, obj, port, use_write, is_file_port(port), ci);
  return(obj);
}


static char *s7_object_to_c_string_1(s7_scheme *sc, s7_pointer obj, use_write_t use_write, int *nlen)
{
  shared_info *ci = NULL;
  unsigned char *str;
  s7_pointer strport;
  int gc_loc;

  strport = open_output_string(sc, FORMAT_PORT_LENGTH);
  gc_loc = s7_gc_protect(sc, strport);

  if (has_structure(obj))
    {
      if (obj != sc->rootlet)
	ci = make_shared_info(sc, obj, use_write != USE_READABLE_WRITE);
      if ((ci) &&
	  (use_write == USE_READABLE_WRITE))
	{
	  setup_shared_reads(sc, strport, ci);
	  object_to_port_with_circle_check(sc, obj, strport, use_write, WITH_ELLIPSES, ci);
	  finish_shared_reads(sc, strport, ci);
	}
      else object_to_port_with_circle_check(sc, obj, strport, use_write, WITH_ELLIPSES, ci);
    }
  else object_to_port(sc, obj, strport, use_write, WITH_ELLIPSES, ci);
  str = port_data(strport);
  if (nlen) (*nlen) = port_position(strport);
  port_data(strport) = NULL;
  port_needs_free(strport) = false;
  s7_close_output_port(sc, strport);
  s7_gc_unprotect_at(sc, gc_loc);

  return((char *)str);
}


char *s7_object_to_c_string(s7_scheme *sc, s7_pointer obj)
{
  return(s7_object_to_c_string_1(sc, obj, USE_WRITE, NULL));
}


s7_pointer s7_object_to_string(s7_scheme *sc, s7_pointer obj, bool use_write) /* unavoidable backwards compatibility rigidity here */
{
  char *str;
  int len = 0;

  str = s7_object_to_c_string_1(sc, obj, (use_write) ? USE_WRITE : USE_DISPLAY, &len);
  if (str)
    return(make_string_uncopied_with_length(sc, str, len));
  return(s7_make_string_with_length(sc, "", 0)); 
}


void s7_newline(s7_scheme *sc, s7_pointer port)
{
  s7_write_char(sc, '\n', port);
}


static s7_pointer g_newline(s7_scheme *sc, s7_pointer args)
{
  #define H_newline "(newline (port (current-output-port))) writes a carriage return to the port"
  s7_pointer port;
  
  if (is_not_null(args))
    port = car(args);
  else port = sc->output_port;
  if (!is_output_port(port))
    {
      if (port == sc->F) return(sc->UNSPECIFIED);
      check_method(sc, port, sc->NEWLINE, args);
      return(simple_wrong_type_argument_with_type(sc, sc->NEWLINE, port, AN_OUTPUT_PORT));
    }
  /* hmmm: shouldn't (newline #t) be the same as (format #t "~%")?
   */
  
  s7_newline(sc, port);
  return(sc->UNSPECIFIED);
}


void s7_write(s7_scheme *sc, s7_pointer obj, s7_pointer port)
{
  if (port != sc->F)
    write_or_display(sc, obj, port, USE_WRITE);
}


static s7_pointer g_write(s7_scheme *sc, s7_pointer args)
{
  #define H_write "(write str (port (current-output-port))) writes str (a string) to the output port"
  s7_pointer port;
  
  if (is_pair(cdr(args)))
    port = cadr(args);
  else port = sc->output_port;
  if (!is_output_port(port))
    {
      if (port == sc->F) return(sc->UNSPECIFIED);
      check_method(sc, port, sc->WRITE, args);
      return(wrong_type_argument_with_type(sc, sc->WRITE, small_int(2), port, AN_OUTPUT_PORT));
    }

  write_or_display(sc, car(args), port, USE_WRITE);
  return(sc->UNSPECIFIED);
}

/* currently: 
 *   (let ((x (list 1 2))) (write (list x x))) 
 * writes (#1=(1 2) #1#) as does display
 * but there are no circles here, so this seems unnecessarily ugly
 */


void s7_display(s7_scheme *sc, s7_pointer obj, s7_pointer port)
{
  if (port != sc->F) 
    write_or_display(sc, obj, port, USE_DISPLAY);
}


static s7_pointer g_display(s7_scheme *sc, s7_pointer args)
{
  #define H_display "(display obj (port (current-output-port))) prints obj"
  s7_pointer port;
  
  if (is_pair(cdr(args)))
    port = cadr(args);
  else port = sc->output_port;
  if (!is_output_port(port))
    {
      if (port == sc->F) return(sc->UNSPECIFIED);
      check_method(sc, port, sc->DISPLAY, args);
      return(wrong_type_argument_with_type(sc, sc->DISPLAY, small_int(2), port, AN_OUTPUT_PORT));
    }
  write_or_display(sc, car(args), port, USE_DISPLAY);
  return(sc->UNSPECIFIED); /* wouldn't it be more useful to return car(args) here and in write? */
}


static s7_pointer g_call_with_output_string(s7_scheme *sc, s7_pointer args)
{
  #define H_call_with_output_string "(call-with-output-string proc) opens a string port applies proc to it, then returns the collected output"
  s7_pointer port, proc;

  proc = car(args);
  if (is_let(proc))
    check_method(sc, proc, sc->CALL_WITH_OUTPUT_STRING, args);
  if (!s7_is_aritable(sc, proc, 1))
    {
      check_method(sc, proc, sc->CALL_WITH_OUTPUT_STRING, args);
      return(wrong_type_argument_with_type(sc, sc->CALL_WITH_OUTPUT_STRING, small_int(1), proc, 
					   make_string_wrapper(sc, "a procedure of one argument (the port)")));
    }
  if ((is_continuation(proc)) || (is_goto(proc)))
    return(wrong_type_argument_with_type(sc, sc->CALL_WITH_OUTPUT_STRING, small_int(1), proc, 
					 make_string_wrapper(sc, "a normal procedure (not a continuation)")));

  port = s7_open_output_string(sc);
  push_stack(sc, OP_GET_OUTPUT_STRING_1, sc->F, port);
  if (is_any_macro(proc))                             /* (let () (define-macro (m p) `(write 123 ,p)) (call-with-output-string m)) */
    {
      push_stack(sc, OP_EVAL_MACRO, sc->NIL, args);
      push_stack(sc, OP_APPLY, list_1(sc, list_2(sc, proc, port)), proc);
    }
  else push_stack(sc, OP_APPLY, list_1(sc, port), proc);
  return(sc->F);
}


static s7_pointer g_call_with_output_file(s7_scheme *sc, s7_pointer args)
{
  #define H_call_with_output_file "(call-with-output-file filename proc) opens filename and calls proc with the output port as its argument"
  s7_pointer port, file, proc;
  
  file = car(args);
  if (!is_string(file))
    {
      check_method(sc, file, sc->CALL_WITH_OUTPUT_FILE, args);
      return(wrong_type_argument(sc, sc->CALL_WITH_OUTPUT_FILE, small_int(1), file, T_STRING));
    }

  proc = cadr(args);
  if (!s7_is_aritable(sc, proc, 1))
    {
      check_method(sc, proc, sc->CALL_WITH_OUTPUT_FILE, args);
      return(wrong_type_argument_with_type(sc, sc->CALL_WITH_OUTPUT_FILE, small_int(2), proc, 
					   make_string_wrapper(sc, "a procedure of one argument (the port)")));
    }
  if ((is_continuation(proc)) || is_goto(proc))
    return(wrong_type_argument_with_type(sc, sc->CALL_WITH_OUTPUT_FILE, small_int(2), proc, 
					 make_string_wrapper(sc, "a normal procedure (not a continuation)")));

  port = s7_open_output_file(sc, string_value(file), "w");
  push_stack(sc, OP_UNWIND_OUTPUT, sc->F, port);
  if (is_any_macro(proc))                             /* (let () (define-macro (m p) `(write 123 ,p)) (call-with-output-file "test" m)) */
    {
      push_stack(sc, OP_EVAL_MACRO, sc->NIL, args);
      push_stack(sc, OP_APPLY, list_1(sc, list_2(sc, proc, port)), proc);
    }
  else push_stack(sc, OP_APPLY, list_1(sc, port), proc);
  return(sc->F);
}


static s7_pointer g_with_output_to_string(s7_scheme *sc, s7_pointer args)
{
  #define H_with_output_to_string "(with-output-to-string thunk) opens a string as a temporary current-output-port, calls thunk, then returns the collected output"
  s7_pointer old_output_port, p;

  p = car(args);
  if (!s7_is_aritable(sc, p, 0))
    {
      check_method(sc, p, sc->WITH_OUTPUT_TO_STRING, args);
      return(wrong_type_argument_with_type(sc, sc->WITH_OUTPUT_TO_STRING, small_int(1), p, A_THUNK));
    }
  old_output_port = sc->output_port;
  sc->output_port = s7_open_output_string(sc);
  push_stack(sc, OP_GET_OUTPUT_STRING_1, old_output_port, sc->output_port);

  if (is_any_macro(p))
    {
      push_stack(sc, OP_EVAL_MACRO, sc->NIL, args);
      push_stack(sc, OP_APPLY, list_1(sc, args), p);
    }
  else push_stack(sc, OP_APPLY, sc->NIL, p);
  return(sc->F);
}

/* (let () (define-macro (mac) (write "123")) (with-output-to-string mac))
 * (string-ref (with-output-to-string (lambda () (write "1234") (values (get-output-string) 1))))
 */


static s7_pointer g_with_output_to_file(s7_scheme *sc, s7_pointer args)
{
  #define H_with_output_to_file "(with-output-to-file filename thunk) opens filename as the temporary current-output-port and calls thunk"
  s7_pointer old_output_port, file, proc;
  
  file = car(args);
  if (!is_string(file))
    {
      check_method(sc, file, sc->WITH_OUTPUT_TO_FILE, args);
      return(wrong_type_argument(sc, sc->WITH_OUTPUT_TO_FILE, small_int(1), file, T_STRING));
    }

  proc = cadr(args);
  if (!s7_is_aritable(sc, proc, 0))
    {
      check_method(sc, proc, sc->WITH_OUTPUT_TO_FILE, args);
      return(wrong_type_argument_with_type(sc, sc->WITH_OUTPUT_TO_FILE, small_int(2), proc, A_THUNK));
    }
  old_output_port = sc->output_port;
  sc->output_port = s7_open_output_file(sc, string_value(file), "w");
  push_stack(sc, OP_UNWIND_OUTPUT, old_output_port, sc->output_port);

  if (is_any_macro(proc))
    {
      push_stack(sc, OP_EVAL_MACRO, sc->NIL, cdr(args));
      push_stack(sc, OP_APPLY, list_1(sc, cdr(args)), proc);
    }
  else push_stack(sc, OP_APPLY, sc->NIL, proc);
  return(sc->F);
}



/* -------------------------------- format -------------------------------- */

static s7_pointer object_to_list(s7_scheme *sc, s7_pointer obj);

static s7_pointer format_error(s7_scheme *sc, const char *msg, const char *str, s7_pointer args, format_data *fdat)
{
  int len;
  char *errmsg;
  s7_pointer x;
#if DEBUGGING
  if (safe_strlen(msg) > 100) fprintf(stderr, "format_error ctrl msg len: %d\n", safe_strlen(msg));
#endif

  /* the control string can't easily be handled here (as opposed to passing it as an argument below)
   *   because it becomes simply incorporated in the error's control string, so we'd have to scan
   *   for tildes and so on.
   */
  if (fdat->loc == 0)
    {
      errmsg = (char *)malloc(128 * sizeof(char));
      if (is_pair(args))
	len = snprintf(errmsg, 128, "format ~S ~{~S~^ ~}: %s", msg);
      else len = snprintf(errmsg, 128, "format ~S: %s", msg);
    }
  else 
    {
      len = 128 + fdat->loc;
      errmsg = (char *)malloc(len * sizeof(char));
      if (is_pair(args))
	len = snprintf(errmsg, len, "format: ~S ~{~S~^ ~}~&~%dT^: %s", fdat->loc + 12, msg);
      else len = snprintf(errmsg, len, "format: ~S~&~%dT^: %s", fdat->loc + 12, msg);
    }

  if (is_pair(args))
    x = list_3(sc, make_string_uncopied_with_length(sc, errmsg, len), make_string_wrapper(sc, str), args);
  else x = list_2(sc, make_string_uncopied_with_length(sc, errmsg, len), make_string_wrapper(sc, str));

  if (fdat->gc_loc >= 0)
    {
      s7_gc_unprotect_at(sc, fdat->gc_loc);
      fdat->gc_loc = -1;
    }

  return(s7_error(sc, sc->FORMAT_ERROR, x));
}


static void format_append_char(s7_scheme *sc, format_data *fdat, char c, s7_pointer port)
{
  port_write_character(port)(sc, c, port);
  sc->format_column++;

  /* if c is #\null, is this the right thing to do? 
   * We used to return "1 2 3 4" because ~C was first turned into a string (empty in this case)
   *   (format #f "1 2~C3 4" #\null)
   *   "1 2"
   * Clisp does this:
   *   (format nil "1 2~C3 4" (int-char 0))
   *   "1 23 4"
   * whereas sbcl says int-char is undefined, and
   * Guile returns "1 2\x003 4"
   */
}

static void format_append_newline(s7_scheme *sc, format_data *fdat, s7_pointer port)
{
  port_write_character(port)(sc, '\n', port);
  sc->format_column = 0;
}


static void format_append_string(s7_scheme *sc, format_data *fdat, const char *str, int len, s7_pointer port)
{
  port_write_string(port)(sc, str, len, port);
  fdat->loc += len;
  sc->format_column += len;
}


static int format_read_integer(s7_scheme *sc, int *cur_i, int str_len, const char *str, s7_pointer args, format_data *fdat)
{
  /* we know that str[*cur_i] is a digit */
  int i, lval = 0;  
  for (i = *cur_i; i < str_len - 1; i++)
    {
      int dig;
      dig = digits[(unsigned char)str[i]];
      if (dig < 10)
	lval = dig + (lval * 10);
      else break;
    }

  if (i >= str_len)
    format_error(sc, "numeric argument, but no directive!", str, args, fdat);
  *cur_i = i;
  return(lval);
}


static void format_number(s7_scheme *sc, format_data *fdat, int radix, int width, int precision, char float_choice, char pad, s7_pointer port)
{
  char *tmp;
  int nlen = 0;
  if (width < 0) width = 0;

  /* precision choice depends on float_choice if it's -1 */
  if (precision < 0)
    {
      if ((float_choice == 'e') ||
	  (float_choice == 'f') ||
	  (float_choice == 'g'))
	precision = 6;
      else
	{
	  /* in the "int" cases, precision depends on the arg type */
	  switch (type(car(fdat->args)))
	    {
	    case T_INTEGER: 
	    case T_RATIO:
	      precision = 0; 
	      break;

	    default:
	      precision = 6;
	      break;
	    }
	}
    }

  /* should (format #f "~F" 1/3) return "1/3"?? in CL it's "0.33333334" */

  tmp = number_to_string_with_radix(sc, car(fdat->args), radix, width, precision, float_choice, &nlen);

  if (pad != ' ')
    {
      char *padtmp;
      padtmp = tmp;
      while (*padtmp == ' ') (*(padtmp++)) = pad;
    }
  format_append_string(sc, fdat, tmp, nlen, port);

  free(tmp);
  fdat->args = cdr(fdat->args);
  fdat->ctr++;
}


static int format_nesting(const char *str, char opener, char closer, int start, int end) /* start=i, end=str_len-1 */
{
  int k, nesting = 1;
  for (k = start + 2; k < end; k++)
    if (str[k] == '~')
      {
	if (str[k + 1] == closer)
	  {
	    nesting--;
	    if (nesting == 0)
	      return(k - start - 1);
	  }
	else
	  {
	    if (str[k + 1] == opener)
	      nesting++;
	  }
      }
  return(-1);
}

static bool format_method(s7_scheme *sc, const char *str, format_data *fdat, s7_pointer port)
{
  s7_pointer obj, func;
  obj = car(fdat->args);
  if ((has_methods(obj)) && 
      ((func = find_method(sc, find_let(sc, obj), sc->FORMAT)) != sc->UNDEFINED))
    {
      obj = s7_apply_function(sc, func, cons(sc, make_string_wrapper(sc, str), fdat->args));
      if (is_string(obj))
	{
	  format_append_string(sc, fdat, string_value(obj), string_length(obj), port);
	  fdat->args = cdr(fdat->args);
	  fdat->ctr++;
	  return(true);
	}
    }
  return(false);
}

#if WITH_GMP
static bool s7_is_one_or_big_one(s7_pointer p);
#else
#define s7_is_one_or_big_one(Num) s7_is_one(Num)
#endif


static s7_pointer format_to_port_1(s7_scheme *sc, s7_pointer port, const char *str, s7_pointer args, 
				   s7_pointer *next_arg, bool with_result, bool columnized, int len)
{
  int i = 0, str_len = 0;
  format_data *fdat = NULL;
  s7_pointer deferred_port = sc->F;
  bool port_has_buffer;
  use_write_t choice;

  if ((!with_result) &&
      (port == sc->F))
    return(sc->F);

  if (len <= 0)
    {
      str_len = safe_strlen(str);
      if (str_len == 0)
	{
	  if (is_not_null(args))
	    {
	      static s7_pointer format_errstr = NULL;
	      if (!format_errstr)
		format_errstr = s7_make_permanent_string("format control string is null, but there are other arguments: ~S");
	      return(s7_error(sc, sc->FORMAT_ERROR, list_2(sc, format_errstr, args)));
	    }
	  return(make_string_wrapper(sc, ""));
	}
    }
  else str_len = len;

  sc->format_depth++;
  if (sc->format_depth >= sc->num_fdats)
    {
      int i, new_num_fdats;
      new_num_fdats = sc->format_depth * 2;
      sc->fdats = (format_data **)realloc(sc->fdats, sizeof(format_data *) * new_num_fdats);
      for (i = sc->num_fdats; i < new_num_fdats; i++) sc->fdats[i] = NULL;
      sc->num_fdats = new_num_fdats;
    }

  fdat = sc->fdats[sc->format_depth];
  if (!fdat)
    {
      fdat = (format_data *)malloc(sizeof(format_data));
      sc->fdats[sc->format_depth] = fdat;
      fdat->curly_len = 0;
      fdat->curly_str = NULL;
      fdat->ctr = 0;
    }
  else
    {
      if (fdat->gc_loc >= 0)
	s7_gc_unprotect_at(sc, fdat->gc_loc); /* I don't think this can happen, but its innocuous. */
    }
  fdat->gc_loc = -1;
  fdat->loc = 0;
  fdat->args = args;

  /* choose whether to write to a temporary string port, or simply use the in-coming port
   *   if with_result, returned string is wanted.
   *   if port is sc->F, no non-string result is wanted.
   *   if port is not boolean, it better be a port.
   *   if we are about to goto START in eval, and main_stack_op(Sc) == OP_BEGIN1, no return string is wanted -- yow, this is not true
   */

  port_has_buffer = port_data(port);
  if (with_result)
    {
      deferred_port = port;
      port = open_output_string(sc, FORMAT_PORT_LENGTH);
      fdat->gc_loc = s7_gc_protect(sc, port);
    }

  for (i = 0; i < str_len; i++)
    {
      if (str[i] == '~') 
	{
	  if (i == str_len - 1)
	    return(format_error(sc, "control string ends in tilde", str, args, fdat));
	  
	  switch (str[i + 1])
	    {
	    case '%':                           /* -------- newline -------- */
	      /* sbcl apparently accepts numeric args here (including 0) */

	      if ((port_has_buffer) &&
		  (port_position(port) < port_data_size(port)))
		{
		  port_data(port)[port_position(port)++] = '\n';
		  /* which is actually a bad idea, but as a desperate stopgap, I simply padded
		   *  the string port string with 8 chars that are not in the length.
		   */
		  sc->format_column = 0;
		}
	      else format_append_newline(sc, fdat, port);
	      i++;
	      break;
	      
	    case '&':                           /* -------- conditional newline -------- */
	      if (sc->format_column > 0)
		format_append_newline(sc, fdat, port);
	      i++;
	      break;
	      
	    case '~':                           /* -------- tilde -------- */
	      format_append_char(sc, fdat, '~', port);
	      i++;
	      break;
	      
	    case '\n':                          /* -------- trim white-space -------- */
	      for (i = i + 2; i <str_len - 1; i++)
		if (!(white_space[(unsigned char)(str[i])]))
		  {
		    i--;
		    break;
		  }
	      break;
	      
	    case '*':                           /* -------- ignore arg -------- */
	      i++;
	      if (is_null(fdat->args))          /* (format #f "~*~A") */
		return(format_error(sc, "can't skip argument!", str, args, fdat));
	      fdat->args = cdr(fdat->args);
	      break;

	    case '|':                           /* -------- exit if args nil or ctr > *vector-print-length* -------- */
	      if ((is_pair(fdat->args)) &&
		  (fdat->ctr >= (int)s7_integer(slot_value(sc->vector_print_length))))
		{
		  format_append_string(sc, fdat, " ...", 4, port);
		  fdat->args = sc->NIL;
		}
	      /* fall through */

	    case '^':                           /* -------- exit -------- */
	      if (is_null(fdat->args))
		{
		  i = str_len;
		  goto ALL_DONE;
		}
	      i++;
	      break;
	      
	    case '@':                           /* -------- plural, 'y' or 'ies' -------- */
	      i += 2;
	      if ((str[i] != 'P') && (str[i] != 'p'))
		return(format_error(sc, "unknown '@' directive", str, args, fdat));
	      if (!s7_is_real(car(fdat->args)))        /* CL accepts non numbers here */
		return(format_error(sc, "'@P' directive it is not a real number", str, args, fdat));
	      
	      if (!s7_is_one_or_big_one(car(fdat->args)))
		format_append_string(sc, fdat, "ies", 3, port);
	      else format_append_char(sc, fdat, 'y', port);
	      
	      fdat->args = cdr(fdat->args);
	      break;
	      
	    case 'P': case 'p':                 /* -------- plural in 's' -------- */
	      if (!s7_is_real(car(fdat->args)))
		return(format_error(sc, "'P' directive it is not a real number", str, args, fdat));
	      if (!s7_is_one_or_big_one(car(fdat->args)))
		format_append_char(sc, fdat, 's', port);
	      i++;
	      fdat->args = cdr(fdat->args);
	      break;

	    case '>':
	      return(format_error(sc, "unmatched '>'", str, args, fdat));

	    case '<':                          /* -------- escape to s7 -------- */
	      {
		int bracket_len;
		bracket_len = format_nesting(str, '<', '>', i, str_len - 1);

		if (bracket_len == -1)
		  return(format_error(sc, "'<' directive, but no matching '>'", str, args, fdat));
		if (bracket_len > 1)
		  {
		    s7_pointer result, eport;
		    char *bracket_str;

		    if (bracket_len > fdat->curly_len)
		      {
			if (fdat->curly_str) free (fdat->curly_str);
			fdat->curly_len = bracket_len;
			fdat->curly_str = (char *)malloc(bracket_len * sizeof(char));
		      }
		    bracket_str = fdat->curly_str;
		    memcpy((void *)bracket_str, (void *)(str + i + 2), bracket_len - 1);
		    bracket_str[bracket_len - 1] = '\0';

		    /* if it's just a variable name (most common case by far), or a number, grab it directly */
		    if ((int)strcspn((const char *)bracket_str, (const char *)"('\"`") == bracket_len - 1)
		      {
			int k; /* "i" is the outer index */
			unsigned char *lstr;

			lstr = (unsigned char *)bracket_str;
			for (k = 0; (k < bracket_len) && (white_space[*lstr]); k++, lstr++);
			bracket_str = (char *)lstr;
			if (k < bracket_len)
			  {
			    while (char_ok_in_a_name[*lstr]) {lstr++; k++; if (k >= bracket_len) break;}
			    if ((char *)lstr == bracket_str)
			      {
				i += (bracket_len + 2); /* I guess we want (format #f "~< ~>") to return "" */
				break;
			      }
			    if (k < bracket_len) *lstr = '\0';
			    result = make_atom(sc, bracket_str, BASE_10, SYMBOL_OK, WITH_OVERFLOW_ERROR);
			    if (result == sc->NIL)
			      return(format_error(sc, "read error?", str, args, fdat));
			    if (is_symbol(result)) 
			      {
				result = s7_symbol_value(sc, result);
				if (result == sc->UNDEFINED)
				  return(format_error(sc, "undefined identifier", str, args, fdat));
			      }
			  }
			else
			  {
			    i += (bracket_len + 2);
			    break;
			  }
		      }
		    else
		      {
			/* if the called code has a catch and raises an error, or anything else that causes a longjmp,
			 *   we won't complete this format call -- we end up at an earlier point in the C stack.
			 *   To fix this will probably require embedding this code in the eval loop somehow.
			 *   I don't think we can push (say) OP_FORMAT to restore all the current fdat state
			 *   because the longjmp will also collapse away the current format recursive calls.
			 *   It does not help to put a setjmp here, nor to call s7_call (saving/restoring the
			 *   setjmp buffer), nor to use s7_eval_c_string but leave sc->envir alone.
			 *   If we had a catch/throw mechanism that did not go through s7_error, then only
			 *   a call from here into scheme into C and back would be problematic.  Can throw simply
			 *   crawl the stack directly in eval?  No -- catch assumes the jump. ("The stack context
			 *   [saved by setjmp] will be invalidated if the function which called setjmp returns").
			 *
			 * so, to fix this, if ~<~> arg is a pair, replace it with ~{~A~} and put the read(?) code on a new arglist,
			 *   complete the current format, then push op_apply with g_format and these new args, and return.
			 *   But we're sending the output as we go in some cases!
			 *
			 * even a rewrite at call time can be fooled -- I think for now I'll add a note in s7.html.
			 */
			eport = s7_open_input_string(sc, bracket_str);
			push_input_port(sc, eport);
			push_stack(sc, OP_BARRIER, eport, sc->code); /* neither of these is superfluous: sc->code if values */
			push_stack(sc, OP_EVAL_STRING, sc->args, sc->code);  
			
			eval(sc, OP_READ_INTERNAL);
			
			if (stack_op(sc->stack, s7_stack_top(sc) - 1) == OP_BARRIER) pop_stack(sc);
			pop_input_port(sc);
			s7_close_input_port(sc, eport);
			result = sc->value;
		      }
		    
		    if (is_string(result))
		      format_append_string(sc, fdat, string_value(result), string_length(result), port);
		    else
		      {
			if (result != sc->NO_VALUE)           /* (format #f "~<(values)~>") -> "" */
			  {
			    if (is_multiple_value(result))    /* (format #f "~<(values 1 2 3)~>") -> "123" */
			      {
				int gc_loc;
				s7_pointer p;
				gc_loc = s7_gc_protect(sc, result);
				for (p = multiple_value(result); is_pair(p); p = cdr(p))
				  if (is_string(car(p)))
				    format_append_string(sc, fdat, string_value(car(p)), string_length(car(p)), port);
				  else
				    {
				      bracket_str = s7_object_to_c_string(sc, car(p));
				      if (bracket_str)
					{
					  format_append_string(sc, fdat, bracket_str, strlen(bracket_str), port);
					  free(bracket_str);
					}
				    }
				s7_gc_unprotect_at(sc, gc_loc);
			      }
			    else
			      {
				bracket_str = s7_object_to_c_string(sc, result);
				if (bracket_str)
				  {
				    format_append_string(sc, fdat, bracket_str, strlen(bracket_str), port);
				    free(bracket_str);
				  }
			      }
			  }
		      }
		  }
		i += (bracket_len + 2); /* jump past the ending '>' too */
	      }
	      break;

	    case '{':                           /* -------- iteration -------- */
	      {
		int curly_len;
		
		if (is_null(fdat->args))
		  return(format_error(sc, "missing argument", str, args, fdat));
		
		curly_len = format_nesting(str, '{', '}', i, str_len - 1);

		if (curly_len == -1)
		  return(format_error(sc, "'{' directive, but no matching '}'", str, args, fdat));
		if (curly_len == 1)
		  return(format_error(sc, "~{~}' doesn't consume any arguments!", str, args, fdat));
		
		/* what about cons's here?  I can't see any way in CL either to specify the car or cdr of a cons within the format string 
		 *   (cons 1 2) is applicable: ((cons 1 2) 0) -> 1
		 *   also there can be applicable objects that won't work in the map context (arg not integer etc)
		 */
		if (is_not_null(car(fdat->args)))               /* (format #f "~{~A ~}" ()) -> "" */
		  {
		    s7_pointer curly_arg;
		    
		    curly_arg = object_to_list(sc, car(fdat->args)); 
		    if (is_not_null(curly_arg))                /* (format #f "~{~A ~}" #()) -> "" */
		      {
			char *curly_str = NULL;                /* this is the local (nested) format control string */
			int curly_gc;
			
			if (!is_proper_list(sc, curly_arg))
			  return(format_error(sc, "'{' directive argument should be a proper list or something we can turn into a list", 
					      str, args, fdat));
			curly_gc = s7_gc_protect(sc, curly_arg);

			if (curly_len > fdat->curly_len)
			  {
			    if (fdat->curly_str) free (fdat->curly_str);
			    fdat->curly_len = curly_len;
			    fdat->curly_str = (char *)malloc(curly_len * sizeof(char));
			  }
			curly_str = fdat->curly_str;
			memcpy((void *)curly_str, (void *)(str + i + 2), curly_len - 1);
			curly_str[curly_len - 1] = '\0';
			
			if ((sc->format_depth < sc->num_fdats - 1) &&
			    (sc->fdats[sc->format_depth + 1]))
			  sc->fdats[sc->format_depth + 1]->ctr = 0;

			while (is_not_null(curly_arg))
			  {
			    s7_pointer new_arg = sc->NIL;
			    
			    format_to_port_1(sc, port, curly_str, curly_arg, &new_arg, false, columnized, curly_len - 1);
			    if (curly_arg == new_arg)
			      {
				s7_gc_unprotect_at(sc, curly_gc);
				return(format_error(sc, "'{...}' doesn't consume any arguments!", str, args, fdat));
			      }
			    curly_arg = new_arg;
			  }
			s7_gc_unprotect_at(sc, curly_gc);
		      }
		  }
		
		i += (curly_len + 2); /* jump past the ending '}' too */
		fdat->args = cdr(fdat->args);
		fdat->ctr++;
	      }
	      break;
	      
	    case '}':
	      return(format_error(sc, "unmatched '}'", str, args, fdat));

	    case 'W': case 'w':
	      choice = USE_READABLE_WRITE;
	      goto OBJSTR;

	    case 'S': case 's':
	      choice = USE_WRITE;
	      goto OBJSTR;

	    case 'A': case 'a': 
	      choice = USE_DISPLAY;
	    OBJSTR:
	      /* object->string */
	      {
		s7_pointer obj;

		if (is_null(fdat->args))
		  return(format_error(sc, "missing argument", str, args, fdat));

		i++;
		obj = car(fdat->args);
		/* for the column check, we need to know the length of the object->string output
		 */
		if (!columnized)
		  {
		    if ((has_structure(obj)) &&
			(obj != sc->rootlet))
		      {
			shared_info *ci = NULL;
			ci = make_shared_info(sc, obj, choice != USE_READABLE_WRITE);
			if ((ci) &&
			    (choice == USE_READABLE_WRITE))
			  {
			    setup_shared_reads(sc, port, ci);
			    object_to_port_with_circle_check(sc, obj, port, choice, is_file_port(port), ci);
			    finish_shared_reads(sc, port, ci);
			  }
			else object_to_port_with_circle_check(sc, obj, port, choice, is_file_port(port), ci);
		      }
		    else object_to_port(sc, obj, port, choice, is_file_port(port), NULL);
		  }
		else
		  {
		    char *s;
		    int nlen = 0;
		    s = s7_object_to_c_string_1(sc, obj, choice, &nlen);
		    if (nlen > 0)
		      format_append_string(sc, fdat, s, nlen, port);
		    free(s);
		  }
		fdat->args = cdr(fdat->args);
		fdat->ctr++;
	      }
	      break;
		    

	      /* -------- numeric args -------- */
	    case '0': case '1': case '2': case '3': case '4': case '5':
	    case '6': case '7': case '8': case '9': case ',':
	      
	    case 'B': case 'b':
	    case 'D': case 'd':
	    case 'E': case 'e':
	    case 'F': case 'f':
	    case 'G': case 'g':
	    case 'O': case 'o':
	    case 'X': case 'x':
	      
	    case 'T': case 't':
	    case 'C': case 'c':
	      {
		int width = -1, precision = -1;
		char pad = ' ';
		i++;
		
		if (isdigit((int)str[i]))
		  {
		    #define MAX_FORMAT_WIDTH 10000
		    width = format_read_integer(sc, &i, str_len, str, args, fdat);
		    if ((width < 0) || /* maybe overflow somewhere? */
			(width > MAX_FORMAT_WIDTH))
		      return(format_error(sc, "width argument too big", str, args, fdat));
		  }
		
		if (str[i] == ',')
		  {
		    i++;
		    if (isdigit((int)str[i]))
		      {
			#define MAX_FORMAT_PRECISION 10000
			precision = format_read_integer(sc, &i, str_len, str, args, fdat);
			if ((precision < 0) ||
			    (precision > MAX_FORMAT_PRECISION))
			  return(format_error(sc, "precision argument too big", str, args, fdat));
		      }
		    /* is (format #f "~12,12D" 1) an error?  The precision has no use here. */
		    else
		      {
			if (str[i] == '\'')       /* (format #f "~12,'xD" 1) -> "xxxxxxxxxxx1" */
			  {
			    pad = str[i + 1];
			    i += 2;
			  }
			/* is (let ((str "~12,'xD")) (set! (str 5) #\null) (format #f str 1)) an error? */
		      }
		  }
		
		switch (str[i])
		  {
		    /* -------- pad to column -------- 
		     *   are columns numbered from 1 or 0?  there seems to be disagreement about this directive
		     *   does "space over to" mean including?
		     */

		  case 'T': case 't':
		    if (width == -1) width = 0;
		    if (precision == -1) precision = 0;
		    if ((width > 0) || (precision > 0))         /* (format #f "a~8Tb") */
		      {
			/* (length (substring (format #f "~%~10T.") 1)) == (length (format #f "~10T."))
			 * (length (substring (format #f "~%-~10T.~%") 1)) == (length (format #f "-~10T.~%"))
			 */
			int j;
			if (precision > 0)
			  {
			    int mult;
			    mult = (int)(ceil((s7_Double)(sc->format_column + 1 - width) / (s7_Double)precision)); /* CLtL2 ("least positive int") */
			    if (mult < 1) mult = 1;
			    width += (precision * mult);
			  }
			for (j = sc->format_column + 1; j < width; j++)
			  format_append_char(sc, fdat, pad, port);
		      }
		    break;

		  case 'C': case 'c':
		    {
		      s7_pointer obj;
		      
		      if (is_null(fdat->args))
			return(format_error(sc, "~~C: missing argument", str, args, fdat));
		      /* the "~~" here and below protects against "~C" being treated as a directive */
		      /* i++; */
		      obj = car(fdat->args);
		      
		      if (!s7_is_character(obj))
			{
			  if (!format_method(sc, str, fdat, port))
			    return(format_error(sc, "'C' directive requires a character argument", str, args, fdat));
			}
		      else
			{
			  /* here use_write is false, so we just add the char, not its name */
			  if (width == -1)
			    format_append_char(sc, fdat, character(obj), port);
			  else
			    {
			      int j;
			      for (j = 0; j < width; j++)
				format_append_char(sc, fdat, character(obj), port);
			    }
			  fdat->args = cdr(fdat->args);
			  fdat->ctr++;
			}
		    }
		    break;
		    
		    /* -------- numbers -------- */
		  case 'F': case 'f':
		    if (is_null(fdat->args))
		      return(format_error(sc, "~~F: missing argument", str, args, fdat));
		    if (!(s7_is_number(car(fdat->args))))
		      {
			if (!format_method(sc, str, fdat, port))
			  return(format_error(sc, "~~F: numeric argument required", str, args, fdat));
		      }
		    else format_number(sc, fdat, 10, width, precision, 'f', pad, port);
		    break;
		    
		  case 'G': case 'g':
		    if (is_null(fdat->args))
		      return(format_error(sc, "~~G: missing argument", str, args, fdat));
		    if (!(s7_is_number(car(fdat->args))))
		      {
			if (!format_method(sc, str, fdat, port))
			  return(format_error(sc, "~~G: numeric argument required", str, args, fdat));
		      }
		    else format_number(sc, fdat, 10, width, precision, 'g', pad, port);
		    break;
		    
		  case 'E': case 'e':
		    if (is_null(fdat->args))
		      return(format_error(sc, "~~E: missing argument", str, args, fdat));
		    if (!(s7_is_number(car(fdat->args))))
		      {
			if (!format_method(sc, str, fdat, port))
			  return(format_error(sc, "~~E: numeric argument required", str, args, fdat));
		      }
		    else format_number(sc, fdat, 10, width, precision, 'e', pad, port);
		    break;
		    
		    /* how to handle non-integer arguments in the next 4 cases?  clisp just returns
		     *   the argument: (format nil "~X" 1.25) -> "1.25" which is perverse (ClTl2 p 581:
		     *   "if arg is not an integer, it is printed in ~A format and decimal base")!!
		     *   I think I'll use the type of the number to choose the output format.
		     */
		  case 'D': case 'd':
		    if (is_null(fdat->args))
		      return(format_error(sc, "~~D: missing argument", str, args, fdat));
		    if (!(s7_is_number(car(fdat->args))))
		      {
			/* (let () (require mockery.scm) (format #f "~D" ((*mock-number* 'mock-number) 123))) 
			 *    port here is a string-port, str has the width/precision data if the caller wants it,
			 *    args is the current arg.  But format_number handles fdat->args and so on, so
			 *    I think I'll pass the format method the current control string (str), the
			 *    current object (car(fdat->args)), and the arglist (args), and assume it will
			 *    return a (scheme) string.
			 */
			if (!format_method(sc, str, fdat, port))
			  return(format_error(sc, "~~D: numeric argument required", str, args, fdat));
		      }
		    else format_number(sc, fdat, 10, width, precision, 'd', pad, port);
		    break;
		    
		  case 'O': case 'o':
		    if (is_null(fdat->args))
		      return(format_error(sc, "~~O: missing argument", str, args, fdat));
		    if (!(s7_is_number(car(fdat->args))))
		      {
			if (!format_method(sc, str, fdat, port))
			  return(format_error(sc, "~~O: numeric argument required", str, args, fdat));
		      }
		    else format_number(sc, fdat, 8, width, precision, 'o', pad, port);
		    break;
		    
		  case 'X': case 'x':
		    if (is_null(fdat->args))
		      return(format_error(sc, "~~X: missing argument", str, args, fdat));
		    if (!(s7_is_number(car(fdat->args))))
		      {
			if (!format_method(sc, str, fdat, port))
			  return(format_error(sc, "~~X: numeric argument required", str, args, fdat));
		      }
		    else format_number(sc, fdat, 16, width, precision, 'x', pad, port);
		    break;
		    
		  case 'B': case 'b':
		    if (is_null(fdat->args))
		      return(format_error(sc, "~~B: missing argument", str, args, fdat));
		    if (!(s7_is_number(car(fdat->args))))
		      {
			if (!format_method(sc, str, fdat, port))
			  return(format_error(sc, "~~B: numeric argument required", str, args, fdat));
		      }
		    else format_number(sc, fdat, 2, width, precision, 'b', pad, port);
		    break;
		    
		  default:
		    if (width > 0)
		      return(format_error(sc, "format directive does not take a numeric argument", str, args, fdat));
		    return(format_error(sc, "unimplemented format directive", str, args, fdat));
		  }
	      }
	      break;
	      
	    default:
	      return(format_error(sc, "unimplemented format directive", str, args, fdat));
	    }
	}
      else /* str[i] is not #\~ */
	{
	  int j, new_len;
	  for (j = i + 1; j < str_len; j++)
	    if (str[j] == '~') 
	      break;
	  
	  new_len = j - i;
	  if ((port_has_buffer) &&
	      ((port_position(port) + new_len) < port_data_size(port)))
	    {
	      memcpy((void *)(port_data(port) + port_position(port)), (void *)(str + i), new_len);
	      port_position(port) += new_len;
	    }
	  else port_write_string(port)(sc, (char *)(str + i), new_len, port);
	  fdat->loc += new_len;
	  sc->format_column += new_len;
	  i = j - 1;
	}
    }

 ALL_DONE:
  if (next_arg)
    (*next_arg) = fdat->args;
  else
    {
      if (is_not_null(fdat->args))
	return(format_error(sc, "too many arguments", str, args, fdat));
    }
  if (i < str_len)
    format_append_char(sc, fdat, str[i], port);    /* possible trailing ~ is sent out */

  sc->format_depth--;

  if (with_result)
    {
      s7_pointer result;

      if ((is_output_port(deferred_port)) &&
	  (port_position(port) > 0))
	port_write_string(deferred_port)(sc, (const char *)port_data(port), port_position(port), deferred_port);

      result = make_string_uncopied_with_length(sc, (char *)port_data(port), port_position(port));
      port_data(port) = NULL;
      port_needs_free(port) = false;
      s7_close_output_port(sc, port);

      if (fdat->gc_loc >= 0)
	{
	  s7_gc_unprotect_at(sc, fdat->gc_loc);
	  fdat->gc_loc = -1;
	}
      return(result);
    }
  return(sc->F);
}


static bool is_columnizing(const char *str)
{
  /* look for ~t ~,<int>T ~<int>,<int>t */
  char *p;

  for (p = (char *)str; (*p);)
    if (*p++ == '~')
      {
	char c;
	c = *p++;
	if ((c == 't') || (c == 'T')) return(true);
	if (!c) return(false); 
	if ((c == ',') || ((c >= '0') && (c <= '9')))
	  {
	    while ((c >= '0') && (c <= '9')) c = *p++;
	    if ((c == 't') || (c == 'T')) return(true);
	    if (!c) return(false);                       /* ~,1 for example */
	    if (c == ',')
	      {
		c = *p++;
		while ((c >= '0') && (c <= '9')) c = *p++;
		if ((c == 't') || (c == 'T')) return(true);
		if (!c) return(false);
	      }
	  }
      }
  return(false);
}


static s7_pointer format_to_port(s7_scheme *sc, s7_pointer port, const char *str, s7_pointer args, s7_pointer *next_arg, bool with_result, int len)
{
  return(format_to_port_1(sc, port, str, args, next_arg, with_result, is_columnizing(str), len));
}


static s7_pointer g_format_1(s7_scheme *sc, s7_pointer args)
{
  s7_pointer pt, str;
  sc->format_column = 0;
  pt = car(args);

  if (is_string(pt))
    return(format_to_port(sc, sc->F, string_value(pt), cdr(args), NULL, !is_output_port(pt), string_length(pt)));

  if (!((s7_is_boolean(pt)) ||               /* #f or #t */
	((is_output_port(pt)) &&             /* (current-output-port) or call-with-open-file arg, etc */
	 (!port_is_closed(pt)))))
    {
      check_method(sc, pt, sc->FORMAT, args);
      return(wrong_type_argument_with_type(sc, sc->FORMAT, small_int(1), pt, AN_OUTPUT_PORT));
    }

  str = cadr(args);
  if (!is_string(str))
    {
      check_method(sc, str, sc->FORMAT, args);
      return(wrong_type_argument(sc, sc->FORMAT, small_int(2), str, T_STRING));
    }
  return(format_to_port(sc, 
			(pt == sc->T) ? sc->output_port : pt, 
			string_value(str),
			cddr(args), NULL, !is_output_port(pt), 
			string_length(str)));
}


static s7_pointer g_format(s7_scheme *sc, s7_pointer args)
{
  #define H_format "(format out str . args) substitutes args into str sending the result to out. Most of \
s7's format directives are taken from CL: ~% = newline, ~& = newline if the preceding output character was \
no a newline, ~~ = ~, ~<newline> trims white space, ~* skips an argument, ~^ exits {} iteration if the arg list is exhausted, \
~nT spaces over to column n, ~A prints a representation of any object, ~S is the same, but puts strings in double quotes, \
~C prints a character, numbers are handled by ~F, ~E, ~G, ~B, ~O, ~D, and ~X with preceding numbers giving \
spacing (and spacing character) and precision.  ~{ starts an embedded format directive which is ended by ~}: \n\
\n\
  >(format #f \"dashed: ~{~A~^-~}\" '(1 2 3))\n\
  \"dashed: 1-2-3\"\n\
\n\
~P inserts \"s\" if the current it is not 1 or 1.0 (use ~@P for \"ies\" or \"y\").\n\
~B is number->string in base 2, ~O in base 8, ~D base 10, ~X base 16,\n\
~E: (format #f \"~E\" 100.1) -&gt; \"1.001000e+02\" (%e in C)\n\
~F: (format #f \"~F\" 100.1) -&gt; \"100.100000\"   (%f in C)\n\
~G: (format #f \"~G\" 100.1) -&gt; \"100.1\"        (%g in C)\n\
\n\
If the 'out' it is not an output port, the resultant string is returned.  If it \
is #t, the string is also sent to the current-output-port."

  return(g_format_1(sc, args));
}


const char *s7_format(s7_scheme *sc, s7_pointer args)
{
  return(string_value(g_format_1(sc, args)));
}



/* -------------------------------- system extras -------------------------------- */

#if WITH_SYSTEM_EXTRAS
#include <fcntl.h>

static s7_pointer g_is_directory(s7_scheme *sc, s7_pointer args)
{
  #define H_is_directory "(directory? str) returns #t if str is the name of a directory"
  s7_pointer name;
  name = car(args);

  if (!is_string(name))
    {
      check_method(sc, name, sc->IS_DIRECTORY, args);
      return(simple_wrong_type_argument(sc, sc->IS_DIRECTORY, name, T_STRING));
    }
  return(s7_make_boolean(sc, is_directory(string_value(name))));
}


static bool file_probe(const char *arg)
{
#if (!MS_WINDOWS)
  return(access(arg, F_OK) == 0);
#else
  int fd;
  fd = open(arg, O_RDONLY, 0);
  if (fd == -1) return(false);
  close(fd);
  return(true);
#endif
}


static s7_pointer g_file_exists(s7_scheme *sc, s7_pointer args)
{
  #define H_file_exists "(file-exists? filename) returns #t if the file exists"
  s7_pointer name;
  name = car(args);

  if (!is_string(name))
    {
      check_method(sc, name, sc->FILE_EXISTS, args);
      return(simple_wrong_type_argument(sc, sc->FILE_EXISTS, name, T_STRING));
    }
  return(s7_make_boolean(sc, file_probe(string_value(name))));
}


static s7_pointer g_delete_file(s7_scheme *sc, s7_pointer args)
{
  #define H_delete_file "(delete-file filename) deletes the file filename."
  s7_pointer name;
  name = car(args);

  if (!is_string(name))
    {
      check_method(sc, name, sc->DELETE_FILE, args);
      return(simple_wrong_type_argument(sc, sc->DELETE_FILE, name, T_STRING));
    }
  return(make_integer(sc, unlink(string_value(name))));
}


static s7_pointer g_getenv_1(s7_scheme *sc, s7_pointer args)
{
  #define H_getenv "(getenv var) returns the value of an environment variable."
  s7_pointer name;
  name = car(args);

  if (!is_string(name))
    {
      check_method(sc, name, sc->GETENV, args);
      return(simple_wrong_type_argument(sc, sc->GETENV, name, T_STRING));
    }
  return(s7_make_string(sc, getenv(string_value(name))));
}


static s7_pointer g_system(s7_scheme *sc, s7_pointer args)
{
  #define H_system "(system command) executes the command.  If the optional second it is #t, \
system captures the output as a string and returns it."
  s7_pointer name;
  name = car(args);

  if (!is_string(name))
    {
      check_method(sc, name, sc->SYSTEM, args);
      return(simple_wrong_type_argument(sc, sc->SYSTEM, name, T_STRING));
    }
  if ((is_pair(cdr(args))) &&
      (cadr(args) == sc->T))
    {
      #define BUF_SIZE 256
      char buf[BUF_SIZE];
      char *str = NULL;
      int cur_len = 0;
      FILE *fd;
      s7_pointer res;

      fd = popen(string_value(name), "r"); 
      while (fgets(buf, BUF_SIZE, fd))
	{
	  int buf_len, full_len = 0;
	  buf_len = strlen(buf);
	  if (cur_len + buf_len > full_len)
	    {
	      full_len += BUF_SIZE * 2;
	      if (str)
		str = (char *)realloc(str, full_len * sizeof(char));
	      else str = (char *)malloc(full_len * sizeof(char));
	    }
	  str[cur_len] = '\0';
	  cur_len += buf_len;
	  strcat(str, buf);
	}
      pclose(fd);

      res = s7_make_string_with_length(sc, str, cur_len);
      if (str) free(str);
      return(res);
    }
  return(make_integer(sc, system(string_value(name))));
}


#ifndef _MSC_VER
#include <dirent.h>

static s7_pointer g_directory_to_list(s7_scheme *sc, s7_pointer args)
{
  #define H_directory_to_list "(directory->list directory) returns the contents of the directory as a list of strings (filenames)."
  DIR *dpos;
  s7_pointer name, result;

  name = car(args);
  if (!is_string(name))
    {
      check_method(sc, name, sc->DIRECTORY_TO_LIST, args);
      return(simple_wrong_type_argument(sc, sc->DIRECTORY_TO_LIST, name, T_STRING));
    }
  sc->w = sc->NIL;
  if ((dpos = opendir(string_value(name))) != NULL)
    {
      struct dirent *dirp;
      while ((dirp = readdir(dpos)) != NULL)
	sc->w = cons(sc, s7_make_string(sc, dirp->d_name), sc->w);
      closedir(dpos);
    }

  result = sc->w;
  sc->w = sc->NIL;
  return(result);
}


static s7_pointer g_file_mtime(s7_scheme *sc, s7_pointer args)
{
  #define H_file_mtime "(file-mtime file): return the write date of file"

  struct stat statbuf;
  int err;
  s7_pointer name;

  name = car(args);
  if (!is_string(name))
    {
      check_method(sc, name, sc->FILE_MTIME, args);
      return(simple_wrong_type_argument(sc, sc->FILE_MTIME, name, T_STRING));
    }

  err = stat(string_value(name), &statbuf);
  if (err < 0) 
    return(file_error(sc, "file-mtime", strerror(errno), string_value(name)));

  return(s7_make_integer(sc, (s7_Int)(statbuf.st_mtime)));
}
#endif
#endif



/* -------------------------------- lists -------------------------------- */

s7_pointer s7_cons(s7_scheme *sc, s7_pointer a, s7_pointer b) 
{
  s7_pointer x;
  NEW_CELL(sc, x);
  car(x) = a;
  cdr(x) = b;
  set_type(x, T_PAIR | T_SAFE_PROCEDURE);
  return(x);
}


static s7_pointer cons_unchecked(s7_scheme *sc, s7_pointer a, s7_pointer b) 
{
  /* apparently slightly faster as a function? */
  s7_pointer x;
  NEW_CELL_NO_CHECK(sc, x);
  car(x) = a;
  cdr(x) = b;
  set_type(x, T_PAIR | T_SAFE_PROCEDURE);
  return(x);
}


static s7_pointer permanent_cons(s7_pointer a, s7_pointer b, unsigned int type)
{
  /* for the symbol table which is never GC'd (and its contents aren't marked) */
  s7_pointer x;
  x = alloc_pointer();
  heap_location(x) = NOT_IN_HEAP;
  car(x) = a;
  cdr(x) = b;
  set_type(x, type);
  return(x);
}


bool s7_is_pair(s7_pointer p)
{
  return(is_pair(p));
}


s7_pointer s7_car(s7_pointer p) {return(car(p));}
s7_pointer s7_cdr(s7_pointer p) {return(cdr(p));}

s7_pointer s7_cadr(s7_pointer p) {return(cadr(p));}
s7_pointer s7_cddr(s7_pointer p) {return(cddr(p));}
s7_pointer s7_cdar(s7_pointer p) {return(cdar(p));}
s7_pointer s7_caar(s7_pointer p) {return(caar(p));}

s7_pointer s7_caadr(s7_pointer p) {return(caadr(p));}
s7_pointer s7_caddr(s7_pointer p) {return(caddr(p));}
s7_pointer s7_cadar(s7_pointer p) {return(cadar(p));}
s7_pointer s7_caaar(s7_pointer p) {return(caaar(p));}
s7_pointer s7_cdadr(s7_pointer p) {return(cdadr(p));}
s7_pointer s7_cdddr(s7_pointer p) {return(cdddr(p));}
s7_pointer s7_cddar(s7_pointer p) {return(cddar(p));}
s7_pointer s7_cdaar(s7_pointer p) {return(cdaar(p));}

s7_pointer s7_caaadr(s7_pointer p) {return(caaadr(p));}
s7_pointer s7_caaddr(s7_pointer p) {return(caaddr(p));}
s7_pointer s7_caadar(s7_pointer p) {return(caadar(p));}
s7_pointer s7_caaaar(s7_pointer p) {return(caaaar(p));}
s7_pointer s7_cadadr(s7_pointer p) {return(cadadr(p));}
s7_pointer s7_cadddr(s7_pointer p) {return(cadddr(p));}
s7_pointer s7_caddar(s7_pointer p) {return(caddar(p));}
s7_pointer s7_cadaar(s7_pointer p) {return(cadaar(p));}

s7_pointer s7_cdaadr(s7_pointer p) {return(cdaadr(p));}
s7_pointer s7_cdaddr(s7_pointer p) {return(cdaddr(p));}
s7_pointer s7_cdadar(s7_pointer p) {return(cdadar(p));}
s7_pointer s7_cdaaar(s7_pointer p) {return(cdaaar(p));}
s7_pointer s7_cddadr(s7_pointer p) {return(cddadr(p));}
s7_pointer s7_cddddr(s7_pointer p) {return(cddddr(p));}
s7_pointer s7_cdddar(s7_pointer p) {return(cdddar(p));}
s7_pointer s7_cddaar(s7_pointer p) {return(cddaar(p));}

s7_pointer s7_set_car(s7_pointer p, s7_pointer q) 
{ 
  car(p) = q;
  return(p);
}


s7_pointer s7_set_cdr(s7_pointer p, s7_pointer q) 
{ 
  cdr(p) = q;
  return(p);
}

/* -------------------------------------------------------------------------------- */

s7_pointer s7_apply_1(s7_scheme *sc, s7_pointer args, s7_pointer (*f1)(s7_pointer a1))
{
  /* not currently used */
  return(f1(car(args)));
}

s7_pointer s7_apply_2(s7_scheme *sc, s7_pointer args, s7_pointer (*f2)(s7_pointer a1, s7_pointer a2))
{
  return(f2(car(args), cadr(args)));
}

s7_pointer s7_apply_3(s7_scheme *sc, s7_pointer args, s7_pointer (*f3)(s7_pointer a1, s7_pointer a2, s7_pointer a3))
{
  s7_pointer a1;
  a1 = car(args);
  args = cdr(args);
  return(f3(a1, car(args), cadr(args)));
}

s7_pointer s7_apply_4(s7_scheme *sc, s7_pointer args, s7_pointer (*f4)(s7_pointer a1, s7_pointer a2, s7_pointer a3, s7_pointer a4))
{
  s7_pointer a1, a2;
  a1 = car(args);
  a2 = cadr(args);
  args = cddr(args);
  return(f4(a1, a2, car(args), cadr(args)));
}

s7_pointer s7_apply_5(s7_scheme *sc, s7_pointer args, s7_pointer (*f5)(s7_pointer a1, s7_pointer a2, s7_pointer a3, s7_pointer a4, s7_pointer a5))
{
  s7_pointer a1, a2, a3, a4;
  a1 = car(args);
  a2 = cadr(args);
  args = cddr(args);
  a3 = car(args);
  a4 = cadr(args);
  args = cddr(args);
  return(f5(a1, a2, a3, a4, car(args)));
}

s7_pointer s7_apply_6(s7_scheme *sc, s7_pointer args, 
		      s7_pointer (*f6)(s7_pointer a1, s7_pointer a2, s7_pointer a3, s7_pointer a4, 
				       s7_pointer a5, s7_pointer a6))
{
  s7_pointer a1, a2, a3, a4;
  a1 = car(args);
  a2 = cadr(args);
  args = cddr(args);
  a3 = car(args);
  a4 = cadr(args);
  args = cddr(args);
  return(f6(a1, a2, a3, a4, car(args), cadr(args)));
}

s7_pointer s7_apply_7(s7_scheme *sc, s7_pointer args, 
		      s7_pointer (*f7)(s7_pointer a1, s7_pointer a2, s7_pointer a3, s7_pointer a4, 
				       s7_pointer a5, s7_pointer a6, s7_pointer a7))
{
  s7_pointer a1, a2, a3, a4, a5, a6;
  a1 = car(args);
  a2 = cadr(args);
  args = cddr(args);
  a3 = car(args);
  a4 = cadr(args);
  args = cddr(args);
  a5 = car(args);
  a6 = cadr(args);
  args = cddr(args);
  return(f7(a1, a2, a3, a4, a5, a6, car(args)));
}

s7_pointer s7_apply_8(s7_scheme *sc, s7_pointer args, 
		      s7_pointer (*f8)(s7_pointer a1, s7_pointer a2, s7_pointer a3, s7_pointer a4, 
				       s7_pointer a5, s7_pointer a6, s7_pointer a7, s7_pointer a8))
{
  s7_pointer a1, a2, a3, a4, a5, a6;
  a1 = car(args);
  a2 = cadr(args);
  args = cddr(args);
  a3 = car(args);
  a4 = cadr(args);
  args = cddr(args);
  a5 = car(args);
  a6 = cadr(args);
  args = cddr(args);
  return(f8(a1, a2, a3, a4, a5, a6, car(args), cadr(args)));
}

s7_pointer s7_apply_9(s7_scheme *sc, s7_pointer args, 
		      s7_pointer (*f9)(s7_pointer a1, s7_pointer a2, s7_pointer a3, s7_pointer a4, 
				       s7_pointer a5, s7_pointer a6, s7_pointer a7, s7_pointer a8, s7_pointer a9))
{
  s7_pointer a1, a2, a3, a4, a5, a6;
  a1 = car(args);
  a2 = cadr(args);
  args = cddr(args);
  a3 = car(args);
  a4 = cadr(args);
  args = cddr(args);
  a5 = car(args);
  a6 = cadr(args);
  args = cddr(args);
  return(f9(a1, a2, a3, a4, a5, a6, car(args), cadr(args), caddr(args)));
}

s7_pointer s7_apply_n_1(s7_scheme *sc, s7_pointer args, s7_pointer (*f1)(s7_pointer a1))
{
  if (is_pair(args)) 
    return(f1(car(args)));
  return(f1(sc->UNDEFINED));
}

s7_pointer s7_apply_n_2(s7_scheme *sc, s7_pointer args, s7_pointer (*f2)(s7_pointer a1, s7_pointer a2))
{
  if (is_pair(args))
    {
      if (is_pair(cdr(args)))
	return(f2(car(args), cadr(args)));
      return(f2(car(args), sc->UNDEFINED));
    }
  return(f2(sc->UNDEFINED, sc->UNDEFINED));
}

s7_pointer s7_apply_n_3(s7_scheme *sc, s7_pointer args, s7_pointer (*f3)(s7_pointer a1, s7_pointer a2, s7_pointer a3))
{
  if (is_pair(args))
    {
      s7_pointer a1;
      a1 = car(args);
      args = cdr(args);
      if (is_pair(args))
	{
	  s7_pointer a2;
	  a2 = car(args);
	  if (is_pair(cdr(args))) 
	    return(f3(a1, a2, cadr(args)));
	  return(f3(a1, a2, sc->UNDEFINED));
	}
      return(f3(a1, sc->UNDEFINED, sc->UNDEFINED));
    }
  return(f3(sc->UNDEFINED, sc->UNDEFINED, sc->UNDEFINED));
}

s7_pointer s7_apply_n_4(s7_scheme *sc, s7_pointer args, s7_pointer (*f4)(s7_pointer a1, s7_pointer a2, s7_pointer a3, s7_pointer a4))
{
  if (is_pair(args))
    {
      s7_pointer a1;
      a1 = car(args);
      args = cdr(args);
      if (is_pair(args))
	{
	  s7_pointer a2;
	  a2 = car(args);
	  args = cdr(args);
	  if (is_pair(args))
	    {
	      s7_pointer a3;
	      a3 = car(args);
	      if (is_pair(cdr(args)))
		return(f4(a1, a2, a3, cadr(args)));
	      return(f4(a1, a2, a3, sc->UNDEFINED));
	    }
	  return(f4(a1, a2, sc->UNDEFINED, sc->UNDEFINED));
	}
      return(f4(a1, sc->UNDEFINED, sc->UNDEFINED, sc->UNDEFINED));
    }
  return(f4(sc->UNDEFINED, sc->UNDEFINED, sc->UNDEFINED, sc->UNDEFINED));
}

s7_pointer s7_apply_n_5(s7_scheme *sc, s7_pointer args, 
			s7_pointer (*f5)(s7_pointer a1, s7_pointer a2, s7_pointer a3, s7_pointer a4, s7_pointer a5))
{
  if (is_pair(args))
    {
      s7_pointer a1;
      a1 = car(args);
      args = cdr(args);
      if (is_pair(args))
	{
	  s7_pointer a2;
	  a2 = car(args);
	  args = cdr(args);
	  if (is_pair(args))
	    {
	      s7_pointer a3;
	      a3 = car(args);
	      args = cdr(args);
	      if (is_pair(args))
		{
		  s7_pointer a4;
		  a4 = car(args);
		  if (is_pair(cdr(args))) 
		    return(f5(a1, a2, a3, a4, cadr(args)));
		  return(f5(a1, a2, a3, a4, sc->UNDEFINED));
		}
	      return(f5(a1, a2, a3, sc->UNDEFINED, sc->UNDEFINED));
	    }
	  return(f5(a1, a2, sc->UNDEFINED, sc->UNDEFINED, sc->UNDEFINED));
	}
      return(f5(a1, sc->UNDEFINED, sc->UNDEFINED, sc->UNDEFINED, sc->UNDEFINED));
    }
  return(f5(sc->UNDEFINED, sc->UNDEFINED, sc->UNDEFINED, sc->UNDEFINED, sc->UNDEFINED));
}

s7_pointer s7_apply_n_6(s7_scheme *sc, s7_pointer args, 
			s7_pointer (*f6)(s7_pointer a1, s7_pointer a2, s7_pointer a3, s7_pointer a4, s7_pointer a5, s7_pointer a6))
{
  s7_pointer a1, a2, a3, a4, a5, a6;
  a1 = sc->UNDEFINED; a2 = sc->UNDEFINED; a3 = sc->UNDEFINED; a4 = sc->UNDEFINED; a5 = sc->UNDEFINED; a6 = sc->UNDEFINED;
  if (is_pair(args))
    {
      a1 = car(args);
      args = cdr(args);
      if (is_pair(args))
	{
	  a2 = car(args);
	  args = cdr(args);
	  if (is_pair(args))
	    {
	      a3 = car(args);
	      args = cdr(args);
	      if (is_pair(args))
		{
		  a4 = car(args);
		  args = cdr(args);
		  if (is_pair(args))
		    {
		      a5 = car(args);
		      if (is_pair(cdr(args))) a6 = cadr(args);
		    }}}}}
  return(f6(a1, a2, a3, a4, a5, a6));
}

s7_pointer s7_apply_n_7(s7_scheme *sc, s7_pointer args, 
			s7_pointer (*f7)(s7_pointer a1, s7_pointer a2, s7_pointer a3, s7_pointer a4, 
					 s7_pointer a5, s7_pointer a6, s7_pointer a7))
{
  s7_pointer a1, a2, a3, a4, a5, a6, a7;
  a1 = sc->UNDEFINED; a2 = sc->UNDEFINED; a3 = sc->UNDEFINED; a4 = sc->UNDEFINED; a5 = sc->UNDEFINED; 
  a6 = sc->UNDEFINED, a7 = sc->UNDEFINED;
  if (is_pair(args))
    {
      a1 = car(args);
      args = cdr(args);
      if (is_pair(args))
	{
	  a2 = car(args);
	  args = cdr(args);
	  if (is_pair(args))
	    {
	      a3 = car(args);
	      args = cdr(args);
	      if (is_pair(args))
		{
		  a4 = car(args);
		  args = cdr(args);
		  if (is_pair(args))
		    {
		      a5 = car(args);
		      args = cdr(args);
		      if (is_pair(args))
			{
			  a6 = car(args);
			  if (is_pair(cdr(args))) a7 = cadr(args);
			}}}}}}
  return(f7(a1, a2, a3, a4, a5, a6, a7));
}

s7_pointer s7_apply_n_8(s7_scheme *sc, s7_pointer args, 
			s7_pointer (*f8)(s7_pointer a1, s7_pointer a2, s7_pointer a3, s7_pointer a4, 
					 s7_pointer a5, s7_pointer a6, s7_pointer a7, s7_pointer a8))
{
  s7_pointer a1, a2, a3, a4, a5, a6, a7, a8;
  a1 = sc->UNDEFINED; a2 = sc->UNDEFINED; a3 = sc->UNDEFINED; a4 = sc->UNDEFINED; a5 = sc->UNDEFINED; 
  a6 = sc->UNDEFINED, a7 = sc->UNDEFINED; a8 = sc->UNDEFINED;
  if (is_pair(args))
    {
      a1 = car(args);
      args = cdr(args);
      if (is_pair(args))
	{
	  a2 = car(args);
	  args = cdr(args);
	  if (is_pair(args))
	    {
	      a3 = car(args);
	      args = cdr(args);
	      if (is_pair(args))
		{
		  a4 = car(args);
		  args = cdr(args);
		  if (is_pair(args))
		    {
		      a5 = car(args);
		      args = cdr(args);
		      if (is_pair(args))
			{
			  a6 = car(args);
			  args = cdr(args);
			  if (is_pair(args))
			    {
			      a7 = car(args);
			      if (is_pair(cdr(args))) a8 = cadr(args);
			    }}}}}}}
  return(f8(a1, a2, a3, a4, a5, a6, a7, a8));
}

s7_pointer s7_apply_n_9(s7_scheme *sc, s7_pointer args, 
			s7_pointer (*f9)(s7_pointer a1, s7_pointer a2, s7_pointer a3, s7_pointer a4, 
					 s7_pointer a5, s7_pointer a6, s7_pointer a7, s7_pointer a8,
					 s7_pointer a9))
{
  s7_pointer a1, a2, a3, a4, a5, a6, a7, a8, a9;
  a1 = sc->UNDEFINED; a2 = sc->UNDEFINED; a3 = sc->UNDEFINED; a4 = sc->UNDEFINED; a5 = sc->UNDEFINED; 
  a6 = sc->UNDEFINED, a7 = sc->UNDEFINED; a8 = sc->UNDEFINED; a9 = sc->UNDEFINED;
  if (is_pair(args))
    {
      a1 = car(args);
      args = cdr(args);
      if (is_pair(args))
	{
	  a2 = car(args);
	  args = cdr(args);
	  if (is_pair(args))
	    {
	      a3 = car(args);
	      args = cdr(args);
	      if (is_pair(args))
		{
		  a4 = car(args);
		  args = cdr(args);
		  if (is_pair(args))
		    {
		      a5 = car(args);
		      args = cdr(args);
		      if (is_pair(args))
			{
			  a6 = car(args);
			  args = cdr(args);
			  if (is_pair(args))
			    {
			      a7 = car(args);
			      args = cdr(args);
			      if (is_pair(args))
				{
				  a8 = car(args);
				  if (is_pair(cdr(args))) a9 = cadr(args);
				}}}}}}}}
  return(f9(a1, a2, a3, a4, a5, a6, a7, a8, a9));
}

/* -------------------------------------------------------------------------------- */



s7_pointer s7_list_ref(s7_scheme *sc, s7_pointer lst, int num)
{
  int i;
  s7_pointer x;

  for (x = lst, i = 0; (i < num) && (is_pair(x)); i++, x = cdr(x)) {}
  if ((i == num) && (is_pair(x)))
    return(car(x));

  return(sc->NIL);
}


s7_pointer s7_list_set(s7_scheme *sc, s7_pointer lst, int num, s7_pointer val)
{
  int i;
  s7_pointer x;

  for (x = lst, i = 0; (i < num) && (is_pair(x)); i++, x = cdr(x)) {}
  if ((i == num) &&
      (is_pair(x)))
    car(x) = val;

  return(val);
}


static void list_set(s7_scheme *sc, s7_pointer lst, int num, s7_pointer val)
{
  /* internal form where we know the list is ok */
  int i;
  s7_pointer x;
  for (x = lst, i = 0; i < num; i++, x = cdr(x)) {}
  car(x) = val;
}


s7_pointer s7_member(s7_scheme *sc, s7_pointer sym, s7_pointer lst)
{
  s7_pointer x;

  for (x = lst; is_pair(x); x = cdr(x))
    if (s7_is_equal(sc, sym, car(x)))
      return(x);

  return(sc->F);
}


static bool symbol_is_in_arg_list(s7_pointer sym, s7_pointer lst)
{
  s7_pointer x;
  for (x = lst; is_pair(x); x = cdr(x))
    if ((sym == car(x)) ||
	((is_pair(car(x))) &&
	 (sym == caar(x))))
	return(true);
  return(sym == x);
}


s7_pointer s7_assoc(s7_scheme *sc, s7_pointer sym, s7_pointer lst)
{
  s7_pointer x, y;

  if (!is_pair(lst))
    return(sc->F);

  x = lst;
  y = lst;
  while (true)
    {
      if ((is_pair(car(x))) && (s7_is_equal(sc, sym, caar(x)))) return(car(x));
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);
      
      if ((is_pair(car(x))) && (s7_is_equal(sc, sym, caar(x)))) return(car(x));
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);

      y = cdr(y);
      if (x == y) return(sc->F);
    }
  return(sc->F);
}


s7_pointer s7_reverse(s7_scheme *sc, s7_pointer a) 
{
  /* reverse list -- produce new list (other code assumes this function does not return the original!) */
  s7_pointer x, p;

  if (is_null(a)) return(a);

  if (!is_pair(cdr(a)))
    {
      if (is_not_null(cdr(a)))
	return(cons(sc, cdr(a), car(a)));
      return(cons(sc, car(a), sc->NIL)); /* don't return 'a' itself */
    }

  sc->w = list_1(sc, car(a));

  for (x = cdr(a), p = a; is_pair(x); x = cdr(x), p = cdr(p))
    {
      sc->w = cons(sc, car(x), sc->w);
      if (is_pair(cdr(x)))
	{
	  x = cdr(x);
	  sc->w = cons(sc, car(x), sc->w);
	}
      if (x == p) /* this can take awhile to notice there's a cycle, but what does the caller expect? */
	break;
    }

  if (is_not_null(x))
    p = cons(sc, x, sc->w);    /* ?? this means that (reverse '(1 2 . 3)) returns '(3 2 1) -- we used to return () here */
  else p = sc->w;

  sc->w = sc->NIL;
  return(p);
}

/* s7_reverse sometimes tacks extra nodes on the end of a reversed circular list (it detects the cycle too late) 
 *  (let ((lst (list 0))) (set! (cdr lst) lst) (reverse lst)) -> (#1=(0 . #1#) 0 0 0)
 */


static s7_pointer reverse_in_place(s7_scheme *sc, s7_pointer term, s7_pointer list) 
{
  s7_pointer p = list, result = term, q;

  while (is_not_null(p))
    {
      q = cdr(p);
      if ((!is_pair(q)) &&
	  (is_not_null(q)))
	return(sc->NIL); /* improper list? */
      cdr(p) = result;
      result = p;
      p = q;
    }
  return(result);
}


static s7_pointer reverse_in_place_unchecked(s7_scheme *sc, s7_pointer term, s7_pointer list) 
{
  s7_pointer p = list, result = term, q;

  while (is_not_null(p))
    {
      q = cdr(p);
      cdr(p) = result;
      result = p;
      p = q;

      if (is_null(p)) break;
      q = cdr(p);
      cdr(p) = result;
      result = p;
      p = q;
    }
  return(result);
}


static s7_pointer safe_reverse_in_place(s7_scheme *sc, s7_pointer list) /* "safe" here means we guarantee this list is unproblematic */
{
  s7_pointer p = list, result, q;
  result = sc->NIL;

  while (is_not_null(p))
    {
      q = cdr(p);
      /*   also if (is_null(list)) || (is_null(cdr(list))) return(list) */
      cdr(p) = result;
      result = p;
      p = q;

      /* unroll the loop for speed */
      if (is_null(p)) break;
      q = cdr(p);
      cdr(p) = result;
      result = p;
      p = q;

      if (is_null(p)) break;
      q = cdr(p);
      cdr(p) = result;
      result = p;
      p = q;

      if (is_null(p)) break;
      q = cdr(p);
      cdr(p) = result;
      result = p;
      p = q;
    }
  return(result);
}


/* is this correct? (let ((x (list 1 2))) (eq? x (append () x))) -> #t
 */

s7_pointer s7_append(s7_scheme *sc, s7_pointer a, s7_pointer b) 
{
  s7_pointer p = b, q;
  
  if (is_not_null(a)) 
    {
      a = s7_reverse(sc, a);
      while (is_not_null(a)) 
	{
	  q = cdr(a);
	  cdr(a) = p;
	  p = a;
	  a = q;
	}
    }
  return(p);
}


static s7_pointer copy_list(s7_scheme *sc, s7_pointer lst)
{
  s7_pointer p, tp, np;
  if (!is_pair(lst)) return(sc->NIL);
  tp = cons(sc, car(lst), sc->NIL);
  for (p = cdr(lst), np = tp; is_pair(p); p = cdr(p), np = cdr(np))
    cdr(np) = cons(sc, car(p), sc->NIL);
  return(tp);
}


static s7_pointer revappend(s7_scheme *sc, s7_pointer a, s7_pointer b) 
{
  /* (map (lambda (x) (if (odd? x) (apply values '(1 2 3)) (values))) (list 1 2 3 4)) 
   *   is a bad case -- we have to copy the incoming list.
   */
   
  s7_pointer p = b, q;
  
  if (is_not_null(a)) 
    {
      a = copy_list(sc, a); 
      while (is_not_null(a)) 
	{
	  q = cdr(a);
	  cdr(a) = p;
	  p = a;
	  a = q;
	}
    }
  return(p);
}

static int safe_list_length(s7_scheme *sc, s7_pointer a)
{
  /* assume that "a" is a proper list */
  int i = 0;
  s7_pointer b;
  for (b = a; is_pair(b); i++, b = cdr(b)) {};
  return(i);
}


int s7_list_length(s7_scheme *sc, s7_pointer a) 
{
  /* returns -len if list is dotted, 0 if it's (directly) circular */
  int i;
  s7_pointer slow, fast;

  slow = fast = a;
  for (i = 0; ; i += 2)
    {
      if (!is_pair(fast))
	{
	  if (is_null(fast))
	    return(i);
	  return(-i);
	}
      
      fast = cdr(fast);
      if (!is_pair(fast)) 
	{
	  if (is_null(fast))
	    return(i + 1);
	  return(-i - 1);
	}
      /* if unrolled further, it's a lot slower? */
      
      fast = cdr(fast);
      slow = cdr(slow);
      if (fast == slow) 
	return(0);
    }
  return(0);
}


static s7_pointer g_is_null(s7_scheme *sc, s7_pointer args)
{
  #define H_is_null "(null? obj) returns #t if obj is the empty list"
  check_boolean_method(sc, is_null, sc->IS_NULL, args);
  /* as a generic this could be: has_structure and length == 0 */
}


static s7_pointer g_is_pair(s7_scheme *sc, s7_pointer args)
{
  #define H_is_pair "(pair? obj) returns #t if obj is a pair (a non-empty list)"
  check_boolean_method(sc, is_pair, sc->IS_PAIR, args);
}


bool s7_is_list(s7_scheme *sc, s7_pointer p)
{
  return((is_pair(p)) ||
	 (is_null(p)));
}


static bool is_proper_list(s7_scheme *sc, s7_pointer lst)
{
  s7_pointer slow, fast;

  fast = lst;
  slow = lst;
  while (true)
    {
      if (!is_pair(fast)) 
	return(is_null(fast)); /* else it's an improper list */

      fast = cdr(fast);
      if (!is_pair(fast)) return(is_null(fast));

      fast = cdr(fast);
      if (!is_pair(fast)) return(is_null(fast));

      fast = cdr(fast);
      slow = cdr(slow);
      if (fast == slow) return(false);
    }
  return(true);
}


static s7_pointer g_is_list(s7_scheme *sc, s7_pointer args)
{
  #define H_is_list "(list? obj) returns #t if obj is a proper list"
  s7_pointer p;
  p = car(args);
  if (is_proper_list(sc, p)) return(sc->T);
  check_method(sc, p, sc->IS_LIST, args);
  return(sc->F);
}


static s7_pointer make_list(s7_scheme *sc, int len, s7_pointer init)
{
  /* sc->v used only here */
  switch (len)
    {
    case 0: return(sc->NIL);
    case 1: return(cons(sc, init, sc->NIL));
    case 2: return(cons_unchecked(sc, init, cons(sc, init, sc->NIL)));
    case 3: return(cons_unchecked(sc, init, cons_unchecked(sc, init, cons(sc, init, sc->NIL))));
    case 4: return(cons_unchecked(sc, init, cons_unchecked(sc, init, cons_unchecked(sc, init, cons(sc, init, sc->NIL)))));
    case 5: return(cons_unchecked(sc, init, cons_unchecked(sc, init, cons_unchecked(sc, init, 
		    cons_unchecked(sc, init, cons(sc, init, sc->NIL))))));
    case 6: return(cons_unchecked(sc, init, cons_unchecked(sc, init, cons_unchecked(sc, init,
		    cons_unchecked(sc, init, cons_unchecked(sc, init, cons(sc, init, sc->NIL)))))));
    case 7: return(cons_unchecked(sc, init, cons_unchecked(sc, init, cons_unchecked(sc, init,
		    cons_unchecked(sc, init, cons_unchecked(sc, init, cons_unchecked(sc, init, cons(sc, init, sc->NIL))))))));
    default:
      sc->v = sc->NIL;
      if (len < (sc->free_heap_top - sc->free_heap))
	{
	  int i;
	  for (i = 0; i < len; i++)
	    sc->v = cons_unchecked(sc, init, sc->v);
	}
      else
	{
	  int i;
	  for (i = 0; i < len; i++)
	    sc->v = cons(sc, init, sc->v);
	}
    }
  return(sc->v);
}


#define MAX_LIST_LENGTH 1073741824

static s7_pointer g_make_list(s7_scheme *sc, s7_pointer args)
{
  #define H_make_list "(make-list length (initial-element #f)) returns a list of 'length' elements whose value is 'initial-element'."

  s7_pointer init;
  s7_Int len;

  if (!s7_is_integer(car(args)))
    {
      check_method(sc, car(args), sc->MAKE_LIST, args);
      return(wrong_type_argument(sc, sc->MAKE_LIST, small_int(1), car(args), T_INTEGER));
    }
  len = s7_integer(car(args));            /* needs to be s7_Int here so that (make-list most-negative-fixnum) is handled correctly */
  if (len < 0)
    return(out_of_range(sc, sc->MAKE_LIST, small_int(1), car(args), ITS_NEGATIVE));
  if (len == 0) return(sc->NIL);          /* what about (make-list 0 123)? */
  if (len > MAX_LIST_LENGTH)
    return(out_of_range(sc, sc->MAKE_LIST, small_int(1), car(args), ITS_TOO_LARGE));

  if (is_pair(cdr(args)))
    init = cadr(args);
  else init = sc->F;
  return(make_list(sc, (int)len, init));
}


static s7_pointer list_ref_ic;
static s7_pointer g_list_ref_ic(s7_scheme *sc, s7_pointer args)
{
  s7_Int i, index;
  s7_pointer lst, p;

  lst = car(args);
  if (!is_pair(lst))
    {
      check_method(sc, lst, sc->LIST_REF, args);
      return(wrong_type_argument(sc, sc->LIST_REF, small_int(1), lst, T_PAIR));
    }
  index = s7_integer(cadr(args));

  for (i = 0, p = lst; (i < index) && is_pair(p); i++, p = cdr(p)) {}
  
  if (!is_pair(p))
    {
      if (is_null(p))
	return(out_of_range(sc, sc->LIST_REF, small_int(2), cadr(args), ITS_TOO_LARGE));
      return(wrong_type_argument_with_type(sc, sc->LIST_REF, small_int(1), lst, A_PROPER_LIST));
    }
  return(car(p));
}


static s7_pointer list_ref_1(s7_scheme *sc, s7_pointer lst, s7_pointer ind)
{
  s7_Int i, index;
  s7_pointer p;

  if (!s7_is_integer(ind))
    {
      if (!s7_is_integer(p = check_values(sc, ind, cons(sc, ind, sc->NIL))))
	{
	  check_method(sc, ind, sc->LIST_REF, list_2(sc, lst, ind));
	  return(wrong_type_argument(sc, sc->LIST_REF, small_int(2), ind, T_INTEGER));
	}
      else ind = p;
    }
  index = s7_integer(ind);
  if (index < 0)
    return(out_of_range(sc, sc->LIST_REF, small_int(2), ind, ITS_NEGATIVE));

  if (index > MAX_LIST_LENGTH)
    return(out_of_range(sc, sc->LIST_REF, small_int(2), ind, ITS_TOO_LARGE));
  
  for (i = 0, p = lst; (i < index) && is_pair(p); i++, p = cdr(p)) {}
  
  if (!is_pair(p))
    {
      if (is_null(p))
	return(out_of_range(sc, sc->LIST_REF, small_int(2), ind, ITS_TOO_LARGE));
      return(wrong_type_argument_with_type(sc, sc->LIST_REF, small_int(1), lst, A_PROPER_LIST));
    }
  return(car(p));
}


static s7_pointer g_list_ref(s7_scheme *sc, s7_pointer args)
{
  #define H_list_ref "(list-ref lst i ...) returns the i-th element (0-based) of the list"
  
  /* (let ((L '((1 2 3) (4 5 6)))) (list-ref L 1 2)) 

    (define (lref L . args) 
      (if (null? (cdr args))
          (list-ref L (car args))
          (apply lref (list-ref L (car args)) (cdr args))))
  */
  s7_pointer lst, inds;

  lst = car(args);
  if (!is_pair(lst))
    {
      check_method(sc, lst, sc->LIST_REF, args);
      return(wrong_type_argument(sc, sc->LIST_REF, small_int(1), lst, T_PAIR));
    }
  inds = cdr(args);
  while (true)
    {
      lst = list_ref_1(sc, lst, car(inds));
      if (is_null(cdr(inds)))
	return(lst);
      inds = cdr(inds);
      if (!is_pair(lst)) /* trying to avoid a cons here at the cost of one extra type check */
	return(implicit_index(sc, lst, inds));
    }
}


static s7_pointer g_list_set_1(s7_scheme *sc, s7_pointer lst, s7_pointer args, int arg_num)
{
  #define H_list_set "(list-set! lst i ... val) sets the i-th element (0-based) of the list to val"
  
  int i;
  s7_Int index;
  s7_pointer p, ind;

  /* (let ((L '((1 2 3) (4 5 6)))) (list-set! L 1 2 32) L) */

  if (!is_pair(lst))
    {
      check_method(sc, lst, sc->LIST_SET, cons(sc, lst, args));
      return(wrong_type_argument(sc, sc->LIST_SET, small_int(1), lst, T_PAIR));
    }
  ind = car(args);
  if (!s7_is_integer(ind))
    {
      if (!s7_is_integer(p = check_values(sc, ind, args)))
	{
	  check_method(sc, ind, sc->LIST_SET, cons(sc, lst, args));
	  return(wrong_type_argument(sc, sc->LIST_SET, small_int(arg_num), ind, T_INTEGER));
	}
      else ind = p;
    }
  index = s7_integer(ind);
  if (index < 0)
    return(out_of_range(sc, sc->LIST_SET, small_int(arg_num), ind, ITS_NEGATIVE));
  
  if (index > MAX_LIST_LENGTH)
    return(out_of_range(sc, sc->LIST_SET, small_int(arg_num), ind, ITS_TOO_LARGE));
  
  for (i = 0, p = lst; (i < index) && is_pair(p); i++, p = cdr(p)) {}
  
  if (!is_pair(p))
    {
      if (is_null(p))
	return(out_of_range(sc, sc->LIST_SET, small_int(arg_num), ind, ITS_TOO_LARGE));
      return(wrong_type_argument_with_type(sc, sc->LIST_SET, small_int(1), lst, A_PROPER_LIST));
    }
  if (is_null(cddr(args)))
    car(p) = cadr(args);
  else return(g_list_set_1(sc, car(p), cdr(args), arg_num + 1));

  return(cadr(args));
}


static s7_pointer g_list_set(s7_scheme *sc, s7_pointer args)
{
  return(g_list_set_1(sc, car(args), cdr(args), 2));
}


static s7_pointer list_set_ic;
static s7_pointer g_list_set_ic(s7_scheme *sc, s7_pointer args)
{
  int i;
  s7_Int index;
  s7_pointer p, lst;

  lst = car(args);
  if (!is_pair(lst))
    {
      check_method(sc, lst, sc->LIST_SET, args);
      return(wrong_type_argument(sc, sc->LIST_SET, small_int(1), lst, T_PAIR));
    }
  index = s7_integer(cadr(args));
  for (i = 0, p = lst; (i < index) && is_pair(p); i++, p = cdr(p)) {}
  
  if (!is_pair(p))
    {
      if (is_null(p))
	return(out_of_range(sc, sc->LIST_SET, small_int(2), cadr(args), ITS_TOO_LARGE));
      return(wrong_type_argument_with_type(sc, sc->LIST_SET, small_int(1), lst, A_PROPER_LIST));
    }

  car(p) = caddr(args);
  return(car(p));
}


static s7_pointer g_list_tail(s7_scheme *sc, s7_pointer args)
{
  #define H_list_tail "(list-tail lst i) returns the list from the i-th element on"
  
  int i;
  s7_Int index;
  s7_pointer p, p1;

  p = car(args);
  if (!s7_is_list(sc, p))
    {
      check_method(sc, p, sc->LIST_TAIL, args);
      return(wrong_type_argument_with_type(sc, sc->LIST_TAIL, small_int(1), p, A_LIST));
    }
  p = cadr(args);
  if (!s7_is_integer(p))
    {
      if (!s7_is_integer(p1 = check_values(sc, p, cdr(args))))
	{
	  check_method(sc, p, sc->LIST_TAIL, args);
	  return(wrong_type_argument(sc, sc->LIST_TAIL, small_int(2), p, T_INTEGER));
	}
      else p = p1;
    }
  index = s7_integer(p);
  if (index < 0)
    return(out_of_range(sc, sc->LIST_TAIL, small_int(2), p, ITS_NEGATIVE));
  if (index > MAX_LIST_LENGTH)
    return(out_of_range(sc, sc->LIST_TAIL, small_int(2), p, ITS_TOO_LARGE));
  
  for (i = 0, p = car(args); (i < index) && (is_pair(p)); i++, p = cdr(p)) {}
  
  if (i < index)
    return(out_of_range(sc, sc->LIST_TAIL, small_int(2), cadr(args), ITS_TOO_LARGE));

  /* I guess this would make sense with more than one index, but I'm not sure it's very important */
  return(p);
}


static s7_pointer g_car(s7_scheme *sc, s7_pointer args)
{
  #define H_car "(car pair) returns the first element of the pair"
  if (!is_pair(car(args)))
    {
      check_method(sc, car(args), sc->CAR, args);
      return(simple_wrong_type_argument(sc, sc->CAR, car(args), T_PAIR));
    }
  return(caar(args));
}


static s7_pointer g_cdr(s7_scheme *sc, s7_pointer args)
{
  #define H_cdr "(cdr pair) returns the second element of the pair"
  if (!is_pair(car(args)))
    {
      check_method(sc, car(args), sc->CDR, args);
      return(simple_wrong_type_argument(sc, sc->CDR, car(args), T_PAIR));
    }
  return(cdar(args));
}


static s7_pointer g_cons(s7_scheme *sc, s7_pointer args)
{
  /* n-ary cons could be the equivalent of CL's list*? */
  /*   it would be neater to have a single cons cell able to contain (directly) any number of elements */
  /*   (set! (cadr (cons 1 2 3)) 4) -> (1 4 . 3) */

  #define H_cons "(cons a b) returns a pair containing a and b"
  
  /* cdr(args) = cadr(args);
   * this is not safe -- it changes a variable's value directly:
   *   (let ((lst (list 1 2))) (list (apply cons lst) lst)) -> '((1 . 2) (1 . 2))
   */
  s7_pointer x;

  NEW_CELL(sc, x);
  car(x) = car(args);
  cdr(x) = cadr(args);
  set_type(x, T_PAIR | T_SAFE_PROCEDURE);
  return(x);
}


static s7_pointer g_set_car(s7_scheme *sc, s7_pointer args)
{
  #define H_set_car "(set-car! pair val) sets the pair's first element to val"
  s7_pointer p;

  p = car(args);
  if (!is_pair(p))
    {
      check_method(sc, p, sc->SET_CAR, args);
      return(wrong_type_argument(sc, sc->SET_CAR, small_int(1), p, T_PAIR));
    }
  car(p) = cadr(args);
  return(car(p));
}


static s7_pointer g_set_cdr(s7_scheme *sc, s7_pointer args)
{
  #define H_set_cdr "(set-cdr! pair val) sets the pair's second element to val"
  s7_pointer p;

  p = car(args);
  if (!is_pair(p))
    {
      check_method(sc, p, sc->SET_CDR, args);
      return(wrong_type_argument(sc, sc->SET_CDR, small_int(1), p, T_PAIR));
    }
  cdr(p) = cadr(args);
  return(cdr(p));
}


static void init_car_a_list(void)
{
  CAR_A_LIST = s7_make_permanent_string("a list whose car is also a list");
  CDR_A_LIST = s7_make_permanent_string("a list whose cdr is also a list");

  CAAR_A_LIST = s7_make_permanent_string("a list whose caar is also a list");
  CADR_A_LIST = s7_make_permanent_string("a list whose cadr is also a list");
  CDAR_A_LIST = s7_make_permanent_string("a list whose cdar is also a list");
  CDDR_A_LIST = s7_make_permanent_string("a list whose cddr is also a list");

  CAAAR_A_LIST = s7_make_permanent_string("a list whose caaar is also a list");
  CAADR_A_LIST = s7_make_permanent_string("a list whose caadr is also a list");
  CADAR_A_LIST = s7_make_permanent_string("a list whose cadar is also a list");
  CADDR_A_LIST = s7_make_permanent_string("a list whose caddr is also a list");
  CDAAR_A_LIST = s7_make_permanent_string("a list whose cdaar is also a list");
  CDADR_A_LIST = s7_make_permanent_string("a list whose cdadr is also a list");
  CDDAR_A_LIST = s7_make_permanent_string("a list whose cddar is also a list");
  CDDDR_A_LIST = s7_make_permanent_string("a list whose cdddr is also a list");

  A_LIST = s7_make_permanent_string("a list");
  AN_EQ_FUNC = s7_make_permanent_string("a procedure that can take 2 arguments");
  AN_ASSOCIATION_LIST = s7_make_permanent_string("an association list");
  A_NORMAL_REAL = s7_make_permanent_string("a normal real");
  A_RATIONAL = s7_make_permanent_string("an integer or a ratio");
  A_NUMBER = s7_make_permanent_string("a number");
  A_PROCEDURE = s7_make_permanent_string("a procedure");
  AN_ENVIRONMENT = s7_make_permanent_string("an environment");
  A_PROPER_LIST = s7_make_permanent_string("a proper list");
  A_FUNCTION = s7_make_permanent_string("a function");
  A_BOOLEAN = s7_make_permanent_string("a boolean");
  AN_INPUT_PORT = s7_make_permanent_string("an input port");
  AN_OPEN_PORT = s7_make_permanent_string("an open port");
  AN_OUTPUT_PORT = s7_make_permanent_string("an output port");
  AN_INPUT_STRING_PORT = s7_make_permanent_string("an input string port");
  AN_INPUT_FILE_PORT = s7_make_permanent_string("an input file port");
  AN_OUTPUT_STRING_PORT = s7_make_permanent_string("an output string port");
  AN_OUTPUT_FILE_PORT = s7_make_permanent_string("an output file port");
  A_THUNK = s7_make_permanent_string("a thunk");
  A_SYMBOL = s7_make_permanent_string("a symbol");
  A_NON_NEGATIVE_INTEGER = s7_make_permanent_string("a non-negative integer");
  AN_UNSIGNED_BYTE = s7_make_permanent_string("an unsigned byte");
  SOMETHING_APPLICABLE = s7_make_permanent_string("a procedure or something applicable");
  CONSTANT_ARG_ERROR = s7_make_permanent_string("lambda* parameter '~A is a constant");
  BAD_BINDING = s7_make_permanent_string("~A: can't bind some variable to ~S");
  A_FORMAT_PORT = s7_make_permanent_string("#f, #t, or an open output port");
  A_BINDING = s7_make_permanent_string("a pair whose car is a symbol: '(symbol . value)");
  A_NON_CONSTANT_SYMBOL = s7_make_permanent_string("a non-constant symbol");
  A_SEQUENCE = s7_make_permanent_string("a sequence");
  RESULT_IS_TOO_LARGE = s7_make_permanent_string("result is too large");
  ITS_TOO_LARGE = s7_make_permanent_string("it is too large");
  ITS_NEGATIVE = s7_make_permanent_string("it is negative");
  ITS_NAN = s7_make_permanent_string("NaN usually indicates a numerical error");
  ITS_INFINITE = s7_make_permanent_string("it is infinite");
  TOO_MANY_INDICES = s7_make_permanent_string("too many indices");
  A_VALID_RADIX = s7_make_permanent_string("should be between 2 and 16");
#if (!HAVE_COMPLEX_NUMBERS)
  NO_COMPLEX_NUMBERS = s7_make_permanent_string("this version of s7 does not support complex numbers");
#endif
}


static s7_pointer g_caar(s7_scheme *sc, s7_pointer args)
{
  #define H_caar "(caar lst) returns (car (car lst)): (caar '((1 2))) -> 1"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CAAR, args);
      return(simple_wrong_type_argument(sc, sc->CAAR, lst, T_PAIR));
    }
  if (!is_pair(car(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CAAR, lst, CAR_A_LIST));
  
  /* it makes absolutely no difference in timing to move lst here or below (i.e. lst=car(lst) then return(car(lst)) and so on) 
   */
  return(car(car(lst)));
}

static s7_pointer g_cadr(s7_scheme *sc, s7_pointer args)
{
  #define H_cadr "(cadr lst) returns (car (cdr lst)): (cadr '(1 2 3)) -> 2"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CADR, args);
      return(simple_wrong_type_argument(sc, sc->CADR, lst, T_PAIR));
    }
  if (!is_pair(cdr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CADR, lst, CDR_A_LIST));

  return(car(cdr(lst)));
}


static s7_pointer g_cdar(s7_scheme *sc, s7_pointer args)
{
  #define H_cdar "(cdar lst) returns (cdr (car lst)): (cdar '((1 2 3))) -> '(2 3)"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CDAR, args);
      return(simple_wrong_type_argument(sc, sc->CDAR, lst, T_PAIR));
    }
  if (!is_pair(car(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDAR, lst, CAR_A_LIST));

  return(cdr(car(lst)));
}


static s7_pointer g_cddr(s7_scheme *sc, s7_pointer args)
{
  #define H_cddr "(cddr lst) returns (cdr (cdr lst)): (cddr '(1 2 3 4)) -> '(3 4)"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CDDR, args);
      return(simple_wrong_type_argument(sc, sc->CDDR, lst, T_PAIR));
    }
  if (!is_pair(cdr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDDR, lst, CDR_A_LIST));

  return(cdr(cdr(lst)));
}


static s7_pointer g_caaar(s7_scheme *sc, s7_pointer args)
{
  #define H_caaar "(caaar lst) returns (car (car (car lst))): (caaar '(((1 2)))) -> 1"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CAAAR, args);
      return(simple_wrong_type_argument(sc, sc->CAAAR, lst, T_PAIR));
    }
  if (!is_pair(car(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CAAAR, lst, CAR_A_LIST));
  if (!is_pair(car(car(lst)))) return(simple_wrong_type_argument_with_type(sc, sc->CAAAR, lst, CAAR_A_LIST));

  return(car(car(car(lst))));
}


static s7_pointer g_caadr(s7_scheme *sc, s7_pointer args)
{
  #define H_caadr "(caadr lst) returns (car (car (cdr lst))): (caadr '(1 (2 3))) -> 2"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CAADR, args);
      return(simple_wrong_type_argument(sc, sc->CAADR, lst, T_PAIR));
    }
  if (!is_pair(cdr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CAADR, lst, CDR_A_LIST));
  if (!is_pair(car(cdr(lst)))) return(simple_wrong_type_argument_with_type(sc, sc->CAADR, lst, CADR_A_LIST));

  return(car(car(cdr(lst))));
}


static s7_pointer g_cadar(s7_scheme *sc, s7_pointer args)
{
  #define H_cadar "(cadar lst) returns (car (cdr (car lst))): (cadar '((1 2 3))) -> 2"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CADAR, args);
      return(simple_wrong_type_argument(sc, sc->CADAR, lst, T_PAIR));
    }
  if (!is_pair(car(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CADAR, lst, CAR_A_LIST));
  if (!is_pair(cdr(car(lst)))) return(simple_wrong_type_argument_with_type(sc, sc->CADAR, lst, CDAR_A_LIST));

  return(car(cdr(car(lst))));
}


static s7_pointer g_cdaar(s7_scheme *sc, s7_pointer args)
{
  #define H_cdaar "(cdaar lst) returns (cdr (car (car lst))): (cdaar '(((1 2 3)))) -> '(2 3)"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CDAAR, args);
      return(simple_wrong_type_argument(sc, sc->CDAAR, lst, T_PAIR));
    }
  if (!is_pair(car(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDAAR, lst, CAR_A_LIST));
  if (!is_pair(car(car(lst)))) return(simple_wrong_type_argument_with_type(sc, sc->CDAAR, lst, CAAR_A_LIST));

  return(cdr(car(car(lst))));
}


static s7_pointer g_caddr(s7_scheme *sc, s7_pointer args)
{
  #define H_caddr "(caddr lst) returns (car (cdr (cdr lst))): (caddr '(1 2 3 4)) -> 3"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CADDR, args);
      return(simple_wrong_type_argument(sc, sc->CADDR, lst, T_PAIR));
    }
  if (!is_pair(cdr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CADDR, lst, CDR_A_LIST));
  if (!is_pair(cdr(cdr(lst)))) return(simple_wrong_type_argument_with_type(sc, sc->CADDR, lst, CDDR_A_LIST));

  return(car(cdr(cdr(lst))));
}


static s7_pointer g_cdddr(s7_scheme *sc, s7_pointer args)
{
  #define H_cdddr "(cdddr lst) returns (cdr (cdr (cdr lst))): (cdddr '(1 2 3 4)) -> '(4)"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CDDDR, args);
      return(simple_wrong_type_argument(sc, sc->CDDDR, lst, T_PAIR));
    }
  if (!is_pair(cdr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDDDR, lst, CDR_A_LIST));
  if (!is_pair(cdr(cdr(lst)))) return(simple_wrong_type_argument_with_type(sc, sc->CDDDR, lst, CDDR_A_LIST));

  return(cdr(cdr(cdr(lst))));
}


static s7_pointer g_cdadr(s7_scheme *sc, s7_pointer args)
{
  #define H_cdadr "(cdadr lst) returns (cdr (car (cdr lst))): (cdadr '(1 (2 3 4))) -> '(3 4)"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CDADR, args);
      return(simple_wrong_type_argument(sc, sc->CDADR, lst, T_PAIR));
    }
  if (!is_pair(cdr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDADR, lst, CDR_A_LIST));
  if (!is_pair(car(cdr(lst)))) return(simple_wrong_type_argument_with_type(sc, sc->CDADR, lst, CADR_A_LIST));

  return(cdr(car(cdr(lst))));
}


static s7_pointer g_cddar(s7_scheme *sc, s7_pointer args)
{
  #define H_cddar "(cddar lst) returns (cdr (cdr (car lst))): (cddar '((1 2 3 4))) -> '(3 4)"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CDDAR, args);
      return(simple_wrong_type_argument(sc, sc->CDDAR, lst, T_PAIR));
    }
  if (!is_pair(car(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDDAR, lst, CAR_A_LIST));
  if (!is_pair(cdr(car(lst)))) return(simple_wrong_type_argument_with_type(sc, sc->CDDAR, lst, CDAR_A_LIST));

  return(cdr(cdr(car(lst))));
}


static s7_pointer g_caaaar(s7_scheme *sc, s7_pointer args)
{
  #define H_caaaar "(caaaar lst) returns (car (car (car (car lst)))): (caaaar '((((1 2))))) -> 1"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CAAAAR, args);
      return(simple_wrong_type_argument(sc, sc->CAAAAR, lst, T_PAIR));
    }
  if (!is_pair(car(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CAAAAR, lst, CAR_A_LIST));
  if (!is_pair(caar(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CAAAAR, lst, CAAR_A_LIST));
  if (!is_pair(caaar(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CAAAAR, lst, CAAAR_A_LIST));

  return(car(car(car(car(lst)))));
}


static s7_pointer g_caaadr(s7_scheme *sc, s7_pointer args)
{
  #define H_caaadr "(caaadr lst) returns (car (car (car (cdr lst)))): (caaadr '(1 ((2 3)))) -> 2"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CAAADR, args);
      return(simple_wrong_type_argument(sc, sc->CAAADR, lst, T_PAIR));
    }
  if (!is_pair(cdr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CAAADR, lst, CDR_A_LIST));
  if (!is_pair(cadr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CAAADR, lst, CADR_A_LIST));
  if (!is_pair(caadr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CAAADR, lst, CAADR_A_LIST));

  return(car(car(car(cdr(lst)))));
}


static s7_pointer g_caadar(s7_scheme *sc, s7_pointer args)
{
  #define H_caadar "(caadar lst) returns (car (car (cdr (car lst)))): (caadar '((1 (2 3)))) -> 2"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CAADAR, args);
      return(simple_wrong_type_argument(sc, sc->CAADAR, lst, T_PAIR));
    }
  if (!is_pair(car(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CAADAR, lst, CAR_A_LIST));
  if (!is_pair(cdar(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CAADAR, lst, CDAR_A_LIST));
  if (!is_pair(cadar(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CAADAR, lst, CADAR_A_LIST));

  return(car(car(cdr(car(lst)))));
}


static s7_pointer g_cadaar(s7_scheme *sc, s7_pointer args)
{
  #define H_cadaar "(cadaar lst) returns (car (cdr (car (car lst)))): (cadaar '(((1 2 3)))) -> 2"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CADAAR, args);
      return(simple_wrong_type_argument(sc, sc->CADAAR, lst, T_PAIR));
    }
  if (!is_pair(car(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CADAAR, lst, CAR_A_LIST));
  if (!is_pair(caar(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CADAAR, lst, CAAR_A_LIST));
  if (!is_pair(cdaar(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CADAAR, lst, CDAAR_A_LIST));

  return(car(cdr(car(car(lst)))));
}


static s7_pointer g_caaddr(s7_scheme *sc, s7_pointer args)
{
  #define H_caaddr "(caaddr lst) returns (car (car (cdr (cdr lst)))): (caaddr '(1 2 (3 4))) -> 3"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CAADDR, args);
      return(simple_wrong_type_argument(sc, sc->CAADDR, lst, T_PAIR));
    }
  if (!is_pair(cdr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CAADDR, lst, CDR_A_LIST));
  if (!is_pair(cddr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CAADDR, lst, CDDR_A_LIST));
  if (!is_pair(caddr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CAADDR, lst, CADDR_A_LIST));

  return(car(car(cdr(cdr(lst)))));
}


static s7_pointer g_cadddr(s7_scheme *sc, s7_pointer args)
{
  #define H_cadddr "(cadddr lst) returns (car (cdr (cdr (cdr lst)))): (cadddr '(1 2 3 4 5)) -> 4"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CADDDR, args);
      return(simple_wrong_type_argument(sc, sc->CADDDR, lst, T_PAIR));
    }
  if (!is_pair(cdr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CADDDR, lst, CDR_A_LIST));
  if (!is_pair(cddr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CADDDR, lst, CDDR_A_LIST));
  if (!is_pair(cdddr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CADDDR, lst, CDDDR_A_LIST));

  return(car(cdr(cdr(cdr(lst)))));
}


static s7_pointer g_cadadr(s7_scheme *sc, s7_pointer args)
{
  #define H_cadadr "(cadadr lst) returns (car (cdr (car (cdr lst)))): (cadadr '(1 (2 3 4))) -> 3"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CADADR, args);
      return(simple_wrong_type_argument(sc, sc->CADADR, lst, T_PAIR));
    }
  if (!is_pair(cdr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CADADR, lst, CDR_A_LIST));
  if (!is_pair(cadr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CADADR, lst, CADR_A_LIST));
  if (!is_pair(cdadr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CADADR, lst, CDADR_A_LIST));

  return(car(cdr(car(cdr(lst)))));
}


static s7_pointer g_caddar(s7_scheme *sc, s7_pointer args)
{
  #define H_caddar "(caddar lst) returns (car (cdr (cdr (car lst)))): (caddar '((1 2 3 4))) -> 3"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CADDAR, args);
      return(simple_wrong_type_argument(sc, sc->CADDAR, lst, T_PAIR));
    }
  if (!is_pair(car(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CADDAR, lst, CAR_A_LIST));
  if (!is_pair(cdar(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CADDAR, lst, CDAR_A_LIST));
  if (!is_pair(cddar(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CADDAR, lst, CDDAR_A_LIST));

  return(car(cdr(cdr(car(lst)))));
}


static s7_pointer g_cdaaar(s7_scheme *sc, s7_pointer args)
{
  #define H_cdaaar "(cdaaar lst) returns (cdr (car (car (car lst)))): (cdaaar '((((1 2 3))))) -> '(2 3)"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CDAAAR, args);
      return(simple_wrong_type_argument(sc, sc->CDAAAR, lst, T_PAIR));
    }
  if (!is_pair(car(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDAAAR, lst, CAR_A_LIST));
  if (!is_pair(caar(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDAAAR, lst, CAAR_A_LIST));
  if (!is_pair(caaar(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDAAAR, lst, CAAAR_A_LIST));

  return(cdr(car(car(car(lst)))));
}


static s7_pointer g_cdaadr(s7_scheme *sc, s7_pointer args)
{
  #define H_cdaadr "(cdaadr lst) returns (cdr (car (car (cdr lst)))): (cdaadr '(1 ((2 3 4)))) -> '(3 4)"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CDAADR, args);
      return(simple_wrong_type_argument(sc, sc->CDAADR, lst, T_PAIR));
    }
  if (!is_pair(cdr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDAADR, lst, CDR_A_LIST));
  if (!is_pair(cadr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDAADR, lst, CADR_A_LIST));
  if (!is_pair(caadr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDAADR, lst, CAADR_A_LIST));

  return(cdr(car(car(cdr(lst)))));
}


static s7_pointer g_cdadar(s7_scheme *sc, s7_pointer args)
{
  #define H_cdadar "(cdadar lst) returns (cdr (car (cdr (car lst)))): (cdadar '((1 (2 3 4)))) -> '(3 4)"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CDADAR, args);
      return(simple_wrong_type_argument(sc, sc->CDADAR, lst, T_PAIR));
    }
  if (!is_pair(car(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDADAR, lst, CAR_A_LIST));
  if (!is_pair(cdar(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDADAR, lst, CDAR_A_LIST));
  if (!is_pair(cadar(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDADAR, lst, CADAR_A_LIST));

  return(cdr(car(cdr(car(lst)))));
}


static s7_pointer g_cddaar(s7_scheme *sc, s7_pointer args)
{
  #define H_cddaar "(cddaar lst) returns (cdr (cdr (car (car lst)))): (cddaar '(((1 2 3 4)))) -> '(3 4)"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CDDAAR, args);
      return(simple_wrong_type_argument(sc, sc->CDDAAR, lst, T_PAIR));
    }
  if (!is_pair(car(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDDAAR, lst, CAR_A_LIST));
  if (!is_pair(caar(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDDAAR, lst, CAAR_A_LIST));
  if (!is_pair(cdaar(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDDAAR, lst, CDAAR_A_LIST));

  return(cdr(cdr(car(car(lst)))));
}


static s7_pointer g_cdaddr(s7_scheme *sc, s7_pointer args)
{
  #define H_cdaddr "(cdaddr lst) returns (cdr (car (cdr (cdr lst)))): (cdaddr '(1 2 (3 4 5))) -> '(4 5)"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CDADDR, args);
      return(simple_wrong_type_argument(sc, sc->CDADDR, lst, T_PAIR));
    }
  if (!is_pair(cdr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDADDR, lst, CDR_A_LIST));
  if (!is_pair(cddr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDADDR, lst, CDDR_A_LIST));
  if (!is_pair(caddr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDADDR, lst, CADDR_A_LIST));

  return(cdr(car(cdr(cdr(lst)))));
}


static s7_pointer g_cddddr(s7_scheme *sc, s7_pointer args)
{
  #define H_cddddr "(cddddr lst) returns (cdr (cdr (cdr (cdr lst)))): (cddddr '(1 2 3 4 5)) -> '(5)"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CDDDDR, args);
      return(simple_wrong_type_argument(sc, sc->CDDDDR, lst, T_PAIR));
    }
  if (!is_pair(cdr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDDDDR, lst, CDR_A_LIST));
  if (!is_pair(cddr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDDDDR, lst, CDDR_A_LIST));
  if (!is_pair(cdddr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDDDDR, lst, CDDDR_A_LIST));

  return(cdr(cdr(cdr(cdr(lst)))));
}


static s7_pointer g_cddadr(s7_scheme *sc, s7_pointer args)
{
  #define H_cddadr "(cddadr lst) returns (cdr (cdr (car (cdr lst)))): (cddadr '(1 (2 3 4 5))) -> '(4 5)"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CDDADR, args);
      return(simple_wrong_type_argument(sc, sc->CDDADR, lst, T_PAIR));
    }
  if (!is_pair(cdr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDDADR, lst, CDR_A_LIST));
  if (!is_pair(cadr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDDADR, lst, CADR_A_LIST));
  if (!is_pair(cdadr(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDDADR, lst, CDADR_A_LIST));

  return(cdr(cdr(car(cdr(lst)))));
}


static s7_pointer g_cdddar(s7_scheme *sc, s7_pointer args)
{
  #define H_cdddar "(cdddar lst) returns (cdr (cdr (cdr (car lst)))): (cdddar '((1 2 3 4 5))) -> '(4 5)"
  s7_pointer lst = car(args);

  if (!is_pair(lst)) 
    {
      check_method(sc, lst, sc->CDDDAR, args);
      return(simple_wrong_type_argument(sc, sc->CDDDAR, lst, T_PAIR));
    }
  if (!is_pair(car(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDDDAR, lst, CAR_A_LIST));
  if (!is_pair(cdar(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDDDAR, lst, CDAR_A_LIST));
  if (!is_pair(cddar(lst))) return(simple_wrong_type_argument_with_type(sc, sc->CDDDAR, lst, CDDAR_A_LIST));

  return(cdr(cdr(cdr(car(lst)))));
}


s7_pointer s7_assq(s7_scheme *sc, s7_pointer obj, s7_pointer x)
{
  s7_pointer y;
  y = x;
  while (true)
    {
      /* we can blithely take the car of anything, since we're not treating it as an object,
       *   then if we get a bogus match, the following check that caar made sense ought to catch it.
       *
       * if car(#<unspecified>) = #<unspecified> (initialization time), then cdr(nil)->unspec
       *   and subsequent caar(unspc)->unspec so we could forgo half the is_pair checks below.
       *   This breaks if "x" is a dotted list -- the last cdr is not nil, so we lose.
       */
      if ((obj == caar(x)) && (is_pair(car(x)))) return(car(x));
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);

      if ((obj == caar(x)) && (is_pair(car(x)))) return(car(x));
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);

      if ((obj == caar(x)) && (is_pair(car(x)))) return(car(x));
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);

      if ((obj == caar(x)) && (is_pair(car(x)))) return(car(x));
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);

      if ((obj == caar(x)) && (is_pair(car(x)))) return(car(x));
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);

      if ((obj == caar(x)) && (is_pair(car(x)))) return(car(x));
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);

      y = cdr(y);
      if (x == y) return(sc->F);
    }
  return(sc->F); /* not reached */
}


static s7_pointer g_assq(s7_scheme *sc, s7_pointer args)
{
  #define H_assq "(assq obj alist) returns the key-value pair associated (via eq?) with the key obj in the association list alist"
  /* this version accepts any kind of list 
   *   my little essay: the scheme standard should not unnecessarily restrict the kinds of arguments
   *                    a function can take (such as saying memq only accepts proper lists).  It is
   *                    trivial for the programmer to add such a check to a built-in function, but
   *                    not trivial to re-invent the built-in function with that restriction removed.
   *                    If some structure exists as a normal scheme object (a dotted or circular list),
   *                    every built-in function should be able to deal with it, if it makes sense at all.
   */

  s7_pointer x;

  x = cadr(args);
  if (!is_pair(x))
    {
      if (is_null(x)) return(sc->F);
      check_method(sc, x, sc->ASSQ, args);
      return(wrong_type_argument_with_type(sc, sc->ASSQ, small_int(2), x, AN_ASSOCIATION_LIST));
    }
  return(s7_assq(sc, car(args), x));
}


static s7_pointer g_assv(s7_scheme *sc, s7_pointer args)
{
  #define H_assv "(assv obj alist) returns the key-value pair associated (via eqv?) with the key obj in the association list alist"
  s7_pointer x, y, obj;

  x = cadr(args);
  if (!is_pair(x))
    {
      if (is_null(x)) return(sc->F);
      check_method(sc, x, sc->ASSV, args);
      return(wrong_type_argument_with_type(sc, sc->ASSV, small_int(2), x, AN_ASSOCIATION_LIST));
    }
  obj = car(args);
  if (is_simple(obj))
    return(s7_assq(sc, obj, x));

  y = x;
  while (true)
    {
      /* here we can't play the assq == game because s7_is_eqv thinks it's getting a legit s7 object */
      if ((is_pair(car(x))) && (s7_is_eqv(obj, caar(x)))) return(car(x));
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);

      if ((is_pair(car(x))) && (s7_is_eqv(obj, caar(x)))) return(car(x));
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);

      y = cdr(y);
      if (x == y) return(sc->F);
    }
  return(sc->F); /* not reached */
}


static s7_pointer g_assoc(s7_scheme *sc, s7_pointer args)
{
  #define H_assoc "(assoc obj alist (func #f)) returns the key-value pair associated (via equal?) with the key obj in the association list alist.\
If 'func' is a function of 2 arguments, it is used for the comparison instead of 'equal?"

  s7_pointer x, y, obj;

  x = cadr(args);      
  if (!is_pair(x))
    {
      if (is_null(x)) 
	return(sc->F);
      check_method(sc, x, sc->ASSOC, args);
      return(wrong_type_argument_with_type(sc, sc->ASSOC, small_int(2), x, AN_ASSOCIATION_LIST));
    }
  if (!is_pair(car(x)))
    return(wrong_type_argument_with_type(sc, sc->ASSOC, small_int(2), x, AN_ASSOCIATION_LIST)); /* we're assuming caar below so it better exist */

  if (is_not_null(cddr(args))) 
    {
      s7_pointer eq_func;
      /* check third arg before second (trailing arg error check) */

      eq_func = caddr(args);
      if (!is_procedure(eq_func))
	return(wrong_type_argument_with_type(sc, sc->ASSOC, small_int(3), eq_func, A_FUNCTION));

      if (!s7_is_aritable(sc, eq_func, 2))
	return(wrong_type_argument_with_type(sc, sc->ASSOC, small_int(3), eq_func, AN_EQ_FUNC));
      /* now maybe there's a simple case */
      if (s7_list_length(sc, x) > 0)
	{
	  if ((is_safe_procedure(eq_func)) &&
	      (is_c_function(eq_func)))
	    {
	      s7_function func;
	      
	      func = c_function_call(eq_func);
	      car(sc->T2_1) = car(args);
	      for (; is_pair(x); x = cdr(x))
		{
		  if (is_pair(car(x)))
		    {
		      car(sc->T2_2) = caar(x);
		      if (is_true(sc, (*func)(sc, sc->T2_1)))
			return(car(x));
		      /* I wonder if the assoc equality function should get the cons, not just caar?
		       */
		    }
		  else return(wrong_type_argument_with_type(sc, sc->ASSOC, small_int(2), cadr(args), AN_ASSOCIATION_LIST));
		}
	      return(sc->F);
	    }

	  if ((is_closure(eq_func)) &&
	      (is_pair(closure_args(eq_func))))
	    {
	      s7_pointer body;
	      body = closure_body(eq_func);
	      if ((is_optimized(car(body))) &&
		  (is_null(cdr(body))) &&
		  (is_all_x_safe(sc, car(body))))
		{
		  s7_function func;
		  s7_pointer b;

		  NEW_FRAME_WITH_TWO_SLOTS(sc, sc->envir, sc->envir, car(closure_args(eq_func)), car(args), cadr(closure_args(eq_func)), sc->F);
		  func = all_x_eval(sc, car(body));
		  b = next_slot(let_slots(sc->envir));

		  for (; is_pair(x); x = cdr(x))
		    {
		      slot_value(b) = caar(x);
		      if (is_true(sc, (*func)(sc, car(body))))
			return(car(x));
		    }
		  return(sc->F);
		}
	    }
	}

      /* sc->value = sc->F; */
      y = cons(sc, args, sc->NIL);
      set_ecdr(y, x);
      set_fcdr(y, x);
      push_stack(sc, OP_ASSOC_IF, y, eq_func);
      push_stack(sc, OP_APPLY, list_2(sc, car(args), caar(x)), eq_func);
      return(sc->UNSPECIFIED);
    }

  x = cadr(args);
  if (!is_pair(x))
    {
      if (is_null(x)) return(sc->F);
      check_method(sc, x, sc->ASSOC, args);
      return(wrong_type_argument_with_type(sc, sc->ASSOC, small_int(2), x, AN_ASSOCIATION_LIST));
    }
  obj = car(args);

  if (is_simple(obj))
    return(s7_assq(sc, obj, x));

  y = x;
  if (is_string(obj))
    {
      s7_pointer val;
      while (true)
	{
	  if (is_pair(car(x)))
	    {
	      val = caar(x);
	      if ((val == obj) ||
		  ((is_string(val)) &&
		   (scheme_strings_are_equal(obj, val))))
		return(car(x));
	    }
	  x = cdr(x);
	  if (!is_pair(x)) return(sc->F);

	  if (is_pair(car(x)))
	    {
	      val = caar(x);
	      if ((val == obj) ||
		  ((is_string(val)) &&
		   (scheme_strings_are_equal(obj, val))))
		return(car(x));
	    }
	  x = cdr(x);
	  if (!is_pair(x)) return(sc->F);
      
	  y = cdr(y);
	  if (x == y) return(sc->F);
	}
      return(sc->F);
    }

  while (true)
    {
      if ((is_pair(car(x))) && (s7_is_equal(sc, obj, caar(x)))) return(car(x));
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);
      
      if ((is_pair(car(x))) && (s7_is_equal(sc, obj, caar(x)))) return(car(x));
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);

      y = cdr(y);
      if (x == y) return(sc->F);
    }
  return(sc->F); /* not reached */
}


/* ---------------- member, memv, memq ---------------- */

s7_pointer s7_memq(s7_scheme *sc, s7_pointer obj, s7_pointer x)
{
  s7_pointer y;
  y = x;
  while (true)
    {
      if (obj == car(x)) return(x);
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);

      if (obj == car(x)) return(x);
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);

      if (obj == car(x)) return(x);
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);

      if (obj == car(x)) return(x);
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);

      y = cdr(y);
      if (x == y) return(sc->F);
    }
  return(sc->F);
}


static s7_pointer g_memq(s7_scheme *sc, s7_pointer args)
{
  #define H_memq "(memq obj list) looks for obj in list and returns the list from that point if it is found, otherwise #f. memq uses eq?"
  s7_pointer x;

  x = cadr(args);
  if (!is_pair(x))
    {
      if (is_null(x)) return(sc->F);
      check_method(sc, x, sc->MEMQ, args);
      return(wrong_type_argument_with_type(sc, sc->MEMQ, small_int(2), x, A_LIST));
    }
  return(s7_memq(sc, car(args), x));
}

/* I think (memq 'c '(a b . c)) should return #f because otherwise
 *   (memq () ...) would return the () at the end.
 */


/* if memq's list is a quoted list, it won't be changing, so we can tell ahead of time that it is
 *   a proper list, and what its length is.
 */
static s7_pointer memq_3, memq_4, memq_any;

static s7_pointer g_memq_3(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x, obj;
  x = cadr(args);
  obj = car(args);
  while (true)
    {
      if (obj == car(x)) return(x);
      x = cdr(x);
      if (obj == car(x)) return(x);
      x = cdr(x);
      if (obj == car(x)) return(x);
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);
    }
  return(sc->F);
}

static s7_pointer g_memq_4(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x, obj;
  x = cadr(args);
  obj = car(args);
  while (true)
    {
      if (obj == car(x)) return(x);
      x = cdr(x);
      if (obj == car(x)) return(x);
      x = cdr(x);
      if (obj == car(x)) return(x);
      x = cdr(x);
      if (obj == car(x)) return(x);
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);
    }
  return(sc->F);
}

static s7_pointer g_memq_any(s7_scheme *sc, s7_pointer args)
{
  /* no circular list check needed in this case */
  s7_pointer x, obj;
  x = cadr(args);
  obj = car(args);
  while (true)
    {
      if (obj == car(x)) return(x);
      x = cdr(x);
      if (!is_pair(x)) return(sc->F); /* every other pair check could be omitted */

      if (obj == car(x)) return(x);
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);

      if (obj == car(x)) return(x);
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);

      if (obj == car(x)) return(x);
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);
    }
  return(sc->F);
}


static s7_pointer memq_car;
static s7_pointer g_memq_car(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x, obj;

  obj = find_symbol_checked(sc, cadar(args));
  if (!is_pair(obj))
    {
      s7_pointer func;
      if ((has_methods(obj)) &&
	  ((func = find_method(sc, find_let(sc, obj), sc->CAR)) != sc->UNDEFINED))
	obj = s7_apply_function(sc, func, list_1(sc, obj));
      if (!is_pair(obj))
	return(simple_wrong_type_argument(sc, sc->CAR, obj, T_PAIR));
    }
  obj = car(obj);
  x = cadr(cadr(args));

  while (true)
    {
      if (obj == car(x)) return(x);
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);

      if (obj == car(x)) return(x);
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);
    }
  return(sc->F);
}

static s7_pointer memq_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if ((is_pair(caddr(expr))) &&
      (car(caddr(expr)) == sc->QUOTE) &&
      (is_pair(cadr(caddr(expr)))))
    {
      int len;

      if ((is_optimized(cadr(expr))) &&
	  (optimize_data(cadr(expr)) == HOP_SAFE_C_S) &&
	  (c_call(cadr(expr)) == g_car))
	{
	  set_optimize_data(expr, HOP_SAFE_C_C);
	  return(memq_car);
	}

      len = s7_list_length(sc, cadr(caddr(expr)));
      if (len > 0)
	{
	  if ((len % 4) == 0)
	    return(memq_4);
	  if ((len % 3) == 0)
	    return(memq_3);
	  return(memq_any);
	}
    }
  return(f);
}


static s7_pointer memv_number(s7_scheme *sc, s7_pointer obj, s7_pointer x)
{
  s7_pointer y;
  y = x;
  while (true)
    {
      if ((s7_is_number(car(x))) && (numbers_are_eqv(obj, car(x)))) return(x);
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);
      if ((s7_is_number(car(x))) && (numbers_are_eqv(obj, car(x)))) return(x);
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);
      if ((s7_is_number(car(x))) && (numbers_are_eqv(obj, car(x)))) return(x);
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);
      if ((s7_is_number(car(x))) && (numbers_are_eqv(obj, car(x)))) return(x);
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);
      y = cdr(y);
      if (x == y) return(sc->F);
    }
  return(sc->F);
}


static s7_pointer g_memv(s7_scheme *sc, s7_pointer args)
{
  #define H_memv "(memv obj list) looks for obj in list and returns the list from that point if it is found, otherwise #f. memv uses eqv?"
  s7_pointer x, y, obj;

  x = cadr(args);
  if (!is_pair(x))
    {
      if (is_null(x)) return(sc->F);
      check_method(sc, x, sc->MEMV, args);
      return(wrong_type_argument_with_type(sc, sc->MEMV, small_int(2), x, A_LIST));
    }
  obj = car(args);

  if (is_simple(obj))
    return(s7_memq(sc, obj, x));

  if (s7_is_number(obj))
    return(memv_number(sc, obj, x));

  y = x;
  while (true)
    {
      if (s7_is_eqv(obj, car(x))) return(x);
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);
      
      if (s7_is_eqv(obj, car(x))) return(x);
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);
      
      y = cdr(y);
      if (x == y) return(sc->F);
    }
  return(sc->F); /* not reached */
}


static s7_pointer member(s7_scheme *sc, s7_pointer obj, s7_pointer x)
{
  s7_pointer y;

  y = x;
  if (is_string(obj))
    {
      while (true)
	{
	  if ((obj == car(x)) ||
	      ((is_string(car(x))) &&
	       (scheme_strings_are_equal(obj, car(x)))))
	    return(x);
	  x = cdr(x);
	  if (!is_pair(x)) return(sc->F);

	  if ((obj == car(x)) ||
	      ((is_string(car(x))) &&
	       (scheme_strings_are_equal(obj, car(x)))))
	    return(x);
	  x = cdr(x);
	  if (!is_pair(x)) return(sc->F);
      
	  y = cdr(y);
	  if (x == y) return(sc->F);
	}
      return(sc->F);
    }

  while (true)
    {
      if (s7_is_equal(sc, obj, car(x))) return(x);
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);
      
      if (s7_is_equal(sc, obj, car(x))) return(x);
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);
      
      if (s7_is_equal(sc, obj, car(x))) return(x);
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);
      
      if (s7_is_equal(sc, obj, car(x))) return(x);
      x = cdr(x);
      if (!is_pair(x)) return(sc->F);
      
      y = cdr(y);
      if (x == y) return(sc->F);
    }
  return(sc->F); /* not reached */
}


static s7_pointer g_member(s7_scheme *sc, s7_pointer args)
{
  #define H_member "(member obj list (func #f)) looks for obj in list and returns the list from that point if it is found, otherwise #f. \
member uses equal?  If 'func' is a function of 2 arguments, it is used for the comparison instead of 'equal?"

  /* this could be extended to accept sequences:
   *    (member #\a "123123abnfc" char=?) -> "abnfc"
   *    (member "abc" "123abc321" string=?) -> "abc321" but there's the string length complication
   *    (member 1 #(0 1 2) =) ? -- for c_objects (ffi) we would need a substring equivalent
   *      and what would it do for a hash-table? -- maybe return whether obj exists as a key, or an environment?
   * also the 3 arg version can implement position, find, etc
   *
   * the third arg can be weird: (member #f (list #t) cons) -> (#t)
   * should this be an error: (member '(1 2 3) () '(1 . 2)) -- the third arg is bogus, but the second is nil
   */

  s7_pointer x, y, obj;

  x = cadr(args);

  if (!is_pair(x))
    {
      if (is_null(x)) return(sc->F);
      check_method(sc, x, sc->MEMBER, args);
      return(wrong_type_argument_with_type(sc, sc->MEMBER, small_int(2), x, A_LIST));
    }

  if (is_not_null(cddr(args))) 
    {
      s7_pointer eq_func;
      /* check third arg before second (trailing arg error check) */

      eq_func = caddr(args);
      if ((!is_procedure(eq_func)) ||
	  (!s7_is_aritable(sc, eq_func, 2)))
	{
	  check_method(sc, eq_func, sc->MEMBER, args);
	  return(wrong_type_argument_with_type(sc, sc->MEMBER, small_int(3), eq_func, AN_EQ_FUNC));
	}

      /* now maybe there's a simple case */
      if (s7_list_length(sc, x) > 0)
	{
	  if ((is_safe_procedure(eq_func)) &&
	      (is_c_function(eq_func)))
	    {
	      s7_function func;

	      func = c_function_call(eq_func);
	      car(sc->T2_1) = car(args);

	      for (; is_pair(x); x = cdr(x))
		{
		  car(sc->T2_2) = car(x);
		  if (is_true(sc, (*func)(sc, sc->T2_1)))
		    return(x);
		}
	      return(sc->F);
	    }

	  if ((is_closure(eq_func)) &&
	      (is_pair(closure_args(eq_func))))
	    {
	      s7_pointer body;
	      body = closure_body(eq_func);
	      if ((is_optimized(car(body))) &&
		  (is_null(cdr(body))) &&
		  (is_all_x_safe(sc, car(body))))
		{
		  s7_function func;
		  s7_pointer b;

		  NEW_FRAME_WITH_TWO_SLOTS(sc, sc->envir, sc->envir, car(closure_args(eq_func)), car(args), cadr(closure_args(eq_func)), sc->F);
		  func = all_x_eval(sc, car(body));
		  b = next_slot(let_slots(sc->envir));

		  for (; is_pair(x); x = cdr(x))
		    {
		      slot_value(b) = car(x);
		      if (is_true(sc, (*func)(sc, car(body))))
			return(x);
		    }
		  return(sc->F);
		}
	    }
	}

      y = cons(sc, args, sc->NIL); /* this could probably be handled with a counter cell (cdr here is unused) */
      set_ecdr(y, x);
      set_fcdr(y, x);
      push_stack(sc, OP_MEMBER_IF, y, eq_func);
      car(sc->T2_1) = car(args);
      car(sc->T2_2) = car(x);
      push_stack(sc, OP_APPLY, sc->T2_1, eq_func); 
      return(sc->UNSPECIFIED);
    }

  obj = car(args);

  if (is_simple(obj))
    return(s7_memq(sc, obj, x));

  /* the only things that aren't simply == here are c_object, string, number, vector, hash-table, pair, and c_pointer
   *   but all the other cases are unlikely.
   */

  if (s7_is_number(obj))
    return(memv_number(sc, obj, x));

  return(member(sc, obj, x));
}


static s7_pointer member_sq;
static s7_pointer g_member_sq(s7_scheme *sc, s7_pointer args)
{
  s7_pointer obj, lst;
  lst = cadr(cadr(args));
  obj = find_symbol_checked(sc, car(args));

  if (is_simple(obj))
    return(s7_memq(sc, obj, lst));

  if (s7_is_number(obj))
    return(memv_number(sc, obj, lst));

  return(member(sc, obj, lst));
}

static s7_pointer member_sym_s;
static s7_pointer g_member_sym_s(s7_scheme *sc, s7_pointer args)
{
  /* first arg is quoted symbol: (member 'a lst) */
  s7_pointer lst;

  lst = find_symbol_checked(sc, cadr(args));
  if (!is_pair(lst))
    {
      if (is_null(lst)) return(sc->F);
      check_method(sc, lst, sc->MEMBER, list_2(sc, cadar(args), lst));
      return(wrong_type_argument_with_type(sc, sc->MEMBER, small_int(2), lst, A_LIST));
    }
  return(s7_memq(sc, cadar(args), lst)); /* car(cadr(args)) removes the quote from (quote sym) */
}

static s7_pointer member_num_s;
static s7_pointer g_member_num_s(s7_scheme *sc, s7_pointer args)
{
  s7_pointer lst;

  lst = find_symbol_checked(sc, cadr(args));
  if (!is_pair(lst))
    {
      if (is_null(lst)) return(sc->F);
      check_method(sc, lst, sc->MEMBER, list_2(sc, car(args), lst));
      return(wrong_type_argument_with_type(sc, sc->MEMBER, small_int(2), lst, A_LIST));
    }
  return(memv_number(sc, car(args), lst));
}

static s7_pointer member_ss;
static s7_pointer g_member_ss(s7_scheme *sc, s7_pointer args)
{
  s7_pointer obj, x;

  obj = find_symbol_checked(sc, car(args));
  x = find_symbol_checked(sc, cadr(args));
  if (!is_pair(x))
    {
      if (is_null(x)) return(sc->F);
      check_method(sc, x, sc->MEMBER, list_2(sc, obj, x));
      return(wrong_type_argument_with_type(sc, sc->MEMBER, small_int(2), x, A_LIST));
    }

  if (is_simple(obj))
    return(s7_memq(sc, obj, x));

  if (s7_is_number(obj))
    return(memv_number(sc, obj, x));

  return(member(sc, obj, x));
}

static s7_pointer member_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if (args == 2)
    {
      if (is_symbol(caddr(expr)))
	{
	  if ((is_pair(cadr(expr))) &&
	      (car(cadr(expr)) == sc->QUOTE) &&
	      (is_symbol(cadr(cadr(expr)))))
	    {
	      /* (member 'a odd) */
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(member_sym_s);
	    }

	  if (s7_is_number(cadr(expr)))
	    {
	      /* (member 4 lst) */
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(member_num_s);
	    }

	  if (is_symbol(cadr(expr)))
	    {
	      /* (member obj lst) */
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(member_ss);
	    }
	}
      else
	{
	  if ((is_symbol(cadr(expr))) &&
	      (is_pair(caddr(expr))) &&
	      (car(caddr(expr)) == sc->QUOTE) &&
	      (is_pair(cadr(caddr(expr)))))
	    {
	      /* (member q '(quote lambda case)) */
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(member_sq);
	    }
	}
    }
  return(f);
}


static bool is_memq(s7_pointer sym, s7_pointer lst)
{
  s7_pointer x;
  for (x = lst; is_pair(x); x = cdr(x))
    if (sym == car(x))
      return(true);
  return(false);
}


static s7_pointer g_is_provided(s7_scheme *sc, s7_pointer args)
{
  #define H_is_provided "(provided? symbol) returns #t if symbol is a member of the *features* list"
  s7_pointer sym, topf, x;
  sym = car(args);
  if (!is_symbol(sym))
    {
      check_method(sc, sym, sc->IS_PROVIDED, args);
      return(simple_wrong_type_argument(sc, sc->IS_PROVIDED, sym, T_SYMBOL));
    }
  /* here the *features* list is spread out (or can be anyway) along the curlet chain,
   *   so we need to travel back all the way to the top level checking each *features* list in turn.
   *   Since *features* grows via cons (newest first), we can stop the scan if we hit the shared
   *   top-level at least.
   */
  topf = slot_value(global_slot(sc->S7_FEATURES));
  if (is_memq(sym, topf))
    return(sc->T);

  if (is_global(sc->S7_FEATURES))
    return(sc->F);
  for (x = sc->envir; symbol_id(sc->S7_FEATURES) < let_id(x); x = outlet(x));
  for (; is_let(x); x = outlet(x))
    {
      s7_pointer y; 
      for (y = let_slots(x); is_slot(y); y = next_slot(y))	
	if (slot_symbol(y) == sc->S7_FEATURES)
	  {
	    if ((slot_value(y) != topf) &&
		(is_memq(sym, slot_value(y))))
	      return(sc->T);
	  }
    }
  return(sc->F);
}


static s7_pointer g_provide(s7_scheme *sc, s7_pointer args)
{
  #define H_provide "(provide symbol) adds symbol to the *features* list"
  /* this has to be relative to the curlet: (load file env)
   *   the things loaded are only present in env, and go away with it, so should not be in the global *features* list
   */
  s7_pointer p, sym, lst;
  sym = car(args);
  if (!is_symbol(sym))
    {
      check_method(sc, sym, sc->PROVIDE, args);
      return(simple_wrong_type_argument(sc, sc->PROVIDE, sym, T_SYMBOL));
    }

  p = find_local_symbol(sc, sc->S7_FEATURES, sc->envir); /* if sc->envir is nil, this returns the global slot, else local slot */
  lst = slot_value(find_symbol(sc, sc->S7_FEATURES));    /* in either case, we want the current *feartures* list */

  if (p == sc->UNDEFINED)
    add_slot(sc, sc->S7_FEATURES, cons(sc, sym, lst));   /* this assumes sc->envir */
  else
    {
      if (!is_memq(sym, lst))
	slot_set_value(p, cons(sc, sym, lst));
    }

  if (!is_slot(find_symbol(sc, sym))) /* *features* name might be the same as an existing function */
    s7_define(sc, sc->envir, sym, sym);
  return(sym);
}


void s7_provide(s7_scheme *sc, const char *feature)
{
  g_provide(sc, cons(sc, s7_make_symbol(sc, feature), sc->NIL));
}


bool s7_is_provided(s7_scheme *sc, const char *feature)
{
  return(is_memq(s7_make_symbol(sc, feature), s7_symbol_value(sc, sc->S7_FEATURES))); /* this goes from local outward */
}


static s7_pointer g_features_set(s7_scheme *sc, s7_pointer args)
{
  /* symbol_access for set/let of *features* which can only be changed via provide */
  if (s7_is_list(sc, cadr(args)))
    return(cadr(args));
  return(sc->ERROR);
}


static s7_pointer g_list(s7_scheme *sc, s7_pointer args)
{
  #define H_list "(list ...) returns its arguments in a list"

  return(copy_list(sc, args));
}


static s7_pointer list_0, list_1, list_2;
static s7_pointer g_list_0(s7_scheme *sc, s7_pointer args)
{
  return(sc->NIL);
}

static s7_pointer g_list_1(s7_scheme *sc, s7_pointer args)
{
  return(cons(sc, car(args), sc->NIL));
}

static s7_pointer g_list_2(s7_scheme *sc, s7_pointer args)
{
  return(cons_unchecked(sc, car(args), cons(sc, cadr(args), sc->NIL)));
}

static s7_pointer list_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  switch (args)
    {
    case 0: return(list_0);
    case 1: return(list_1);
    case 2: return(list_2);
    }
  return(f);
}


s7_pointer s7_list(s7_scheme *sc, int num_values, ...)
{
  int i;
  va_list ap;
  s7_pointer p;
  
  if (num_values == 0)
    return(sc->NIL);

  sc->w = sc->NIL;
  va_start(ap, num_values);
#if DEBUGGING
  for (i = 0; i < num_values; i++)
    {
      p = (s7_pointer)va_arg(ap, s7_pointer);
      if (!s7_is_valid(sc, p))
	fprintf(stderr, "s7_list at %d, %p is not a valid s7_pointer!\n", i, p);
      sc->w = cons(sc, p, sc->w);
    }
#else
  for (i = 0; i < num_values; i++)
    sc->w = cons(sc, va_arg(ap, s7_pointer), sc->w);
#endif
  va_end(ap);

  p = sc->w;
  sc->w = sc->NIL;

  return(safe_reverse_in_place(sc, p));
}


static s7_pointer g_append(s7_scheme *sc, s7_pointer args)
{
  #define H_append "(append ...) returns its argument lists appended into one list"
  /* but weirdly (append () 1) returns 1 
   */
  s7_pointer x, y;

  if (is_null(args)) 
    return(sc->NIL);

  if (is_null(cdr(args)))
    return(car(args));   /* no check_method here because we aren't checking the type? */

  x = sc->NIL;
  for (y = args; is_not_null(y); y = cdr(y)) 
    {
      /* the original version used s7_append but that copies the arguments too many times if there are 3 or more lists */
      s7_pointer p;
      p = car(y);

      check_method(sc, p, sc->APPEND, (x != sc->NIL) ? cons(sc, reverse_in_place_unchecked(sc, sc->NIL, x), y) : args);

      if (is_null(cdr(y)))
	return(reverse_in_place_unchecked(sc, p, x)); /* i.e. tack car(y) onto end of x copied and reversed */

      if (!is_proper_list(sc, p))
	return(wrong_type_argument_n_with_type(sc, sc->APPEND, position_of(y, args), p, A_PROPER_LIST));
      /* is this error correct?
       *   (append '(3) '(1 . 2))
       *   '(3 1 . 2) ; (old) guile also returns this
       * but
       *   (append '(1 . 2) '(3))
       *   gives this error
       */

      while (is_not_null(p))
	{
	  x = cons(sc, car(p), x);
	  p = cdr(p);

	  if (is_null(p)) break;
	  x = cons_unchecked(sc, car(p), x);
	  p = cdr(p);
	}
    }
  return(x);
}


static s7_pointer append_in_place(s7_scheme *sc, s7_pointer a, s7_pointer b)
{
  /* tack b onto the end of a without copying either -- 'a' is changed! */
  s7_pointer p;
  if (is_null(a))
    return(b);
  p = a;
  while (is_not_null(cdr(p))) p = cdr(p);
  cdr(p) = b;
  return(a);
}



/* -------------------------------- vectors -------------------------------- */

bool s7_is_vector(s7_pointer p)    
{ 
  return(t_vector_p[type(p)]);
}


bool s7_is_sundry_vector(s7_pointer p)    
{ 
  return(type(p) == T_VECTOR);
}


bool s7_is_float_vector(s7_pointer p)    
{ 
  return(type(p) == T_FLOAT_VECTOR);
}


bool s7_is_int_vector(s7_pointer p)    
{ 
  return(type(p) == T_INT_VECTOR);
}


static s7_pointer default_vector_setter(s7_scheme *sc, s7_pointer vec, s7_Int loc, s7_pointer val)
{
  vector_element(vec, loc) = val;
  return(val);
}

static s7_pointer default_vector_getter(s7_scheme *sc, s7_pointer vec, s7_Int loc) 
{
  return(vector_element(vec, loc));
}

static s7_pointer int_vector_setter(s7_scheme *sc, s7_pointer vec, s7_Int loc, s7_pointer val)
{
  int_vector_element(vec, loc) = s7_number_to_integer_with_caller(sc, val, "vector-set!");
  return(val);
}

static s7_pointer int_vector_getter(s7_scheme *sc, s7_pointer vec, s7_Int loc) 
{
  return(s7_make_integer(sc, int_vector_element(vec, loc)));
}

static s7_pointer float_vector_setter(s7_scheme *sc, s7_pointer vec, s7_Int loc, s7_pointer val)
{
  float_vector_element(vec, loc) = number_to_double(sc, val, "float-vector-set!");
  return(val);
}

static s7_pointer float_vector_getter(s7_scheme *sc, s7_pointer vec, s7_Int loc) 
{
  return(make_real(sc, float_vector_element(vec, loc)));
}


#if (SIZEOF_VOID_P == 4)
  #define MAX_VECTOR_LENGTH (1LL << 30)
#else
  #define MAX_VECTOR_LENGTH (1LL << 40)
#endif

#define FILLED true
#define NOT_FILLED false

static s7_pointer make_vector_1(s7_scheme *sc, s7_Int len, bool filled, unsigned int typ) 
{
  s7_pointer x;
  if (len < 0)
    return(wrong_type_argument_with_type(sc, sc->MAKE_VECTOR, small_int(1), make_integer(sc, len), A_NON_NEGATIVE_INTEGER));
  if (len > MAX_VECTOR_LENGTH)
    return(out_of_range(sc, sc->MAKE_VECTOR, small_int(1), make_integer(sc, len), ITS_TOO_LARGE));

  /* if (len == 0) typ = T_VECTOR; *//* all empty vectors are the same type */
  /* this produces odd results: (float-vector? (float-vector)) -> #f etc 
   *   but keeping the type means equal? has to be smarter about empty vectors.
   */

  /* this has to follow the error checks!  (else garbage in free_heap temps portion confuses GC when "vector" is finalized) */
  NEW_CELL(sc, x);                     
  vector_length(x) = 0;
  vector_elements(x) = NULL;
  set_type(x, typ | T_SAFE_PROCEDURE); /* (v 0) as vector-ref is safe */

  if (len > 0)
    {
      vector_length(x) = len;
      if (typ == T_VECTOR)
	{
	  vector_elements(x) = (s7_pointer *)malloc(len * sizeof(s7_pointer));
	  vector_getter(x) = default_vector_getter;
	  vector_setter(x) = default_vector_setter;
	  /* make_hash_table assumes nil as the default value */
	  if (filled) s7_vector_fill(sc, x, sc->NIL);
	}
      else
	{
	  if (typ == T_INT_VECTOR)
	    {
	      if (filled)
		int_vector_elements(x) = (s7_Int *)calloc(len, sizeof(s7_Int));
	      else int_vector_elements(x) = (s7_Int *)malloc(len * sizeof(s7_Int));
	      vector_getter(x) = int_vector_getter;
	      vector_setter(x) = int_vector_setter;
	    }
	  else 
	    {
	      if (filled)
		float_vector_elements(x) = (s7_Double *)calloc(len, sizeof(s7_Double));
	      else float_vector_elements(x) = (s7_Double *)malloc(len * sizeof(s7_Double));
	      vector_getter(x) = float_vector_getter;
	      vector_setter(x) = float_vector_setter;
	    }
	}
      if (!(vector_elements(x)))
	return(s7_error(sc, make_symbol(sc, "out-of-memory"), 
			list_1(sc, make_string_wrapper(sc, "make-vector allocation failed!"))));
    }

  vector_dimension_info(x) = NULL;
  add_vector(sc, x);
  return(x);
}


s7_pointer s7_make_vector(s7_scheme *sc, s7_Int len)
{
  return(make_vector_1(sc, len, FILLED, T_VECTOR));
}

static vdims_t *make_wrap_only(s7_scheme *sc)
{
  vdims_t *v;
  v = (vdims_t *)malloc(sizeof(vdims_t));
  v->original = sc->F;
  v->elements_allocated = false;
  v->ndims = 1;
  v->dimensions_allocated = false;
  v->dims = NULL;
  v->offsets = NULL;
  return(v);
}

#define make_vdims(Sc, Alloc, Dims, Info) ((((Dims) == 1) && (!(Alloc))) ? sc->wrap_only : make_vdims_1(Sc, Alloc, Dims, Info))

static vdims_t *make_vdims_1(s7_scheme *sc, bool elements_allocated, int dims, s7_Int *dim_info)
{
  vdims_t *v;
  
  v = (vdims_t *)malloc(sizeof(vdims_t));
  v->original = sc->F;
  v->elements_allocated = elements_allocated;
  v->ndims = dims;
  if (dims > 1)
    {
      int i;
      s7_Int offset = 1;
      v->dimensions_allocated = true;
      v->dims = (s7_Int *)malloc(v->ndims * sizeof(s7_Int));
      v->offsets = (s7_Int *)malloc(v->ndims * sizeof(s7_Int));
      
      for (i = 0; i < dims; i++)
	v->dims[i] = dim_info[i];
      for (i = v->ndims - 1; i >= 0; i--)
	{
	  v->offsets[i] = offset;
	  offset *= v->dims[i];
	}
    }
  else 
    {
      v->dimensions_allocated = false;
      v->dims = NULL;
      v->offsets = NULL;
    }
  return(v);
}


s7_pointer s7_make_int_vector(s7_scheme *sc, s7_Int len, int dims, s7_Int *dim_info)
{
  s7_pointer p;
  p = make_vector_1(sc, len, FILLED, T_INT_VECTOR);
  if (dim_info)
    vector_dimension_info(p) = make_vdims(sc, true, dims, dim_info);
  return(p);
}


s7_pointer s7_make_float_vector(s7_scheme *sc, s7_Int len, int dims, s7_Int *dim_info)
{
  s7_pointer p;
  p = make_vector_1(sc, len, FILLED, T_FLOAT_VECTOR);
  if (dim_info)
    vector_dimension_info(p) = make_vdims(sc, true, dims, dim_info);
  return(p);
}


s7_pointer s7_make_float_vector_wrapper(s7_scheme *sc, s7_Int len, s7_Double *data, int dims, s7_Int *dim_info, bool free_data)
{
  /* this wraps up a C-allocated/freed double array as an s7 vector.
   */
  s7_pointer x;

  NEW_CELL(sc, x);                     
  set_type(x, T_FLOAT_VECTOR | T_SAFE_PROCEDURE);
  float_vector_elements(x) = data;
  vector_getter(x) = float_vector_getter;
  vector_setter(x) = float_vector_setter;
  vector_length(x) = len;
  if (dim_info)
    vector_dimension_info(x) = make_vdims(sc, free_data, dims, dim_info);
  else vector_dimension_info(x) = NULL;
  add_vector(sc, x);

  return(x);
}


s7_Int s7_vector_length(s7_pointer vec)
{
  return(vector_length(vec));
}


s7_Int s7_vector_print_length(s7_scheme *sc)
{
  return(s7_integer(slot_value(sc->vector_print_length)));
}


s7_Int s7_set_vector_print_length(s7_scheme *sc, s7_Int new_len)
{
  s7_Int old_len;
  old_len = s7_integer(slot_value(sc->vector_print_length));
  slot_set_value(sc->vector_print_length, make_integer(sc, new_len));
  return(old_len);
}


static s7_pointer g_vector_print_length_set(s7_scheme *sc, s7_pointer args)
{
  if (s7_is_integer(cadr(args)))
    {
      s7_Int len;
      len = s7_integer(cadr(args));
      if (len >= 0)
	return(cadr(args));
    }
  return(sc->ERROR);
}


#if (!WITH_GMP)
void s7_vector_fill(s7_scheme *sc, s7_pointer vec, s7_pointer obj)
#else
static void vector_fill(s7_scheme *sc, s7_pointer vec, s7_pointer obj)
#endif
{
  s7_Int len, i, left;

  len = vector_length(vec);
  if (len == 0) return;
  left = len - 8;
  i = 0;

  switch (type(vec))
    {
    case T_FLOAT_VECTOR:
      if (!s7_is_real(obj))
	s7_wrong_type_arg_error(sc, "(float) vector-fill!", 2, obj, "a real");
      else
	{
	  s7_Double x;
	  x = number_to_double(sc, obj, "vector-fill!");
	  if (x == 0.0)
	    memset((void *)float_vector_elements(vec), 0, len * sizeof(s7_Double));
	  else
	    {
	      s7_Double *orig;
	      orig = float_vector_elements(vec);
	      while (i <= left)
		{
		  orig[i++] = x;
		  orig[i++] = x;
		  orig[i++] = x;
		  orig[i++] = x;
		  orig[i++] = x;
		  orig[i++] = x;
		  orig[i++] = x;
		  orig[i++] = x;
		}
	      for (; i < len; i++)
		orig[i] = x;
	    }
	}
      break;

    case T_INT_VECTOR:
      if (!s7_is_integer(obj)) /* possibly a bignum */
	s7_wrong_type_arg_error(sc, "(int) vector-fill!", 2, obj, "an integer");
      else
	{
	  s7_Int k;
	  k = s7_integer(obj);
	  if (k == 0)
	    memset((void *)int_vector_elements(vec), 0, len * sizeof(s7_Int));
	  else
	    {
	      s7_Int* orig;
	      orig = int_vector_elements(vec);
	      while (i <= left)
		{
		  orig[i++] = k;
		  orig[i++] = k;
		  orig[i++] = k;
		  orig[i++] = k;
		  orig[i++] = k;
		  orig[i++] = k;
		  orig[i++] = k;
		  orig[i++] = k;
		}
	      for (; i < len; i++)
		orig[i] = k;
	    }
	}
      break;

    default:
      {
	s7_pointer *orig;
	orig = vector_elements(vec);
	while (i <= left)
	  {
	    orig[i++] = obj;
	    orig[i++] = obj;
	    orig[i++] = obj;
	    orig[i++] = obj;
	    orig[i++] = obj;
	    orig[i++] = obj;
	    orig[i++] = obj;
	    orig[i++] = obj;
	  }
	for (; i < len; i++)
	  orig[i] = obj;
      }
    }
}


static s7_pointer g_vector_fill(s7_scheme *sc, s7_pointer args)
{
  #define H_vector_fill "(vector-fill! v val start end) sets all elements of the vector v between start and end to val"
  s7_pointer x, fill;
  s7_Int start = 0, end;

  x = car(args);
  if (!s7_is_vector(x))
    {
      check_method(sc, x, sc->VECTOR_FILL, args); 
      /* not two_methods (and fill!) here else we get stuff like:
       *   (let ((e (openlet (inlet 'fill! (lambda (obj val) (string-fill! (obj 'value) val)) 'value "01234")))) (vector-fill! e #\a) (e 'value)) -> "aaaaa"
       */
      return(wrong_type_argument(sc, sc->VECTOR_FILL, small_int(1), x, T_VECTOR));
    }

  fill = cadr(args);
  if (is_float_vector(x))
    {
      if (!s7_is_real(fill)) /* possibly a bignum */
	{
	  check_two_methods(sc, fill, sc->VECTOR_FILL, sc->FILL, args);
	  s7_wrong_type_arg_error(sc, "(float) vector-fill!", 2, fill, "a real");
	}
    }
  else
    {
      if (is_int_vector(x))
	{
	  if (!s7_is_integer(fill))
	    {
	      check_two_methods(sc, fill, sc->VECTOR_FILL, sc->FILL, args);
	      s7_wrong_type_arg_error(sc, "(int) vector-fill!", 2, fill, "an integer");
	    }
	}
    }

  end = vector_length(x);
  if (end == 0) return(fill);
  if (!is_null(cddr(args)))
    {
      s7_pointer p;
      p = start_and_end(sc, sc->VECTOR_FILL, sc->FILL, cddr(args), args, 3, &start, &end);
      if (p != sc->GC_NIL) return(p);
      if (start == end) return(fill);
    }

  if ((start == 0) && (end == vector_length(x)))
    s7_vector_fill(sc, x, fill);
  else
    {
      s7_Int i;
      if (type(x) == T_VECTOR)
	{
	  for (i = start; i < end; i++)
	    vector_element(x, i) = fill;
	}
      else
	{
	  if (is_int_vector(x))
	    {
	      s7_Int k;
	      k = s7_integer(fill);
	      if (k == 0)
		memset((void *)(int_vector_elements(x) + start), 0, (end - start) * sizeof(s7_Int));
	      else
		{
		  for (i = start; i < end; i++)
		    int_vector_element(x, i) = k;
		}
	    }
	  else
	    {
	      if (is_float_vector(x))
		{
		  s7_Double y;
		  y = number_to_double(sc, fill, "vector-fill!");
		  if (y == 0.0)
		    memset((void *)(float_vector_elements(x) + start), 0, (end - start) * sizeof(s7_Double));
		  else
		    {
		      s7_Double *orig;
		      s7_Int left;
		      orig = float_vector_elements(x);
		      left = end - 8;
		      i = start;
		      while (i <= left)
			{
			  orig[i++] = y;
			  orig[i++] = y;
			  orig[i++] = y;
			  orig[i++] = y;
			  orig[i++] = y;
			  orig[i++] = y;
			  orig[i++] = y;
			  orig[i++] = y;
			}
		      for (; i < end; i++)
			orig[i] = y;
		    }
		}
	    }
	}
    }
  return(fill);
}


s7_pointer s7_vector_ref(s7_scheme *sc, s7_pointer vec, s7_Int index) 
{
  if (index >= vector_length(vec))
    return(out_of_range(sc, sc->VECTOR_REF, small_int(2), make_integer(sc, index), ITS_TOO_LARGE));

  return(vector_getter(vec)(sc, vec, index));
}


s7_pointer s7_vector_set(s7_scheme *sc, s7_pointer vec, s7_Int index, s7_pointer a) 
{
  if (index >= vector_length(vec))
    return(out_of_range(sc, sc->VECTOR_SET, small_int(2), make_integer(sc, index), ITS_TOO_LARGE));

  vector_setter(vec)(sc, vec, index, a);
  return(a);
}


s7_pointer *s7_vector_elements(s7_pointer vec)
{
  return(vector_elements(vec));
}


s7_Int *s7_int_vector_elements(s7_pointer vec)
{
  return(int_vector_elements(vec));
}


s7_Double *s7_float_vector_elements(s7_pointer vec)
{
  return(float_vector_elements(vec));
}


s7_Int *s7_vector_dimensions(s7_pointer vec)
{
  s7_Int *dims;
  if (vector_dimension_info(vec))
    return(vector_dimensions(vec));
  dims = (s7_Int *)malloc(sizeof(s7_Int));
  dims[0] = vector_length(vec);
  return(dims);
}


s7_Int *s7_vector_offsets(s7_pointer vec)
{
  s7_Int *offs;
  if (vector_dimension_info(vec))
    return(vector_offsets(vec));
  offs = (s7_Int *)malloc(sizeof(s7_Int));
  offs[0] = 1;
  return(offs);
}


static s7_pointer g_vector_append(s7_scheme *sc, s7_pointer args)
{
  /* returns a one-dimensional vector.  To handle multidimensional vectors, we'd need to
   *   ensure all the dimensional data matches (rank, size of each dimension except the last etc),
   *   which is too much trouble.
   */
  #define H_vector_append "(vector-append . vectors) returns a new (1-dimensional) vector containing the elements of its vector arguments."
  s7_Int len = 0;
  s7_pointer p, v, x;
  int result_type = -1;
  bool parlous_gc = false;

  if (is_null(args))
    return(make_vector_1(sc, 0, NOT_FILLED, T_VECTOR));

  for (p = args; is_pair(p); p = cdr(p))
    {
      x = car(p);
      if (!s7_is_vector(x))
	{
	  if (has_methods(x))
	    {
	      s7_pointer func;
	      func = find_method(sc, find_let(sc, x), sc->VECTOR_APPEND);
	      if (func != sc->UNDEFINED)
		{
		  s7_pointer y;
		  if (len == 0)
		    return(s7_apply_function(sc, func, p));
		  for (y = args; cdr(y) != p; y = cdr(y));
		  cdr(y) = sc->NIL;
		  v = g_vector_append(sc, args);
		  cdr(y) = p;
		  return(s7_apply_function(sc, func, cons(sc, v, p)));
		}
	    }
	  return(wrong_type_argument_n(sc, sc->VECTOR_APPEND, position_of(p, args), x, T_VECTOR));
	}
      if (vector_length(x) > 0)
	{
	  len += vector_length(x);
	  if (type(x) != T_VECTOR)
	    parlous_gc = true;            /* might create a new object during append, gc possible: new vector needs fill and gc-protect */
	  if (result_type == -1)
	    result_type = type(x);
	  else
	    {
	      if (result_type != type(x))
		result_type = T_VECTOR;
	    }
	}
    }
  if (result_type == -1)
    result_type = type(car(args));

  if (result_type == T_VECTOR)
    {
      s7_pointer *dest;
      int gc_loc = 0;
      s7_Int i = 0;

      /* here the gc might be called since float_vector_getter makes a new real */
      v = make_vector_1(sc, len, parlous_gc, result_type);
      if (parlous_gc) gc_loc = s7_gc_protect(sc, v);
      dest = vector_elements(v);
      for (p = args; is_pair(p); p = cdr(p))
	{
	  s7_Int source_len, j;
	  x = car(p);
	  source_len = vector_length(x);
	  if (source_len > 0)
	    for (j = 0; j < source_len; j++, i++)
	      dest[i] = vector_getter(x)(sc, x, j);
	}
      if (parlous_gc) s7_gc_unprotect_at(sc, gc_loc);
    }
  else
    {
      v = make_vector_1(sc, len, NOT_FILLED, result_type); 
      if (result_type == T_INT_VECTOR)
	{
	  s7_Int *dest;
	  s7_Int i = 0;
	  dest = int_vector_elements(v);
	  for (p = args; is_pair(p); p = cdr(p))
	    {
	      s7_Int source_len;
	      x = car(p);
	      source_len = vector_length(x);
	      if (source_len > 0)
		{
		  s7_Int *source;
		  source = int_vector_elements(x);
		  memcpy((void *)(dest + i), (void *)source, source_len * sizeof(s7_Int));
		  i += source_len;
		}
	    }
	}
      else
	{
	  s7_Double *dest;
	  s7_Int i = 0;
	  dest = float_vector_elements(v);
	  for (p = args; is_pair(p); p = cdr(p))
	    {
	      s7_Int source_len;
	      x = car(p);
	      source_len = vector_length(x);
	      if (source_len > 0)
		{
		  s7_Double *source;
		  source = float_vector_elements(x);
		  memcpy((void *)(dest + i), (void *)source, source_len * sizeof(s7_Double));
		  i += source_len;
		}
	    }
	}
    }
  return(v);
}


s7_pointer s7_vector_ref_n(s7_scheme *sc, s7_pointer vector, int indices, ...)
{
  /* from s7.html */
  int ndims;

  ndims = s7_vector_rank(vector);
  if (ndims == indices)
    {
      va_list ap;
      s7_Int index = 0;
      va_start(ap, indices);

      if (ndims == 1)
	{
	  index = va_arg(ap, s7_Int);
	  va_end(ap);
	  return(s7_vector_ref(sc, vector, index));
	}
      else
	{
	  int i;
	  s7_Int *offsets, *dimensions;

	  dimensions = s7_vector_dimensions(vector);
	  offsets = s7_vector_offsets(vector);

	  for (i = 0; i < indices; i++)
	    {
	      int ind;
	      ind = va_arg(ap, int);
	      if ((ind < 0) ||
		  (ind >= dimensions[i]))
		{
		  va_end(ap);
		  return(out_of_range(sc, sc->VECTOR_REF, small_int(i), make_integer(sc, ind), (ind < 0) ? ITS_NEGATIVE : ITS_TOO_LARGE));
		}
	      index += (ind * offsets[i]);
	    }
	  va_end(ap);
	  return(vector_getter(vector)(sc, vector, index));
	}
    }
  return(s7_wrong_number_of_args_error(sc, "s7_vector_ref_n: wrong number of indices: ~A", s7_make_integer(sc, indices)));
}


s7_pointer s7_vector_set_n(s7_scheme *sc, s7_pointer vector, s7_pointer value, int indices, ...)
{
  int ndims;

  ndims = s7_vector_rank(vector);
  if (ndims == indices)
    {
      va_list ap;
      s7_Int index = 0;
      va_start(ap, indices);

      if (ndims == 1)
	{
	  index = va_arg(ap, s7_Int);
	  va_end(ap);
	  s7_vector_set(sc, vector, index, value);
	  return(value);
	}
      else
	{
	  int i;
	  s7_Int *offsets, *dimensions;

	  dimensions = s7_vector_dimensions(vector);
	  offsets = s7_vector_offsets(vector);

	  for (i = 0; i < indices; i++)
	    {
	      int ind;
	      ind = va_arg(ap, int);
	      if ((ind < 0) ||
		  (ind >= dimensions[i]))
		{
		  va_end(ap);
		  return(s7_out_of_range_error(sc, "s7_vector_set_n", i, s7_make_integer(sc, ind), "should be a valid index"));
		}
	      index += (ind * offsets[i]);
	    }
	  va_end(ap);
	  vector_setter(vector)(sc, vector, index, value);
	  return(value);
	}
    }
  return(s7_wrong_number_of_args_error(sc, "s7_vector_set_n: wrong number of indices: ~A", s7_make_integer(sc, indices)));
}


s7_pointer s7_vector_to_list(s7_scheme *sc, s7_pointer vect)
{
  s7_Int i, len;
  s7_pointer p;

  len = vector_length(vect);
  if (len == 0) return(sc->NIL);

  if (len < (sc->free_heap_top - sc->free_heap))
    {
      sc->w = cons(sc, vector_getter(vect)(sc, vect, len - 1), sc->NIL);
      for (i = len - 2; i >= 0; i--)
	sc->w = cons_unchecked(sc, vector_getter(vect)(sc, vect, i), sc->w);
    }
  else
    {
      sc->w = sc->NIL;
      for (i = len - 1; i >= 0; i--)
	sc->w = cons(sc, vector_getter(vect)(sc, vect, i), sc->w);
    }
  p = sc->w;
  sc->w = sc->NIL;
  return(p);
}


static s7_pointer g_vector_to_list(s7_scheme *sc, s7_pointer args)
{
  s7_Int i, start = 0, end;
  s7_pointer p, vec;
  #define H_vector_to_list "(vector->list v start end) returns the elements of the vector v as a list; (map values v)"

  vec = car(args);
  if (!s7_is_vector(vec))
    {
      check_method(sc, vec, sc->VECTOR_TO_LIST, args);
      return(simple_wrong_type_argument(sc, sc->VECTOR_TO_LIST, vec, T_VECTOR));
    }

  end = vector_length(vec);
  if (!is_null(cdr(args)))
    {
      p = start_and_end(sc, sc->VECTOR_TO_LIST, NULL, cdr(args), args, 2, &start, &end);
      if (p != sc->GC_NIL) return(p);
      if (start == end) return(sc->NIL);
    }
  if ((start == 0) && (end == vector_length(vec)))
    return(s7_vector_to_list(sc, vec));

  sc->w = sc->NIL;
  for (i = end - 1; i >= start; i--)
    sc->w = cons(sc, vector_getter(vec)(sc, vec, i), sc->w);
  p = sc->w;
  sc->w = sc->NIL;
  return(p);
}


s7_pointer s7_make_and_fill_vector(s7_scheme *sc, s7_Int len, s7_pointer fill)
{
  s7_pointer vect;
  vect = make_vector_1(sc, len, NOT_FILLED, T_VECTOR);
  s7_vector_fill(sc, vect, fill);
  return(vect);
}


static s7_pointer g_vector(s7_scheme *sc, s7_pointer args)
{
  #define H_vector "(vector ...) returns a vector whose elements are the arguments"
  s7_Int len;
  s7_pointer vec;

  len = s7_list_length(sc, args);
  vec = make_vector_1(sc, len, NOT_FILLED, T_VECTOR);
  if (len > 0)
    {
      s7_Int i;
      s7_pointer x;
      for (x = args, i = 0; is_pair(x); x = cdr(x), i++) 
	vector_element(vec, i) = car(x);
    }
  return(vec);
}


static s7_pointer g_is_float_vector(s7_scheme *sc, s7_pointer args)
{
  #define H_is_float_vector "(float-vector? obj) returns #t if obj is an homogenous float vector"
  check_boolean_method(sc, s7_is_float_vector, sc->IS_FLOAT_VECTOR, args);
}

static s7_pointer g_float_vector(s7_scheme *sc, s7_pointer args)
{
  #define H_float_vector "(float-vector ...) returns an homogenous float vector whose elements are the arguments"
  s7_Int i, len;
  s7_pointer vec, x;
  
  len = s7_list_length(sc, args);
  if (len == 0)
    return(make_vector_1(sc, 0, NOT_FILLED, T_VECTOR));

  vec = make_vector_1(sc, len, NOT_FILLED, T_FLOAT_VECTOR);
  for (x = args, i = 0; is_pair(x); x = cdr(x), i++) 
    {
      if (s7_is_real(car(x))) /* bignum is ok here */
	float_vector_element(vec, i) = number_to_double(sc, car(x), "float-vector");
      else return(simple_wrong_type_argument(sc, sc->FLOAT_VECTOR, car(x), T_REAL));
    }
  return(vec);
}


static s7_pointer g_list_to_vector(s7_scheme *sc, s7_pointer args)
{
  #define H_list_to_vector "(list->vector lst) returns a vector containing the elements of lst; (apply vector lst)"
  
  if (is_null(car(args)))
    return(s7_make_vector(sc, 0));

  if (!is_proper_list(sc, car(args)))
    {
      check_method(sc, car(args), sc->LIST_TO_VECTOR, args);
      return(simple_wrong_type_argument_with_type(sc, sc->LIST_TO_VECTOR, car(args), A_PROPER_LIST));
    }
  return(g_vector(sc, car(args)));
}


static s7_pointer g_vector_length(s7_scheme *sc, s7_pointer args)
{
  s7_pointer vec;
  #define H_vector_length "(vector-length v) returns the length of vector v"

  vec = car(args);
  if (!s7_is_vector(vec))
    {
      check_method(sc, vec, sc->VECTOR_LENGTH, args);
      return(simple_wrong_type_argument(sc, sc->VECTOR_LENGTH, vec, T_VECTOR));
    }
  return(make_integer(sc, vector_length(vec)));
}


static s7_pointer make_shared_vector(s7_scheme *sc, s7_pointer vect, int skip_dims, s7_Int index)
{
  s7_pointer x;
  vdims_t *v;

  /* (let ((v #2d((1 2) (3 4)))) (v 1)) 
   * (let ((v (make-vector '(2 3 4) 0))) (v 1 2))
   * (let ((v #3d(((0 1 2 3) (4 5 6 7) (8 9 10 11)) ((12 13 14 15) (16 17 18 19) (20 21 22 23))))) (v 0 1))
   */

  NEW_CELL(sc, x);
  vector_length(x) = 0;
  vector_elements(x) = NULL;
  set_type(x, type(vect) | T_SAFE_PROCEDURE);
  vector_getter(x) = vector_getter(vect);
  vector_setter(x) = vector_setter(vect);

  v = (vdims_t *)malloc(sizeof(vdims_t));
  v->ndims = vector_ndims(vect) - skip_dims;
  v->dims = (s7_Int *)(vector_dimensions(vect) + skip_dims);
  v->offsets = (s7_Int *)(vector_offsets(vect) + skip_dims);
  v->original = vect;
  v->elements_allocated = false;
  v->dimensions_allocated = false;
  vector_dimension_info(x) = v;

  if (skip_dims > 0)
    vector_length(x) = vector_offset(vect, skip_dims - 1);
  else vector_length(x) = vector_length(vect);

  if (is_int_vector(vect))
    int_vector_elements(x) = (s7_Int *)(int_vector_elements(vect) + index);
  else
    {
      if (is_float_vector(vect))
	float_vector_elements(x) = (s7_Double *)(float_vector_elements(vect) + index);
      else vector_elements(x) = (s7_pointer *)(vector_elements(vect) + index);
    }
  add_vector(sc, x);
  return(x);
}


static s7_pointer g_make_shared_vector(s7_scheme *sc, s7_pointer args)
{
  #define H_make_shared_vector "(make-shared-vector original-vector new-dimensions (offset 0)) returns \
a vector that points to the same elements as the original-vector but with different dimensional info."

  /* (let ((v1 #2d((1 2 3) (4 5 6)))) (let ((v2 (make-shared-vector v1 '(6)))) v2)) -> #(1 2 3 4 5 6)
   * (let ((v1 #(1 2 3 4 5 6))) (let ((v2 (make-shared-vector v1 '(3 2)))) v2)) -> #2D((1 2) (3 4) (5 6))
   * this is most useful in generic functions -- they can still use (v n) as the accessor.
   */
  s7_pointer orig, dims, y, x;
  vdims_t *v;
  int i;
  s7_Int new_len = 1, orig_len, offset = 0;

  orig = car(args);
  if (!s7_is_vector(orig))
    {
      check_method(sc, orig, sc->MAKE_SHARED_VECTOR, args);
      return(wrong_type_argument(sc, sc->MAKE_SHARED_VECTOR, small_int(1), orig, T_VECTOR));
    }
  orig_len = vector_length(orig);

  if (!is_null(cddr(args)))
    {
      s7_pointer off;
      off = caddr(args);
      if (s7_is_integer(off))
	{
	  offset = s7_integer(off);
	  if ((offset < 0) ||
	      (offset >= orig_len))  /* we need this if, for example, offset == 9223372036854775807 */
	    return(out_of_range(sc, sc->MAKE_SHARED_VECTOR, small_int(3), off, (offset < 0) ? ITS_NEGATIVE : ITS_TOO_LARGE));
	}
      else 
	{
	  check_method(sc, off, sc->MAKE_SHARED_VECTOR, args);
	  return(wrong_type_argument(sc, sc->MAKE_SHARED_VECTOR, small_int(3), off, T_INTEGER));
	}
    }

  dims = cadr(args);
  if ((is_null(dims)) ||
      (!is_proper_list(sc, dims)))
    {
      check_method(sc, dims, sc->MAKE_SHARED_VECTOR, args);
      return(wrong_type_argument(sc, sc->MAKE_SHARED_VECTOR, small_int(2), dims, T_PAIR));
    }
  for (y = dims; is_pair(y); y = cdr(y))
    if ((!s7_is_integer(car(y)))        ||       /* (make-shared-vector v '((1 2) (3 4))) */
	(s7_integer(car(y)) > orig_len) ||
	(s7_integer(car(y)) < 0))
      return(s7_error(sc, sc->WRONG_TYPE_ARG, list_1(sc, make_string_wrapper(sc, "a list of integers that fits the original vector"))));

  v = (vdims_t *)malloc(sizeof(vdims_t));
  v->ndims = safe_list_length(sc, dims);
  v->dims = (s7_Int *)malloc(v->ndims * sizeof(s7_Int));
  v->offsets = (s7_Int *)malloc(v->ndims * sizeof(s7_Int));
  v->dimensions_allocated = true;
  v->elements_allocated = false;
  v->original = orig;

  for (i = 0, y = dims; is_pair(y); i++, y = cdr(y))
    v->dims[i] = s7_integer(car(y));

  for (i = v->ndims - 1; i >= 0; i--)
    {
      v->offsets[i] = new_len;
      new_len *= v->dims[i];
    }

  if ((new_len < 0) || ((new_len + offset) > vector_length(orig)))
    {
      free(v->dims);
      free(v->offsets);
      free(v);
      return(out_of_range(sc, sc->MAKE_SHARED_VECTOR, small_int(2), dims, make_string_wrapper(sc, "a shared vector has to fit in the original vector")));
    }

  NEW_CELL(sc, x);
  set_type(x, type(orig) | T_SAFE_PROCEDURE);
  vector_dimension_info(x) = v;
  vector_length(x) = new_len;                 /* might be less than original length */
  vector_getter(x) = vector_getter(orig);
  vector_setter(x) = vector_setter(orig);

  if (is_int_vector(orig))
    int_vector_elements(x) = (s7_Int *)(int_vector_elements(orig) + offset);
  else
    {
      if (is_float_vector(orig))
	float_vector_elements(x) = (s7_Double *)(float_vector_elements(orig) + offset);
      else vector_elements(x) = (s7_pointer *)(vector_elements(orig) + offset);
    }
  add_vector(sc, x);

  return(x);
}


static s7_pointer make_vector_wrapper(s7_scheme *sc, s7_Int size, s7_pointer *elements)
{
  s7_pointer x;
  NEW_CELL(sc, x);
  vector_length(x) = size;
  vector_elements(x) = elements;
  set_type(x, T_VECTOR | T_SAFE_PROCEDURE);
  vector_getter(x) = default_vector_getter;
  vector_setter(x) = default_vector_setter;
  vector_dimension_info(x) = NULL;
  /* don't add_vector -- no need for sweep to see this */
  return(x);
}


static s7_pointer vector_ref_1(s7_scheme *sc, s7_pointer vect, s7_pointer indices)
{
  s7_Int index = 0;
  if (vector_length(vect) == 0)
    return(out_of_range(sc, sc->VECTOR_REF, small_int(1), vect, ITS_TOO_LARGE));

  if (vector_rank(vect) > 1)
    {
      unsigned int i;
      s7_pointer x;
      for (x = indices, i = 0; (is_not_null(x)) && (i < vector_ndims(vect)); x = cdr(x), i++)
	{
	  s7_Int n;
	  s7_pointer p, p1;
	  p = car(x);
	  if (!s7_is_integer(p))
	    {
	      if (!s7_is_integer(p1 = check_values(sc, p, x)))
		{
		  check_method(sc, p, sc->VECTOR_REF, cons(sc, vect, indices));
		  return(wrong_type_argument_n(sc, sc->VECTOR_REF, i + 2, p, T_INTEGER));
		}
	      else p = p1;
	    }
	  n = s7_integer(p);
	  if ((n < 0) || 
	      (n >= vector_dimension(vect, i)))
	    return(out_of_range(sc, sc->VECTOR_REF, make_integer(sc, i + 2), p, (n < 0) ? ITS_NEGATIVE : ITS_TOO_LARGE));

	  index += n * vector_offset(vect, i);
	}
      if (is_not_null(x))
	{
	  if (type(vect) != T_VECTOR)
	    return(out_of_range(sc, sc->VECTOR_REF, small_int(2), indices, TOO_MANY_INDICES));
	  return(implicit_index(sc, vector_element(vect, index), x));
	}

      /* if not enough indices, return a shared vector covering whatever is left */
      if (i < vector_ndims(vect))
	return(make_shared_vector(sc, vect, i, index));
    }
  else 
    {
      s7_pointer p, p1;
      /* (let ((hi (make-vector 3 0.0)) (sum 0.0)) (do ((i 0 (+ i 1))) ((= i 3)) (set! sum (+ sum (hi i)))) sum) */
      p = car(indices);

      if (!s7_is_integer(p))
	{
	  if (!s7_is_integer(p1 = check_values(sc, p, indices)))
	    {
	      check_method(sc, p, sc->VECTOR_REF, cons(sc, vect, indices));
	      return(wrong_type_argument(sc, sc->VECTOR_REF, small_int(2), p, T_INTEGER));
	    }
	  else p = p1;
	}
      index = s7_integer(p);
      if ((index < 0) ||
	  (index >= vector_length(vect)))
	return(out_of_range(sc, sc->VECTOR_REF, small_int(2), p, (index < 0) ? ITS_NEGATIVE : ITS_TOO_LARGE));
      
      if (is_not_null(cdr(indices)))                /* (let ((L #(#(1 2 3) #(4 5 6)))) (vector-ref L 1 2)) */
	{
	  if (type(vect) != T_VECTOR)
	    return(out_of_range(sc, sc->VECTOR_REF, small_int(2), indices, TOO_MANY_INDICES));
	  return(implicit_index(sc, vector_element(vect, index), cdr(indices)));
	}
    }
  return((vector_getter(vect))(sc, vect, index));
}


static s7_pointer g_vector_ref(s7_scheme *sc, s7_pointer args)
{
  #define H_vector_ref "(vector-ref v ... i) returns the i-th element of vector v."
  s7_pointer vec;

  vec = car(args);
  if (!s7_is_vector(vec))
    {
      check_method(sc, vec, sc->VECTOR_REF, args);
      return(wrong_type_argument(sc, sc->VECTOR_REF, small_int(1), vec, T_VECTOR));
    }
  return(vector_ref_1(sc, vec, cdr(args)));
}

static s7_pointer g_vector_ref_ic_n(s7_scheme *sc, s7_pointer args, s7_Int index)
{
  s7_pointer vec;
  vec = find_symbol_checked(sc, car(args));
  if (!s7_is_vector(vec))
    {
      check_method(sc, vec, sc->VECTOR_REF, list_2(sc, vec, cadr(args)));
      return(wrong_type_argument(sc, sc->VECTOR_REF, small_int(1), vec, T_VECTOR));
    }
  if (index >= vector_length(vec))
    return(out_of_range(sc, sc->VECTOR_REF, small_int(2), cadr(args), ITS_TOO_LARGE));
  if (vector_rank(vec) > 1)
    return(make_shared_vector(sc, vec, 1, index));
  return(vector_getter(vec)(sc,vec, index));
}

static s7_pointer vector_ref_ic;
static s7_pointer g_vector_ref_ic(s7_scheme *sc, s7_pointer args) {return(g_vector_ref_ic_n(sc, args, s7_integer(cadr(args))));}
static s7_pointer vector_ref_ic_0;
static s7_pointer g_vector_ref_ic_0(s7_scheme *sc, s7_pointer args) {return(g_vector_ref_ic_n(sc, args, 0));}
static s7_pointer vector_ref_ic_1;
static s7_pointer g_vector_ref_ic_1(s7_scheme *sc, s7_pointer args) {return(g_vector_ref_ic_n(sc, args, 1));}
static s7_pointer vector_ref_ic_2;
static s7_pointer g_vector_ref_ic_2(s7_scheme *sc, s7_pointer args) {return(g_vector_ref_ic_n(sc, args, 2));}
static s7_pointer vector_ref_ic_3;
static s7_pointer g_vector_ref_ic_3(s7_scheme *sc, s7_pointer args) {return(g_vector_ref_ic_n(sc, args, 3));}

static s7_pointer vector_ref_gs;
static s7_pointer g_vector_ref_gs(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x, vec;
  s7_Int index;

  vec = find_global_symbol_checked(sc, car(args));
  x = find_symbol_checked(sc, cadr(args));

  if (!s7_is_vector(vec))
    {
      check_method(sc, vec, sc->VECTOR_REF, list_2(sc, vec, x));
      return(wrong_type_argument(sc, sc->VECTOR_REF, small_int(1), vec, T_VECTOR));
    }
  if (!s7_is_integer(x))
    {
      check_method(sc, x, sc->VECTOR_REF, list_2(sc, vec, x));
      return(wrong_type_argument(sc, sc->VECTOR_REF, small_int(2), x, T_INTEGER));
    }

  index = s7_integer(x);
  if ((index < 0) ||
      (index >= vector_length(vec)))
    return(out_of_range(sc, sc->VECTOR_REF, small_int(2), cadr(args), (index < 0) ? ITS_NEGATIVE : ITS_TOO_LARGE));

  if (vector_rank(vec) > 1)
    return(make_shared_vector(sc, vec, 1, index));

  return(vector_getter(vec)(sc, vec, index));
}

static s7_pointer vector_ref_add1;
static s7_pointer g_vector_ref_add1(s7_scheme *sc, s7_pointer args)
{
  /* (vector-ref v (+ s 1)) I think */
  s7_pointer vec, x;
  s7_Int index;

  vec = find_symbol_checked(sc, car(args));
  x = find_symbol_checked(sc, cadr(cadr(args)));

  if (!s7_is_integer(x))
    {
      check_method(sc, x, sc->VECTOR_REF, list_2(sc, vec, x));
      return(wrong_type_argument(sc, sc->VECTOR_REF, small_int(2), x, T_INTEGER));
    }
  index = s7_integer(x) + 1;

  if (!s7_is_vector(vec))
    {
      check_method(sc, vec, sc->VECTOR_REF, list_2(sc, vec, s7_make_integer(sc, index)));
      return(wrong_type_argument(sc, sc->VECTOR_REF, small_int(1), vec, T_VECTOR));
    }

  if ((index < 0) ||
      (index >= vector_length(vec)))
    return(out_of_range(sc, sc->VECTOR_REF, small_int(2), cadr(args), (index < 0) ? ITS_NEGATIVE : ITS_TOO_LARGE));

  if (vector_rank(vec) > 1)
    return(make_shared_vector(sc, vec, 1, index));
  return(vector_getter(vec)(sc, vec, index));
}


static s7_pointer vector_ref_2;
static s7_pointer g_vector_ref_2(s7_scheme *sc, s7_pointer args)
{
  s7_pointer vec, ind;
  s7_Int index;

  vec = car(args);
  if (!s7_is_vector(vec))
    {
      check_method(sc, vec, sc->VECTOR_REF, args); /* should be ok because we go to g_vector_ref below */
      return(wrong_type_argument(sc, sc->VECTOR_REF, small_int(1), vec, T_VECTOR));
    }
  if (vector_rank(vec) > 1)
    return(g_vector_ref(sc, args));
  
  ind = cadr(args);
  if (!s7_is_integer(ind))
    {
      check_method(sc, ind, sc->VECTOR_REF, args);
      return(wrong_type_argument(sc, sc->VECTOR_REF, small_int(2), ind, T_INTEGER));
    }
  index = s7_integer(ind);
  if ((index < 0) ||
      (index >= vector_length(vec)))
    return(out_of_range(sc, sc->VECTOR_REF, small_int(2), ind, (index < 0) ? ITS_NEGATIVE : ITS_TOO_LARGE));

  return(vector_getter(vec)(sc, vec, index));
}


static s7_pointer g_vector_set(s7_scheme *sc, s7_pointer args)
{
  #define H_vector_set "(vector-set! v i ... value) sets the i-th element of vector v to value."

  s7_pointer vec, val;
  s7_Int index;

  vec = car(args);
  if (!s7_is_vector(vec))
    {
      check_method(sc, vec, sc->VECTOR_SET, args);
      return(wrong_type_argument(sc, sc->VECTOR_SET, small_int(1), vec, T_VECTOR));
    }
  if (vector_length(vec) == 0)
    return(out_of_range(sc, sc->VECTOR_SET, small_int(1), vec, ITS_TOO_LARGE));
  
  if (vector_rank(vec) > 1)
    {
      unsigned int i;
      s7_pointer x;
      index = 0;
      for (x = cdr(args), i = 0; (is_not_null(cdr(x))) && (i < vector_ndims(vec)); x = cdr(x), i++)
	{
	  s7_Int n;
	  s7_pointer p, p1;
	  p = car(x);
	  if (!s7_is_integer(p))
	    {
	      if (!s7_is_integer(p1 = check_values(sc, p, x)))
		{
		  check_method(sc, p, sc->VECTOR_SET, args);
		  return(wrong_type_argument_n(sc, sc->VECTOR_SET, i + 2, p, T_INTEGER));
		}
	      else p = p1;
	    }
	  n = s7_integer(p);
	  if ((n < 0) || 
	      (n >= vector_dimension(vec, i)))
	    return(out_of_range(sc, sc->VECTOR_SET, make_integer(sc, i + 2), p, (n < 0) ? ITS_NEGATIVE : ITS_TOO_LARGE));

	  index += n * vector_offset(vec, i);
	}

      if (is_not_null(cdr(x)))
	return(s7_wrong_number_of_args_error(sc, "too many args for vector-set!: ~S", args));
      if (i != vector_ndims(vec))
	return(s7_wrong_number_of_args_error(sc, "not enough args for vector-set!: ~S", args));

      val = car(x);
    }
  else
    {
      s7_pointer p, p1;
      p = cadr(args);
      if (!s7_is_integer(p))
	{
	  if (!s7_is_integer(p1 = check_values(sc, p, cdr(args))))
	    {
	      check_method(sc, p, sc->VECTOR_SET, args);
	      return(wrong_type_argument(sc, sc->VECTOR_SET, small_int(2), p, T_INTEGER));
	    }
	  else p = p1;
	}
      index = s7_integer(p);
      if ((index < 0) ||
	  (index >= vector_length(vec)))
	return(out_of_range(sc, sc->VECTOR_SET, small_int(2), p, (index < 0) ? ITS_NEGATIVE : ITS_TOO_LARGE));

      if (is_not_null(cdddr(args)))
	{
	  car(sc->temp_cell_4) = vector_getter(vec)(sc, vec, index);
	  cdr(sc->temp_cell_4) = cddr(args);
	  return(g_vector_set(sc, sc->temp_cell_4));
	}
      val = caddr(args);
    }
  
  vector_setter(vec)(sc, vec, index, val);
  return(val);
}


static s7_pointer vector_set_ic;
static s7_pointer g_vector_set_ic(s7_scheme *sc, s7_pointer args)
{
  /* (vector-set! vec 0 x) */
  s7_pointer vec, val;
  s7_Int index;

  vec = find_symbol_checked(sc, car(args));
  if (!s7_is_vector(vec))
    {
      check_method(sc, vec, sc->VECTOR_SET, list_3(sc, vec, cadr(args), find_symbol_checked(sc, caddr(args)))); /* the list_3 happens only if we find the method */
      return(wrong_type_argument(sc, sc->VECTOR_SET, small_int(1), vec, T_VECTOR));
    }
  if (vector_rank(vec) > 1)
    return(g_vector_set(sc, list_3(sc, vec, cadr(args), find_symbol_checked(sc, caddr(args)))));

  index = s7_integer(cadr(args));
  if (index >= vector_length(vec))
    return(out_of_range(sc, sc->VECTOR_SET, small_int(2), cadr(args), ITS_TOO_LARGE));

  val = find_symbol_checked(sc, caddr(args));
  vector_setter(vec)(sc, vec, index, val);
  return(val);
}


static s7_pointer vector_set_vref;
static s7_pointer g_vector_set_vref(s7_scheme *sc, s7_pointer args)
{
  /* (vector-set! vec i (vector-ref vec j)) -- checked that the vector is the same */
  s7_pointer vec, val1, val2;
  s7_Int index1, index2;

  vec = find_symbol_checked(sc, car(args));
  val1 = find_symbol_checked(sc, cadr(args));
  val2 = find_symbol_checked(sc, caddr(caddr(args)));

  if ((!s7_is_vector(vec)) ||
      (vector_rank(vec) > 1) ||
      (!s7_is_integer(val1)) ||
      (!s7_is_integer(val2))) 
    return(g_vector_set(sc, list_3(sc, vec, val1, g_vector_ref(sc, list_2(sc, vec, val2)))));

  index1 = s7_integer(val1);
  if (index1 >= vector_length(vec))
    return(out_of_range(sc, sc->VECTOR_SET, small_int(2), val1, ITS_TOO_LARGE));

  index2 = s7_integer(val2);
  if (index2 >= vector_length(vec))
    return(out_of_range(sc, sc->VECTOR_REF, small_int(2), val2, ITS_TOO_LARGE));

  vector_setter(vec)(sc, vec, index1, val1 = vector_getter(vec)(sc, vec, index2));
  return(val1);
}


static s7_pointer vector_set_vector_ref;
static s7_pointer g_vector_set_vector_ref(s7_scheme *sc, s7_pointer args)
{
  /* (vector-set! data i|j (+|- (vector-ref data i) tc)) */
  s7_pointer vec, val, val2, tc, arg3;
  s7_Int index1, index2;

  vec = find_symbol_checked(sc, car(args));
  val = find_symbol_checked(sc, cadr(args));

  arg3 = caddr(args);
  tc = find_symbol_checked(sc, caddr(arg3));
  val2 = caddr(cadr(arg3));

  if ((!s7_is_vector(vec)) ||
      (vector_rank(vec) > 1) ||
      (!s7_is_integer(val)))
    return(g_vector_set(sc, list_3(sc, vec, val, c_call(arg3)(sc, list_2(sc, g_vector_ref(sc, list_2(sc, vec, find_symbol_checked(sc, val2))), tc)))));

  index1 = s7_integer(val);
  if (index1 >= vector_length(vec))
    return(out_of_range(sc, sc->VECTOR_SET, small_int(2), val, ITS_TOO_LARGE));

  if (val2 != cadr(args))
    {
      val2 = find_symbol_checked(sc, val2);
      if (!s7_is_integer(val2))
	{
	  s7_pointer p;
	  if (!s7_is_integer(p = check_values(sc, val2, list_1(sc, val2))))
	    return(wrong_type_argument(sc, sc->VECTOR_REF, small_int(2), val2, T_INTEGER));
	  else val2 = p;
	}
      index2 = s7_integer(val2);
      if (index2 >= vector_length(vec))
	return(out_of_range(sc, sc->VECTOR_REF, small_int(2), val, ITS_TOO_LARGE));
    }
  else index2 = index1;

  car(sc->Z2_1) = vector_getter(vec)(sc, vec, index2);
  car(sc->Z2_2) = tc;
  vector_setter(vec)(sc, vec, index1, tc = c_call(arg3)(sc, sc->Z2_1));
  return(tc);
}

static s7_pointer vector_set_3;
static s7_pointer g_vector_set_3(s7_scheme *sc, s7_pointer args)
{
  /* (vector-set! vec ind val) where are all predigested */
  s7_pointer vec, ind, val;
  s7_Int index;

  vec = car(args);
  if (!s7_is_vector(vec))
    {
      check_method(sc, vec, sc->VECTOR_SET, args);
      return(wrong_type_argument(sc, sc->VECTOR_SET, small_int(1), vec, T_VECTOR));
    }
  if (vector_rank(vec) > 1)
    return(g_vector_set(sc, args));
  
  ind = cadr(args);
  if (!s7_is_integer(ind))
    {
      s7_pointer p;
      if (!s7_is_integer(p = check_values(sc, ind, cdr(args))))
	return(wrong_type_argument(sc, sc->VECTOR_SET, small_int(2), ind, T_INTEGER));
      else ind = p;
    }
  index = s7_integer(ind);
  if ((index < 0) ||
      (index >= vector_length(vec)))
    return(out_of_range(sc, sc->VECTOR_SET, small_int(2), ind, (index < 0) ? ITS_NEGATIVE : ITS_TOO_LARGE));

  val = caddr(args);
  vector_setter(vec)(sc, vec, index, val);
  return(val);
}


#define MAX_VECTOR_DIMENSIONS 512

static s7_pointer g_make_vector(s7_scheme *sc, s7_pointer args)
{
  #define H_make_vector "(make-vector len (value #f) (homogenous #f)) returns a vector of len elements initialized to value. \
To create a multidimensional vector, put the dimension bounds in a list (this is to avoid ambiguities such as \
(make-vector 1 2) where it's not clear whether the '2' is an initial value or a dimension size).  (make-vector '(2 3) 1.0) \
returns a 2 dimensional vector of 6 total elements, all initialized to 1.0.  If homogenous is #t, and value is either an integer \
or a real, the vector can only hold numbers of that type (s7_Int or s7_Double)."

  s7_Int len;
  s7_pointer x, fill, vec;
  int result_type = T_VECTOR;

  fill = sc->UNSPECIFIED;
  x = car(args);
  if (s7_is_integer(x))
    {
      len = s7_integer(x);
      if (len < 0)
	return(wrong_type_argument_with_type(sc, sc->MAKE_VECTOR, small_int(1), x, A_NON_NEGATIVE_INTEGER));
    }
  else
    {
      if (!(is_pair(x)))
	{
	  check_method(sc, x, sc->MAKE_VECTOR, args);
	  return(wrong_type_argument_with_type(sc, sc->MAKE_VECTOR, small_int(1), x, 
					       make_string_wrapper(sc, "an integer or a list of integers")));
	}
      if (!s7_is_integer(car(x)))
	return(wrong_type_argument_with_type(sc, sc->MAKE_VECTOR, small_int(1), car(x), 
					     make_string_wrapper(sc, "each dimension should be an integer")));
      if (is_null(cdr(x)))
	len = s7_integer(car(x));
      else
	{
	  int dims;
	  s7_pointer y;

	  dims = s7_list_length(sc, x);
	  if (dims <= 0)                /* 0 if circular, negative if dotted */
	    return(wrong_type_argument_with_type(sc, sc->MAKE_VECTOR, small_int(1), x, A_PROPER_LIST));
	  if (dims > MAX_VECTOR_DIMENSIONS)
	    return(out_of_range(sc, sc->MAKE_VECTOR, small_int(1), x, ITS_TOO_LARGE));

	  for (len = 1, y = x; is_not_null(y); y = cdr(y))
	    {
	      if (!s7_is_integer(car(y)))
		return(wrong_type_argument_n(sc, sc->MAKE_VECTOR, position_of(y, x), car(y), T_INTEGER));
	      len *= s7_integer(car(y));
	      if (len < 0)
		return(wrong_type_argument_n_with_type(sc, sc->MAKE_VECTOR, position_of(y, x), car(y), A_NON_NEGATIVE_INTEGER));
	    }
	}
    }

  if (is_not_null(cdr(args)))
    {
      fill = cadr(args);
      if (is_not_null(cddr(args)))
	{
	  if (caddr(args) == sc->T)
	    {
	      /* here bignums can cause confusion, so use is_integer not s7_is_integer etc */
	      if (is_integer(fill))
		result_type = T_INT_VECTOR;
	      else
		{
		  if (s7_is_real(fill)) /* might be gmp with big_real by accident (? see above) */
		    result_type = T_FLOAT_VECTOR;
		  else 
		    {
		      check_method(sc, fill, sc->MAKE_VECTOR, args);
		      return(wrong_type_argument_with_type(sc, sc->MAKE_VECTOR, small_int(2), fill,
							   make_string_wrapper(sc, "an integer or a real since 'homogenous' is #t")));
		    }
		}
	    }
	  else 
	    {
	      if (caddr(args) != sc->F)
		{
		  check_method(sc, caddr(args), sc->MAKE_VECTOR, args);
		  return(wrong_type_argument_with_type(sc, sc->MAKE_VECTOR, small_int(3), caddr(args), A_BOOLEAN));
		}
	    }
	}
    }

  vec = make_vector_1(sc, len, NOT_FILLED, result_type);
  if (len > 0) s7_vector_fill(sc, vec, fill);

  if ((is_pair(x)) &&
      (is_pair(cdr(x))))
    {
      int i;
      s7_Int offset = 1;
      s7_pointer y;
      vdims_t *v;

      v = (vdims_t *)malloc(sizeof(vdims_t));
      v->ndims = safe_list_length(sc, x);
      v->dims = (s7_Int *)malloc(v->ndims * sizeof(s7_Int));
      v->offsets = (s7_Int *)malloc(v->ndims * sizeof(s7_Int));
      v->original = sc->F;
      v->dimensions_allocated = true;
      v->elements_allocated = (len > 0);

      for (i = 0, y = x; is_not_null(y); i++, y = cdr(y))
	v->dims[i] = s7_integer(car(y));

      for (i = v->ndims - 1; i >= 0; i--)
	{
	  v->offsets[i] = offset;
	  offset *= v->dims[i];
	}
      vector_dimension_info(vec) = v;
    }
  return(vec);
}


static s7_pointer g_make_float_vector(s7_scheme *sc, s7_pointer args)
{
  #define H_make_float_vector "(make-float-vector len (init 0.0)) returns a float-vector."
  return(g_make_vector(sc, list_3(sc, car(args), (is_null(cdr(args))) ? real_zero : cadr(args), sc->T)));
}


static s7_pointer g_is_vector(s7_scheme *sc, s7_pointer args)
{
  #define H_is_vector "(vector? obj) returns #t if obj is a vector"
  check_boolean_method(sc, s7_is_vector, sc->IS_VECTOR, args);
}


int s7_vector_rank(s7_pointer vect)
{
  return(vector_rank(vect));
}


static s7_pointer g_vector_dimensions(s7_scheme *sc, s7_pointer args)
{
  #define H_vector_dimensions "(vector-dimensions vect) returns a list of vect's dimensions.  In srfi-63 terms:\n\
    (define array-dimensions vector-dimensions)\n\
    (define (array-rank v) (length (vector-dimensions v)))"

  s7_pointer x;

  x = car(args);
  if (!s7_is_vector(x))
    {
      check_method(sc, x, sc->VECTOR_DIMENSIONS, args);
      return(simple_wrong_type_argument(sc, sc->VECTOR_DIMENSIONS, x, T_VECTOR));
    }

  if (vector_rank(x) > 1)
    {
      int i;
      sc->w = sc->NIL;
      for (i = vector_ndims(x) - 1; i >= 0; i--)
	sc->w = cons(sc, make_integer(sc, vector_dimension(x, i)), sc->w);
      x = sc->w;
      sc->w = sc->NIL;
      return(x);
    }
  return(list_1(sc, make_integer(sc, vector_length(x))));
}


#define MULTIVECTOR_TOO_MANY_ELEMENTS -1
#define MULTIVECTOR_NOT_ENOUGH_ELEMENTS -2

static int traverse_vector_data(s7_scheme *sc, s7_pointer vec, int flat_ref, int dimension, int dimensions, int *sizes, s7_pointer lst)
{
  /* we're filling vec, we're currently looking for element (flat-wise) flat_ref,
   *   we're at ref in dimension of dimensions, where sizes gives the bounds, and lst is our data
   *   #3D(((1 2 3) (4 5 6)) ((7 8 9) (10 11 12)))
   */
  int i;
  s7_pointer x;

  for (i = 0, x = lst; i < sizes[dimension]; i++, x = cdr(x))
    {
      if (!is_pair(x))
	return(MULTIVECTOR_NOT_ENOUGH_ELEMENTS);

      if (dimension == (dimensions - 1))
	vector_setter(vec)(sc, vec, flat_ref++, car(x));
      else 
	{
	  flat_ref = traverse_vector_data(sc, vec, flat_ref, dimension + 1, dimensions, sizes, car(x));
	  if (flat_ref < 0) return(flat_ref);
	}
    }
  if (is_not_null(x))
    return(MULTIVECTOR_TOO_MANY_ELEMENTS);
  return(flat_ref);
}


static s7_pointer s7_multivector_error(s7_scheme *sc, const char *message, s7_pointer data)
{
  return(s7_error(sc, sc->READ_ERROR, list_3(sc, make_string_wrapper(sc, "reading constant vector, ~A: ~A"), make_string_wrapper(sc, message), data)));
}


static s7_pointer g_multivector(s7_scheme *sc, s7_Int dims, s7_pointer data)
{
  /* get the dimension bounds from data, make the new vector, fill it from data 
   *
   * dims needs to be s7_Int so we can at least give correct error messages.
   * also should we let an empty vector have any number of dimensions? currently ndims is an int.
   */
  s7_pointer vec, x;
  int i, vec_loc, err;
  int *sizes;
  
  /* (#2d((1 2 3) (4 5 6)) 0 0) -> 1
   * (#2d((1 2 3) (4 5 6)) 0 1) -> 2
   * (#2d((1 2 3) (4 5 6)) 1 1) -> 5
   * (#3D(((1 2) (3 4)) ((5 6) (7 8))) 0 0 0) -> 1
   * (#3D(((1 2) (3 4)) ((5 6) (7 8))) 1 1 0) -> 7
   * #3D(((1 2) (3 4)) ((5 6) (7))) -> error, #3D(((1 2) (3 4)) ((5 6) (7 8 9))), #3D(((1 2) (3 4)) (5 (7 8 9))) etc
   *
   * but a special case: #nD() is an n-dimensional empty vector
   */

  if (dims <= 0)      /* #0d(...) #2147483649D() [if dims is int this is negative] */
    return(s7_out_of_range_error(sc, "#nD(...) dimensions", 1, make_integer(sc, dims), "must be 1 or more"));

  sc->w = sc->NIL;
  if (is_null(data))  /* dims are already 0 (calloc above) */
    return(g_make_vector(sc, list_1(sc, make_list(sc, dims, small_int(0)))));

  sizes = (int *)calloc(dims, sizeof(int));
  for (x = data, i = 0; i < dims; i++)
    {
      sizes[i] = safe_list_length(sc, x);
      sc->w = cons(sc, make_integer(sc, sizes[i]), sc->w);
      x = car(x);
      if ((i < (dims - 1)) && 
	  (!is_pair(x)))
	{
	  free(sizes);
	  return(s7_multivector_error(sc, "we need a list that fully specifies the vector's elements", data));
	}
    }

  vec = g_make_vector(sc, list_1(sc, sc->w = safe_reverse_in_place(sc, sc->w)));
  vec_loc = s7_gc_protect(sc, vec);
  sc->w = sc->NIL;

  /* now fill the vector checking that all the lists match */
  err = traverse_vector_data(sc, vec, 0, 0, dims, sizes, data);

  free(sizes);
  s7_gc_unprotect_at(sc, vec_loc);
  if (err < 0) 
    return(s7_multivector_error(sc, (err == MULTIVECTOR_TOO_MANY_ELEMENTS) ? "found too many elements" : "not enough elements found", data));

  return(vec);
}


static s7_pointer g_qq_multivector(s7_scheme *sc, s7_pointer args)
{
  /* `#2d((1 2) ,(list 3 4)) */
  #define H_qq_multivector "quasiquote internal support for multidimensional vector constants"
  return(g_multivector(sc, s7_integer(car(args)), cdr(args)));
}


s7_pointer s7_vector_copy(s7_scheme *sc, s7_pointer old_vect)
{
  s7_Int len;
  s7_pointer new_vect;

  len = vector_length(old_vect);
  if (is_float_vector(old_vect))
    {
      if (vector_rank(old_vect) > 1)
	new_vect = g_make_vector(sc, list_3(sc, g_vector_dimensions(sc, list_1(sc, old_vect)), real_zero, sc->T));
      else new_vect = make_vector_1(sc, len, NOT_FILLED, T_FLOAT_VECTOR);
      memcpy((void *)(float_vector_elements(new_vect)), (void *)(float_vector_elements(old_vect)), len * sizeof(s7_Double));
    }
  else
    {
      if (is_int_vector(old_vect))
	{
	  if (vector_rank(old_vect) > 1)
	    new_vect = g_make_vector(sc, list_3(sc, g_vector_dimensions(sc, list_1(sc, old_vect)), small_int(0), sc->T));
	  else new_vect = make_vector_1(sc, len, NOT_FILLED, T_INT_VECTOR);
	  memcpy((void *)(int_vector_elements(new_vect)), (void *)(int_vector_elements(old_vect)), len * sizeof(s7_Int));
	}
      else
	{
	  if (vector_rank(old_vect) > 1)
	    new_vect = g_make_vector(sc, list_1(sc, g_vector_dimensions(sc, list_1(sc, old_vect))));
	  else new_vect = make_vector_1(sc, len, NOT_FILLED, T_VECTOR);

	  /* here and in vector-fill! we have a problem with bignums -- should new bignums be allocated? (copy_list also) */
	  memcpy((void *)(vector_elements(new_vect)), (void *)(vector_elements(old_vect)), len * sizeof(s7_pointer));
	}
    }
  return(new_vect);
}


static s7_pointer g_float_vector_ref(s7_scheme *sc, s7_pointer args)
{
  #define H_float_vector_ref "(float-vector-ref v ...) returns an element of the float-vector v."

  s7_pointer v;
  s7_Int ind;

  v = car(args);
  if (!is_float_vector(v))
    {
      check_method(sc, v, sc->FLOAT_VECTOR_REF, args);
      return(wrong_type_argument(sc, sc->FLOAT_VECTOR_REF, small_int(1), v, T_FLOAT_VECTOR)); 
    }
  if (vector_rank(v) == 1)
    {
      s7_pointer index;
      index = cadr(args);
      if (!s7_is_integer(index))
	{
	  s7_pointer p;
	  if (!s7_is_integer(p = check_values(sc, index, cdr(args))))
	    return(wrong_type_argument_n(sc, sc->FLOAT_VECTOR_REF, 2, index, T_INTEGER)); 
	  else index = p;
	}
      ind = s7_integer(index);
      if ((ind < 0) || (ind >= vector_length(v)))
	return(simple_out_of_range(sc, sc->FLOAT_VECTOR_REF, index, (ind < 0) ? ITS_NEGATIVE : ITS_TOO_LARGE));
      if (!is_null(cddr(args)))
	return(out_of_range(sc, sc->FLOAT_VECTOR_REF, small_int(2), cdr(args), TOO_MANY_INDICES));
    }
  else
    {
      unsigned int i;
      s7_pointer x;
      ind = 0;
      for (x = cdr(args), i = 0; (is_not_null(x)) && (i < vector_ndims(v)); x = cdr(x), i++)
	{
	  s7_Int n;
	  if (!s7_is_integer(car(x)))
	    {
	      s7_pointer p;
	      if (!s7_is_integer(p = check_values(sc, car(x), x)))
		return(wrong_type_argument_n(sc, sc->FLOAT_VECTOR_REF, i + 2, car(x), T_INTEGER));
	      n = s7_integer(p);
	    }
	  else n = s7_integer(car(x));
	  if ((n < 0) || 
	      (n >= vector_dimension(v, i)))
	    return(out_of_range(sc, sc->FLOAT_VECTOR_REF, make_integer(sc, i + 2), car(x), (n < 0) ? ITS_NEGATIVE : ITS_TOO_LARGE));

	  ind += n * vector_offset(v, i);
	}
      if (is_not_null(x))
	return(out_of_range(sc, sc->FLOAT_VECTOR_REF, small_int(2), cdr(args), TOO_MANY_INDICES));

      /* if not enough indices, return a shared vector covering whatever is left */
      if (i < vector_ndims(v))
	return(make_shared_vector(sc, v, i, ind));
    }
  return(make_real(sc, float_vector_element(v, ind)));
}


static s7_pointer g_float_vector_set(s7_scheme *sc, s7_pointer args)
{
  #define H_float_vector_set "(float-vector-set! v i ... value) sets the i-th element of the float vector v to value."

  s7_pointer vec, val;
  s7_Int index;

  vec = car(args);
  if (!is_float_vector(vec))
    {
      check_method(sc, vec, sc->FLOAT_VECTOR_SET, args);
      return(wrong_type_argument(sc, sc->FLOAT_VECTOR_SET, small_int(1), vec, T_FLOAT_VECTOR));
    }
  
  if (vector_rank(vec) > 1)
    {
      unsigned int i;
      s7_pointer x;
      index = 0;
      for (x = cdr(args), i = 0; (is_not_null(cdr(x))) && (i < vector_ndims(vec)); x = cdr(x), i++)
	{
	  s7_Int n;
	  if (!s7_is_integer(car(x)))
	    {
	      s7_pointer p;
	      if (!s7_is_integer(p = check_values(sc, car(x), x)))
		{
		  check_method(sc, car(x), sc->FLOAT_VECTOR_SET, args);
		  return(wrong_type_argument_n(sc, sc->FLOAT_VECTOR_SET, i + 2, car(x), T_INTEGER));
		}
	      n = s7_integer(p);
	    }
	  else n = s7_integer(car(x));
	  if ((n < 0) || 
	      (n >= vector_dimension(vec, i)))
	    return(out_of_range(sc, sc->FLOAT_VECTOR_SET, make_integer(sc, i + 2), car(x), (n < 0) ? ITS_NEGATIVE : ITS_TOO_LARGE));

	  index += n * vector_offset(vec, i);
	}

      if (is_not_null(cdr(x)))
	return(s7_wrong_number_of_args_error(sc, "too many args for float-vector-set!: ~S", args));
      if (i != vector_ndims(vec))
	return(s7_wrong_number_of_args_error(sc, "not enough args for float-vector-set!: ~S", args));

      val = car(x);
    }
  else
    {
      if (!s7_is_integer(cadr(args)))
	{
	  s7_pointer p;
	  if (!s7_is_integer(p = check_values(sc, cadr(args), cdr(args))))
	    {
	      check_method(sc, cadr(args), sc->FLOAT_VECTOR_SET, args);
	      return(wrong_type_argument(sc, sc->FLOAT_VECTOR_SET, small_int(2), cadr(args), T_INTEGER));
	    }
	  index = s7_integer(p);
	}
      else index = s7_integer(cadr(args));
      if ((index < 0) ||
	  (index >= vector_length(vec)))
	return(out_of_range(sc, sc->FLOAT_VECTOR_SET, small_int(2), cadr(args), (index < 0) ? ITS_NEGATIVE : ITS_TOO_LARGE));

      if (is_not_null(cdddr(args)))
	return(s7_wrong_number_of_args_error(sc, "too many args for float-vector-set!: ~S", args));
      val = caddr(args);
    }
  
  float_vector_element(vec, index) = number_to_double(sc, val, "float-vector-set!");
  /* currently this accepts a complex value and assigns real_part(val) to the float-vector -- maybe an error instead? */
  return(val);
}



/* -------- sort! -------- */

static bool c_function_is_ok(s7_scheme *sc, s7_pointer x) 
{
  /* macro version of this (below) is much slower! */
  s7_pointer p;
  
  p = car(x);
  if (is_global(p)) p = slot_value(global_slot(p)); else p = find_symbol_unchecked(sc, p);
  /* this is nearly always global and p == ecdr(x) */
#if 0
  if ((!is_global(car(x))) || (symbol_id(car(x)) != 0))
    fprintf(stderr, "%s: %d %lld, ecdr: %p %p, ok: %d\n", 
	    DISPLAY(x), is_global(car(x)), symbol_id(car(x)), p, ecdr(x),
	    (p == ecdr(x)) ||
	    ((is_any_c_function(p)) &&
	     (c_function_class(p) == c_function_class(ecdr(x)))));
#endif
  return((p == ecdr(x)) ||
	 ((is_any_c_function(p)) &&
	  (c_function_class(p) == c_function_class(ecdr(x)))));
}

#if 0
#define c_function_is_ok(Sc, X) \
  ({ s7_pointer _X_, _p_;  _X_ = X; _p_ = find_global_symbol_checked(Sc, car(_X_)); \
   ((_p_ == ecdr(_X_)) || ((is_any_c_function(_p_)) && (c_function_class(_p_) == c_function_class(ecdr(_X_))))); })
#endif

static bool arglist_has_rest(s7_scheme *sc, s7_pointer args)
{
  s7_pointer p;
  for (p = args; is_pair(p); p = cdr(p))
    if (car(p) == sc->KEY_REST)
      return(true);
  return(false);
}


static bool arglist_has_keyword(s7_pointer args)
{
  s7_pointer p;
  for (p = args; is_pair(p); p = cdr(p))
    if (is_keyword(car(p)))
      return(true);
  return(false);
}


static s7_function end_dox_eval(s7_scheme *sc, s7_pointer code);
static s7_pointer end_dox_or_not_ic(s7_scheme *sc, s7_pointer code);
static s7_pointer end_dox_or_not_ss(s7_scheme *sc, s7_pointer code);

static void initialize_dox_end_vars(s7_scheme *sc, s7_pointer end)
{
  switch (optimize_data(end))
    {
    case HOP_SAFE_C_C:
      break;
      
    case HOP_SAFE_C_S:
    case HOP_SAFE_C_SC:
    case HOP_SAFE_C_S_opCq:
      dox_slot1(sc->envir) = find_symbol(sc, cadr(end));
      break;
      
    case HOP_SAFE_C_CS:
    case HOP_SAFE_C_QS:
      dox_slot1(sc->envir) = find_symbol(sc, caddr(end));
      break;
      
    case HOP_SAFE_C_SS:
      dox_slot1(sc->envir) = find_symbol(sc, cadr(end));
      dox_slot2(sc->envir) = find_symbol(sc, caddr(end));
      break;
      
    case HOP_SAFE_C_opSq_S:
      dox_slot1(sc->envir) = find_symbol(sc, cadr(cadr(end)));
      dox_slot2(sc->envir) = find_symbol(sc, caddr(end));
      break;
      
    case HOP_SAFE_C_S_opSq:
    case HOP_SAFE_C_S_opSCq:
    case HOP_SAFE_C_S_opSSq:
      dox_slot1(sc->envir) = find_symbol(sc, cadr(end));
      dox_slot2(sc->envir) = find_symbol(sc, ecdr(cdr(end)));
      break;

    case HOP_SAFE_C_opSq_opSq:
      dox_slot1(sc->envir) = find_symbol(sc, cadr(cadr(end)));
      dox_slot2(sc->envir) = find_symbol(sc, cadr(caddr(end)));
      break;
    }
}


static s7_scheme *compare_sc;
static s7_function compare_func;
static s7_pointer compare_args;

static int vector_compare(const void *v1, const void *v2)
{
  car(compare_args) = (*(s7_pointer *)v1);
  cadr(compare_args) = (*(s7_pointer *)v2);
  if (is_true(compare_sc, (*(compare_func))(compare_sc, compare_args)))
    return(-1);
  return(1);
}

static int dox_compare(const void *v1, const void *v2)
{
  /* here v1 and v2 are the lambda args.  We need to place these in the current env slots,
   *   then call compare_func with the expression as its argument. So compare_args in this
   *   case is expr.
   */
  slot_set_value(let_slots(compare_sc->envir), (*(s7_pointer *)v1));
  slot_set_value(next_slot(let_slots(compare_sc->envir)), (*(s7_pointer *)v2));
  if (is_true(compare_sc, (*(compare_func))(compare_sc, compare_args)))
    return(-1);
  return(1);
}


static s7_pointer g_sort(s7_scheme *sc, s7_pointer args)
{
  #define H_sort "(sort! sequence less?) sorts a sequence using the function 'less?' to compare elements."

  s7_pointer data, lessp;
  s7_Int len = 0, n, k;
  int (*sort_func)(const void *v1, const void *v2);
  s7_pointer *elements;

  data = car(args);
  if (is_null(data)) 
    {
      /* (apply sort! () #f) should be an error I think 
       */
      lessp = cadr(args);
      if ((!is_procedure(lessp)) ||
	  (!s7_is_aritable(sc, lessp, 2)))
	{
	  check_method(sc, lessp, sc->SORT, args);
	  return(wrong_type_argument_with_type(sc, sc->SORT, small_int(2), lessp, AN_EQ_FUNC));
	}
      return(sc->NIL);
    }

  lessp = cadr(args);
  if (!is_procedure(lessp))
    {
      check_method(sc, lessp, sc->SORT, args);
      return(wrong_type_argument_with_type(sc, sc->SORT, small_int(2), lessp, A_FUNCTION));
    }
  if ((is_continuation(lessp)) || is_goto(lessp))
    return(wrong_type_argument_with_type(sc, sc->SORT, small_int(2), lessp, make_string_wrapper(sc, "a normal procedure (not a continuation)")));
  if (!s7_is_aritable(sc, lessp, 2))
    return(wrong_type_argument_with_type(sc, sc->SORT, small_int(2), lessp, AN_EQ_FUNC));

  sort_func = vector_compare;
  compare_func = NULL;
  compare_args = sc->T2_1;
  compare_sc = sc;

  /* (define (hi a) (sort! a (lambda (x y) (< x y))))
   */
  if ((is_safe_procedure(lessp)) &&
      (is_c_function(lessp)))
    compare_func = c_function_call(lessp);
  else
    {
      /* other similar cases are assoc/member (and for-each/map but they never happen in this context -- see op_for_each_ls_2 below)
       */
      if ((is_closure(lessp)) &&
	  (is_null(cdr(closure_body(lessp)))) &&
	  (is_optimized(car(closure_body(lessp)))))
	{
	  /* since (sort seq (lambda (a b) ...)) can't return a "safe closure" (the hop bit is off in
	   *   optimize in this case, for some arcane reason), the optimized expression won't be hop_safe,
	   *   but that is irrelevant at this point -- if c_function_is_ok, we're good to go.
	   *   So, treat it as if it were a dox end expression, creating a frame and using dox1|2 for
	   *   any symbols. This way we can handle slightly tricky cases like (lambda (a b) (< (car b) a))
	   *   without any extra work up front.  compare_func needs to be a dox-like-version of c_call(expr)
	   *   where the 2 args are placed in dox1|2 before calling the actual function.
	   */
	  s7_pointer expr, args;
	  s7_function sort_f;
	  expr = car(closure_body(lessp));
	  args = closure_args(lessp);

	  if ((is_pair(args)) &&
	      (!arglist_has_rest(sc, args)) &&
	      (((optimize_data(expr) & 1) != 0) ||
	       (c_function_is_ok(sc, expr))))
	    {
	      int orig_data;
	      orig_data = optimize_data(expr);
	      set_optimize_data(expr, optimize_data(expr) | 1);
	      sort_f = end_dox_eval(sc, expr);
	      if (sort_f)
		{
		  NEW_FRAME_WITH_TWO_SLOTS(sc, closure_let(lessp), sc->envir, car(args), sc->F, cadr(args), sc->F);
		  initialize_dox_end_vars(sc, expr);
		  compare_func = sort_f;
		  sort_func = dox_compare;
		  compare_args = expr;
		}
	      set_optimize_data(expr, orig_data);
	    }
	}
    }

  switch (type(data))
    {
    case T_PAIR:
      len = s7_list_length(sc, data);            /* nil disposed of above, so 0 here == infinite */
      if (len <= 0)
	return(s7_error(sc, sc->WRONG_TYPE_ARG, list_2(sc, make_string_wrapper(sc, "sort! argument 1 should be a proper list: ~S"), data)));
      if (len < 2) 
	return(data);

      if (compare_func)
	{
	  int i;
	  s7_pointer vec, p;

	  vec = g_vector(sc, data);
	  elements = s7_vector_elements(vec);

	  sc->v = vec;
	  qsort((void *)elements, len, sizeof(s7_pointer), sort_func);
	  for (p = data, i = 0; i < len; i++, p = cdr(p))
	    car(p) = elements[i];

	  return(data);
	}

      push_stack(sc, OP_SORT_PAIR_END, cons(sc, data, lessp), sc->code); /* save and gc protect the original list and func */
      car(args) = g_vector(sc, data);
      break;

    case T_STRING:
      {
	int i;
	s7_pointer vec;
	unsigned char *chrs;

	len = string_length(data);
	if (len < 2) return(data);
	vec = make_vector_1(sc, len, NOT_FILLED, T_VECTOR);
	elements = s7_vector_elements(vec);
	chrs = (unsigned char *)string_value(data);

	if (is_bytevector(data))
	  {
	    for (i = 0; i < len; i++)
	      elements[i] = small_int(chrs[i]);
	  }
	else
	  {
	    for (i = 0; i < len; i++)
	      elements[i] = chars[chrs[i]];
	  }

	if (compare_func)
	  {
	    sc->v = vec;	  
	    qsort((void *)elements, len, sizeof(s7_pointer), sort_func);
	    
	    if (is_bytevector(data))
	      {
		for (i = 0; i < len; i++)
		  chrs[i] = (char)integer(elements[i]);
	      }
	    else
	      {
		for (i = 0; i < len; i++)
		  chrs[i] = character(elements[i]);
	      }
	    return(data);
	  }
	
	push_stack(sc, OP_SORT_STRING_END, cons(sc, data, lessp), sc->code);
	car(args) = vec;
      }
      break;

    case T_INT_VECTOR:
    case T_FLOAT_VECTOR:
      {
	int i;
	s7_pointer vec;
	
	/* currently we have to make the ordinary vector here even if not compare_func
	 *   because the sorter uses vector_element to access sort args (see SORT_DATA in eval).
	 *   This is probably better than passing down getter/setter (fewer allocations).
	 *   get/set macro in eval is SORT_DATA(k) then s7_vector_to_list if pair at start (sort_*_end)
	 *
	 * if we know the underlying C function (say < or >), we could greatly optimize this --
	 *   is it worth the effort?  What are the most common cases here -- I expect sort vector of strings.
	 */
	len = vector_length(data);
	if (len < 2) return(data);
	vec = make_vector_1(sc, len, NOT_FILLED, T_VECTOR);
	elements = s7_vector_elements(vec);

	for (i = 0; i < len; i++)
	  elements[i] = vector_getter(data)(sc, data, i);

	if (compare_func)
	  {
	    sc->v = vec;	  
	    qsort((void *)elements, len, sizeof(s7_pointer), sort_func);
	    
	    for (i = 0; i < len; i++)
	      vector_setter(data)(sc, data, i, elements[i]);
	    return(data);
	  }
	
	push_stack(sc, OP_SORT_VECTOR_END, cons(sc, data, lessp), sc->code); /* save and gc protect the original homogenous vector and func */
	car(args) = vec;
      }
      break;

    case T_VECTOR:
      len = vector_length(data);
      if (len < 2) return(data);
      if (compare_func)
	{
	  qsort((void *)s7_vector_elements(data), len, sizeof(s7_pointer), sort_func);
	  return(data);
	}
      break;

    default:
      check_method(sc, data, sc->SORT, args);
      return(wrong_type_argument_with_type(sc, sc->SORT, small_int(1), data, A_SEQUENCE));
    }

  n = len - 1;
  k = ((int)(n / 2)) + 1;
  {
    s7_pointer lx;

    lx = s7_make_vector(sc, (sc->safety == 0) ? 4 : 6); 
    sc->v = lx;

    vector_element(lx, 0) = make_mutable_integer(sc, n);
    vector_element(lx, 1) = make_mutable_integer(sc, k);
    vector_element(lx, 2) = make_mutable_integer(sc, 0);
    vector_element(lx, 3) = make_mutable_integer(sc, 0);
    if (sc->safety != 0)
      {
	vector_element(lx, 4) = make_mutable_integer(sc, 0);
	vector_element(lx, 5) = make_integer(sc, n * n);
      }
    push_stack(sc, OP_SORT, args, lx);
  }
  return(sc->F);
  
  /* if the comparison function waffles, sort! can hang: (sort! '(1 2 3) (lambda (a b) (= a b)))
   * set 'safety to 1 to add a check for this loop, but the "safe" procedures are direct, so unchecked.
   */
}



/* -------- hash tables -------- */

bool s7_is_hash_table(s7_pointer p)
{
  return(is_hash_table(p));
}


static s7_pointer g_is_hash_table(s7_scheme *sc, s7_pointer args)
{
  #define H_is_hash_table "(hash-table? obj) returns #t if obj is a hash-table"
  check_boolean_method(sc, is_hash_table, sc->IS_HASH_TABLE, args);
}


static s7_pointer g_hash_table_size(s7_scheme *sc, s7_pointer args)
{
  #define H_hash_table_size "(hash-table-size obj) returns the size of the hash-table obj"
  if (!is_hash_table(car(args)))
    {
      check_method(sc, car(args), sc->HASH_TABLE_SIZE, args);
      return(simple_wrong_type_argument(sc, sc->HASH_TABLE_SIZE, car(args), T_HASH_TABLE));
    }
  return(make_integer(sc, hash_table_length(car(args))));
}


static s7_pointer g_hash_table_entries(s7_scheme *sc, s7_pointer args)
{
  #define H_hash_table_entries "(hash-table-entries obj) returns the number of entries in the hash-table obj"
  if (!is_hash_table(car(args)))
    {
      check_method(sc, car(args), sc->HASH_TABLE_ENTRIES, args);
      return(simple_wrong_type_argument(sc, sc->HASH_TABLE_ENTRIES, car(args), T_HASH_TABLE));
    }
  return(make_integer(sc, hash_table_entries(car(args))));
}


static int hash_float_location(s7_Double x)
{
  int loc;
  if ((is_inf(x)) || (is_NaN(x)))
    return(0);

  x = fabs(x);
  if (x < 100.0)
    loc = 1000.0 * x;     /* this means hash_table_float_epsilon only works if it is less than about .001 */
  else loc = x;

  if (loc < 0) /* (hash-table-index 1e+18) -> -2147483648 */
    return(0);

  return(loc);
}


static unsigned int hash_loc(s7_scheme *sc, s7_pointer key)
{
  unsigned int loc = 0;

  switch (type(key))
    {
    case T_STRING:
      if (string_hash(key) != 0)
	return(string_hash(key));
      loc = raw_string_hash((const unsigned char *)string_value(key), string_length(key));
      string_hash(key) = loc;
      return(loc);

    case T_INTEGER:
      if (integer(key) < 0)
	return((unsigned int)(-integer(key)));
      return((unsigned int)integer(key)); /* overflow possible! */

    case T_REAL:
      return(hash_float_location(real(key)));
      /* currently 1e300 goes to most-negative-fixnum! -> 0 after logand size, I hope 
       *
       * we need round, not floor for the location calculation in the real/complex cases else
       *    1-eps doesn't match 1.0, but 1+eps does.  And what if round(val) is too big for int?
       *    lrint is complex and requires special compiler flags to get any speed (-fno-math-errno).
       *    all we need is (int)(val+0.5) -- all the other stuff is pointless in this context
       */

    case T_RATIO:
      loc = (unsigned int)denominator(key); /* overflow possible */
      /* if (loc < 0) return(-loc); */
      return(loc);

    case T_COMPLEX:
      return(hash_float_location(real_part(key)));

    case T_SYMBOL:
      return(symbol_hash(key));

    case T_SYNTAX:
      return(symbol_hash(syntax_symbol(key)));

    case T_CHARACTER:
      return(character(key));

    case T_HASH_TABLE:
    case T_INT_VECTOR:
    case T_FLOAT_VECTOR:
    case T_VECTOR:
      return(vector_length(key));

    case T_PAIR:
      /* len+loc(car) is not horrible, but it means (for example) every list '(set! a b) is hashed to the same location,
       *   so at least we need to take cadr into account if possible.  Better would combine the list_length(max 5 == safe_strlen5?) call
       *   with stats like symbols/pairs/constants at top level, then use those to spread it out over all the locs.
       */
      {
	s7_pointer p1;
	if (car(key) != key)
	  loc = hash_loc(sc, car(key)) + 1;
	p1 = cdr(key);
	if (is_pair(p1))
	  {
	    s7_pointer p2;
	    if ((car(p1) != key) && (car(p1) != p1))
	      {
		if (is_pair(car(p1)))
		  loc = hash_loc(sc, caar(p1)) + 1 + loc * 2 + s7_list_length(sc, car(p1));
		else loc = hash_loc(sc, car(p1)) + 1 + loc * 2;
	      }
	    p2 = cdr(p1);
	    if (is_pair(p2))
	      {
		if ((car(p2) != key) && (car(p2) != p1) && (car(p2) != p2))
		  {
		    if (is_pair(car(p2)))
		      loc = hash_loc(sc, caar(p2)) + 1 + loc * 2 + s7_list_length(sc, car(p2));
		    else loc = hash_loc(sc, car(p2)) + 1 + loc * 2;
		  }
	      }
	  }
	return(loc);
      }
#if 0
      loc = (int)s7_list_length(sc, key);
      if ((loc > 0) && (key != car(key)))      /* avoid infinite loop if #1=(#1#)! */
	return(loc + hash_loc(sc, car(key)));
      return(0);
#endif

    default:
      break;
    }

  return(type(key));
}


static s7_pointer g_hash_table_index(s7_scheme *sc, s7_pointer args)
{
  return(make_integer(sc, hash_loc(sc, car(args))));
}


static s7_pointer hash_empty(s7_scheme *sc, s7_pointer table, s7_pointer key)
{
  return(sc->NIL);
}


static s7_pointer hash_int(s7_scheme *sc, s7_pointer table, s7_pointer key)
{
  if (s7_is_integer(key))
    {
      s7_Int keyval;
      s7_pointer x;
      unsigned int loc;
      unsigned long long int hash_len;

      hash_len = (unsigned long long int)hash_table_length(table) - 1;
      keyval = integer(key);
      if (keyval < 0)
	loc = (unsigned int)((-keyval) & hash_len);
      else loc = (unsigned int)(keyval & hash_len);

      for (x = hash_table_elements(table)[loc]; is_pair(x); x = cdr(x))
	if (integer(fcdr(x)) == keyval)
	  return(car(x));
    }
  return(sc->NIL);
}


static s7_pointer hash_string(s7_scheme *sc, s7_pointer table, s7_pointer key)
{
  /* cityhash and spookyhash are the top-of-the-line, but do we need that much power? 
   */
  if (is_string(key))
    {
      s7_pointer x;
      unsigned int hash_len, loc, orig;

      hash_len = (unsigned int)hash_table_length(table) - 1;
      loc = string_hash(key);
      if (loc == 0)
	{
	  loc = raw_string_hash((const unsigned char *)string_value(key), string_length(key));
	  string_hash(key) = loc;
	}
      orig = loc;
      loc &= hash_len;
      for (x = hash_table_elements(table)[loc]; is_pair(x); x = cdr(x))
	if ((orig == string_hash(fcdr(x))) &&
	    (strings_are_equal(string_value(fcdr(x)), string_value(key))))
	  return(car(x));
    }
  return(sc->NIL);
}


static s7_pointer hash_char(s7_scheme *sc, s7_pointer table, s7_pointer key)
{
  if (s7_is_character(key))
    {
      s7_pointer x;
      int hash_len, loc;
      hash_len = (int)hash_table_length(table) - 1;
      loc = character(key) & hash_len;
      for (x = hash_table_elements(table)[loc]; is_pair(x); x = cdr(x))
	if (fcdr(x) == key)
	  return(car(x));
    }
  return(sc->NIL);
}


static s7_pointer hash_float_1(s7_scheme *sc, s7_pointer table, int loc, s7_Double keyval)
{
  s7_pointer x;
  bool look_for_nan;
  look_for_nan = is_NaN(keyval);
  
  for (x = hash_table_elements(table)[loc]; is_pair(x); x = cdr(x))
    {
      s7_pointer y;
      y = fcdr(x);
      if (type(y) == T_REAL) /* we're possibly called from hash_equal, so keys might not be T_REAL */
	{
	  s7_Double val;
	  val = real(y);
	  if (look_for_nan)
	    {
	      if (is_NaN(val))
		return(car(x));
	    }
	  else
	    {
	      if ((val == keyval) ||   /* inf case */
		  (fabs(val - keyval) < sc->hash_table_float_epsilon))
		return(car(x));
	    }
	}
    }
  return(sc->NIL);
}


static s7_pointer hash_float(s7_scheme *sc, s7_pointer table, s7_pointer key)
{
  /* give the equality check some room.  We only get here if key is T_REAL.
   *   also inf == inf and nan == nan
   */
  s7_Double keyval;
  int hash_len, loc;

  hash_len = (int)hash_table_length(table) - 1;
  keyval = real(key);
  loc = hash_float_location(keyval) & hash_len;

  return(hash_float_1(sc, table, loc, keyval));
}


static s7_pointer hash_complex_1(s7_scheme *sc, s7_pointer table, int loc, s7_pointer key)
{
  s7_pointer x;
  for (x = hash_table_elements(table)[loc]; is_pair(x); x = cdr(x))
    {
      s7_pointer y;
      y = fcdr(x);
      if ((type(y) == T_COMPLEX) &&
	  (s7_is_morally_equal(sc, y, key, NULL)))
	return(car(x));
    }
  return(sc->NIL);
}


static s7_pointer hash_equal(s7_scheme *sc, s7_pointer table, s7_pointer key)
{
  /* there is a problem here if we hash mixed numeric types and floats are compared using float-epsilon.
   *   to remain consistent even in mixed cases, we need to trap the floats here before s7_is_equal.
   *   complex numbers are similar.
   *
   * morally-equal? can't be used here unless we use it even in hash_int and other such cases.
   *   otherwise, what matches depends on what else is in the hash, and on the order in which 
   *   they were placed there.  But I think the key 1 should not match the key 1.0+epsilon,
   *   and similarly for ratios, yet NaN should definitely match NaN (else it's a nutty no-op
   *   to use such a key). 
   *
   * yet another problem: hashing a closure can't work.  A closure is not necessarily equal to itself.
   */
  s7_pointer x;
  unsigned int hash_len, loc;

  hash_len = (int)hash_table_length(table) - 1;
  loc = hash_loc(sc, key) & hash_len;

  switch (type(key))
    {
    case T_REAL:
      return(hash_float_1(sc, table, loc, real(key)));

    case T_COMPLEX:
      return(hash_complex_1(sc, table, loc, key));

    case T_SYNTAX:
      for (x = hash_table_elements(table)[loc]; is_pair(x); x = cdr(x))
	if ((is_syntax(fcdr(x))) && 
	    (syntax_symbol(fcdr(x))) == syntax_symbol(key)) /* the opcodes might differ, but the symbols should not */
	  return(car(x));
      break;

    case T_HASH_TABLE:
    case T_SYMBOL:
    case T_CHARACTER:
      for (x = hash_table_elements(table)[loc]; is_pair(x); x = cdr(x))
	if (fcdr(x) == key)
	  return(car(x));
      break;
      
    default:
      /* we can get into an infinite loop here, but it requires 2 hash tables that are members of each other
       *    and key is one of them, so I changed the equality check above to use eq? -- not sure this is right.
       */
      /* hope for an easy case... */
      for (x = hash_table_elements(table)[loc]; is_pair(x); x = cdr(x))
	if (fcdr(x) == key)
	  return(car(x));

      for (x = hash_table_elements(table)[loc]; is_pair(x); x = cdr(x))
	if (s7_is_equal(sc, fcdr(x), key))
	  return(car(x));
    }
  return(sc->NIL);
}


static s7_pointer hash_eq_func(s7_scheme *sc, s7_pointer table, s7_pointer key)
{
  s7_pointer x;
  unsigned int hash_len, loc;
  s7_function f;

  f = c_function_call(hash_table_eq_function(table));
  hash_len = (unsigned int)hash_table_length(table) - 1;
  loc = hash_loc(sc, key) & hash_len;

  car(sc->T2_1) = key;
  for (x = hash_table_elements(table)[loc]; is_pair(x); x = cdr(x))
    {
      car(sc->T2_2) = fcdr(x);
      if (is_true(sc, (*f)(sc, sc->T2_1)))
	return(car(x));
    }
  return(sc->NIL);
}


static s7_pointer hash_symbol(s7_scheme *sc, s7_pointer table, s7_pointer key)
{
  if (is_symbol(key))
    {
      s7_pointer x;
      int hash_len, loc;
      hash_len = (int)hash_table_length(table) - 1;
      loc = symbol_hash(key) & hash_len;
      for (x = hash_table_elements(table)[loc]; is_pair(x); x = cdr(x))
	if (fcdr(x) == key)
	  return(car(x));
    }
  return(sc->NIL);
}


/* default size (511) may be too big -- smaller is faster in lint (even 31), but not in make-index.
 *   another approach: bi-level tables where we index twice, and thereby skip over (sqrt n) (or whatever) at a time.  
 *   does this happen often? would it matter in the gc enough to warrant the code?
 * or perhaps combine the back-and-forth indexing to a hash-table, so we'd only have the sections we need
 *
 * also what about a simplified hash_table with just the keys => a set, similar to the memq/assq difference
 *   we could add T_SET just like T_BYTEVECTOR and specialize where necessary.
 *   vector->set->hash-table->environment->library
 */

s7_pointer s7_make_hash_table(s7_scheme *sc, s7_Int size)
{
  s7_pointer table;
  int i;
  /* size is rounded up to the next power of 2 */

  if ((size & (size + 1)) != 0)      /* already 2^n - 1 ? */
    {
      size--;
      size |= (size >> 1);
      size |= (size >> 2);
      size |= (size >> 4);
      size |= (size >> 8);
      size |= (size >> 16);
      if (s7_int_bits > 31) /* this is either 31 or 63 */
	size |= (size >> 32);
    }
  size++;

  NEW_CELL(sc, table);
  hash_table_length(table) = size;
  hash_table_elements(table) = (s7_pointer *)malloc(size * sizeof(s7_pointer));
  if (!(hash_table_elements(table)))
    return(s7_error(sc, make_symbol(sc, "out-of-memory"), 
		    list_1(sc, make_string_wrapper(sc, "make-hash-table allocation failed!"))));
  for (i = 0; i < size; i++)
    hash_table_element(table, i) = sc->NIL;
  /* or use the vector fill stuff here, but that now just unrolls the loop */
  hash_table_function(table) = hash_empty;
  hash_table_eq_function(table) = NULL;
  hash_table_entries(table) = 0;
  set_type(table, T_HASH_TABLE | T_SAFE_PROCEDURE);
  add_hash_table(sc, table);

  return(table);
}


static s7_pointer g_make_hash_table(s7_scheme *sc, s7_pointer args)
{
  #define H_make_hash_table "(make-hash-table (size 511) eq-func) returns a new hash table"
  s7_Int size;
  size = sc->default_hash_table_length;

  if (is_not_null(args))
    {
      s7_pointer p;
      p = car(args);
      if (!s7_is_integer(p))
	{
	  s7_pointer p1;
	  if (!s7_is_integer(p1 = check_values(sc, p, args)))
	    {
	      check_method(sc, p, sc->MAKE_HASH_TABLE, args);
	      return(wrong_type_argument(sc, sc->MAKE_HASH_TABLE, small_int(1), p, T_INTEGER));
	    }
	  p = p1;
	}
      size = s7_integer(p);
      if (size <= 0)                      /* we need s7_Int here to catch (make-hash-table most-negative-fixnum) etc */
	return(simple_out_of_range(sc, sc->MAKE_HASH_TABLE, p, make_string_wrapper(sc, "should be a positive integer")));
      if (size > MAX_VECTOR_LENGTH)
	return(simple_out_of_range(sc, sc->MAKE_HASH_TABLE, p, ITS_TOO_LARGE));

      if (is_not_null(cdr(args)))
	{
	  s7_pointer p2;
	  int typ;
	  p2 = cadr(args);
	  typ = type(p2);
	  /* true procedure needed here else (make-hash-table ()) will work
	   */
	  if ((typ < T_C_FUNCTION) && (typ != T_CLOSURE) && (typ != T_CLOSURE_STAR)) 
	    {
	      check_method(sc, p2, sc->MAKE_HASH_TABLE, list_2(sc, p, p2));
	      return(simple_wrong_type_argument_with_type(sc, sc->MAKE_HASH_TABLE, p2, A_PROCEDURE));
	    }
	  if (is_c_function(p2)) /* we need to restrict this function so that hash-table-ref|set! remain safe functions */
	    {
	      s7_pointer ht;
	      ht = s7_make_hash_table(sc, size);
	      hash_table_function(ht) = hash_eq_func;
	      hash_table_eq_function(ht) = p2;
	      return(ht);
	    }

	  /* and if it isn't?? */
	}
    }
  return(s7_make_hash_table(sc, size));
}


s7_pointer s7_hash_table_ref(s7_scheme *sc, s7_pointer table, s7_pointer key)
{
  s7_pointer x;
  x = (*hash_table_function(table))(sc, table, key);

  if (is_not_null(x))
    return(cdr(x));
  return(sc->F);
}


s7_pointer s7_hash_table_set(s7_scheme *sc, s7_pointer table, s7_pointer key, s7_pointer value)
{
  s7_pointer x;
  x = (*hash_table_function(table))(sc, table, key);

  if (is_not_null(x))
    cdr(x) = value;
  else
    {
      unsigned int hash_len, loc;

      hash_len = hash_table_length(table) - 1;
      loc = hash_loc(sc, key) & hash_len;
      hash_table_entries(table)++;

      if (hash_table_function(table) != hash_eq_func)
	{
	  int typ;
	  typ = type(key);
	  if (hash_table_function(table) == hash_empty)
	    {
	      switch (typ)
		{
		case T_STRING:
		  hash_table_function(table) = hash_string;
		  break;
		  
		case T_INTEGER:
		  hash_table_function(table) = hash_int;
		  break;
		  
		case T_REAL:
		  hash_table_function(table) = hash_float;
		  break;
		  
		case T_RATIO:
		case T_COMPLEX:
		  hash_table_function(table) = hash_equal;
		  break;
		  
		case T_SYMBOL:
		  hash_table_function(table) = hash_symbol;
		  break;
		  
		case T_CHARACTER:
		  hash_table_function(table) = hash_char;
		  break;
		  
		default:
		  hash_table_function(table) = hash_equal;
		  /* if every key structure is simple, we'd like to use a simple_equal checker (no circles),
		   *    but circles can sneak in!  lists (etc) are dangerous keys:
		   *
		   (let ((ht (make-hash-table))
		         (lst1 (list 1 2))
		         (lst2 (list 1 2)))
		     (set! (ht lst1) 32)
		     (let ((start (ht lst2)))
		       (set! (lst1 0) 3)
		       (list start (ht lst2))))
		   (32 #f)
		   *
		   * but this applies to all such variables, even strings.  Do other schemes copy the key?
		   */
		  break;
		}
	    }
	  else
	    {
	      switch (typ)
		{
		case T_STRING:
		  if (hash_table_function(table) != hash_string)
		    hash_table_function(table) = hash_equal;
		  break;
		  
		case T_INTEGER:
		  if (hash_table_function(table) != hash_int)
		    hash_table_function(table) = hash_equal;
		  break;
		  
		case T_REAL:
		  if (hash_table_function(table) != hash_float)
		    hash_table_function(table) = hash_equal;
		  break;
		  
		case T_RATIO:
		case T_COMPLEX:
		  hash_table_function(table) = hash_equal;
		  break;
		  
		case T_CHARACTER:
		  if (hash_table_function(table) != hash_char)
		    hash_table_function(table) = hash_equal;
		  break;
		  
		case T_SYMBOL:
		  if (hash_table_function(table) != hash_symbol)
		    hash_table_function(table) = hash_equal;
		  break;
		  
		default:
		  hash_table_function(table) = hash_equal;
		  break;
		}
	    }
	}

      x = cons_unchecked(sc, cons(sc, key, value), hash_table_elements(table)[loc]);
      hash_table_elements(table)[loc] = x;
      set_fcdr(x, key);
    }
  return(value);
}


static s7_pointer g_hash_table_ref(s7_scheme *sc, s7_pointer args)
{
  #define H_hash_table_ref "(hash-table-ref table key) returns the value associated with key in the hash table"
  s7_pointer table;

  table = car(args);
  
  if (!is_hash_table(table))
    {
      check_method(sc, table, sc->HASH_TABLE_REF, args);
      return(wrong_type_argument(sc, sc->HASH_TABLE_REF, small_int(1), table, T_HASH_TABLE));
    }
  /*
    (define (href H . args) 
      (if (null? (cdr args))
          (hash-table-ref H (car args))
          (apply href (hash-table-ref H (car args)) (cdr args))))
  */
  if (is_null(cddr(args)))
    return(s7_hash_table_ref(sc, table, cadr(args)));
  return(implicit_index(sc, s7_hash_table_ref(sc, table, cadr(args)), cddr(args)));
}


static s7_pointer hash_table_ref_2;
static s7_pointer g_hash_table_ref_2(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x, table;
  table = car(args);
  
  if (!is_hash_table(table))
    {
      check_method(sc, table, sc->HASH_TABLE_REF, args);
      return(wrong_type_argument(sc, sc->HASH_TABLE_REF, small_int(1), table, T_HASH_TABLE));
    }
  if (hash_table_entries(table) == 0)
    return(sc->F);

  x = (*hash_table_function(table))(sc, table, cadr(args));
  if (is_not_null(x))
    return(cdr(x));
  return(sc->F);
}

static s7_pointer hash_table_ref_ss;
static s7_pointer g_hash_table_ref_ss(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x, table;
  table = find_symbol_checked(sc, car(args));
  
  if (!is_hash_table(table))
    {
      check_method(sc, table, sc->HASH_TABLE_REF, list_2(sc, table, find_symbol_checked(sc, cadr(args))));
      return(wrong_type_argument(sc, sc->HASH_TABLE_REF, small_int(1), table, T_HASH_TABLE));
    }
  if (hash_table_entries(table) == 0)
    return(sc->F);

  x = (*hash_table_function(table))(sc, table, find_symbol_checked(sc, cadr(args)));
  if (is_not_null(x))
    return(cdr(x));
  return(sc->F);
}

static s7_pointer hash_table_ref_car;
static s7_pointer g_hash_table_ref_car(s7_scheme *sc, s7_pointer args)
{
  s7_pointer x, y, table;
  table = find_symbol_checked(sc, car(args));
  
  if (!is_hash_table(table))
    {
      check_method(sc, table, sc->HASH_TABLE_REF, list_2(sc, table, car(find_symbol_checked(sc, cadadr(args)))));
      return(wrong_type_argument(sc, sc->HASH_TABLE_REF, small_int(1), table, T_HASH_TABLE));
    }
  if (hash_table_entries(table) == 0)
    return(sc->F);

  y = find_symbol_checked(sc, cadadr(args));
  if (!is_pair(y))
    return(simple_wrong_type_argument(sc, sc->CAR, y, T_PAIR));

  x = (*hash_table_function(table))(sc, table, car(y));
  if (is_not_null(x))
    return(cdr(x));
  return(sc->F);
}


static s7_pointer g_hash_table_set(s7_scheme *sc, s7_pointer args)
{
  #define H_hash_table_set "(hash-table-set! table key value) sets the value associated with key in the hash table to value"
  s7_pointer table;

  table = car(args);
  
  if (!is_hash_table(table))
    {
      check_method(sc, table, sc->HASH_TABLE_SET, args);
      return(wrong_type_argument(sc, sc->HASH_TABLE_SET, small_int(1), table, T_HASH_TABLE));
    }
  /* how would (set! (ht a b) c) choose the inner table if (ht a b) is not found?
   *   I'm not sure the multi-index case makes sense here
   */
  return(s7_hash_table_set(sc, table, cadr(args), caddr(args)));
}


static s7_pointer g_hash_table(s7_scheme *sc, s7_pointer args)
{
  #define H_hash_table "(hash-table ...) returns a hash-table containing the cons's passed as its arguments. \
That is, (hash-table '(\"hi\" . 3) (\"ho\" . 32)) returns a new hash-table with the two key/value pairs preinstalled."

  int len;
  s7_pointer x, ht;

  /* this accepts repeated keys: (hash-table '(a . 1) '(a . 1)) -- or just '(a) for that matter
   */
  for (len = 0, x = args; is_pair(x); x = cdr(x), len++)
    if ((!is_pair(car(x))) &&
	(!is_null(car(x))))
      return(wrong_type_argument(sc, sc->HASH_TABLE, make_integer(sc, position_of(x, args)), car(x), T_PAIR));

  ht = s7_make_hash_table(sc, (len > 512) ? 4095 : 511);
  if (len > 0)
    {
      int ht_loc;
      ht_loc = s7_gc_protect(sc, ht); /* hash_table_set can cons, so we need to protect this */
      for (x = args; is_pair(x); x = cdr(x)) 
	{
	  if (is_pair(car(x)))
	    s7_hash_table_set(sc, ht, caar(x), cdar(x));
	}
      s7_gc_unprotect_at(sc, ht_loc);
    }
  return(ht);
}


static s7_pointer g_hash_table_star(s7_scheme *sc, s7_pointer args)
{
  #define H_hash_table_star "(hash-table* ...) returns a hash-table containing the symbol/value pairs passed as its arguments. \
That is, (hash-table* 'a 1 'b 2) returns a new hash-table with the two key/value pairs preinstalled."

  int len;
  s7_pointer ht;

  len = safe_list_length(sc, args);
  if (len & 1)
    return(s7_error(sc, sc->WRONG_TYPE_ARG, list_2(sc, make_string_wrapper(sc, "hash-table* got an odd number of arguments: ~S"), args)));
  len /= 2;

  ht = s7_make_hash_table(sc, (len > 512) ? 4095 : 511);
  if (len > 0)
    {
      int ht_loc;
      s7_pointer x, y;
      ht_loc = s7_gc_protect(sc, ht); /* hash_table_set can cons, so we need to protect this */

      for (x = args, y = cdr(args); is_pair(y); x = cddr(x), y = cddr(y)) 
	s7_hash_table_set(sc, ht, car(x), car(y));

      s7_gc_unprotect_at(sc, ht_loc);
    }
  return(ht);
}


static s7_pointer hash_list_copy(s7_scheme *sc, s7_pointer obj)
{
  if (is_pair(obj))
    {
      s7_pointer p;
      p = cons(sc, s7_copy(sc, car(obj)), hash_list_copy(sc, cdr(obj)));
      set_fcdr(p, caar(p));
      return(p);
    }
  return(obj);
}


static s7_pointer hash_table_copy(s7_scheme *sc, s7_pointer old_hash)
{
  /* this has to copy not only the lists but the cons's in the lists! */
  int i, len;
  s7_pointer new_hash;
  s7_pointer *old_lists, *new_lists;
  int gc_loc;

  len = hash_table_length(old_hash);
  new_hash = s7_make_hash_table(sc, len);
  gc_loc = s7_gc_protect(sc, new_hash);

  old_lists = hash_table_elements(old_hash);
  new_lists = hash_table_elements(new_hash);

  for (i = 0; i < len; i++)
    if (is_not_null(old_lists[i]))
      new_lists[i] = hash_list_copy(sc, old_lists[i]);

  hash_table_entries(new_hash) = hash_table_entries(old_hash);
  hash_table_function(new_hash) = hash_table_function(old_hash);
  hash_table_eq_function(new_hash) = hash_table_eq_function(old_hash);

  s7_gc_unprotect_at(sc, gc_loc);
  return(new_hash);
}


static s7_pointer hash_table_reverse(s7_scheme *sc, s7_pointer old_hash)
{
  int i, len;
  s7_pointer new_hash;
  s7_pointer *old_lists;
  int gc_loc;

  len = hash_table_length(old_hash);
  new_hash = s7_make_hash_table(sc, len);
  gc_loc = s7_gc_protect(sc, new_hash);

  old_lists = hash_table_elements(old_hash);
  /* don't set entries or function -- s7_hash_table_set below will handle those (really?) */

  for (i = 0; i < len; i++)
    {
      s7_pointer x;
      for (x = old_lists[i]; is_not_null(x); x = cdr(x))
	s7_hash_table_set(sc, new_hash, cdar(x), caar(x));
    }

  if (hash_table_function(old_hash) == hash_eq_func)
    {
      hash_table_function(new_hash) = hash_eq_func;
      hash_table_eq_function(new_hash) = hash_table_eq_function(old_hash);
    }
    
  s7_gc_unprotect_at(sc, gc_loc);
  return(new_hash);
}


static s7_pointer hash_table_clear(s7_scheme *sc, s7_pointer table)
{
  int i, len;

  len = hash_table_length(table);
  for (i = 0; i < len; i++)
    hash_table_element(table, i) = sc->NIL;
  hash_table_entries(table) = 0;
  if (hash_table_function(table) != hash_eq_func)
    hash_table_function(table) = hash_empty;
  return(table);
}



/* ---------------- hash-table iterators ---------------- */

typedef struct {
  s7_pointer table, lst;
  int loc;
} ht_iter;

static char *print_ht_iter(s7_scheme *sc, void *val)
{
  char *str;
  str = (char *)malloc(32 * sizeof(char));
  snprintf(str, 32, "#<hash-table-iterator>");
  return(str);
}

static void ht_iter_free(void *p) {free(p);}

static bool equal_ht_iter(void *val1, void *val2)
{
  ht_iter *h1, *h2;
  if (val1 == val2) return(true);
  h1 = (ht_iter *)val1;
  h2 = (ht_iter *)val2;
  return((h1->table == h2->table) &&
	 (h1->loc == h2->loc)&&
	 (h1->lst == h2->lst));
}

static void mark_ht_iter(void *val)
{
  ht_iter *iter = (ht_iter *)val;
  s7_mark_object(iter->table);
  s7_mark_object(iter->lst);
}


static char *write_ht_iter_readably(s7_scheme *sc, void *val)
{
  write_readably_error(sc, "a hash-table-iterator");
  return(NULL);
}


static s7_pointer copy_ht_iter(s7_scheme *sc, s7_pointer obj)
{
  ht_iter *new_iter, *old_iter;
  old_iter = (ht_iter *)s7_object_value(obj);
  
  new_iter = (ht_iter *)malloc(sizeof(ht_iter));
  new_iter->lst = old_iter->lst;
  new_iter->table = old_iter->table;
  new_iter->loc = old_iter->loc;

  return(s7_make_object(sc, sc->ht_iter_tag, (void *)new_iter));
}


static s7_pointer g_make_hash_table_iterator(s7_scheme *sc, s7_pointer args)
{
  #define H_make_hash_table_iterator "(make-hash-table-iterator table) returns a function of no arguments that \
returns the next (key . value) pair in the hash-table each time it is called.  When there are no more pairs, it returns nil."

  ht_iter *iter;
  if (!is_hash_table(car(args)))
    {
      check_method(sc, car(args), sc->MAKE_HASH_TABLE_ITERATOR, args);
      return(simple_wrong_type_argument(sc, sc->MAKE_HASH_TABLE_ITERATOR, car(args), T_HASH_TABLE));
    }
  iter = (ht_iter *)malloc(sizeof(ht_iter));
  iter->lst = sc->NIL;
  iter->table = car(args);
  iter->loc = -1;

  return(s7_make_object(sc, sc->ht_iter_tag, (void *)iter));
}


static s7_pointer g_is_hash_table_iterator(s7_scheme *sc, s7_pointer args)
{
  #define H_is_hash_table_iterator "(hash-table-iterator? obj) returns #t if obj is a hash-table iterator."
  s7_pointer obj;

  obj = car(args);
  if ((s7_is_object(obj)) && 
      (c_object_type(obj) == sc->ht_iter_tag))
    return(sc->T);

  check_method(sc, obj, sc->IS_HASH_TABLE_ITERATOR, args);
  return(sc->F);
}


static s7_pointer hash_table_iterate(s7_scheme *sc, s7_pointer iterator)
{
  s7_pointer lst, table;
  ht_iter *iter;
  int loc, len;
  s7_pointer *elements;

  iter = (ht_iter *)s7_object_value(iterator);
  lst = iter->lst;
  if (is_pair(lst))
    {
      iter->lst = cdr(lst);
      return(car(lst));
    }

  table = iter->table;
  len = hash_table_length(table);
  elements = hash_table_elements(table);

  for (loc = iter->loc + 1; loc < len;  loc++)
    {
      s7_pointer x;
      x = elements[loc];
      if (is_not_null(x))
	{
	  iter->loc = loc;
	  iter->lst = cdr(x);
	  return(car(x));
	}
    }
  iter->loc = len;
  return(sc->NIL);
}


static s7_pointer ref_ht_iter(s7_scheme *sc, s7_pointer obj, s7_pointer args)
{
  /* don't check args -- it's simpler in map/for-each to pass an int to all objects in this context
   */
  return(hash_table_iterate(sc, obj));
}

/* no obvious meaning for length of a hash-table-iterator, or set!, fill!, and reverse
 */



/* -------------------------------- objects and functions -------------------------------- */

bool s7_is_function(s7_pointer p)  
{ 
  return(is_c_function(p));
}


bool s7_is_object(s7_pointer p) 
{ 
  return(is_c_object(p));
}


void s7_function_set_step_safe(s7_pointer f)
{
  set_step_safe(f);
}


static s7_pointer fallback_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  return(f);
}


void s7_function_set_class(s7_pointer f, s7_pointer base_f)
{
  c_function_class(f) = c_function_class(base_f);
  c_function_base(f) = base_f;
}


void s7_function_set_looped(s7_pointer f, s7_pointer c)
{
  if (!c_function_ext(f))
    c_function_ext(f) = (c_proc_ext_t *)calloc(1, sizeof(c_proc_ext_t));
  c_function_looped(f) = c;
}


void s7_function_set_let_looped(s7_pointer f, s7_pointer c)
{
  if (!c_function_ext(f))
    c_function_ext(f) = (c_proc_ext_t *)calloc(1, sizeof(c_proc_ext_t));
  c_function_let_looped(f) = c;
}


void s7_function_set_ex_parser(s7_pointer f, s7_ex *(*func)(s7_scheme *sc, s7_pointer expr))
{
  if (!c_function_ext(f))
    c_function_ext(f) = (c_proc_ext_t *)calloc(1, sizeof(c_proc_ext_t));
  c_function_ex_parser(f) = func;
}

s7_ex *(*s7_function_ex_parser(s7_pointer f))(s7_scheme *sc, s7_pointer expr)
{
  if (c_function_ext(f))
    return(c_function_ex_parser(f));
  return(NULL);
}

s7_ex *(*s7_ex_fallback(s7_scheme *sc))(s7_scheme *sc, s7_pointer expr, s7_pointer locals)
{
  return(sc->ex_fallback);
}

void s7_set_ex_fallback(s7_scheme *sc, s7_ex *(*fallback)(s7_scheme *sc, s7_pointer expr, s7_pointer locals))
{
  sc->ex_fallback = fallback;
}


s7_pointer (*s7_function_chooser(s7_scheme *sc, s7_pointer fnc))(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  return(c_function_chooser(fnc));
}

void s7_function_set_chooser(s7_scheme *sc, s7_pointer fnc,  s7_pointer (*chooser)(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr))
{
  c_function_chooser(fnc) = chooser;
}


void *s7_function_chooser_data(s7_scheme *sc, s7_pointer expr)
{
  if ((is_optimized(expr)) &&
      (is_symbol(car(expr))) &&
      (car(expr) != sc->QUOTE) &&
      (ecdr(expr)) &&
      (is_c_function(ecdr(expr))) &&
      (c_function_ext(ecdr(expr))))
    return(c_function_chooser_data(ecdr(expr)));
  return(NULL);
}

void *s7_function_chooser_data_direct(s7_pointer f)
{
  if ((is_c_function(f)) &&
      (c_function_ext(f)))
    return(c_function_chooser_data(f));
  return(NULL);
}

void s7_function_chooser_set_data(s7_scheme *sc, s7_pointer f, void *data)
{
  if (!c_function_ext(f))
    c_function_ext(f) = (c_proc_ext_t *)calloc(1, sizeof(c_proc_ext_t));
  c_function_chooser_data(f) = data;
}


s7_function s7_function_choice(s7_scheme *sc, s7_pointer expr)
{
  if ((is_optimized(expr)) &&
      (is_symbol(car(expr))) &&
      (car(expr) != sc->QUOTE) &&
      (ecdr(expr)) &&
      (is_c_function(ecdr(expr))))
    return(c_function_call(ecdr(expr)));
  return(NULL);
}

void s7_function_choice_set_direct(s7_scheme *sc, s7_pointer expr)
{
  set_optimize_data(expr, HOP_SAFE_C_C);
  clear_unsafe(expr);
}

bool s7_function_choice_is_direct(s7_scheme *sc, s7_pointer expr)
{
  return((is_optimized(expr)) && 
	 ((optimize_data(expr) == HOP_SAFE_C_C) ||
	  ((optimize_data(expr) == OP_SAFE_C_C) &&
	   (symbol_id(car(expr)) == 0))));
}

bool s7_function_choice_is_direct_to_real(s7_scheme *sc, s7_pointer expr)
{
  return((is_optimized(expr)) && 
	 (ecdr(expr)) &&
	 (returns_temp(ecdr(expr))) &&
	 ((optimize_data(expr) == HOP_SAFE_C_C) ||
	  ((optimize_data(expr) == OP_SAFE_C_C) &&
	   (symbol_id(car(expr)) == 0))));
}


s7_pointer s7_call_direct(s7_scheme *sc, s7_pointer expr)
{
  return(c_function_call(ecdr(expr))(sc, cdr(expr)));
}


s7_Double s7_call_direct_to_real_and_free(s7_scheme *sc, s7_pointer expr)
{
  s7_Double val;
  s7_pointer temp;

  temp = c_function_call(ecdr(expr))(sc, cdr(expr));
#if (!WITH_GMP)
  val = real(temp);
#else
  val = s7_real(temp);
#endif
  if (is_simple_real(temp)) /* i.e. not immutable, not integer or something else unexpected */
    {
      clear_type(temp);
      (*(sc->free_heap_top++)) = temp;
    }
  return(val);
}


s7_pointer s7_make_function(s7_scheme *sc, const char *name, s7_function f, int required_args, int optional_args, bool rest_arg, const char *doc)
{
  c_proc_t *ptr;
  unsigned int ftype = T_C_FUNCTION;
  s7_pointer x;

  x = alloc_pointer();
  heap_location(x) = NOT_IN_HEAP;

  ptr = (c_proc_t *)calloc(1, sizeof(c_proc_t));
  if (required_args == 0)
    {
      if (rest_arg)
	ftype = T_C_ANY_ARGS_FUNCTION;
      else 
	{
	  if (optional_args != 0)
	    ftype = T_C_OPT_ARGS_FUNCTION;
	  /* a thunk needs to check for no args passed */
	}
    }
  else
    {
      if (rest_arg)
	ftype = T_C_RST_ARGS_FUNCTION;
    }
  
  set_type(x, ftype | T_PROCEDURE);

  c_function_data(x) = ptr;
  c_function_call(x) = f;
  c_function_base(x) = x;
  c_function_setter(x) = sc->F;
  c_function_name(x) = name;   /* (procedure-name proc) => (format #f "~A" proc) */
  c_function_name_length(x) = safe_strlen(name);
  if (doc)
    c_function_documentation(x) = make_permanent_string(doc);

  c_function_required_args(x) = required_args;
  c_function_optional_args(x) = optional_args;
  c_function_has_rest_arg(x) = rest_arg;
  if (rest_arg)
    c_function_all_args(x) = MAX_ARITY;
  else c_function_all_args(x) = required_args + optional_args;

  c_function_class(x) = ++sc->f_class;
  c_function_chooser(x) = fallback_chooser;
  c_function_ext(x) = NULL;

  return(x);
}


s7_pointer s7_make_safe_function(s7_scheme *sc, const char *name, s7_function f, int required_args, int optional_args, bool rest_arg, const char *doc)
{
  s7_pointer p;
  p = s7_make_function(sc, name, f, required_args, optional_args, rest_arg, doc);
  set_type(p, type(p) | T_SAFE_PROCEDURE);
  return(p);
}


bool s7_is_procedure(s7_pointer x)
{
  return(is_procedure(x)); /* this returns "is applicable" so it is true for applicable c_objects, macros, etc */
}


static s7_pointer g_is_procedure(s7_scheme *sc, s7_pointer args)
{
  #define H_is_procedure "(procedure? obj) returns #t if obj is a procedure"
  s7_pointer x;
  int typ;

  x = car(args);
  if ((!is_procedure(x)) || (is_c_object(x)))
    {
      check_method(sc, x, sc->IS_PROCEDURE, args);
      return(sc->F);
    }
  typ = type(x);

  /* make_object sets the T_PROCEDURE bit if the object has an apply function,
   *   but we currently return (procedure? "hi") -> #f, so we can't simply use
   *   is_procedure. 
   * 
   * Unfortunately much C code depends on s7_is_procedure treating applicable
   *  objects and macros as procedures.  We can use arity = applicable?
   */
  return(make_boolean(sc,
		      (typ == T_CLOSURE)         || 
		      (typ == T_CLOSURE_STAR)    ||
		      (typ >= T_C_FUNCTION_STAR) ||
		      (typ == T_GOTO)            ||
		      (typ == T_CONTINUATION)));
}


static void s7_function_set_setter(s7_scheme *sc, const char *getter, const char *setter)
{
  /* this is internal, used only with c_function setters, so we don't need to worry about the GC mark choice
   */
  c_function_setter(s7_name_to_value(sc, getter)) = s7_name_to_value(sc, setter);
}


s7_pointer s7_procedure_source(s7_scheme *sc, s7_pointer p)
{
  /* in this context, there's no way to distinguish between:
   *    (procedure-source (let ((b 1)) (lambda (a) (+ a b))))
   * and
   *    (let ((b 1)) (procedure-source (lambda (a) (+ a b))))
   * both become:
   * ((a) (+ a b)) (((b . 1)) #(() () () () () ((make-filtered-comb . make-filtered-comb)) () () ...))
   */
  
  if (is_any_closure(p) || is_macro(p) || is_bacro(p))
    {
      return(cons(sc, 
		  append_in_place(sc, list_2(sc, (is_closure_star(p)) ? sc->LAMBDA_STAR : sc->LAMBDA, closure_args(p)),
				  closure_body(p)),
		  closure_let(p)));
    }
  return(sc->NIL);
}


static s7_pointer g_procedure_source(s7_scheme *sc, s7_pointer args)
{
  #define H_procedure_source "(procedure-source func) tries to return the definition of func"
  /* make it look like a scheme-level lambda */
  s7_pointer p;
  p = car(args);

  if (is_symbol(p))
    {
      p = s7_symbol_value(sc, p);
      if (p == sc->UNDEFINED)
	return(s7_error(sc, sc->WRONG_TYPE_ARG, 
			list_2(sc, make_string_wrapper(sc, "procedure-source arg, '~S, is unbound"), car(args))));
    }

  if (is_c_function(p))
    return(sc->NIL);
  if (is_c_macro(p))
    return(simple_wrong_type_argument_with_type(sc, sc->PROCEDURE_SOURCE, p, make_string_wrapper(sc, "a scheme macro")));

  check_method(sc, p, sc->PROCEDURE_SOURCE, args);
  if (is_any_closure(p) || is_macro(p) || is_bacro(p))
    {
      s7_pointer body;
      body = closure_body(p);
      if (is_safe_closure(body))
	clear_safe_closure(body);
      return(append_in_place(sc, 
			     list_2(sc, (is_closure_star(p)) ? sc->LAMBDA_STAR : sc->LAMBDA, closure_args(p)),
			     body));
    }

  if (!is_procedure(p))
    return(simple_wrong_type_argument_with_type(sc, sc->PROCEDURE_SOURCE, p, make_string_wrapper(sc, "a procedure or a macro")));

  return(sc->NIL);
}


s7_pointer s7_funclet(s7_scheme *sc, s7_pointer p)    
{ 
  if (is_any_closure(p) || is_macro(p) || is_bacro(p))
    return(closure_let(p));
  return(sc->rootlet);
}


static s7_pointer g_funclet(s7_scheme *sc, s7_pointer args)
{
  s7_pointer p, e;
  #define H_funclet "(funclet func) tries to return an object's environment"

  /* this procedure gives direct access to a function's closure -- see s7test.scm 
   *   for some wild examples.  At least it provides a not-too-kludgey way for several functions
   *   to share a closure.
   */ 
  
  p = car(args);
  if (is_symbol(p))
    {
      p = s7_symbol_value(sc, p);
      if (p == sc->UNDEFINED)
	return(s7_error(sc, sc->WRONG_TYPE_ARG, 
			list_2(sc, make_string_wrapper(sc, "funclet arg, '~S, is unbound"), car(args)))); /* not p here */
    }

  check_method(sc, p, sc->FUNCLET, args);

  if (!is_procedure_or_macro(p))
    return(simple_wrong_type_argument_with_type(sc, sc->FUNCLET, p, make_string_wrapper(sc, "a procedure or a macro")));

  e = find_let(sc, p);
  if ((is_null(e)) &&
      (!is_c_object(p)))
    return(sc->rootlet);

  return(e);
}


s7_pointer s7_define_function(s7_scheme *sc, const char *name, s7_function fnc, int required_args, int optional_args, bool rest_arg, const char *doc)
{
  s7_pointer func, sym;
  func = s7_make_function(sc, name, fnc, required_args, optional_args, rest_arg, doc);
  sym = make_symbol(sc, name);
  s7_define(sc, sc->NIL, sym, func);
  return(sym);
}


s7_pointer s7_define_safe_function(s7_scheme *sc, const char *name, s7_function fnc, int required_args, int optional_args, bool rest_arg, const char *doc)
{
  s7_pointer func, sym;
  func = s7_make_function(sc, name, fnc, required_args, optional_args, rest_arg, doc);
  typeflag(func) |= T_SAFE_PROCEDURE;
  sym = make_symbol(sc, name);
  s7_define(sc, sc->NIL, sym, func);
  return(sym);
}


static s7_pointer s7_define_constant_function(s7_scheme *sc, const char *name, s7_function fnc, int required_args, int optional_args, bool rest_arg, const char *doc)
{
  s7_pointer func, sym, p;
  sym = make_symbol(sc, name);
  func = s7_make_function(sc, name, fnc, required_args, optional_args, rest_arg, doc);
  s7_define(sc, sc->NIL, sym, func);
  set_immutable(slot_symbol(global_slot(sym)));

  p = s7_symbol_value(sc, sym); /* make_symbol(sc, name)); */
  typeflag(p) &= ~(T_SAFE_PROCEDURE);
  return(p);
}


s7_pointer s7_define_macro(s7_scheme *sc, const char *name, s7_function fnc, int required_args, int optional_args, bool rest_arg, const char *doc)
{
  s7_pointer func, sym;
  func = s7_make_function(sc, name, fnc, required_args, optional_args, rest_arg, doc);
  set_type(func, T_C_MACRO | T_DONT_EVAL_ARGS); /* this used to include T_PROCEDURE */
  sym = make_symbol(sc, name);
  s7_define(sc, sc->NIL, sym, func);
  return(sym);
}

/* would define_bacro be useful in C?
 */


bool s7_is_macro(s7_scheme *sc, s7_pointer x)
{
  return((is_macro(x)) || (is_bacro(x)) || (is_c_macro(x)));
}


static s7_pointer g_is_macro(s7_scheme *sc, s7_pointer args)
{
  #define H_is_macro "(macro? arg) returns #t if 'arg' is a macro or a bacro"
  s7_pointer p;
  p = car(args);
  if (s7_is_macro(sc, p)) return(sc->T);
  check_method(sc, p, sc->IS_MACRO, args);
  return(sc->F);

  /* it would be more consistent (with procedure? for example) if this returned #f for a symbol,
   *   but fully-expand expects this version.
   */
}


static void define_function_star_1(s7_scheme *sc, const char *name, s7_function fnc, const char *arglist, const char *doc, bool safe)
{
  s7_pointer func, sym, local_args, p;
  char *internal_arglist;
  int i, len, n_args, gc_loc;
  s7_pointer *names, *defaults;

  len = safe_strlen(arglist) + 8;
  internal_arglist = (char *)malloc(len * sizeof(char));
  snprintf(internal_arglist, len, "'(%s)", arglist);
  local_args = s7_eval_c_string(sc, internal_arglist);
  gc_loc = s7_gc_protect(sc, local_args);
  free(internal_arglist);
  n_args = safe_list_length(sc, local_args);  /* currently rest arg not supported, and we don't notice :key :allow-other-keys etc */
  
  func = s7_make_function(sc, name, fnc, 0, n_args, false, doc);
  if (safe) 
    set_type(func, T_C_FUNCTION_STAR | T_PROCEDURE | T_SAFE_PROCEDURE);
  else set_type(func, T_C_FUNCTION_STAR | T_PROCEDURE);

  if (!c_function_ext(func))
    c_function_ext(func) = (c_proc_ext_t *)calloc(1, sizeof(c_proc_ext_t));
  c_function_keyed_args(func) = 2 * n_args;
  c_function_call_args(func) = make_list(sc, n_args, sc->F);
  s7_remove_from_heap(sc, c_function_call_args(func));

  sym = make_symbol(sc, name);
  s7_define(sc, sc->NIL, sym, func);

  names = (s7_pointer *)malloc(n_args * sizeof(s7_pointer));
  c_function_arg_names(func) = names;
  defaults = (s7_pointer *)malloc(n_args * sizeof(s7_pointer));
  c_function_arg_defaults(func) = defaults;
  c_function_simple_defaults(func) = true;

  for (p = local_args, i = 0; i < n_args; p = cdr(p), i++)
    {
      s7_pointer arg;
      arg = car(p);
      if (is_pair(arg))
	{
	  names[i] = s7_make_keyword(sc, symbol_name(car(arg)));
	  defaults[i] = cadr(arg);
	  s7_remove_from_heap(sc, cadr(arg));
	  if ((is_symbol(defaults[i])) ||
	      (is_pair(defaults[i])))
	    {
	      c_function_simple_defaults(func) = false;
	      mark_function[T_C_FUNCTION_STAR] = mark_c_proc_star;
	    }
	}
      else 
	{
	  names[i] = s7_make_keyword(sc, symbol_name(arg));
	  defaults[i] = sc->F;
	}
    }
  s7_gc_unprotect_at(sc, gc_loc);
}

void s7_define_function_star(s7_scheme *sc, const char *name, s7_function fnc, const char *arglist, const char *doc)
{
  return(define_function_star_1(sc, name, fnc, arglist, doc, false));
}

void s7_define_safe_function_star(s7_scheme *sc, const char *name, s7_function fnc, const char *arglist, const char *doc)
{
  return(define_function_star_1(sc, name, fnc, arglist, doc, true));
}


s7_pointer set_c_function_call_args(s7_scheme *sc)
{
  int i, j, n_args;
  s7_pointer arg, par, call_args, func;
  s7_pointer *df;
  
  func = sc->code;
  n_args = c_function_all_args(func);
  call_args = c_function_call_args(func);

  df = c_function_arg_defaults(func);  
  for (i = 0, par = call_args; is_pair(par); i++, par = cdr(par))
    {
      clear_checked(par);
      car(par) = df[i];
    }

  df = c_function_arg_names(func);
  for (i = 0, arg = sc->args, par = call_args; (i < n_args) && (is_pair(arg)); i++, arg = cdr(arg), par = cdr(par))
    {
      if (!is_keyword(car(arg)))
	{
	  if (is_checked(par))
	    return(s7_error(sc, sc->WRONG_TYPE_ARG,
			    list_3(sc, make_string_wrapper(sc, "parameter set twice, ~S in ~S"), car(par), sc->args)));
	  set_checked(par);
	  car(par) = car(arg);
	}
      else
	{
	  s7_pointer p;
	  for (j = 0, p = call_args; j < n_args; j++, p = cdr(p))
	    if (df[j] == car(arg))
	      break;
	  if (j == n_args)
	    return(s7_error(sc, sc->WRONG_TYPE_ARG, list_2(sc, make_string_wrapper(sc, "~A: not a parameter name?"), car(arg))));
	  if (is_checked(p))
	    return(s7_error(sc, sc->WRONG_TYPE_ARG,
			    list_3(sc, make_string_wrapper(sc, "parameter set twice, ~S in ~S"), car(p), sc->args)));
	  set_checked(p);
	  arg = cdr(arg);
	  car(p) = car(arg);
	}
    }

  if (!is_null(arg))
    return(s7_error(sc, sc->WRONG_NUMBER_OF_ARGS, list_3(sc, sc->TOO_MANY_ARGUMENTS, func, sc->args)));

  if (!c_function_simple_defaults(func))
    for (i = 0, par = call_args; i < n_args; i++, par = cdr(par)) 
      if (!is_checked(par))
	{
	  if (is_symbol(car(par)))
	    car(par) = find_symbol_checked(sc, car(par));
	  else
	    {
	      if (is_pair(car(par)))
		car(par) = s7_eval_form(sc, car(par), sc->NIL);
	    }
	}
  return(call_args);
}

/* at first glance, it looks simple to get the doc string out of the eval loop by saving the original
 *   closure_body, setting the eval version to cdr(body), and using the original in the gc mark sweep,
 *   by placing closure_arity in the same block as funclet_file, freeing up a pointer for the original.
 *   But it's tricky: the optimizer places a lot of info in car(body) and the arity setting needs to
 *   be accessible in different contexts than funclet*.  
 */
const char *s7_procedure_documentation(s7_scheme *sc, s7_pointer x)
{
  char *help = NULL;
  #define HELP_ARGLIST_SIZE 512

  if (is_symbol(x))
    {
      if (symbol_has_help(x))
	{
	  if (is_global(x))
	    return(symbol_help(x));
	  help = symbol_help(x);
	}
      x = s7_symbol_value(sc, x); /* this is needed by Snd */
    }

  if ((is_any_c_function(x)) ||
      (is_c_macro(x)))
    return((char *)c_function_documentation(x));
  
  if (is_any_closure(x))
    {
      char *args;
      if (is_string(car(closure_body(x))))
	return(string_value(car(closure_body(x))));

      if (help) return(help);

      if (!sc->help_arglist)
	sc->help_arglist = (char *)malloc(HELP_ARGLIST_SIZE * sizeof(char));
      args = s7_object_to_c_string(sc, closure_args(x));
      if (args)
	{
	  snprintf(sc->help_arglist, HELP_ARGLIST_SIZE, "args: %s", args);
	  free(args);
	  return(sc->help_arglist);
	}
    }
  
  if ((s7_is_macro(sc, x)) &&
      (is_pair(closure_body(x))))
    {
      s7_pointer p;
      p = car(closure_body(x));
      if ((is_pair(p)) &&
	  (is_pair(cdr(p))) &&
	  (is_pair(cadr(p))) &&
	  (is_pair(cdadr(p))) &&
	  (is_pair(cddr(cadr(p)))) &&
	  (is_string(caddr(cadr(p)))))
	return(string_value(caddr(cadr(p))));
    }
  return(help);
}


static s7_pointer g_procedure_documentation(s7_scheme *sc, s7_pointer args)
{
  #define H_procedure_documentation "(procedure-documentation func) returns func's documentation string"
  s7_pointer p;

  p = car(args);
  if (is_symbol(p))
    {
      if ((symbol_has_help(p)) &&
	  (is_global(p)))
	return(s7_make_string(sc, symbol_help(p)));
      p = s7_symbol_value(sc, p);
    }

  check_method(sc, p, sc->PROCEDURE_DOCUMENTATION, args);
  if ((!is_procedure(p)) &&
      (!s7_is_macro(sc, p)))
    return(simple_wrong_type_argument_with_type(sc, sc->PROCEDURE_DOCUMENTATION, p, A_PROCEDURE));

  return(s7_make_string(sc, s7_procedure_documentation(sc, p)));
}


const char *s7_help(s7_scheme *sc, s7_pointer obj)
{
  if (is_syntax(obj))
    {
      switch (syntax_opcode(obj))
	{
	case OP_QUOTE:
	  return("(quote obj) returns obj unevaluated.  'obj is an abbreviation for (quote obj).");
	  
	case OP_IF:
	  return("(if expr true-stuff optional-false-stuff) evaluates expr, then \
if it is true, evaluates true-stuff; otherwise, if optional-false-stuff exists, it is evaluated.");
	  
	case OP_WHEN:
	  return("(when expr ...) evaluates expr, and if it is true, evaluates each form in its body, returning the value of the last");
	  
	case OP_UNLESS:
	  return("(unless expr ...) evaluates expr, and if it is false, evaluates each form in its body, returning the value of the last");
	  
	case OP_BEGIN:
	  return("(begin ...) evaluates each form in its body, returning the value of the last one");

	case OP_SET:
	  return("(set! variable value) sets the value of variable to value.");

	case OP_LET:
	  return("(let ((var val)...) ...) binds each variable to its initial value, then evaluates its body,\
returning the value of the last form.  The let variables are local to it, and in the case of let (as opposed to let* for example), \
the variables are not available for use until all have been initialized.");

	case OP_LET_STAR:
	  return("(let* ((var val)...) ...) binds each variable to its initial value, then evaluates its body,\
returning the value of the last form.  The let* variables are local to it, and are available immediately (unlike let, for example).");

	case OP_LETREC:
	  return("(letrec ((var (lambda ...)))...) is like let, but var can refer to itself in \
its value (i.e. you can define local recursive functions)");

	case OP_LETREC_STAR:
	  return("(letrec* ((var val))...) is like letrec, but successive bindings are handled as in let*");

	case OP_COND:
	  return("(cond (expr clause...)...) is like if..then.  Each expr is evaluated in \
order, and if one is not #f, the associated clauses are evaluated, whereupon cond returns.");

	case OP_AND:
	  return("(and expr expr ...) evaluates each of its arguments in order, quitting (and returning #f) as soon \
as one of them returns #f.  If all are non-#f, it returns the last value.");

	case OP_OR:
	  return("(or expr expr ...) evaluates each of its argments in order, quitting as soon as \
one of them is not #f.  If all are #f, or returns #f.");

	case OP_CASE:
	  return("(case val ((key...) clause...)...) looks for val in the various lists of keys, \
and if a match is found (via eqv?), the associated clauses are evaluated, and case returns.");

	case OP_DO:
	  return("(do (vars...) (loop control and return value) ...) is a do-loop.");

	case OP_WITH_LET:
	  return("(with-let env ...) evaluates its body in the environment env.");

	case OP_WITH_BAFFLE:
	  return("(with-baffle ...) evaluates its body in a context that is safe from outside interference.");

	case OP_LAMBDA:
	  return("(lambda args ...) returns a function.");

	case OP_LAMBDA_STAR:
	  return("(lambda* args ...) returns a function; the args list can have default values, \
the parameters themselves can be accessed via keywords.");

	case OP_DEFINE:
	  return("(define var val) assigns val to the variable (symbol) var.  (define (func args) ...) is \
shorthand for (define func (lambda args ...))");

	case OP_DEFINE_STAR:
	  return("(define* (func args) ...) defines a function with optional/keyword arguments.");

	case OP_DEFINE_CONSTANT:
	  return("(define-constant var val) defines var to be a constant (it can't be set or bound), with the value val.");

	case OP_DEFINE_MACRO:
	  return("(define-macro (mac args) ...) defines mac to be a macro.");

	case OP_DEFINE_MACRO_STAR:
	  return("(define-macro* (mac args) ...) defines mac to be a macro with optional/keyword arguments.");

	case OP_DEFINE_EXPANSION:
	  return("(define-expansion (mac args) ...) defines mac to be a read-time macro.");

	case OP_DEFINE_BACRO:
	  return("(define-bacro (mac args) ...) defines mac to be a bacro.");

	case OP_DEFINE_BACRO_STAR:
	  return("(define-bacro* (mac args) ...) defines mac to be a bacro with optional/keyword arguments.");
	}
      
      return(NULL);

      /* others: macroexpand
            :(define-macro (hiho a) "a test" `(+ 1 ,a))
            hiho
            :(hiho 2)
            3
            :(s7-help hiho)
            "a test"
            with-sound #f etc -- all macros at least
       */
    }

  if (is_symbol(obj))
    {
      /* here look for name */
      if (s7_symbol_documentation(sc, obj))
	return(s7_symbol_documentation(sc, obj));
      obj = s7_symbol_value(sc, obj);
    }
  
  if (is_procedure_or_macro(obj))
    return(s7_procedure_documentation(sc, obj));

  /* if is string, apropos? (can scan symbol table)
   */
  return(NULL);
}


static s7_pointer g_help(s7_scheme *sc, s7_pointer args)
{
  #define H_help "(help obj) returns obj's documentation"
  const char *doc;

  check_method(sc, car(args), sc->HELP, args);
  doc = s7_help(sc, car(args));
  if (!doc)
    return(sc->F);
  return(s7_make_string(sc, doc));
}


static s7_pointer closure_name(s7_scheme *sc, s7_pointer closure)
{
  /* this is used by the error handlers to get the current function name 
   */
  s7_pointer x;

  x = find_closure(sc, closure, sc->envir);
  if (is_symbol(x))
    return(x);

  if (is_pair(sc->cur_code))
    return(sc->cur_code);

  return(closure); /* desperation -- the parameter list (caar here) will cause endless confusion in OP_APPLY errors! */
}




/* -------------------------------- new types -------------------------------- */

static char *fallback_print(s7_scheme *sc, void *val)
{
  return(copy_string("#<unprintable object>"));
}

static char *fallback_print_readably(s7_scheme *sc, void *val) 
{
  eval_error(sc, "can't print this object readably", sc->F);
  return(NULL);
}

static void fallback_free(void *value) {}

static bool fallback_equal(void *val1, void *val2)
{
  return(val1 == val2);
}

static s7_pointer fallback_ref(s7_scheme *sc, s7_pointer obj, s7_pointer args)
{
  return(apply_error(sc, obj, args));
}

static s7_pointer fallback_set(s7_scheme *sc, s7_pointer obj, s7_pointer args)
{
  return(eval_error(sc, "attempt to set ~S?", obj));
}

static s7_pointer fallback_length(s7_scheme *sc, s7_pointer obj)
{
  return(eval_error(sc, "attempt to get length of ~S?", obj));
}


static s7_pointer g_internal_object_set(s7_scheme *sc, s7_pointer args)
{
  return((*(c_object_set(car(args))))(sc, car(args), cdr(args)));
}


int s7_new_type(const char *name, 
		char *(*print)(s7_scheme *sc, void *value), 
		void (*free)(void *value), 
		bool (*equal)(void *val1, void *val2),
		void (*gc_mark)(void *val),
                s7_pointer (*apply)(s7_scheme *sc, s7_pointer obj, s7_pointer args),
                s7_pointer (*set)(s7_scheme *sc, s7_pointer obj, s7_pointer args))
{
  int tag;
  tag = num_types++;
  if (tag >= object_types_size)
    {
      if (object_types_size == 0)
	{
	  object_types_size = 8;
	  object_types = (c_object_t **)calloc(object_types_size, sizeof(c_object_t *));
	}
      else
	{
	  object_types_size = tag + 8;
	  object_types = (c_object_t **)realloc((void *)object_types, object_types_size * sizeof(c_object_t *));
	}
    }
  object_types[tag] = (c_object_t *)calloc(1, sizeof(c_object_t));
  object_types[tag]->type = tag;
  object_types[tag]->name = copy_string(name);
  object_types[tag]->scheme_name = s7_make_permanent_string(name);

  if (free)
    object_types[tag]->free = free;
  else object_types[tag]->free = fallback_free;

  if (print)
    object_types[tag]->print = print;
  else object_types[tag]->print = fallback_print;

  if (equal)
    object_types[tag]->equal = equal;
  else object_types[tag]->equal = fallback_equal;

  object_types[tag]->gc_mark = gc_mark;

  if (apply)
    object_types[tag]->ref = apply;
  else object_types[tag]->ref = fallback_ref; /* ref set only here */
  if (object_types[tag]->ref != fallback_ref)
    object_types[tag]->outer_type = (T_C_OBJECT | T_PROCEDURE | T_SAFE_PROCEDURE);
  else object_types[tag]->outer_type = T_C_OBJECT;

  if (set)
    object_types[tag]->set = set;
  else object_types[tag]->set = fallback_set;

  object_types[tag]->length = fallback_length;
  object_types[tag]->copy = NULL;
  object_types[tag]->reverse = NULL;
  object_types[tag]->fill = NULL;
  object_types[tag]->print_readably = fallback_print_readably;

  return(tag);
}


int s7_new_type_x(const char *name, 
		  char *(*print)(s7_scheme *sc, void *value), 
		  void (*free)(void *value), 
		  bool (*equal)(void *val1, void *val2),
		  void (*gc_mark)(void *val),
		  s7_pointer (*apply)(s7_scheme *sc, s7_pointer obj, s7_pointer args),
		  s7_pointer (*set)(s7_scheme *sc, s7_pointer obj, s7_pointer args),
		  s7_pointer (*length)(s7_scheme *sc, s7_pointer obj),
		  s7_pointer (*copy)(s7_scheme *sc, s7_pointer obj),
		  s7_pointer (*reverse)(s7_scheme *sc, s7_pointer obj),
		  s7_pointer (*fill)(s7_scheme *sc, s7_pointer obj, s7_pointer val))
{
  int tag;
  tag = s7_new_type(name, print, free, equal, gc_mark, apply, set);

  if (length)
    object_types[tag]->length = length;
  else object_types[tag]->length = fallback_length;

  object_types[tag]->copy = copy;
  object_types[tag]->reverse = reverse;
  object_types[tag]->fill = fill;
  return(tag);
}


static void free_object(s7_pointer a)
{
  (*(c_object_free(a)))(c_object_value(a));
}


static bool objects_are_equal(s7_scheme *sc, s7_pointer a, s7_pointer b)
{
  if (c_object_type(a) == c_object_type(b))
    {
      if (c_object_equal(a))
	return((*(c_object_equal(a)))(c_object_value(a), c_object_value(b)));
      return(a == b);
    }
  return(false);
}


static void mark_embedded_objects(s7_pointer a) /* called by gc, calls c_obj's mark func */
{
  if (c_object_mark(a))
    (*(c_object_mark(a)))(c_object_value(a));
}


void *s7_object_value(s7_pointer obj)
{
  return(c_object_value(obj));
}


void *s7_object_value_checked(s7_pointer obj, int type)
{
  if ((is_c_object(obj)) &&
      (c_object_type(obj) == type))
    return(c_object_value(obj));
  return(NULL);
}


void *s7_vector_ref_object_value_checked(s7_scheme *sc, s7_pointer args, int type)
{
  s7_pointer vec, obj, i;
  s7_Int index;

  vec = find_symbol_checked(sc, car(args));
  if ((!s7_is_vector(vec)) ||
      (vector_rank(vec) != 1))
    return(wrong_type_argument_n(sc, sc->VECTOR_REF, 1, car(args), T_VECTOR));
  
  i = find_symbol_checked(sc, cadr(args));
  if (!s7_is_integer(i))
    return(wrong_type_argument_n(sc, sc->VECTOR_REF, 2, cadr(args), T_INTEGER));

  index = s7_integer(i);
  if (index < 0)
    return(out_of_range(sc, sc->VECTOR_REF, small_int(2), make_integer(sc, index), ITS_NEGATIVE));
  if (index >= vector_length(vec))
    return(out_of_range(sc, sc->VECTOR_REF, small_int(2), make_integer(sc, index), ITS_TOO_LARGE));

  obj = vector_element(vec, index);
  return(s7_object_value_checked(obj, type));
}


void s7_set_object_print_readably(int type, char *(*printer)(s7_scheme *sc, void *val))
{
  object_types[type]->print_readably = printer;
}


int s7_object_type(s7_pointer obj)
{
  if (is_c_object(obj))
    return(c_object_type(obj));
  return(-1);
}


s7_pointer s7_make_object(s7_scheme *sc, int type, void *value)
{
  s7_pointer x;
  NEW_CELL(sc, x);
  
  /* c_object_info(x) = &(object_types[type]); */
  /* that won't work because object_types can move when it is realloc'd and the old stuff is freed by realloc
   *   and since we're checking (for example) ref_2 existence as not null, we can't use a table of c_object_t's!
   */
  c_object_type(x) = type;
  c_object_value(x) = value;
  c_object_let(x) = sc->NIL;
  set_type(x, c_object_outer_type(x));
  add_c_object(sc, x);
  return(x);
}

/* make_permanent_object (for Snd) -- permanent_alloc, and don't add_c_object -- very little gained */


s7_pointer s7_object_let(s7_pointer obj)
{
  return(c_object_let(obj));
}


s7_pointer s7_object_set_let(s7_pointer obj, s7_pointer e)
{
  c_object_let(obj) = e;
  return(e);
}


static s7_pointer object_length(s7_scheme *sc, s7_pointer obj)
{
  if (c_object_length(obj))
    return((*(c_object_length(obj)))(sc, obj));
  return(eval_error(sc, "attempt to get length of ~S?", obj));
}


static s7_Int object_length_to_int(s7_scheme *sc, s7_pointer obj)
{
  s7_pointer res;
  if (c_object_length(obj))
    {
      res = (*(c_object_length(obj)))(sc, obj);
      if (s7_is_integer(res))
	return(s7_integer(res));
    }
  return(-1);
}


static s7_pointer object_copy(s7_scheme *sc, s7_pointer obj)
{
  if (c_object_copy(obj))
    return((*(c_object_copy(obj)))(sc, obj));
  return(eval_error(sc, "attempt to copy ~S?", obj));
}


static s7_pointer object_reverse(s7_scheme *sc, s7_pointer obj)
{
  if (c_object_reverse(obj))
    return((*(c_object_reverse(obj)))(sc, obj));
  return(eval_error(sc, "attempt to reverse ~S?", obj));
}


/* -------- dilambda -------- */

s7_pointer s7_dilambda(s7_scheme *sc, 
		       const char *name,
		       s7_pointer (*getter)(s7_scheme *sc, s7_pointer args), 
		       int get_req_args, int get_opt_args,
		       s7_pointer (*setter)(s7_scheme *sc, s7_pointer args),
		       int set_req_args, int set_opt_args,
		       const char *documentation)
{
  s7_pointer get_func, set_func;
  char *internal_set_name;
  int len;

  len = 16 + safe_strlen(name);
  internal_set_name = (char *)malloc(len * sizeof(char));
  snprintf(internal_set_name, len, "[set-%s]", name);

  get_func = s7_make_function(sc, name, getter, get_req_args, get_opt_args, false, documentation); 
  typeflag(get_func) |= T_SAFE_PROCEDURE; 
  s7_define(sc, sc->NIL, make_symbol(sc, name), get_func);
  set_func = s7_make_function(sc, internal_set_name, setter, set_req_args, set_opt_args, false, documentation); 
  /* typeflag(set_func) |= T_SAFE_PROCEDURE; */
  /* s7_define(sc, sc->NIL, make_symbol(sc, internal_set_name), set_func); */
  c_function_setter(get_func) = set_func;

  return(get_func);
}
  

bool s7_is_dilambda(s7_pointer obj)
{
  return(((is_c_function(obj)) &&
	  (is_c_function(c_function_setter(obj)))) ||
	 ((is_any_closure(obj)) && 
	  (is_procedure(closure_setter(obj)))));
}

static s7_pointer g_is_dilambda(s7_scheme *sc, s7_pointer args)
{
  #define H_is_dilambda "(dilambda? obj) returns #t if obj is a procedure with setter."
  check_boolean_method(sc, s7_is_dilambda, sc->IS_DILAMBDA, args);
}


s7_pointer s7_procedure_setter(s7_scheme *sc, s7_pointer obj)
{
  if (is_c_function(obj)) 
    return(c_function_setter(obj));

  return(closure_setter(obj));
}


static s7_pointer g_procedure_setter(s7_scheme *sc, s7_pointer args)
{
  #define H_procedure_setter "(procedure-setter obj) returns the setter associated with obj, or #f"
  s7_pointer p;

  p = car(args);
  switch (type(p))
    {
    case T_MACRO:
    case T_BACRO:
      /* macros are closures internally */
    case T_CLOSURE:
    case T_CLOSURE_STAR:
      return(closure_setter(p));

    case T_C_FUNCTION:
    case T_C_FUNCTION_STAR:
    case T_C_ANY_ARGS_FUNCTION:
    case T_C_OPT_ARGS_FUNCTION:
    case T_C_RST_ARGS_FUNCTION:
      return(c_function_setter(p));

    case T_C_MACRO:
      return(c_macro_setter(p));

    case T_GOTO:
    case T_CONTINUATION:
      return(sc->F);

    case T_ENVIRONMENT:
    case T_C_OBJECT:
      check_method(sc, p, s7_make_symbol(sc, "procedure-setter"), args);
      break;
    }
  return(s7_wrong_type_arg_error(sc, "procedure-setter", 0, p, "a procedure or a reasonable facsimile thereof"));
}


static s7_pointer g_procedure_set_setter(s7_scheme *sc, s7_pointer args)
{
  s7_pointer p, setter;

  p = car(args);
  if (!is_procedure_or_macro(p))
    return(s7_wrong_type_arg_error(sc, "set! procedure-setter procedure", 1, p, "a procedure"));

  setter = cadr(args);
  if ((setter != sc->F) &&
      (!is_procedure_or_macro(setter)))
    return(s7_wrong_type_arg_error(sc, "set! procedure-setter setter", 2, setter, "a procedure or #f"));

  /* should we check that p != setter?
   *
   * :(set! (procedure-setter <) <)
   * <
   * :(set! (< 3 2) 3)
   * #f
   * :(set! (< 1) 2)
   * #t
   *
   * can this make sense?
   */

  switch (type(p))
    {
    case T_MACRO:
    case T_BACRO:
    case T_CLOSURE:
    case T_CLOSURE_STAR:
      closure_setter(p) = setter;
      break;

    case T_C_FUNCTION:
    case T_C_ANY_ARGS_FUNCTION:
    case T_C_OPT_ARGS_FUNCTION:
    case T_C_RST_ARGS_FUNCTION:
      c_function_setter(p) = setter;
      if (is_any_closure(setter))
	mark_function[type(p)] = mark_c_proc;
      break;

    case T_C_FUNCTION_STAR:
      c_function_setter(p) = setter;
      if (is_any_closure(setter))
	mark_function[T_C_FUNCTION_STAR] = mark_c_proc_star;
      break;

    case T_C_MACRO:
      if (is_any_closure(setter))
	mark_function[T_C_MACRO] = mark_c_proc;
      c_macro_setter(p) = setter;
      break;

    case T_GOTO:
      return(s7_wrong_type_arg_error(sc, "set! procedure-setter", 1, p, "a normal procedure (not a call-with-exit exit procedure)"));

    case T_CONTINUATION:
      return(s7_wrong_type_arg_error(sc, "set! procedure-setter", 1, p, "a normal procedure (not a continuation)"));
    }
  return(setter);
}


void s7_define_function_with_setter(s7_scheme *sc, const char *name, s7_function get_fnc, s7_function set_fnc, int req_args, int opt_args, const char *doc)
{
  s7_dilambda(sc, name, get_fnc, req_args, opt_args, set_fnc, req_args + 1, opt_args, doc);
}


const char *s7_procedure_name(s7_scheme *sc, s7_pointer proc)
{
  int nlen = 0;
  switch (type(proc))
    {
    case T_CLOSURE:
    case T_CLOSURE_STAR:
      return(c_closure_name(sc, proc, &nlen));
  
    case T_C_OPT_ARGS_FUNCTION:
    case T_C_RST_ARGS_FUNCTION:
    case T_C_ANY_ARGS_FUNCTION:
    case T_C_FUNCTION:
    case T_C_FUNCTION_STAR:
      return(c_function_name(proc));

    case T_C_MACRO:
      return(c_macro_name(proc));

    case T_SYMBOL:
      {
	s7_pointer slot; 
	slot = find_symbol(sc, proc);           /* we don't want unbound-variable errors here */
	if ((is_slot(slot)) &&
	    (proc != slot_value(slot)) &&       /* (procedure-name :hi) -> infinite loop */
	    (is_procedure(slot_value(slot))))   /* a symbol -> symbol -> back to first?? */
	  return(s7_procedure_name(sc, slot_value(slot)));
	return(NULL);
      }

      /* T_MACRO et all don't have an easily accessible name -- I suppose we could search the curlet?
       *
       * other things get here: #<goto>->"", presumably continuations
       *   if we're active, the goto must be in the current env:
       *   (call-with-exit (lambda (return) (curlet))) -> #<let 'return #<goto>>
       *   (let () (define-macro (mac x) `(+ ,x 1)) (curlet)) -> #<let 'mac #<macro>>
       * need a reverse lookup for the current env (value->symbol in s7.html):
       */
#if 0
    case T_MACRO:
    case T_BACRO:
    case T_GOTO:
    case T_CONTINUATION:
      {
	s7_pointer x;
	for (x = sc->envir; is_let(x); x = outlet(x))
	  {
	    s7_pointer y;
	    for (y = let_slots(x); is_slot(y); y = next_slot(y))
	      if (slot_value(y) == proc)
		return(symbol_name(slot_symbol(y)));
	  }
	/* now search the global env?? */
      }
#endif
    }
  return(NULL);
}


static s7_pointer g_procedure_name(s7_scheme *sc, s7_pointer args)
{
  #define H_procedure_name "(procedure-name proc) returns the name of the procedure or closure proc, if it can be found."
  s7_pointer p;
  p = car(args);
  check_method(sc, p, sc->PROCEDURE_NAME, args);
  if ((is_procedure_or_macro(p)) ||
      (is_symbol(p)))
    return(s7_make_string(sc, s7_procedure_name(sc, p)));
  return(simple_wrong_type_argument_with_type(sc, sc->PROCEDURE_NAME, p, A_PROCEDURE));
}



/* -------------------------------- arity -------------------------------- */

s7_pointer s7_procedure_arity(s7_scheme *sc, s7_pointer x)
{
  if (is_any_c_function(x))
    return(list_3(sc, 
		  make_integer(sc, c_function_required_args(x)),
		  make_integer(sc, c_function_optional_args(x)),
		  make_boolean(sc, c_function_has_rest_arg(x))));
  /* this was optimized to be a permanent immutable list, but makes a mess of all the setters,
   *   and then there are cases like: (fill! (append (list 1) (procedure-arity abs)) 0)
   */

  if (is_any_closure(x))
    {
      int len;
      
      if (is_pair(x))
	len = s7_list_length(sc, closure_args(x));
      else 
	{
	  if (is_symbol(closure_args(x)))
	    return(list_3(sc, small_int(0), small_int(0), sc->T));
	  len = s7_list_length(sc, closure_args(x));
	}

      if (is_closure_star(x))
	{
	  s7_pointer tmp;        /* make sure we aren't counting :optional and friends as arguments */
	  int opts = 0;

	  if (is_pair(x))
	    tmp = car(x);
	  else tmp = closure_args(x);

	  for (; is_pair(tmp); tmp = cdr(tmp))
	    {
	      if (car(tmp) == sc->KEY_ALLOW_OTHER_KEYS)
		opts++;
	      else
		{
		  if (car(tmp) == sc->KEY_REST)
		    {
		      opts += 2;     /* both :rest and the arg name are not counted as optional args */
		      if (len > 0) len = -len;
		    }
		}
	    }
	  return(list_3(sc, small_int(0), make_integer(sc, abs(len) - opts), make_boolean(sc, len < 0)));
	}
      return(list_3(sc, make_integer(sc, abs(len)), small_int(0), make_boolean(sc, len < 0)));
    }

  if (is_c_object(x))
    return(list_3(sc, small_int(0), small_int(0), sc->T));
  
  if ((is_continuation(x)) ||
      (is_goto(x)))
    return(list_3(sc, small_int(0), small_int(0), sc->T));

  return(sc->NIL);
}


static s7_pointer g_procedure_arity(s7_scheme *sc, s7_pointer args)
{
  #define H_procedure_arity "(procedure-arity func) returns a list describing func's arguments: '(required optional rest)"
  s7_pointer p;

  p = car(args);
  if (is_symbol(p))
    {
      p = s7_symbol_value(sc, p);
      if (p == sc->UNDEFINED)
	return(s7_error(sc, sc->WRONG_TYPE_ARG, 
			list_2(sc, make_string_wrapper(sc, "procedure-arity arg, '~S, is unbound"), car(args))));
    }

  check_method(sc, p, sc->PROCEDURE_ARITY, args);
  if (!is_procedure(p))
    return(simple_wrong_type_argument_with_type(sc, sc->PROCEDURE_ARITY, p, A_PROCEDURE));
  return(s7_procedure_arity(sc, p));
}


static s7_pointer closure_arity_to_cons(s7_scheme *sc, s7_pointer x, s7_pointer x_args)
{
  /* x_args is unprocessed -- it is exactly the list as used in the closure[*] definition
   */
  int len;

  if (is_symbol(x_args))                    /* any number of args is ok */
    return(s7_cons(sc, small_int(0), max_arity));
	
  if (closure_arity_unknown(x))
    closure_arity(x) = s7_list_length(sc, x_args);
  len = closure_arity(x);
  if (len < 0)                               /* dotted list => rest arg, (length '(a b . c)) is -2 */
    return(s7_cons(sc, s7_make_integer(sc, -len), max_arity));
  return(s7_cons(sc, s7_make_integer(sc, len), s7_make_integer(sc, len)));
}


static s7_pointer closure_star_arity_to_cons(s7_scheme *sc, s7_pointer x, s7_pointer x_args)
{
  if (is_symbol(x_args))
    return(s7_cons(sc, small_int(0), max_arity));

  if (closure_arity_unknown(x))
    {
      s7_pointer p;
      int i;
      for (i = 0, p = x_args; is_pair(p); p = cdr(p))
	{
	  s7_pointer arg;
	  arg = car(p);
	  if ((arg == sc->KEY_REST) ||
	      (arg == sc->KEY_ALLOW_OTHER_KEYS))
	    break;
	  i++;
	}
      if (is_null(p))
	closure_arity(x) = i;
      else closure_arity(x) = -1; /* see below */
    }

  if (closure_arity(x) == -1)
    return(s7_cons(sc, small_int(0), max_arity));
  return(s7_cons(sc, small_int(0), s7_make_integer(sc, closure_arity(x))));
}


static int closure_arity_to_int(s7_scheme *sc, s7_pointer x)
{
  /* not lambda* here */
  if (closure_arity_unknown(x))
    {
      int i;
      s7_pointer b;
      for (i = 0, b = closure_args(x); is_pair(b); i++, b = cdr(b)) {};
      if (is_null(b))
	closure_arity(x) = i;
      else 
	{
	  if (i == 0)
	    return(-1);
	  closure_arity(x) = -i;
	}
    }
  return(closure_arity(x));
}


static int closure_star_arity_to_int(s7_scheme *sc, s7_pointer x)
{
  /* not lambda here */
  if (closure_arity_unknown(x))
    {
      s7_pointer p;
      int i;
      for (i = 0, p = closure_args(x); is_pair(p); p = cdr(p))
	{
	  s7_pointer arg;
	  arg = car(p);
	  if ((arg == sc->KEY_REST) ||
	      (arg == sc->KEY_ALLOW_OTHER_KEYS))
	    break;
	  i++;
	}
      if (is_null(p))
	closure_arity(x) = i;
      else closure_arity(x) = -1;
    }
  return(closure_arity(x));
}


static s7_pointer g_arity(s7_scheme *sc, s7_pointer args)
{
  #define H_arity "(arity obj) the min and max acceptable args for obj if it is applicable, otherwise #f."
  s7_pointer x;

  x = car(args);
  switch (type(x))
    {
    case T_C_OPT_ARGS_FUNCTION:
    case T_C_RST_ARGS_FUNCTION:
    case T_C_FUNCTION:
      return(s7_cons(sc, s7_make_integer(sc, c_function_required_args(x)), s7_make_integer(sc, c_function_all_args(x))));

    case T_C_ANY_ARGS_FUNCTION:
    case T_C_FUNCTION_STAR:
      return(s7_cons(sc, small_int(0), s7_make_integer(sc, c_function_all_args(x)))); /* should this be *2? */

    case T_CLOSURE:
      return(closure_arity_to_cons(sc, x, closure_args(x)));

    case T_CLOSURE_STAR:
      return(closure_star_arity_to_cons(sc, x, closure_args(x)));

    case T_C_MACRO:
      return(s7_cons(sc, s7_make_integer(sc, c_macro_required_args(x)), s7_make_integer(sc, c_macro_all_args(x))));

    case T_MACRO:
    case T_BACRO:
      {
	s7_pointer op;
	op = cadar(closure_body(x));
	if ((car(op) == sc->LAMBDA) || (car(op) == sc->LAMBDA_UNCHECKED))
	  return(closure_arity_to_cons(sc, x, cadr(op)));
	return(closure_star_arity_to_cons(sc, x, cadr(op)));
      }

    case T_GOTO:
    case T_CONTINUATION:
      return(s7_cons(sc, small_int(0), max_arity));

    case T_STRING:
      if (string_length(x) == 0)
	return(sc->F);

    case T_ENVIRONMENT:
      check_method(sc, x, sc->ARITY, args);
      return(s7_cons(sc, small_int(1), small_int(1)));
      
    case T_C_OBJECT:
      check_method(sc, x, sc->ARITY, args);
      if (is_procedure(x))
	return(s7_cons(sc, small_int(0), max_arity));
      return(sc->F);

    case T_INT_VECTOR:
    case T_FLOAT_VECTOR:
    case T_VECTOR:
      if (vector_length(x) == 0)
	return(sc->F);

    case T_PAIR:
    case T_HASH_TABLE:
      return(s7_cons(sc, small_int(1), max_arity));

    case T_SYNTAX:
      switch (syntax_opcode(x))
	{
	case OP_QUOTE:
	  return(s7_cons(sc, small_int(1), small_int(1)));
	  
	case OP_IF:
	  return(s7_cons(sc, small_int(2), small_int(3)));

	case OP_SET:
	  return(s7_cons(sc, small_int(3), small_int(3)));

	case OP_COND:
	case OP_WITH_LET:
	  return(s7_cons(sc, small_int(1), max_arity));

	case OP_WHEN:
	case OP_UNLESS:
	case OP_LET:
	case OP_LET_STAR:
	case OP_LETREC:
	case OP_LETREC_STAR:
	case OP_CASE:
	case OP_LAMBDA:
	case OP_LAMBDA_STAR:
	case OP_DEFINE:
	case OP_DEFINE_STAR:
	case OP_DEFINE_CONSTANT:
	case OP_DEFINE_MACRO:
	case OP_DEFINE_MACRO_STAR:
	case OP_DEFINE_EXPANSION:
	case OP_DEFINE_BACRO:
	case OP_DEFINE_BACRO_STAR:
	  return(s7_cons(sc, small_int(2), max_arity));

	case OP_DO:
	  return(s7_cons(sc, small_int(3), max_arity));

	default:
	  return(s7_cons(sc, small_int(0), max_arity));
	}
    }

  return(sc->F);
}


static bool closure_is_aritable(s7_scheme *sc, s7_pointer x, s7_pointer x_args, int args)
{
  /* x_args is unprocessed -- it is exactly the list as used in the closure[*] definition
   */
  int len;
  if (is_symbol(x_args))                    /* any number of args is ok */
    return(true);
	
  len = closure_arity(x);
  if (len == CLOSURE_ARITY_NOT_SET)
    {
      len = s7_list_length(sc, x_args);
      closure_arity(x) = len;
    }
  if (len < 0)                               /* dotted list => rest arg, (length '(a b . c)) is -2 */
    return((-len) <= args);                  /*   so we have enough to take care of the required args */
  return(args == len);                       /* in a normal lambda list, there are no other possibilities */
}


static bool closure_star_is_aritable(s7_scheme *sc, s7_pointer x, s7_pointer x_args, int args)
{
  if (is_symbol(x_args))
    return(true);

  if (closure_arity_unknown(x))
    {
      /* The lambda* list can contain the pure noise words :key and :optional,
       *   :rest = any number ok, :allow-other-keywords = any number of key/value pairs
       * but all args are optional, so here arity reflects the max args possible, min is always 0.
       * arity = mx or -1
       */
      s7_pointer p;
      int i;
      for (i = 0, p = x_args; is_pair(p); p = cdr(p))
	{
	  s7_pointer arg;
	  arg = car(p);
	  if ((arg == sc->KEY_REST) ||
	      (arg == sc->KEY_ALLOW_OTHER_KEYS))
	    break;
	  i++;
	}
      if (is_null(p))
	closure_arity(x) = i;
      else closure_arity(x) = -1;
    }

  return((closure_arity(x) == -1) ||
	 (args <= closure_arity(x)));
}


bool s7_is_aritable(s7_scheme *sc, s7_pointer x, int args)
{
  switch (type(x))
    {
    case T_C_OPT_ARGS_FUNCTION:
    case T_C_RST_ARGS_FUNCTION:
    case T_C_FUNCTION:
      return(((int)c_function_required_args(x) <= args) &&
	     ((int)c_function_all_args(x) >= args));

    case T_C_ANY_ARGS_FUNCTION:
    case T_C_FUNCTION_STAR:
      return((int)c_function_all_args(x) >= args);

    case T_CLOSURE:
      return(closure_is_aritable(sc, x, closure_args(x), args));

    case T_CLOSURE_STAR:
      return(closure_star_is_aritable(sc, x, closure_args(x), args));

    case T_C_MACRO:
      return(((int)c_macro_required_args(x) <= args) &&
	     ((int)c_macro_all_args(x) >= args));

    case T_MACRO:
    case T_BACRO:
      {
	s7_pointer op;
	op = cadar(closure_body(x));
	/* we need to look at the embedded lambda to choose between closure and closure_star 
	 *   closure_body here is of the form: ((apply (lambda (a b) ...) (cdr {mac}-14)))
	 *   so car is                          (apply (lambda (a b) ...) (cdr {mac}-14))
	 *      cadr(car) is                           (lambda (a b) ...)
	 */
	if ((car(op) == sc->LAMBDA) || (car(op) == sc->LAMBDA_UNCHECKED))
	  return(closure_is_aritable(sc, x, cadr(op), args));
	return(closure_star_is_aritable(sc, x, cadr(op), args));
      }

    case T_GOTO:
    case T_CONTINUATION:
      return(true);

    case T_STRING:
      return((args == 1) &&
	     (string_length(x) > 0)); /* ("" 0) -> error */
      
    case T_C_OBJECT:
      /* check_method(sc, x, sc->IS_ARITABLE, list_2(sc, x, s7_make_integer(sc, args))); -- see below */
      return(is_procedure(x)); /* i.e. is_applicable */

    case T_INT_VECTOR:
    case T_FLOAT_VECTOR:
    case T_VECTOR:
      return((args > 0) &&
	     (vector_length(x) > 0) &&   /* (#() 0) -> error */
	     ((unsigned int)args <= vector_rank(x)));

    case T_ENVIRONMENT:
      /* check_method(sc, x, sc->IS_ARITABLE, list_2(sc, x, s7_make_integer(sc, args))); */
      /* this slows us down a lot */
    case T_HASH_TABLE:
    case T_PAIR:
      return(args == 1);

    case T_SYNTAX:
      switch (syntax_opcode(x))
	{
	case OP_QUOTE:
	  return(args == 1);
	  
	case OP_IF:
	  return((args == 2) || (args == 3));

	case OP_SET:
	  return(args == 3);

	case OP_COND:
	case OP_WITH_LET:
	  return(args > 0);
	  
	case OP_WHEN:
	case OP_UNLESS:
	case OP_LET:
	case OP_LET_STAR:
	case OP_LETREC:
	case OP_LETREC_STAR:
	case OP_CASE:
	case OP_LAMBDA:
	case OP_LAMBDA_STAR:
	case OP_DEFINE:
	case OP_DEFINE_STAR:
	case OP_DEFINE_CONSTANT:
	case OP_DEFINE_MACRO:
	case OP_DEFINE_MACRO_STAR:
	case OP_DEFINE_EXPANSION:
	case OP_DEFINE_BACRO:
	case OP_DEFINE_BACRO_STAR:
	  return(args > 1);

	case OP_DO:
	  return(args > 2);

	default:
	  return(true); /* and begin etc */
	}
    }
  return(false);
}


static s7_pointer g_is_aritable(s7_scheme *sc, s7_pointer args)
{
  #define H_is_aritable "(aritable? obj num-args) returns #t if 'obj can be applied to 'num-args arguments."
  s7_pointer n;
  s7_Int num;

  n = cadr(args);
  if (!s7_is_integer(n)) /* remember gmp case! */
    {
      check_method(sc, n, sc->IS_ARITABLE, args);
      return(wrong_type_argument(sc, sc->IS_ARITABLE, small_int(2), n, T_INTEGER));
    }
  num = s7_integer(n);
  if (num < 0)
    return(out_of_range(sc, sc->IS_ARITABLE, small_int(2), n, ITS_NEGATIVE));
  if (num > MAX_ARITY) num = MAX_ARITY;

  return(make_boolean(sc, s7_is_aritable(sc, car(args), (int)num)));
}


static s7_pointer is_aritable_ic;
static s7_pointer g_is_aritable_ic(s7_scheme *sc, s7_pointer args)
{
  return(make_boolean(sc, s7_is_aritable(sc, car(args), (int)integer(cadr(args)))));
}

static s7_pointer is_aritable_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if ((args == 2) &&
      (s7_is_integer(cadr(expr))) &&
      (s7_integer(cadr(expr)) < MAX_ARITY) &&
      (s7_integer(cadr(expr)) >= 0))
    return(is_aritable_ic);
  return(f);
}



/* -------------------------------- symbol-access ------------------------------------------------ */

s7_pointer s7_symbol_access(s7_scheme *sc, s7_pointer sym)
{
  /* these refer to the rootlet */
  if (slot_has_accessor(global_slot(sym)))
    return(s7_gc_protected_at(sc, symbol_global_accessor_gc_index(sym)));
  return(sc->F);
}


s7_pointer s7_symbol_set_access(s7_scheme *sc, s7_pointer symbol, s7_pointer func)
{
  if (slot_has_accessor(global_slot(symbol)))
    {
      if (is_immutable(s7_gc_protected_at(sc, symbol_global_accessor_gc_index(symbol))))
	return(func); 
      s7_gc_unprotect_at(sc, symbol_global_accessor_gc_index(symbol));
    }
  else 
    {
      if (func != sc->F)
	{
	  slot_set_has_accessor(global_slot(symbol));
	  symbol_set_has_accessor(symbol);
	}
    }
  symbol_global_accessor_gc_index(symbol) = s7_gc_protect(sc, func);
  slot_accessor(global_slot(symbol)) = func;
  return(func); 
}

/* a bug: (let () (define xxx 23) (define (hix) (set! xxx 24)) (hix) (set! (symbol-access 'xxx) (lambda (sym val) (format *stderr* "val: ~A~%" val) val)) (hix))
 */

static s7_pointer g_symbol_access(s7_scheme *sc, s7_pointer args)
{
  #define H_symbol_access "(symbol-access sym (env (curlet))) is the function called when the symbol is set!."
  s7_pointer sym, p, e;

  sym = car(args);
  if (!is_symbol(sym))
    {
      check_method(sc, sym, sc->SYMBOL_ACCESS, args);
      return(simple_wrong_type_argument(sc, sc->SYMBOL_ACCESS, sym, T_SYMBOL));
    }

  if (is_pair(cdr(args)))
    {
      e = cadr(args);
      if (!is_let(e))
	return(wrong_type_argument(sc, sc->SYMBOL_ACCESS, small_int(2), e, T_ENVIRONMENT));
    }
  else e = sc->envir;

  if ((e == sc->rootlet) ||
      (e == sc->NIL))
    {
      if (is_free(s7_symbol_access(sc, sym)))
	{
#if DEBUGGING
	  fprintf(stderr, "%s (global) accessor is freed\n", symbol_name(sym));
#endif
	  return(sc->F);
	}
      return(s7_symbol_access(sc, sym));
    }

  if (is_null(cdr(args)))
    p = find_symbol(sc, sym);
  else p = find_local_symbol(sc, sym, e);
  if ((is_slot(p)) &&
      (slot_has_accessor(p)))
    {
      if (is_free(slot_accessor(p)))
	{
#if DEBUGGING
	  fprintf(stderr, "%s (slot) accessor is freed\n", symbol_name(sym));
#endif
	  return(sc->F);
	}
      return(slot_accessor(p));
    }
  return(sc->F);
}


static s7_pointer g_symbol_set_access(s7_scheme *sc, s7_pointer args)
{
  s7_pointer sym, func, e, p;
  /* perhaps: check func */

  sym = car(args);
  if (!is_symbol(sym))                 /* no check method because no method name? */
    return(s7_wrong_type_arg_error(sc, "set! symbol-access", 1, sym, "a symbol"));

  /* (set! (symbol-access sym) f) or (set! (symbol-access sym env) f) */
  if (is_pair(cddr(args)))
    {
      e = cadr(args);
      if (!is_let(e))
	return(s7_wrong_type_arg_error(sc, "set! symbol-access", 2, e, "an environment"));
      func = caddr(args);
    }
  else
    {
      e = sc->envir;
      func = cadr(args);
    }

  if ((!is_procedure_or_macro(func)) &&
      (func != sc->F))
    return(s7_wrong_type_arg_error(sc, "set! symbol-access", 3, func, "a function or #f"));

  if ((e == sc->rootlet) ||
      (e == sc->NIL))
    return(s7_symbol_set_access(sc, sym, func));

  if (is_null(cddr(args)))
    p = find_symbol(sc, sym);
  else p = find_local_symbol(sc, sym, e);

  if (is_slot(p))
    {
      slot_accessor(p) = func;
      if (func != sc->F)
	{
	  slot_set_has_accessor(p);
	  symbol_set_has_accessor(sym);
	}
      return(func);
    }
  return(sc->F);
}


static s7_pointer bind_accessed_symbol(s7_scheme *sc, opcode_t op, s7_pointer symbol, s7_pointer new_value)
{
  /* this refers to (define (sym ...)) and friends -- define cases
   *    see call_accessor for the set! cases
   */
  s7_pointer func;

  func = g_symbol_access(sc, list_2(sc, symbol, sc->envir));
  if (is_procedure(func))
    {
      if (is_c_function(func))
	{
	  s7_pointer old_value;
	  old_value = new_value;
	  car(sc->T2_1) = symbol;
	  car(sc->T2_2) = new_value;
	  new_value = c_function_call(func)(sc, sc->T2_1);
	  if (new_value == sc->ERROR)
	    return(s7_error(sc, sc->ERROR,
			    list_3(sc, make_string_wrapper(sc, "can't bind ~S to ~S"), symbol, old_value)));
	}
      else
	{
	  sc->args = list_2(sc, symbol, new_value);
	  push_stack(sc, op, sc->args, sc->code);
	  sc->code = func;
	  return(sc->NO_VALUE); /* this means the accessor in set! needs to goto APPLY to get the new value */
	}
    }
  /* perhaps add macro case? */
  return(new_value);
}



/* -------------------------------- hooks -------------------------------- */

s7_pointer s7_hook_functions(s7_scheme *sc, s7_pointer hook)
{
  return(s7_symbol_local_value(sc, sc->BODY, closure_let(hook)));
}


s7_pointer s7_hook_set_functions(s7_scheme *sc, s7_pointer hook, s7_pointer functions)
{
  if (s7_is_list(sc, functions))
    s7_let_set(sc, closure_let(hook), sc->BODY, functions);
  return(functions);
}



/* -------------------------------- eq etc -------------------------------- */

bool s7_is_eq(s7_pointer obj1, s7_pointer obj2)
{
  return(obj1 == obj2); /* so floats and NaNs might be eq? but not eqv? */
}


bool s7_is_eqv(s7_pointer a, s7_pointer b) 
{
  if ((a == b) && (!is_number(a)))
    return(true);
  
#if WITH_GMP
  if ((is_big_number(a)) || (is_big_number(b)))
    return(big_numbers_are_eqv(a, b));
#endif

  if (type(a) != type(b)) 
    return(false);
  
  if (is_string(a)) 
    return(string_value(a) == string_value(b));
  
  if (s7_is_number(a))
    return(numbers_are_eqv(a, b));
  
  if (is_unspecified(a))                             /* types are the same so we know b is also unspecified */
    return(true);

  return(false);
}


/* -------- structure equality -------- 
 *
 * equal? examines the entire structure (possibly a tree etc), which might contain
 *   cycles (vector element is the vector etc), so list/vector/hash-table equality
 *   needs to carry along a list of pointers seen so far.
 */

static bool structures_are_equal(s7_scheme *sc, s7_pointer x, s7_pointer y, shared_info *ci);

static bool s7_is_equal_tracking_circles(s7_scheme *sc, s7_pointer x, s7_pointer y, shared_info *ci)
{
  if ((x == y) && (!is_number(x))) /* this needs to be just like the g_is_equal outer test: (equal? '(1/0) '(1/0)) */
    return(true);
  
#if WITH_GMP
  if ((is_big_number(x)) || (is_big_number(y)))
    return(big_numbers_are_eqv(x, y));
#endif

  if (type(x) != type(y)) 
    return(false);
  
  switch (type(x))
    {
    case T_UNSPECIFIED:
      return(true);

    case T_UNIQUE:
      return(false);

    case T_STRING:
      return(scheme_strings_are_equal(x, y));

    case T_C_OBJECT:
      return(objects_are_equal(sc, x, y));

    case T_INTEGER:
    case T_RATIO:
    case T_REAL:
    case T_COMPLEX:
      return(numbers_are_eqv(x, y));

    case T_COUNTER:
      return(counter_count(x) == counter_count(y)); /* ?? not in s7_is_equal because it only occurs in a structure?? */

    case T_SYNTAX:
      return(syntax_symbol(x) == syntax_symbol(y)); 

    case T_ENVIRONMENT:
    case T_INT_VECTOR:
    case T_FLOAT_VECTOR:
    case T_VECTOR:
    case T_HASH_TABLE:
    case T_PAIR:
      return(structures_are_equal(sc, x, y, ci));

    case T_C_POINTER:       /* might have a list of these for example */
      return(raw_pointer(x) == raw_pointer(y));
    }
  return(false); /* we already checked that x != y (port etc) */
}


static bool hash_tables_are_equal(s7_scheme *sc, s7_pointer x, s7_pointer y, shared_info *ci)
{
  s7_pointer *lists;
  int i, len;

  if (hash_table_entries(x) != hash_table_entries(y))
    return(false);
  if (hash_table_entries(x) == 0)
    return(true);
  if (hash_table_function(x) != hash_table_function(y))
    return(false);
  if (hash_table_eq_function(x) != hash_table_eq_function(y))
    return(false);

  len = hash_table_length(x);
  lists = hash_table_elements(x);

  for (i = 0; i < len; i++)
    {
      s7_pointer p;
      for (p = lists[i]; is_not_null(p); p = cdr(p))
	{
	  s7_pointer key, x_val, y_val;

	  key = caar(p);
	  x_val = cdar(p);
	  y_val = (*hash_table_function(y))(sc, y, key);

	  if (is_null(y_val))
	    return(false);
	  if (!s7_is_equal_tracking_circles(sc, x_val, cdr(y_val), ci))
	    return(false);
	}
    }
  /* if we get here, every key/value in x has a corresponding key/value in y, and the number of entries match,
   *   so surely the tables are equal??
   */
  return(true);
}


static bool environments_are_equal(s7_scheme *sc, s7_pointer x, s7_pointer y, shared_info *ci, bool normal)
{
  /* x == y if all unshadowed vars match
   *   envs can contain envs, even themselves
   *   (equal? (inlet 'a (inlet 'b 1)) (inlet 'a (inlet 'b 1))) 
   *
   * order does not matter, but shadowing does, and shadowed slots are ignored
   *   it is possible to get shadowing within an environment:
   *   (inlet 'a 1 'a 2) -> #<let 'a 2 'a 1>
   */
  s7_pointer ex, ey;
  bool (*checker)(s7_scheme *sc, s7_pointer x, s7_pointer y, shared_info *ci);

  if (normal)
    checker = s7_is_equal_tracking_circles;
  else checker = s7_is_morally_equal;

  for (ex = x, ey = y; is_let(ex) && is_let(ey); ex = outlet(ex), ey = outlet(ey))
    {
      s7_pointer px, py;

      if (ex == ey)
	return(true);
      if ((ex == sc->rootlet) ||
	  (ey == sc->rootlet))
	return(false);

      /* first check that we have the same symbols -- this requires two checks! */
      clear_syms_in_list(sc);
      for (px = let_slots(ex); is_slot(px); px = next_slot(px))
	add_sym_to_list(sc, slot_symbol(px));
      for (py = let_slots(ey); is_slot(py); py = next_slot(py))
	if (symbol_tag(slot_symbol(py)) != sc->syms_tag)
	  return(false);

      for (py = let_slots(ey); is_slot(py); py = next_slot(py))
	symbol_tag(slot_symbol(py)) = 0;
      for (px = let_slots(ex); is_slot(px); px = next_slot(px))
	if (symbol_tag(slot_symbol(px)) != 0)
	  return(false);

      for (px = let_slots(ex); is_slot(px); px = next_slot(px))
	{
	  for (py = let_slots(ey); is_slot(py); py = next_slot(py))
	    {
	      if (slot_symbol(px) == slot_symbol(py)) /* we know something will match */
		{
		  if (!(checker(sc, slot_value(px), slot_value(py), ci)))
		    {
		      s7_pointer ppx;
		      /* check for shadowing */
		      for (ppx = let_slots(ex); is_slot(ppx); ppx = next_slot(ppx))
			if (slot_symbol(ppx) == slot_symbol(px))
			  {
			    if (ppx == px)
			      return(false);
			    break;
			  }
		    }
		  break; /* we found one, so ignore possible shadows */
		}
	    }
	}
    }
  if ((is_let(ex)) ||
      (is_let(ey)))
    return(false);
  return(true);
}


static bool structures_are_equal(s7_scheme *sc, s7_pointer x, s7_pointer y, shared_info *ci)
{
  /* here we know x and y are pointers to the same type of structure */
  if (ci)
    {
      int ref_x, ref_y;
      ref_x = peek_shared_ref(ci, x);
      ref_y = peek_shared_ref(ci, y);

      if ((ref_x != 0) && (ref_y != 0))
	return(ref_x == ref_y);
  
      if ((ref_x != 0) || (ref_y != 0))
	{
	  /* try to harmonize the new guy -- there can be more than one structure equal to the current one */
	  if (ref_x != 0)
	    add_shared_ref(ci, y, ref_x);
	  else add_shared_ref(ci, x, ref_y);
	}
      else add_equal_ref(ci, x, y);
    }

  /* now compare the elements of the structures. */
  switch (type(x))
    {
    case T_PAIR:
      return((s7_is_equal_tracking_circles(sc, car(x), car(y), ci)) &&
	     (s7_is_equal_tracking_circles(sc, cdr(x), cdr(y), ci)));

    case T_HASH_TABLE:
      return(hash_tables_are_equal(sc, x, y, ci));

    case T_INT_VECTOR:
    case T_FLOAT_VECTOR:
    case T_VECTOR:
      {
	s7_Int i, len;
	int x_dims = 1, y_dims = 1, j;

	len = vector_length(x);
	if (len != vector_length(y)) return(false);

	/* there's one special case: shared vectors can have 1 dimension but include the dimension info */
	if (vector_has_dimensional_info(x))
	  x_dims = vector_ndims(x);
	if (vector_has_dimensional_info(y))
	  y_dims = vector_ndims(y);
	    
	if (x_dims != y_dims)
	  return(false);
	    
	if (x_dims > 1)
	  for (j = 0; j < x_dims; j++)
	    if (vector_dimension(x, j) != vector_dimension(y, j))
	      return(false);

	if (is_int_vector(x))
	  {
	    for (i = 0; i < len; i++)
	      if (int_vector_element(x, i) != int_vector_element(y, i))
		return(false);
	    return(true);
	  }

	if (is_float_vector(x))
	  {
	    for (i = 0; i < len; i++)
	      {
		s7_Double z;
		z = float_vector_element(x, i);
		if ((is_NaN(z)) ||
		    (z != float_vector_element(y, i)))
		  return(false);
	      }
	    return(true);
	  }

	for (i = 0; i < len; i++)
	  if (!(s7_is_equal_tracking_circles(sc, vector_element(x, i), vector_element(y, i), ci)))
	    return(false);
	return(true);
      }

    case T_ENVIRONMENT:
      return(environments_are_equal(sc, x, y, ci, true));
    }
  return(true);
}


bool s7_is_equal(s7_scheme *sc, s7_pointer x, s7_pointer y)
{
  if ((x == y) && (!is_number(x)))  /* (equal? 1/0 1/0) should be #f */
    return(true);

#if WITH_GMP
  if ((is_big_number(x)) || (is_big_number(y)))
    return(big_numbers_are_eqv(x, y));
#endif

  if (type(x) != type(y)) 
    {
      /* special case here: (equal? (vector) (float-vector)) -> #t
       */
      return((s7_is_vector(x)) &&
	     (s7_is_vector(y)) &&
	     (vector_length(x) == 0) &&
	     (vector_length(y) == 0) &&
	     (structures_are_equal(sc, x, y, NULL))); /* check dimensional info */
    }

  switch (type(x))
    {
    case T_UNIQUE:
      return(false);

    case T_UNSPECIFIED:
      return(true);

    case T_STRING:
      return(scheme_strings_are_equal(x, y));

    case T_C_OBJECT:
      return(objects_are_equal(sc, x, y));

    case T_INTEGER:
    case T_RATIO:
    case T_REAL:
    case T_COMPLEX:
      return(numbers_are_eqv(x, y));

    case T_SYNTAX:
      return(syntax_symbol(x) == syntax_symbol(y));

    case T_ENVIRONMENT:
      if (has_methods(x))
	{
	  if (has_methods(y))
	    {
	      s7_pointer equal_func;
	      equal_func = find_method(sc, x, sc->IS_EQUAL);
	      if (equal_func != sc->UNDEFINED)
		return(s7_boolean(sc, s7_apply_function(sc, equal_func, list_2(sc, x, y))));
	    }
	  else return(false);
	}
      return(structures_are_equal(sc, x, y, new_shared_info(sc)));

    case T_CLOSURE:
    case T_CLOSURE_STAR:
      if ((has_methods(x)) &&
	  (has_methods(y)))
	{
	  s7_pointer equal_func;
	  equal_func = find_method(sc, closure_let(x), sc->IS_EQUAL);
	  if (equal_func != sc->UNDEFINED)
	    return(s7_boolean(sc, s7_apply_function(sc, equal_func, list_2(sc, x, y))));
	}
      return(false);

    case T_INT_VECTOR:
    case T_FLOAT_VECTOR:
    case T_VECTOR:
      return((vector_length(x) == vector_length(y)) &&
	     (structures_are_equal(sc, x, y, (type(x) == T_VECTOR) ? new_shared_info(sc) : NULL)));

    case T_HASH_TABLE:
      return(hash_tables_are_equal(sc, x, y, new_shared_info(sc)));

    case T_PAIR:
#if (!WITH_GMP)
      return((type(car(x)) == type(car(y))) &&
	     (structures_are_equal(sc, x, y, new_shared_info(sc))));
#else
      return(structures_are_equal(sc, x, y, new_shared_info(sc)));
#endif

    case T_C_POINTER:
      return(raw_pointer(x) == raw_pointer(y));
    }
  return(false); /* we already checked that x != y (port etc) */
}


static s7_pointer g_is_eq(s7_scheme *sc, s7_pointer args)
{
  #define H_is_eq "(eq? obj1 obj2) returns #t if obj1 is eq to (the same object as) obj2"
  return(make_boolean(sc, ((car(args) == cadr(args)) ||
			   ((is_unspecified(car(args))) && (is_unspecified(cadr(args)))))));
  /* (eq? (apply apply apply values '(())) #<unspecified>) should return #t
   */
}


static s7_pointer g_is_eqv(s7_scheme *sc, s7_pointer args)
{
  #define H_is_eqv "(eqv? obj1 obj2) returns #t if obj1 is equivalent to obj2"
  return(make_boolean(sc, s7_is_eqv(car(args), cadr(args))));
}


static s7_pointer g_is_equal(s7_scheme *sc, s7_pointer args)
{
  #define H_is_equal "(equal? obj1 obj2) returns #t if obj1 is equal to obj2"
  return(make_boolean(sc, s7_is_equal(sc, car(args), cadr(args))));
}


static bool hash_tables_are_morally_equal(s7_scheme *sc, s7_pointer x, s7_pointer y, shared_info *ci)
{
  s7_pointer *lists;
  int i, len;

  if (hash_table_entries(x) != hash_table_entries(y))
    return(false);
  if (hash_table_entries(x) == 0)
    return(true);
  /* can't precheck the function here (2 != 2.0) */

  len = hash_table_length(x);
  lists = hash_table_elements(x);

  for (i = 0; i < len; i++)
    {
      s7_pointer p;
      for (p = lists[i]; is_not_null(p); p = cdr(p))
	{
	  s7_pointer key, x_val, y_val;

	  key = caar(p);
	  x_val = cdar(p);
	  y_val = (*hash_table_function(y))(sc, y, key);

	  if (is_null(y_val))
	    return(false);
	  if (!s7_is_morally_equal(sc, x_val, cdr(y_val), ci))
	    return(false);
	}
    }

  /* if we get here, every key/value in x has a corresponding key/value in y, and the number of entries match,
   *   so surely the tables are equal??
   */
  return(true);
}


static bool floats_are_morally_equal(s7_scheme *sc, s7_Double x, s7_Double y)
{
  if (x == y) return(true);

  if ((is_NaN(x)) || (is_NaN(y)))
    return((is_NaN(x)) && (is_NaN(y)));

  return(fabs(x - y) <= sc->morally_equal_float_epsilon);
}


static bool structures_are_morally_equal(s7_scheme *sc, s7_pointer x, s7_pointer y, shared_info *ci)
{
  /* here we know x and y are pointers to the same type of structure */
  if (ci)
    {
      int ref_x, ref_y;
      ref_x = peek_shared_ref(ci, x);
      ref_y = peek_shared_ref(ci, y);
      
      if ((ref_x != 0) && (ref_y != 0))
	return(ref_x == ref_y);
      
      if ((ref_x != 0) || (ref_y != 0))
	{
	  /* try to harmonize the new guy -- there can be more than one structure equal to the current one */
	  if (ref_x != 0)
	    add_shared_ref(ci, y, ref_x);
	  else add_shared_ref(ci, x, ref_y);
	}
      else add_equal_ref(ci, x, y);
    }

  /* now compare the elements of the structures. */
  switch (type(x))
    {
    case T_PAIR:
      return((s7_is_morally_equal(sc, car(x), car(y), ci)) &&
	     (s7_is_morally_equal(sc, cdr(x), cdr(y), ci)));

    case T_HASH_TABLE:
      return(hash_tables_are_morally_equal(sc, x, y, ci));

    case T_INT_VECTOR:
    case T_FLOAT_VECTOR:
    case T_VECTOR:
      {
	s7_Int i, len;
	int x_dims = 1, y_dims = 1, j;

	len = vector_length(x);
	if (len != vector_length(y)) return(false);
	if (len == 0) return(true);

	if (vector_has_dimensional_info(x))
	  x_dims = vector_ndims(x);
	if (vector_has_dimensional_info(y))
	  y_dims = vector_ndims(y);
	    
	if (x_dims != y_dims)
	  return(false);
	    
	if (x_dims > 1)
	  for (j = 0; j < x_dims; j++)
	    if (vector_dimension(x, j) != vector_dimension(y, j))
	      return(false);

	if (is_float_vector(x))
	  {
	    s7_Double *arr1, *arr2;
	    s7_Double fudge;
	    arr1 = float_vector_elements(x);
	    arr2 = float_vector_elements(y);
	    fudge = sc->morally_equal_float_epsilon;
	    if (fudge == 0.0)
	      {
		for (i = 0; i < len; i++)
		  if ((arr1[i] != arr2[i]) &&
		      ((!is_NaN(arr1[i])) || (!is_NaN(arr2[i]))))
		    return(false);
	      }
	    else
	      {
		for (i = 0; i < len; i++)
		  {
		    s7_Double diff;
		    diff = fabs(arr1[i] - arr2[i]);
		    if (diff > fudge) return(false);
		    if ((is_NaN(diff)) &&
			((!is_NaN(arr1[i])) || (!is_NaN(arr2[i]))))
		      return(false);
		  }
	      }
	    return(true);
	  }

	if (is_int_vector(x))
	  {
	    for (i = 0; i < len; i++)
	      if (int_vector_element(x, i) != int_vector_element(y, i))
		return(false);
	    return(true);
	  }

	for (i = 0; i < len; i++)
	  if (!(s7_is_morally_equal(sc, vector_element(x, i), vector_element(y, i), ci)))
	    return(false);
	return(true);
      }

    case T_ENVIRONMENT:
      return(environments_are_equal(sc, x, y, ci, false));
    }
  return(true);
}


static bool unmatched_vectors_are_morally_equal(s7_scheme *sc, s7_pointer x, s7_pointer y)
{
  /* (morally-equal? (make-vector 3 0 #t) (make-vector 3 0)) -> #t
   * (morally-equal? (make-vector 3 1.0 #t) (vector 1 1 1)) -> #t
   * (morally-equal? (make-vector 0 1.0 #t) (vector)) -> #t
   */
  s7_Int len;
  len = vector_length(x);
  if (len == vector_length(y))
    {
      s7_Int i;
      if (len == 0)   /* all empty vectors are morally equal */
	return(true);
      for (i = 0; i < len; i++)
	if (!s7_is_morally_equal(sc, vector_getter(x)(sc, x, i), vector_getter(y)(sc, y, i), NULL))
	  return(false);
      return(true);
    }
  return(false);
}

static bool let_equal(s7_scheme *sc, s7_pointer x, s7_pointer y)
{
  s7_pointer equal_func;
  equal_func = find_method(sc, x, sc->IS_MORALLY_EQUAL);
  if (equal_func != sc->UNDEFINED)
    return(s7_boolean(sc, s7_apply_function(sc, equal_func, list_2(sc, x, y))));
  return(false);
}

static bool s7_is_morally_equal(s7_scheme *sc, s7_pointer x, s7_pointer y, shared_info *ci)
{
  if (x == y) 
    return(true);

  if (type(x) != type(y))
    {
      if ((is_let(x)) && (has_methods(x)))
	return(let_equal(sc, x, y));
      if ((is_let(y)) && (has_methods(y)))
	return(let_equal(sc, y, x));

      if ((s7_is_vector(x)) &&
	  (s7_is_vector(y)))
	return(unmatched_vectors_are_morally_equal(sc, x, y));

      if (((!is_number(x)) && (!is_big_number(x))) ||
	  ((!is_number(y)) && (!is_big_number(y))))
	return(false);
    }

  switch (type(x))
    {
    case T_UNIQUE: 
      return(false);

    case T_UNSPECIFIED:
      return(true);

    case T_STRING:
      return(scheme_strings_are_equal(x, y));

    case T_C_OBJECT:
      return(objects_are_equal(sc, x, y)); /* perhaps a slot for this? */

    case T_C_POINTER:
      return(raw_pointer(x) == raw_pointer(y));

    case T_INPUT_PORT:
    case T_OUTPUT_PORT:
      return((port_is_closed(x)) &&     /* closed ports of same type are morally equal */
	     (port_type(x) == port_type(y)) &&
	     (port_is_closed(y)));
  
    case T_HASH_TABLE:
      return(structures_are_morally_equal(sc, x, y, (ci) ? ci : new_shared_info(sc)));

    case T_ENVIRONMENT:
      if (has_methods(x))
	return(let_equal(sc, x, y));
      if (has_methods(y))
	return(let_equal(sc, y, x));

      /* if the environment contains a function whose environment is the current environment, infinite loop?
       *    so we pass the ci arg in the macro/function cases below
       */
      return(structures_are_morally_equal(sc, x, y, (ci) ? ci : new_shared_info(sc)));

    case T_INT_VECTOR:
    case T_FLOAT_VECTOR:
    case T_VECTOR:
      if (vector_length(x) == vector_length(y))
	{
	  if (vector_length(x) == 0)   /* all empty vectors are morally equal */
	    return(true);
	  return(structures_are_morally_equal(sc, x, y, (ci) ? ci : ((type(x) == T_VECTOR) ? new_shared_info(sc) : NULL)));
	}
      return(false);

    case T_PAIR:
      return(structures_are_morally_equal(sc, x, y, (ci) ? ci : new_shared_info(sc)));

#if WITH_GMP
      /* not sure how to handle these cases -- what should the "float-epsilon" be?
       */
    case T_BIG_INTEGER:
    case T_BIG_RATIO:
    case T_BIG_REAL:
    case T_BIG_COMPLEX:
      return(big_equal(sc, list_2(sc, x, y)) != sc->F);
#endif

    case T_INTEGER:
      switch (type(y))
	{
#if WITH_GMP
	case T_BIG_INTEGER:
	case T_BIG_REAL:
	case T_BIG_COMPLEX:
	case T_BIG_RATIO:
	  return(big_equal(sc, list_2(sc, x, y)) != sc->F);
#endif
	  
	case T_INTEGER:
	  return(integer(x) == integer(y));

	case T_RATIO:
	  return(fabs(integer(x) - fraction(y)) <= sc->morally_equal_float_epsilon);

	case T_REAL:
	  return((!is_NaN(real(y))) &&
		 (fabs(integer(x) - real(y)) <= sc->morally_equal_float_epsilon));

	case T_COMPLEX:
	  return((!is_NaN(real_part(y))) &&
		 (!is_NaN(imag_part(y))) &&
		 (fabs(integer(x) - real_part(y)) <= sc->morally_equal_float_epsilon) &&
		 (fabs(imag_part(y)) <= sc->morally_equal_float_epsilon));

	default:
	  return(false);
	}

    case T_RATIO:
      switch (type(y))
	{
#if WITH_GMP
	case T_BIG_INTEGER:
	case T_BIG_REAL:
	case T_BIG_COMPLEX:
	case T_BIG_RATIO:
	  return(big_equal(sc, list_2(sc, x, y)) != sc->F);
#endif
	  
	case T_INTEGER:
	  return(fabs(fraction(x) - integer(y)) <= sc->morally_equal_float_epsilon);

	case T_RATIO:
	  return(fabs(fraction(x) - fraction(y)) <= sc->morally_equal_float_epsilon);

	case T_REAL:
	  return(floats_are_morally_equal(sc, fraction(x), real(y)));

	case T_COMPLEX:
	  return((!is_NaN(real_part(y))) &&
		 (!is_NaN(imag_part(y))) &&
		 (fabs(fraction(x) - real_part(y)) <= sc->morally_equal_float_epsilon) &&
		 (fabs(imag_part(y)) <= sc->morally_equal_float_epsilon));

	default:
	  return(false);
	}

    case T_REAL:
      switch (type(y))
	{
#if WITH_GMP
	case T_BIG_INTEGER:
	case T_BIG_REAL:
	case T_BIG_COMPLEX:
	case T_BIG_RATIO:
	  return(big_equal(sc, list_2(sc, x, y)) != sc->F);
#endif
	  
	case T_INTEGER:
	  return((!is_NaN(real(x))) &&
		 (fabs(real(x) - integer(y)) <= sc->morally_equal_float_epsilon));

	case T_RATIO:
	  return((!is_NaN(real(x))) &&
		 (fabs(real(x) - fraction(y)) <= sc->morally_equal_float_epsilon));

	case T_REAL:
	  return(floats_are_morally_equal(sc, real(x), real(y)));

	case T_COMPLEX:
	  if (is_NaN(real(x)))
	    return((is_NaN(real_part(y))) &&
		   (fabs(imag_part(y)) <= sc->morally_equal_float_epsilon));

	  return((!is_NaN(real(x))) &&
		 (!is_NaN(real_part(y))) &&
		 (!is_NaN(imag_part(y))) &&
		 ((real(x) == real_part(y)) ||
		  (fabs(real(x) - real_part(y)) <= sc->morally_equal_float_epsilon)) &&
		 (fabs(imag_part(y)) <= sc->morally_equal_float_epsilon));

	default:
	  return(false);
	}

    case T_COMPLEX:
      switch (type(y))
	{
#if WITH_GMP
	case T_BIG_INTEGER:
	case T_BIG_REAL:
	case T_BIG_COMPLEX:
	case T_BIG_RATIO:
	  return(big_equal(sc, list_2(sc, x, y)) != sc->F);
#endif
	  
	case T_INTEGER:
	  return((!is_NaN(real_part(x))) &&
		 (!is_NaN(imag_part(x))) &&
		 (fabs(real_part(x) - integer(y)) <= sc->morally_equal_float_epsilon) &&
		 (fabs(imag_part(x)) <= sc->morally_equal_float_epsilon));

	case T_RATIO:
	  return((!is_NaN(real_part(x))) &&
		 (!is_NaN(imag_part(x))) &&
		 (fabs(real_part(x) - fraction(y)) <= sc->morally_equal_float_epsilon) &&
		 (fabs(imag_part(x)) <= sc->morally_equal_float_epsilon));

	case T_REAL:
	  if ((is_NaN(real_part(x))) ||
	      (is_NaN(imag_part(x))) ||
	      (is_NaN(real(y))))
	    return(false);
	  return(((real_part(x) == real(y)) ||
		  (fabs(real_part(x) - real(y)) <= sc->morally_equal_float_epsilon)) &&
		 (fabs(imag_part(x)) <= sc->morally_equal_float_epsilon));

	case T_COMPLEX:
	  /* should (morally-equal? nan.0 (make-rectangular nan.0 nan.0)) be #t? */
	  if (is_NaN(real_part(x)))
	    return((is_NaN(real_part(y))) &&
		   (((is_NaN(imag_part(x))) && (is_NaN(imag_part(y)))) ||
		    (imag_part(x) == imag_part(y)) ||
		    (fabs(imag_part(x) - imag_part(y)) <= sc->morally_equal_float_epsilon)));

	  if (is_NaN(imag_part(x)))
	    return((is_NaN(imag_part(y))) &&
		   ((real_part(x) == real_part(y)) ||
		    (fabs(real_part(x) - real_part(y)) <= sc->morally_equal_float_epsilon)));
		   
	  if ((is_NaN(real_part(y))) ||
	      (is_NaN(imag_part(y))))
	    return(false);

	  return(((real_part(x) == real_part(y)) ||
		  (fabs(real_part(x) - real_part(y)) <= sc->morally_equal_float_epsilon)) &&
		 ((imag_part(x) == imag_part(y)) ||
		  (fabs(imag_part(x) - imag_part(y)) <= sc->morally_equal_float_epsilon)));

	default:
	  return(false);
	}

    case T_CLOSURE:
    case T_CLOSURE_STAR:
      if (type(x) != type(y))
	return(false);
      if ((has_methods(x)) &&
	  (has_methods(y)))
	{
	  s7_pointer equal_func;
	  equal_func = find_method(sc, closure_let(x), sc->IS_MORALLY_EQUAL);
	  if (equal_func != sc->UNDEFINED)
	    return(s7_boolean(sc, s7_apply_function(sc, equal_func, list_2(sc, x, y))));
	}
      return((s7_is_morally_equal(sc, closure_args(x), closure_args(y), ci)) &&
	     (s7_is_morally_equal(sc, closure_let(x), closure_let(y), ci)) &&
	     (s7_is_morally_equal(sc, closure_body(x), closure_body(y), ci)));

    case T_MACRO:
    case T_BACRO:
      /* these are hard to check because the body has the gensymmed arg name
       *
       * args:   ({mac}-16) 
       *         ({mac}-17)
       * bodies: ((apply (lambda (a) ({list} '+ 1 a)) (cdr {mac}-16))) 
       *         ((apply (lambda (a) ({list} '+ 1 a)) (cdr {mac}-17)))
       * 
       * the "args" and "body" are not relevant: we create these shells internally, so the true args are in the applied lambda
       * so ignore args, and check (cadr (car body)) => (lambda (a) ({list} '+ 1 a))
       *
       * if the "ci" arg is omitted, this hangs:
       *    (morally-equal? 
       *      (let () 
       *        (define-macro* (a_func (an_arg (lambda () #t))) 
       *          `,an_arg) 
       *        (a_func)) 
       *      (let () 
       *        (define-macro (a_func an_arg) 
       *          `,an_arg) 
       *        (a_func (lambda () #t))))
       */
      return((type(y) == type(x)) &&
	     (s7_is_morally_equal(sc, closure_let(x), closure_let(y), ci)) &&
	     (s7_is_morally_equal(sc, cadr(car(closure_body(x))), cadr(car(closure_body(y))), ci)));

    }
  return(false);
}


static s7_pointer g_is_morally_equal(s7_scheme *sc, s7_pointer args)
{
  #define H_is_morally_equal "(morally-equal? obj1 obj2) returns #t if obj1 is close enough to obj2."
  return(make_boolean(sc, s7_is_morally_equal(sc, car(args), cadr(args), NULL)));
}



/* ---------------------------------------- length, copy, fill ---------------------------------------- */

static s7_pointer g_length(s7_scheme *sc, s7_pointer args)
{
  #define H_length "(length obj) returns the length of obj, which can be a list, vector, string, or hash-table. \
The length of a dotted list does not include the final cdr, and is returned as a negative number.  A circular \
list has infinite length.  Length of anything else returns #f."

  s7_pointer lst;
  lst = car(args);

  switch (type(lst))
    {
    case T_PAIR:
      {
	int len;
	len = s7_list_length(sc, lst);
	/* len < 0 -> dotted and (abs len) is length not counting the final cdr
	 * len == 0, circular so length is infinite
	 */
	if (len == 0)
	  return(real_infinity);
	return(make_integer(sc, len));
      }

    case T_NIL:
      return(small_int(0));

    case T_INT_VECTOR:
    case T_FLOAT_VECTOR:
    case T_VECTOR:
      return(make_integer(sc, vector_length(lst)));

    case T_STRING:
      return(make_integer(sc, string_length(lst)));

    case T_HASH_TABLE:
      return(make_integer(sc, hash_table_length(lst)));

    case T_C_OBJECT:
      check_method(sc, lst, sc->LENGTH, args);
      return(object_length(sc, lst));

    case T_ENVIRONMENT:
      check_method(sc, lst, sc->LENGTH, args);
      return(make_integer(sc, let_length(sc, lst)));

    case T_CLOSURE:
    case T_CLOSURE_STAR:
      if (has_methods(lst))
	return(make_integer(sc, closure_length(sc, lst)));

    default:
      return(sc->F);
      /* return(simple_wrong_type_argument_with_type(sc, sc->LENGTH, lst, A_SEQUENCE)); */
    }
  return(small_int(0));
}

/* what about (length file)?  input port, read_file gets the file length, so perhaps save it
 *   but we're actually looking at the port, so its length is what remains to be read? (if input port)
 */


s7_pointer s7_copy(s7_scheme *sc, s7_pointer obj)
{
  switch (type(obj))
    {
    case T_STRING:
      {
	s7_pointer ns;
	ns = s7_make_string_with_length(sc, string_value(obj), string_length(obj));
	if (is_bytevector(obj))
	  set_bytevector(ns);
	return(ns);
      }

    case T_C_OBJECT:
      check_method(sc, obj, sc->COPY, list_1(sc, obj));
      return(object_copy(sc, obj));

    case T_HASH_TABLE:              /* this has to copy nearly everything */
      return(hash_table_copy(sc, obj));
      
    case T_ENVIRONMENT:             
      check_method(sc, obj, sc->COPY, list_1(sc, obj)); /* the cons happens only if we have a copy method, but it's still wasteful */
      return(let_copy(sc, obj));   /* this copies only the local env and points to outer envs */

    case T_CLOSURE:
    case T_CLOSURE_STAR:
      check_method(sc, obj, sc->COPY, list_1(sc, obj));
      return(obj);

    case T_INT_VECTOR:
    case T_FLOAT_VECTOR:
    case T_VECTOR:
      return(s7_vector_copy(sc, obj)); /* "shallow" copy */

    case T_PAIR:                    /* top level only, as in the other cases, last arg checks for circles */
      return(cons(sc, car(obj), list_copy(sc, cdr(obj), obj, true))); 
      /* list_copy assumes obj is in the heap (visible to the GC) -- don't use it with the symbol-table! */

#if WITH_GMP
    case T_BIG_INTEGER:
      return(mpz_to_big_integer(sc, big_integer(obj)));

    case T_BIG_RATIO:
      return(mpq_to_big_ratio(sc, big_ratio(obj)));

    case T_BIG_REAL:
      return(mpfr_to_big_real(sc, big_real(obj)));

    case T_BIG_COMPLEX:
      return(mpc_to_big_complex(sc, big_complex(obj)));
#endif

    case T_C_POINTER:
      return(s7_make_c_pointer(sc, s7_c_pointer(obj)));
    }
  return(obj);
}


static s7_pointer string_setter(s7_scheme *sc, s7_pointer str, s7_Int loc, s7_pointer val) 
{
  if (s7_is_character(val))
    {
      string_value(str)[loc] = s7_character(val); 
      return(val);
    }
  return(wrong_type_argument(sc, sc->STRING_SET, small_int(3), val, T_CHARACTER));
}

static s7_pointer string_getter(s7_scheme *sc, s7_pointer str, s7_Int loc) 
{
  return(s7_make_character(sc, (unsigned char)(string_value(str)[loc]))); /* cast needed else (copy (string (integer->char 255))...) is trouble */
}

static s7_pointer list_setter(s7_scheme *sc, s7_pointer lst, s7_Int loc, s7_pointer val)
{
  int i;
  s7_pointer p;
  for (p = lst, i = 0; i < loc; i++, p = cdr(p));
  car(p) = val;
  return(val);
}

static s7_pointer list_getter(s7_scheme *sc, s7_pointer lst, s7_Int loc) 
{
  int i;
  s7_pointer p;
  for (p = lst, i = 0; i < loc; i++, p = cdr(p));
  return(car(p));
}

static s7_pointer c_object_setter(s7_scheme *sc, s7_pointer obj, s7_Int loc, s7_pointer val)
{
  car(sc->T2_1) = make_integer(sc, loc);
  car(sc->T2_2) = val;
  return((*(c_object_set(obj)))(sc, obj, sc->T2_1));
} 

static s7_pointer c_object_getter(s7_scheme *sc, s7_pointer obj, s7_Int loc)
{
  car(sc->T1_1) = make_integer(sc, loc);
  return((*(c_object_ref(obj)))(sc, obj, sc->T1_1));
}

static s7_pointer env_getter(s7_scheme *sc, s7_pointer e, s7_Int loc) 
{
  int i;
  s7_pointer p;
  for (p = let_slots(e), i = 0; i < loc; i++, p = next_slot(p));
  return(s7_cons(sc, slot_symbol(p), slot_value(p)));
}

static s7_pointer env_setter(s7_scheme *sc, s7_pointer e, s7_Int loc, s7_pointer val)
{
  /* loc is irrelevant here
   * val has to be of the form (cons symbol value)
   * if symbol is already in e, its value is changed, otherwise a new slot is added to e
   */
  if (!is_pair(val))
    return(wrong_type_argument_with_type(sc, sc->COPY, small_int(3), e, make_string_wrapper(sc, "(cons symbol value)"))); 
  if (s7_let_set(sc, e, car(val), cdr(val)) != cdr(val))
    s7_make_slot(sc, e, car(val), cdr(val));
  return(val);
}

static s7_pointer hash_table_getter(s7_scheme *sc, s7_pointer e, s7_Int loc) 
{
  int i, count, len;
  s7_pointer p;
  s7_pointer *elements;

  len = hash_table_length(e);
  elements = hash_table_elements(e);

  for (i = 0, count = 0; i < len; i++)
    for (p = elements[i]; is_pair(p); p = cdr(p))
      {
	if (count == loc)
	  return(car(p));
	count++;
      }
  return(sc->NIL);
}

static s7_pointer hash_table_setter(s7_scheme *sc, s7_pointer e, s7_Int loc, s7_pointer val)
{
  /* loc is irrelevant here
   * val has to be of the form (cons key value)
   * if key is already in e, its value is changed, otherwise a new slot is added to e
   */
  if (!is_pair(val))
    return(wrong_type_argument_with_type(sc, sc->COPY, small_int(3), e, A_LIST));
  return(s7_hash_table_set(sc, e, car(val), cdr(val)));
}


static s7_pointer g_copy(s7_scheme *sc, s7_pointer args)
{
  #define H_copy "(copy obj) returns a copy of obj, (copy src dest) copies src into dest, (copy src dest start end) copies src from start to end."
  #define COPY_CIRCLE_LEN 2147483641
  s7_pointer source, dest;
  s7_Int i, j, dest_len, start, end, source_len;
  s7_pointer (*set)(s7_scheme *sc, s7_pointer obj, s7_Int loc, s7_pointer val);
  s7_pointer (*get)(s7_scheme *sc, s7_pointer obj, s7_Int loc) = NULL;
  bool have_indices;

  if (is_null(cdr(args)))                /* (copy obj) -- this works for environments, hash-tables and bignums */
    return(s7_copy(sc, car(args)));
  have_indices = (is_pair(cddr(args)));
  source = car(args);
  dest = cadr(args);
  if ((source == dest) && (!have_indices))
    return(dest);

  switch (type(source))
    {
    case T_PAIR:     
      get = list_getter;     
      end = s7_list_length(sc, source); 
      if (end == 0) end = COPY_CIRCLE_LEN; /* end == 0 -> circular */
      if (end < 0) end = -end; 
      break; 

    case T_INT_VECTOR:   
    case T_FLOAT_VECTOR: 
    case T_VECTOR:       get = vector_getter(source); end = vector_length(source);            break;
    case T_STRING:       get = string_getter;         end = string_length(source);            break;
    case T_HASH_TABLE:   get = hash_table_getter;     end = hash_table_entries(source);       break;

    case T_C_OBJECT:     
      check_method(sc, source, sc->COPY, args);
      get = c_object_getter;       
      end = object_length_to_int(sc, source); 
      break;

    case T_ENVIRONMENT:  
      check_method(sc, source, sc->COPY, args);
      if (source == sc->rootlet)
	return(wrong_type_argument_with_type(sc, sc->COPY, small_int(1), source, make_string_wrapper(sc, "a sequence other than the rootlet")));
      get = env_getter;            
      end = let_length(sc, source);   
      break;

    case T_NIL: 
      end = 0;
      if (is_sequence(dest))
	break;

    default: 
      return(wrong_type_argument_with_type(sc, sc->COPY, small_int(1), source, A_SEQUENCE));
      /* copy doesn't have to duplicate fill!, so (copy 1 #(...)) need not be supported */
    }

  start = 0;
  if (have_indices)
    {
      s7_pointer p;
      p = start_and_end(sc, sc->COPY, NULL, cddr(args), args, 3, &start, &end);
      if (p != sc->GC_NIL) return(p);
    }
  if ((start == 0) && (source == dest))
    return(dest);
  source_len = end - start;

  switch (type(dest))
    {
    case T_PAIR:     
      set = list_setter;     
      dest_len = s7_list_length(sc, dest); 
      if (dest_len == 0) dest_len = circular_list_entries(dest);
      if (dest_len < 0) dest_len = -dest_len;
      break;

    case T_INT_VECTOR:   
    case T_FLOAT_VECTOR: 
    case T_VECTOR:       set = vector_setter(dest); dest_len = vector_length(dest);            break;
    case T_STRING:       set = string_setter;       dest_len = string_length(dest);            break;
    case T_HASH_TABLE:   set = hash_table_setter;   dest_len = source_len;                     break;
    case T_C_OBJECT:     set = c_object_setter;     dest_len = object_length_to_int(sc, dest); break;

    case T_ENVIRONMENT: 
      if (source == sc->rootlet)
	return(wrong_type_argument_with_type(sc, sc->COPY, small_int(2), dest, make_string_wrapper(sc, "a sequence other than the rootlet")));
      set = env_setter;          
      dest_len = source_len;                     /* grows via set, so dest_len isn't relevant */
      break; 

    case T_NIL:
      return(sc->NIL);

    default: 
      return(wrong_type_argument_with_type(sc, sc->COPY, small_int(2), dest, A_SEQUENCE));
    }
  
  if ((source_len == 0) || (dest_len == 0))
    return(dest);

  /* end is source_len if not set explicitly */
  if (dest_len < (end - start))
    end = dest_len + start;

  if ((source != dest) &&
      (type(source) == type(dest)))
    {
      switch (type(source))
	{
	case T_PAIR:
	  {
	    s7_pointer slow, ps, pd;

	    ps = source;
	    for (i = 0; i < start; i++)
	      ps = cdr(ps);
	    slow = ps;

	    pd = dest;
	    for (; (i < end) && is_pair(ps) && is_pair(pd); i++)
	      {
		car(pd) = car(ps);
		ps = cdr(ps);
		pd = cdr(pd);
		if ((!is_pair(ps)) || (!is_pair(pd))) return(dest);

		car(pd) = car(ps);
		ps = cdr(ps);
		pd = cdr(pd);

		slow = cdr(slow);
		if (slow == ps) return(dest);
	      }
	    return(dest);
	  }

	case T_VECTOR:
	  /* ignore possible dimensional mismatch */
	  for (i = start, j = 0; i < end; i++, j++)
	    vector_element(dest, j) = vector_element(source, i);
	  return(dest);

	case T_INT_VECTOR:
	  for (i = start, j = 0; i < end; i++, j++)
	    int_vector_element(dest, j) = int_vector_element(source, i);
	  return(dest);

	case T_FLOAT_VECTOR:
	  for (i = start, j = 0; i < end; i++, j++)
	    float_vector_element(dest, j) = float_vector_element(source, i);
	  return(dest);

	case T_STRING:
	  for (i = start, j = 0; i < end; i++, j++)
	    string_value(dest)[j] = string_value(source)[i];
	  return(dest);
	  
	case T_C_OBJECT:
	case T_ENVIRONMENT:
	case T_HASH_TABLE:
	  break;

	default:
	  return(dest);
	}
    }

  /* if source == dest here, we're moving data backwards, so this is safe in either case */

  for (i = start, j = 0; i < end; i++, j++)
    set(sc, dest, j, get(sc, source, i));

  /* some choices probably should raise an error, but don't:
   *   (copy (make-hash-table) "1") ; nothing to copy (empty hash table), so no error
   */
  return(dest);
}


static s7_pointer g_reverse(s7_scheme *sc, s7_pointer args)
{
  #define H_reverse "(reverse lst) returns a list with the elements of lst in reverse order.  reverse \
also accepts a string or vector argument."
  s7_pointer p, np;
  
  p = car(args);
  np = sc->NIL;

  switch (type(p))
    {
    case T_NIL:
      return(sc->NIL);

    case T_PAIR:
      return(s7_reverse(sc, p));
      /* if (is_null(np)) return(simple_wrong_type_argument_with_type(sc, sc->REVERSE, p, A_PROPER_LIST)); */
      break;

    case T_STRING:
      {
	int i, j, len;
	len = string_length(p);
	np = make_empty_string(sc, len, 0);
	for (i = 0, j = len - 1; i < len; i++, j--)
	  string_value(np)[i] = string_value(p)[j];
	if (is_bytevector(p))
	  set_bytevector(np);
      }
      break;

    case T_INT_VECTOR:
      {
	s7_Int i, j, len;
	len = vector_length(p);
	if (vector_rank(p) > 1)
	  np = g_make_vector(sc, list_3(sc, g_vector_dimensions(sc, list_1(sc, p)), small_int(0), sc->T));
	else np = make_vector_1(sc, len, NOT_FILLED, T_INT_VECTOR);
	for (i = 0, j = len - 1; i < len; i++, j--)
	  int_vector_element(np, i) = int_vector_element(p, j);
      }
      break;

    case T_FLOAT_VECTOR:
      {
	s7_Int i, j, len;
	len = vector_length(p);
	if (vector_rank(p) > 1)
	  np = g_make_vector(sc, list_3(sc, g_vector_dimensions(sc, list_1(sc, p)), real_zero, sc->T));
	else np = make_vector_1(sc, len, NOT_FILLED, T_FLOAT_VECTOR);
	for (i = 0, j = len - 1; i < len; i++, j--)
	  float_vector_element(np, i) = float_vector_element(p, j);
      }
      break;

    case T_VECTOR:
      {
	s7_Int i, j, len;
	len = vector_length(p);
	if (vector_rank(p) > 1)
	  np = g_make_vector(sc, list_1(sc, g_vector_dimensions(sc, list_1(sc, p))));
	else np = make_vector_1(sc, len, NOT_FILLED, T_VECTOR);
	for (i = 0, j = len - 1; i < len; i++, j--)
	  vector_element(np, i) = vector_element(p, j);
      }
      break;

    case T_HASH_TABLE:
      return(hash_table_reverse(sc, p));

    case T_C_OBJECT:
      check_method(sc, p, sc->REVERSE, args);
      return(object_reverse(sc, p));

    default:
      check_method(sc, p, sc->REVERSE, args);
      return(simple_wrong_type_argument_with_type(sc, sc->REVERSE, p, A_SEQUENCE));
    }
  return(np);
}


static s7_pointer g_reverse_in_place(s7_scheme *sc, s7_pointer args)
{
  #define H_reverse_in_place "(reverse! lst) reverses lst in place"
  s7_pointer p;

  p = car(args);
  switch (type(p))
    {
    case T_NIL:
      return(sc->NIL);

    case T_PAIR:
      p = reverse_in_place(sc, sc->NIL, p);
      if (is_null(p))
	return(simple_wrong_type_argument_with_type(sc, sc->REVERSEB, p, A_PROPER_LIST));
      break;

    case T_STRING:
      {
	int i, j, len;
	char *str;

	len = string_length(p);
	str = string_value(p);
	for (i = 0, j = len - 1; i < j; i++, j--)
	  {
	    char c;
	    c = str[i];
	    str[i] = str[j];
	    str[j] = c;
	  }
      }
      break;

    case T_INT_VECTOR:
      {
	s7_Int i, j, len;
	s7_Int *d;

	len = vector_length(p);
	d = int_vector_elements(p);
	for (i = 0, j = len - 1; i < j; i++, j--)
	  {
	    s7_Int temp;
	    temp = d[i];
	    d[i] = d[j];
	    d[j] = temp;
	  }
      }
      break;

    case T_FLOAT_VECTOR:
      {
	s7_Int i, j, len;
	s7_Double *d;

	len = vector_length(p);
	d = float_vector_elements(p);
	for (i = 0, j = len - 1; i < j; i++, j--)
	  {
	    s7_Double temp;
	    temp = d[i];
	    d[i] = d[j];
	    d[j] = temp;
	  }
      }
      break;

    case T_VECTOR:
      {
	s7_Int i, j, len;
	s7_pointer *d;

	len = vector_length(p);
	d = vector_elements(p);
	for (i = 0, j = len - 1; i < j; i++, j--)
	  {
	    s7_pointer temp;
	    temp = d[i];
	    d[i] = d[j];
	    d[j] = temp;
	  }
      }
      break;

    default:
      check_method(sc, p, sc->REVERSEB, args);
      return(simple_wrong_type_argument_with_type(sc, sc->REVERSEB, p, A_SEQUENCE));
    }
  return(p);
}


static s7_pointer list_fill(s7_scheme *sc, s7_pointer obj, s7_pointer val)
{
  /* ambiguous ("tree-fill"?) but if it's like vector-fill, we just stomp on the top level */
  s7_pointer x, y;

  x = obj;
  y = obj;

  while (true)
    {
      if (!is_pair(x)) return(val);
      car(x) = val;
      if (is_pair(cdr(x)))
	{
	  x = cdr(x);
	  car(x) = val;
	  if (is_pair(cdr(x)))
	    {
	      x = cdr(x);
	      y = cdr(y);
	      if (x == y) return(val);
	    }
	  else
	    {
	      if (is_not_null(cdr(x)))
		cdr(x) = val;
	      return(val);
	    }
	}
      else
	{
	  if (is_not_null(cdr(x)))
	    cdr(x) = val;
	  return(val);
	}
    }
  return(val);
}


static s7_pointer g_fill(s7_scheme *sc, s7_pointer args)
{
  #define H_fill "(fill! obj val) fills obj with the value val"
  s7_pointer p;
  
  p = car(args);
  switch (type(p))
    {
    case T_STRING:
      return(g_string_fill(sc, args)); /* redundant type check here and below */

    case T_HASH_TABLE:
      if (is_not_null(cadr(args)))
	{
	  check_method(sc, cadr(args), sc->FILL, args);
	  return(wrong_type_argument(sc, sc->FILL, small_int(2), cadr(args), T_NIL));
	}
      return(hash_table_clear(sc, p));

    case T_INT_VECTOR:
    case T_FLOAT_VECTOR:
    case T_VECTOR:
      return(g_vector_fill(sc, args));

    case T_C_OBJECT:
      check_method(sc, p, sc->FILL, args);
      if (c_object_fill(p))
	return((*(c_object_fill(p)))(sc, p, cadr(args)));
      return(eval_error(sc, "attempt to fill ~S?", p));

    case T_PAIR:
      return(list_fill(sc, p, cadr(args)));

    case T_NIL:
      return(cadr(args));        /* this parallels the empty vector case */

    default:
      check_method(sc, p, sc->FILL, args);
    }
  return(wrong_type_argument_with_type(sc, sc->FILL, small_int(1), p, A_SEQUENCE)); /* (fill! 1 0) */
}


#define SAVE_X_Z(X, Z)	     \
  do {                               \
    X = ((is_null(sc->x)) ? -1 : s7_gc_protect(sc, sc->x));	\
    Z = ((is_null(sc->z)) ? -1 : s7_gc_protect(sc, sc->z));	\
  } while (0)

#define RESTORE_X_Z(X, Z)                \
  do {                                        \
    if (X == -1) sc->x = sc->NIL; else {sc->x = s7_gc_protected_at(sc, X); s7_gc_unprotect_at(sc, X);} \
    if (Z == -1) sc->z = sc->NIL; else {sc->z = s7_gc_protected_at(sc, Z); s7_gc_unprotect_at(sc, Z);} \
    } while (0)


static s7_pointer object_to_list(s7_scheme *sc, s7_pointer obj)
{
  /* used only in format_to_port_1 */
  switch (type(obj))
    {
    case T_INT_VECTOR:
    case T_FLOAT_VECTOR:
    case T_VECTOR:
      return(s7_vector_to_list(sc, obj));

    case T_STRING:
      if (is_bytevector(obj))
	return(bytevector_to_list(sc, string_value(obj), string_length(obj)));
      return(s7_string_to_list(sc, string_value(obj), string_length(obj)));

    case T_HASH_TABLE:
      if (hash_table_entries(obj) > 0)
	{
	  s7_pointer x, iterator;
	  int gc_iter;
	  /* (format #f "~{~A ~}" (hash-table '(a . 1) '(b . 2))) */
	  
	  iterator = g_make_hash_table_iterator(sc, list_1(sc, obj));
	  gc_iter = s7_gc_protect(sc, iterator);
	  
	  sc->w = sc->NIL;
	  while (true)
	    {
	      x = hash_table_iterate(sc, iterator);
	      if (is_null(x)) break;
	      sc->w = cons(sc, x, sc->w);
	    }
	  
	  x = sc->w;
	  sc->w = sc->NIL;
	  s7_gc_unprotect_at(sc, gc_iter);
	  return(x);
	}
      return(sc->NIL);
      
    case T_ENVIRONMENT:
      check_method(sc, obj, sc->LET_TO_LIST, sc->NIL);
      return(s7_let_to_list(sc, obj));

    case T_C_OBJECT:
      {
	long int i, len; /* the "long" matters on 64-bit machines */
	s7_pointer x, z, result;
	int save_x = -1, save_z = -1, gc_res = -1, gc_z = -1;

	x = object_length(sc, obj);
	if (s7_is_integer(x))
	  len = s7_integer(x);
	else return(sc->F);

	if (len < 0)
	  return(sc->F);
	if (len == 0)
	  return(sc->NIL);

	result = make_list(sc, len, sc->NIL);
	gc_res = s7_gc_protect(sc, result);
	z = list_1(sc, sc->F);
	gc_z = s7_gc_protect(sc, z);

	SAVE_X_Z(save_x, save_z);
	for (i = 0, x = result; i < len; i++, x = cdr(x))
	  {
	    car(z) = make_integer(sc, i);
	    car(x) = (*(c_object_ref(obj)))(sc, obj, z);
	  }
	RESTORE_X_Z(save_x, save_z);
	
	s7_gc_unprotect_at(sc, gc_z);
	s7_gc_unprotect_at(sc, gc_res);
	return(result);
      }
    }
  return(obj);
}



/* ---------------- stacktrace ---------------- */

static s7_pointer stacktrace_find_caller(s7_scheme *sc, s7_pointer e)
{
  if ((is_let(e)) && (e != sc->rootlet))
    {
      if (is_function_env(e))
	return(funclet_function(e));
      return(stacktrace_find_caller(sc, outlet(e)));
    }
  return(sc->F);
}

static bool stacktrace_find_let(s7_scheme *sc, int loc, s7_pointer e)
{
  return((loc > 0) &&
	 ((stack_let(sc->stack, loc) == e) ||
	  (stacktrace_find_let(sc, loc - 4, e))));
}

static int stacktrace_find_error_hook_quit(s7_scheme *sc)
{
  int i;
  for (i = s7_stack_top(sc) - 1; i >= 3; i -= 4)
    if (stack_op(sc->stack, i) == OP_ERROR_HOOK_QUIT)
      return(i);
  return(-1);
}

static bool stacktrace_in_error_handler(s7_scheme *sc, int loc)
{
  return((outlet(sc->owlet) == sc->envir) ||
	 (stacktrace_find_let(sc, loc * 4, outlet(sc->owlet))) ||
	 (stacktrace_find_error_hook_quit(sc) > 0));
}


static bool direct_memq(s7_pointer symbol, s7_pointer symbols);

static bool stacktrace_error_hook_function(s7_scheme *sc, s7_pointer sym)
{
  if (is_symbol(sym))
    {
      s7_pointer f;
      f = s7_symbol_value(sc, sym);
      return((is_procedure(f)) &&
	     (is_procedure(sc->error_hook)) &&
	     (is_pair(s7_hook_functions(sc, sc->error_hook))) &&
	     (direct_memq(f, s7_hook_functions(sc, sc->error_hook))));
    }
  return(false);
}

static char *stacktrace_walker(s7_scheme *sc, s7_pointer code, s7_pointer e, 
			       char *notes, int gc_syms,
			       int code_cols, int total_cols, int notes_start_col, 
			       bool as_comment)
{
  s7_pointer syms;
  syms = gc_protected_at(sc, gc_syms);

  if (is_symbol(code))
    {
      if ((!direct_memq(code, syms)) &&
	  (global_slot(code) == sc->UNDEFINED))
	{
	  s7_pointer val;

	  syms = cons(sc, code, syms);
	  gc_protected_at(sc, gc_syms) = syms;

	  val = s7_symbol_local_value(sc, code, e);
	  if ((val) && (val != sc->UNDEFINED) &&
	      (!is_any_macro(val)))
	    {
	      int typ;

	      typ = type(val);
	      if ((typ != T_CLOSURE)      &&
		  (typ != T_CLOSURE_STAR) &&
		  (typ < T_C_FUNCTION)    &&
		  (typ != T_GOTO)         &&
		  (typ != T_CONTINUATION))
		{
		  char *objstr, *str;
		  const char *spaces;
		  int objlen, new_note_len, notes_max, cur_line_len = 0, spaces_len;
		  bool new_notes_line = false;

		  spaces = "                                                                                ";
		  spaces_len = strlen(spaces);

		  if (notes_start_col < 0) notes_start_col = 50;
		  notes_max = total_cols - notes_start_col;
		  objstr = s7_object_to_c_string(sc, val);
		  objlen = strlen(objstr);
		  if (objlen > notes_max)
		    {
		      objstr[notes_max - 4] = '.';
		      objstr[notes_max - 3] = '.';
		      objstr[notes_max - 2] = '.';
		      objstr[notes_max - 1] = '\0';
		      objlen = notes_max;
		    }

		  new_note_len = symbol_name_length(code) + 3 + objlen;
		  /* we want to append this much info to the notes, but does it need a new line?
		   */
		  if (notes_start_col < code_cols)
		    new_notes_line = true;
		  else
		    {
		      if (notes)
			{
			  char *last_newline;
			  last_newline = strrchr(notes, (int)'\n'); /* returns ptr to end if none = nil if not found? */
			  if (last_newline)
			    cur_line_len = strlen(notes) - strlen(last_newline);
			  else cur_line_len = strlen(notes);
			  new_notes_line = ((cur_line_len + new_note_len) > notes_max);
			}
		    }

		  if (new_notes_line)
		    {
		      new_note_len += (4 + notes_start_col + ((notes) ? strlen(notes) : 0));
		      str = (char *)malloc(new_note_len * sizeof(char));
		      snprintf(str, new_note_len, "%s\n%s%s%s%s: %s", 
			       (notes) ? notes : "", 
			       (as_comment) ? "; " : "",
			       (spaces_len >= notes_start_col) ? (char *)(spaces + spaces_len - notes_start_col) : "",
			       (as_comment) ? "" : " ; ",
			       symbol_name(code), 
			       objstr);
		    }
		  else
		    {
		      new_note_len += ((notes) ? strlen(notes) : 0) + 4;
		      str = (char *)malloc(new_note_len * sizeof(char));
		      snprintf(str, new_note_len, "%s%s%s: %s", 
			       (notes) ? notes : "", 
			       (notes) ? ", " : " ; ",
			       symbol_name(code), 
			       objstr);
		    }
		  free(objstr);
		  if (notes) free(notes);
		  return(str);
		}
	    }
	}
      return(notes);
    }
  if (is_pair(code))
    {
      notes = stacktrace_walker(sc, car(code), e, notes, gc_syms, code_cols, total_cols, notes_start_col, as_comment);
      return(stacktrace_walker(sc, cdr(code), e, notes, gc_syms, code_cols, total_cols, notes_start_col, as_comment));
    }
  return(notes);
}

static char *stacktrace_add_func(s7_scheme *sc, s7_pointer f, s7_pointer code, char *errstr, char *notes, int code_max, bool as_comment)
{
  int newlen, errlen;
  char *newstr, *str;

  errlen = strlen(errstr);
  if ((is_symbol(f)) &&
      (f != car(code)))
    {
      newlen = symbol_name_length(f) + errlen + 10;
      newstr = (char *)malloc(newlen * sizeof(char));
      errlen = snprintf(newstr, newlen, "%s: %s", symbol_name(f), errstr);
    }
  else
    {
      newlen = errlen + 8;
      newstr = (char *)malloc(newlen * sizeof(char));
      if ((errlen > 2) && (errstr[2] == '('))
	errlen = snprintf(newstr, newlen, "  %s", errstr);
      else errlen = snprintf(newstr, newlen, "%s", errstr);
    }

  newlen = code_max + 8 + ((notes) ? strlen(notes) : 0);
  str = (char *)malloc(newlen * sizeof(char));

  if (errlen >= code_max)
    {
      newstr[code_max - 4] = '.';
      newstr[code_max - 3] = '.';
      newstr[code_max - 2] = '.';
      newstr[code_max - 1] = '\0';
      snprintf(str, newlen, "%s%s%s\n", (as_comment) ? "; " : "", newstr, (notes) ? notes : "");
    }
  else
    {
      /* send out newstr, pad with spaces to code_max, then notes */
      int len;
      len = snprintf(str, newlen, "%s%s", (as_comment) ? "; " : "", newstr);
      if (notes)
	{
	  int i;
	  for (i = len; i < code_max - 1; i++)
	    str[i] = ' ';
	  str[i] = '\0';
	  strcat(str, notes); 
	  strcat(str, "\n");
	}
    }
  free(newstr);
  return(str);
}


static char *stacktrace_1(s7_scheme *sc, int frames_max, int code_cols, int total_cols, int notes_start_col, bool as_comment)
{
  char *str;
  int loc, top, frames = 0, gc_syms;

  gc_syms = s7_gc_protect(sc, sc->NIL);
  str = NULL;
  top = (sc->stack_end - sc->stack_start) / 4; /* (*s7* 'stack_top), not s7_stack_top! */
  
  if (stacktrace_in_error_handler(sc, top))
    {
      s7_pointer err_code;
      err_code = slot_value(sc->error_code);
      if (is_pair(err_code))
	{
	  char *errstr, *notes = NULL;
	  s7_pointer cur_env, f;

	  errstr = s7_object_to_c_string(sc, err_code);
	  cur_env = outlet(sc->owlet);
	  f = stacktrace_find_caller(sc, cur_env); /* this is a symbol */
	  if ((is_let(cur_env)) &&
	      (cur_env != sc->rootlet))
	    notes = stacktrace_walker(sc, err_code, cur_env, NULL, gc_syms, code_cols, total_cols, notes_start_col, as_comment);
	  str = stacktrace_add_func(sc, f, err_code, errstr, notes, code_cols, as_comment);
	  free(errstr);
	}

      /* now if OP_ERROR_HOOK_QUIT is in the stack, jump past it!
       */
      loc = stacktrace_find_error_hook_quit(sc);
      if (loc > 0) top = (loc + 1) / 4;
    }
     
  for (loc = top - 1; loc > 0; loc--)
    {
      s7_pointer code;
      int true_loc;

      true_loc = (int)(loc + 1) * 4 - 1;
      code = stack_code(sc->stack, true_loc);
      
      if (is_pair(code))
	{
	  char *codestr;
	  codestr = s7_object_to_c_string(sc, code);
	  if (codestr)
	    {
	      if ((strcmp(codestr, "(result)") != 0) &&
		  (strcmp(codestr, "(#f)") != 0) &&
		  (strstr(codestr, "(stacktrace)") == NULL) &&
		  (strstr(codestr, "(stacktrace ") == NULL))
		{
		  s7_pointer e, f;

		  e = stack_let(sc->stack, true_loc);
		  f = stacktrace_find_caller(sc, e);
		  if (!stacktrace_error_hook_function(sc, f))
		    {
		      char *notes = NULL, *newstr;
		      int newlen;

		      frames++;
		      if (frames > frames_max)
			{
			  free(codestr);
			  s7_gc_unprotect_at(sc, gc_syms);
			  return(str);
			}

		      if ((is_let(e)) && (e != sc->rootlet))
			notes = stacktrace_walker(sc, code, e, NULL, gc_syms, code_cols, total_cols, notes_start_col, as_comment);
		      newstr = stacktrace_add_func(sc, f, code, codestr, notes, code_cols, as_comment);
		      free(codestr);
		      if (notes) free(notes);

		      newlen = strlen(newstr) + 1 + ((str) ? strlen(str) : 0);
		      codestr = (char *)malloc(newlen * sizeof(char));
		      snprintf(codestr, newlen, "%s%s", (str) ? str : "", newstr);
		      if (str) free(str);
		      free(newstr);
		      str = codestr;
		      codestr = NULL;
		    }
		  else free(codestr);
		}
	      else free(codestr);
	    }
	}
    }

  s7_gc_unprotect_at(sc, gc_syms);      
  return(str);
}


s7_pointer s7_stacktrace(s7_scheme *sc)
{
  char *str;
  str = stacktrace_1(sc, 30, 45, 80, 45, false);
  return(make_string_uncopied_with_length(sc, str, safe_strlen(str)));
}


static s7_pointer g_stacktrace(s7_scheme *sc, s7_pointer args)
{
  #define H_stacktrace "(stacktrace (max-frames 30) (code-cols 50) (total-cols 80) (note-col 50) as-comment) returns \
a stacktrace as a string.  Each line has two portions, the code being evaluated and a note giving \
the value of local variables in that code.  The first argument sets how many lines are displayed. \
The next three arguments set the length and layout of those lines.  'as-comment' if #t causes each \
line to be preceded by a semicolon."

  s7_Int max_frames = 30, code_cols = 50, total_cols = 80, notes_start_col = 50;
  bool as_comment = false;
  char *str;

  if (!is_null(args))
    {
      if (s7_is_integer(car(args)))
	{
	  max_frames = s7_integer(car(args));
	  if ((max_frames <= 0) || (max_frames > S7_LONG_MAX))
	    max_frames = 30;
	  args = cdr(args);
	  if (!is_null(args))
	    {
	      if (s7_is_integer(car(args)))
		{
		  code_cols = s7_integer(car(args));
		  if ((code_cols <= 8) || (code_cols > S7_LONG_MAX))
		    code_cols = 50;
		  args = cdr(args);
		  if (!is_null(args))
		    {
		      if (s7_is_integer(car(args)))
			{
			  total_cols = s7_integer(car(args));
			  if ((total_cols <= code_cols) || (total_cols > S7_LONG_MAX))
			    total_cols = 80;
			  args = cdr(args);
			  if (!is_null(args))
			    {
			      if (s7_is_integer(car(args)))
				{
				  notes_start_col = s7_integer(car(args));
				  if ((notes_start_col <= 0) || (notes_start_col > S7_LONG_MAX))
				    notes_start_col = 50;
				  args = cdr(args);
				  if (!is_null(args))
				    {
				      if (s7_is_boolean(car(args)))
					as_comment = s7_boolean(sc, car(args));
				      else return(wrong_type_argument(sc, sc->STACKTRACE, small_int(5), car(args), T_BOOLEAN));
				    }
				}
			      else return(wrong_type_argument(sc, sc->STACKTRACE, small_int(4), car(args), T_INTEGER));
			    }
			}
		      else return(wrong_type_argument(sc, sc->STACKTRACE, small_int(3), car(args), T_INTEGER));
		    }
		}
	      else return(wrong_type_argument(sc, sc->STACKTRACE, small_int(2), car(args), T_INTEGER));
	    }
	}
      else 
	{
	  check_method(sc, car(args), sc->STACKTRACE, args);
	  return(wrong_type_argument(sc, sc->STACKTRACE, small_int(1), car(args), T_INTEGER));
	}
    }
  str = stacktrace_1(sc, (int)max_frames, (int)code_cols, (int)total_cols, (int)notes_start_col, as_comment);
  return(make_string_uncopied_with_length(sc, str, safe_strlen(str)));
}



/* -------- error handlers -------- */

static const char *make_type_name(s7_scheme *sc, const char *name, int article)
{
  int i, slen, len;

  slen = safe_strlen(name);
  len = slen + 8;
  if (len > sc->typnam_len)
    {
      if (sc->typnam) free(sc->typnam);
      sc->typnam = (char *)malloc(len * sizeof(char));
      sc->typnam_len = len;
    }
  if (article == INDEFINITE_ARTICLE)
    {
      i = 1;
      sc->typnam[0] = 'a';
      if ((name[0] == 'a') || (name[0] == 'e') || (name[0] == 'i') || (name[0] == 'o') || (name[0] == 'u'))
	sc->typnam[i++] = 'n'; 
      sc->typnam[i++] = ' ';
    }
  else
    {
      if (article == DEFINITE_ARTICLE)
	{
	  sc->typnam[0] = 't'; sc->typnam[1] = 'h'; sc->typnam[2] = 'e'; sc->typnam[3] = ' '; 
	  i = 4;
	}
      else i = 0;
    }
  memcpy((void *)(sc->typnam + i), (void *)name, slen);
  sc->typnam[i + slen] = '\0';
  return(sc->typnam);
}


static const char *type_name_from_type(s7_scheme *sc, int typ, int article)
{
  static const char *frees[3] =         {"free cell",          "the free cell",           "a free cell"};
  static const char *nils[3] =          {"nil",                "the null list",           "nil"};
  static const char *uniques[3] =       {"untyped",            "the untyped",             "untyped"};
  static const char *booleans[3] =      {"boolean",            "the boolean",             "boolean"};
  static const char *strings[3] =       {"string",             "the string",              "a string"};
  static const char *symbols[3] =       {"symbol",             "the symbol",              "a symbol"};
  static const char *syntaxes[3] =      {"syntax",             "the syntactic",           "syntactic"};
  static const char *pairs[3] =         {"pair",               "the pair",                "a pair"};
  static const char *gotos[3] =         {"goto",               "the call-with-exit goto", "a goto (from call-with-exit)"};
  static const char *continuations[3] = {"continuation",       "the continuation",        "a continuation"};
  static const char *c_functions[3] =   {"c-function",         "the c-function",          "a c-function"};
  static const char *macros[3] =        {"macro",              "the macro",               "a macro"};
  static const char *c_macros[3] =      {"c-macro",            "the c-macro",             "a c-macro"};
  static const char *bacros[3] =        {"bacro",              "the bacro",               "a bacro"};
  static const char *vectors[3] =       {"vector",             "the vector",              "a vector"};
  static const char *int_vectors[3] =   {"int-vector",         "the int-vector",          "an int-vector"};
  static const char *float_vectors[3] = {"float-vector",       "the float-vector",        "a float-vector"};
  static const char *c_pointers[3] =    {"C pointer",          "the raw C pointer",       "a raw C pointer"};
  static const char *counters[3] =      {"internal counter",   "the internal counter",    "an internal counter"};
  static const char *baffles[3] =       {"baffle",             "the baffle",              "a baffle"};
  static const char *slots[3] =         {"slot",               "the slot (name binding)", "a slot (variable binding)"};
  static const char *characters[3] =    {"character",          "the character",           "a character"};
  static const char *catches[3] =       {"catch",              "the catch",               "a catch"};
  static const char *dynamic_winds[3] = {"dynamic-wind",       "the dynamic-wind",        "a dynamic-wind"};
  static const char *hash_tables[3] =   {"hash-table",         "the hash-table",          "a hash-table"};
  static const char *environments[3] =  {"environment",        "the environment",         "an environment"};
  static const char *integers[3] =      {"integer",            "the integer",             "an integer"};
  static const char *big_integers[3] =  {"big integer",        "the big integer",         "a big integer"};
  static const char *ratios[3] =        {"ratio",              "the ratio",               "a ratio"};
  static const char *big_ratios[3] =    {"big ratio",          "the big ratio",           "a big ratio"};
  static const char *reals[3] =         {"real",               "the real",                "a real"};
  static const char *big_reals[3] =     {"big real",           "the big real",            "a big real"};
  static const char *complexes[3] =     {"complex number",     "the complex number",      "a complex number"};
  static const char *big_complexes[3] = {"big complex number", "the big complex number",  "a big complex number"};
  static const char *functions[3] =     {"function",           "the function",            "a function"};
  static const char *function_stars[3] ={"function*",          "the function*",           "a function*"};

  switch (typ)
    {
    case T_FREE:         return(frees[article]);
    case T_NIL:          return(nils[article]);
    case T_UNIQUE:       return(uniques[article]);
    case T_UNSPECIFIED:  return(uniques[article]);
    case T_BOOLEAN:      return(booleans[article]);
    case T_STRING:       return(strings[article]);
    case T_SYMBOL:       return(symbols[article]);
    case T_SYNTAX:       return(syntaxes[article]);
    case T_PAIR:         return(pairs[article]);
    case T_GOTO:         return(gotos[article]);
    case T_CONTINUATION: return(continuations[article]);
    case T_C_OPT_ARGS_FUNCTION:
    case T_C_RST_ARGS_FUNCTION:
    case T_C_ANY_ARGS_FUNCTION:
    case T_C_FUNCTION_STAR:
    case T_C_FUNCTION:   return(c_functions[article]);
    case T_CLOSURE:      return(functions[article]);
    case T_CLOSURE_STAR: return(function_stars[article]);
    case T_C_MACRO:      return(c_macros[article]);
    case T_C_POINTER:    return(c_pointers[article]);
    case T_CHARACTER:    return(characters[article]);
    case T_VECTOR:       return(vectors[article]);
    case T_INT_VECTOR:   return(int_vectors[article]);
    case T_FLOAT_VECTOR: return(float_vectors[article]);
    case T_MACRO:        return(macros[article]);
    case T_BACRO:        return(bacros[article]);
    case T_CATCH:        return(catches[article]); /* are these 2 possible? */
    case T_DYNAMIC_WIND: return(dynamic_winds[article]);
    case T_HASH_TABLE:   return(hash_tables[article]);
    case T_ENVIRONMENT:  return(environments[article]);
    case T_COUNTER:      return(counters[article]);
    case T_BAFFLE:       return(baffles[article]);
    case T_SLOT:         return(slots[article]);
    case T_INTEGER:      return(integers[article]);
    case T_RATIO:        return(ratios[article]);
    case T_REAL:         return(reals[article]);
    case T_COMPLEX:      return(complexes[article]);
    case T_BIG_INTEGER:  return(big_integers[article]);
    case T_BIG_RATIO:    return(big_ratios[article]);
    case T_BIG_REAL:     return(big_reals[article]);
    case T_BIG_COMPLEX:  return(big_complexes[article]);
    }
  return(NULL);
}


static const char *type_name(s7_scheme *sc, s7_pointer arg, int article)
{
  switch (type(arg))
    {
    case T_C_OBJECT:     
      return(make_type_name(sc, object_types[c_object_type(arg)]->name, article));

    case T_INPUT_PORT:   
      return(make_type_name(sc, (is_file_port(arg)) ? "input file port" : ((is_string_port(arg)) ? "input string port" : "input port"), article));

    case T_OUTPUT_PORT:  
      return(make_type_name(sc, (is_file_port(arg)) ? "output file port" : ((is_string_port(arg)) ? "output string port" : "output port"), article));

    case T_ENVIRONMENT:  
      if (has_methods(arg))
	{
	  s7_pointer class_name;
	  class_name = find_method(sc, arg, sc->CLASS_NAME);
	  if (is_symbol(class_name))
	    return(make_type_name(sc, symbol_name(class_name), article));
	}

    default:
      {
	const char *str;
	str = type_name_from_type(sc, type(arg), article);
	if (str) return(str);
      }
    }
  return("messed up object");
}


static s7_pointer prepackaged_type_name(s7_scheme *sc, s7_pointer x)
{
  s7_pointer p;

  if (has_methods(x))
    {
      p = find_method(sc, find_let(sc, x), sc->CLASS_NAME);
      if (is_symbol(p))
	return(symbol_name_cell(p));
    }

  p = prepackaged_type_names[type(x)];
  if (is_string(p)) return(p);

  switch (type(x))
    {
    case T_C_OBJECT:     
      return(object_types[c_object_type(x)]->scheme_name);

    case T_INPUT_PORT:   
      return((is_file_port(x)) ? AN_INPUT_FILE_PORT : ((is_string_port(x)) ? AN_INPUT_STRING_PORT : AN_INPUT_PORT));

    case T_OUTPUT_PORT:  
      return((is_file_port(x)) ? AN_OUTPUT_FILE_PORT : ((is_string_port(x)) ? AN_OUTPUT_STRING_PORT : AN_OUTPUT_PORT));
    }
  return(make_string_wrapper(sc, "unknown type!"));
}


static s7_pointer wrong_type_arg_error_prepackaged(s7_scheme *sc, s7_pointer caller, s7_pointer arg_n, s7_pointer arg, s7_pointer typnam, s7_pointer descr)
{
  /* info list is '(format_string caller arg_n arg type_name descr) */
  car(sc->WTA1) = caller;
  car(sc->WTA2) = arg_n;
  car(sc->WTA3) = arg;
  if (typnam == sc->GC_NIL)
    car(sc->WTA4) = prepackaged_type_name(sc, arg);
  else car(sc->WTA4) = typnam;
  car(sc->WTA5) = descr;
  return(s7_error(sc, sc->WRONG_TYPE_ARG, sc->WRONG_TYPE_ARG_INFO));
}


static s7_pointer simple_wrong_type_arg_error_prepackaged(s7_scheme *sc, s7_pointer caller, s7_pointer arg, s7_pointer typnam, s7_pointer descr)
{
  list_set(sc, sc->SIMPLE_WRONG_TYPE_ARG_INFO, 1, caller);
  list_set(sc, sc->SIMPLE_WRONG_TYPE_ARG_INFO, 2, arg);
  if (typnam == sc->GC_NIL)
    list_set(sc, sc->SIMPLE_WRONG_TYPE_ARG_INFO, 3, prepackaged_type_name(sc, arg));
  else list_set(sc, sc->SIMPLE_WRONG_TYPE_ARG_INFO, 3, typnam);
  list_set(sc, sc->SIMPLE_WRONG_TYPE_ARG_INFO, 4, descr);
  return(s7_error(sc, sc->WRONG_TYPE_ARG, sc->SIMPLE_WRONG_TYPE_ARG_INFO));
}


s7_pointer s7_wrong_type_arg_error(s7_scheme *sc, const char *caller, int arg_n, s7_pointer arg, const char *descr)
{
  /* info list is '(format_string caller arg_n arg type_name descr) */
  if (arg_n < 0) arg_n = 0;

  if (arg_n > 0)
    return(wrong_type_arg_error_prepackaged(sc, 
					    make_string_wrapper(sc, caller),
					    make_integer(sc, arg_n),
					    arg,
					    make_string_wrapper(sc, type_name(sc, arg, INDEFINITE_ARTICLE)),
					    make_string_wrapper(sc, descr)));
  return(simple_wrong_type_arg_error_prepackaged(sc,
						 make_string_wrapper(sc, caller),
						 arg,
						 make_string_wrapper(sc, type_name(sc, arg, INDEFINITE_ARTICLE)),
						 make_string_wrapper(sc, descr)));
}


static s7_pointer out_of_range_error_prepackaged(s7_scheme *sc, s7_pointer caller, s7_pointer arg_n, s7_pointer arg, s7_pointer descr)
{
  /* info list is '(format_string caller arg_n arg descr) */
  list_set(sc, sc->OUT_OF_RANGE_INFO, 1, caller);
  list_set(sc, sc->OUT_OF_RANGE_INFO, 2, arg_n);
  list_set(sc, sc->OUT_OF_RANGE_INFO, 3, arg);
  list_set(sc, sc->OUT_OF_RANGE_INFO, 4, descr);
  return(s7_error(sc, sc->OUT_OF_RANGE, sc->OUT_OF_RANGE_INFO));
}


static s7_pointer simple_out_of_range_error_prepackaged(s7_scheme *sc, s7_pointer caller, s7_pointer arg, s7_pointer descr)
{
  list_set(sc, sc->SIMPLE_OUT_OF_RANGE_INFO, 1, caller);
  list_set(sc, sc->SIMPLE_OUT_OF_RANGE_INFO, 2, arg);
  list_set(sc, sc->SIMPLE_OUT_OF_RANGE_INFO, 3, descr);
  return(s7_error(sc, sc->OUT_OF_RANGE, sc->SIMPLE_OUT_OF_RANGE_INFO));
}


s7_pointer s7_out_of_range_error(s7_scheme *sc, const char *caller, int arg_n, s7_pointer arg, const char *descr)
{
  /* info list is '(format_string caller arg_n arg descr) */
  if (arg_n < 0) arg_n = 0;

  if (arg_n > 0)
    return(out_of_range_error_prepackaged(sc, make_string_wrapper(sc, caller), make_integer(sc, arg_n), arg, make_string_wrapper(sc, descr)));
  return(simple_out_of_range_error_prepackaged(sc, make_string_wrapper(sc, caller), arg, make_string_wrapper(sc, descr)));
}


s7_pointer s7_wrong_number_of_args_error(s7_scheme *sc, const char *caller, s7_pointer args)
{
  return(s7_error(sc, sc->WRONG_NUMBER_OF_ARGS, 
		  list_2(sc, make_string_wrapper(sc, caller), /* "caller" includes the format directives */
			 args)));
}


static s7_pointer division_by_zero_error(s7_scheme *sc, s7_pointer caller, s7_pointer arg)
{
  return(s7_error(sc, sc->DIVISION_BY_ZERO, list_3(sc, sc->DIVISION_BY_ZERO_ERROR, caller, arg)));
}


static s7_pointer file_error(s7_scheme *sc, const char *caller, const char *descr, const char *name)
{
  return(s7_error(sc, sc->IO_ERROR,
		  list_4(sc, make_string_wrapper(sc, "~A: ~A ~S"), make_string_wrapper(sc, caller), make_string_wrapper(sc, descr), make_string_wrapper(sc, name))));
}


static s7_pointer closure_or_f(s7_scheme *sc, s7_pointer p)
{
  s7_pointer body;
  if (!is_closure(p)) return(p);
  body = closure_body(p);
  if (is_pair(cdr(body))) return(p);
  if (!is_pair(car(body))) return(sc->F);
  if (caar(body) == sc->QUOTE) return(sc->F);
  return(p);
}


static s7_pointer g_dynamic_wind(s7_scheme *sc, s7_pointer args)
{
  #define H_dynamic_wind "(dynamic-wind init body finish) calls init, then body, then finish, \
each a function of no arguments, guaranteeing that finish is called even if body is exited"
  s7_pointer p;

  if (!s7_is_aritable(sc, car(args), 0))
    {
      check_method(sc, car(args), sc->DYNAMIC_WIND, args);
      return(wrong_type_argument_with_type(sc, sc->DYNAMIC_WIND, small_int(1), car(args), A_THUNK));
    }
  if (!s7_is_aritable(sc, cadr(args), 0))
    {
      check_method(sc, cadr(args), sc->DYNAMIC_WIND, args);
      return(wrong_type_argument_with_type(sc, sc->DYNAMIC_WIND, small_int(2), cadr(args), A_THUNK));
    }
  if (!s7_is_aritable(sc, caddr(args), 0))
    {
      check_method(sc, caddr(args), sc->DYNAMIC_WIND, args);
      return(wrong_type_argument_with_type(sc, sc->DYNAMIC_WIND, small_int(3), caddr(args), A_THUNK));
    }

  /* this won't work:

       (let ((final (lambda (a b c) (list a b c))))
         (dynamic-wind
           (lambda () #f)
           (lambda () (set! final (lambda () (display "in final"))))
           final))

   * but why not?  'final' is a thunk by the time it is evaluated.
   *   catch (the error handler) is similar.
   *
   * It can't work here because we set up the dynamic_wind_out slot below and
   *   even if the thunk check was removed, we'd still be trying to apply the original function.
   */
  
  NEW_CELL(sc, p);
  dynamic_wind_in(p) = closure_or_f(sc, car(args));
  dynamic_wind_body(p) = cadr(args);
  dynamic_wind_out(p) = closure_or_f(sc, caddr(args));
  set_type(p, T_DYNAMIC_WIND);                          /* atom -> don't mark car/cdr, don't copy */
  
  /* since we don't care about the in and out results, and they are thunks, if the body is not a pair,
   *   or is a quoted thing, we just ignore that function.
   */

  push_stack(sc, OP_DYNAMIC_WIND, sc->NIL, p);          /* args will be the saved result, code = s7_dynwind_t obj */
  if (dynamic_wind_in(p) != sc->F)
    {
      dynamic_wind_state(p) = DWIND_INIT;
      if (is_any_macro(dynamic_wind_in(p)))
	{
	  push_stack(sc, OP_EVAL_MACRO, sc->NIL, args);
	  push_stack(sc, OP_APPLY, list_1(sc, list_1(sc, dynamic_wind_in(p))), dynamic_wind_in(p));
	}
      else push_stack(sc, OP_APPLY, sc->NIL, dynamic_wind_in(p));
    }
  else
    {
      dynamic_wind_state(p) = DWIND_BODY;
      if (is_any_macro(dynamic_wind_body(p)))
	{
	  push_stack(sc, OP_EVAL_MACRO, sc->NIL, args);
	  push_stack(sc, OP_APPLY, list_1(sc, list_1(sc, dynamic_wind_body(p))), dynamic_wind_body(p));
	}
      else push_stack(sc, OP_APPLY, sc->NIL, dynamic_wind_body(p));
    }
  return(sc->F);
}

/* C-side dynamic-wind would need at least void* context pointer passed to each function,
 *   and to fit with the scheme-side stuff above, the functions need to be s7 functions,
 *   so I wonder if it could be s7_dynamic_wind(s7_scheme *sc, s7_pointer init, s7_pointer body, s7_pointer finish)
 *   and the caller would use the C-closure idea (s7.html) to package up C-side data.
 *   Then, the caller would probably assume a return value, requiring s7_call?
 *   -> g_dynamic_wind(sc, list_3(sc, init, body, finish)) but with eval(sc, OP_APPLY) at end?
 */


static s7_pointer g_catch(s7_scheme *sc, s7_pointer args)
{
  #define H_catch "(catch tag thunk handler) evaluates thunk; if an error occurs that matches the tag (#t matches all), the handler is called"
  s7_pointer p, proc, err;

  proc = cadr(args);
  if (!s7_is_aritable(sc, proc, 0))
    return(wrong_type_argument_with_type(sc, sc->CATCH, small_int(2), proc, A_THUNK));

  err = caddr(args);
  if (!is_applicable(err))
    return(wrong_type_argument_with_type(sc, sc->CATCH, small_int(3), err, SOMETHING_APPLICABLE));
  /* if (is_let(err)) check_method(sc, err, sc->CATCH, args); */ /* causes exit from s7! */

  NEW_CELL(sc, p);
  catch_tag(p) = car(args);
  catch_goto_loc(p) = s7_stack_top(sc);
  catch_op_loc(p) = (int)(sc->op_stack_now - sc->op_stack);
  catch_handler(p) = err;
  set_type(p, T_CATCH);

  if (is_any_macro(err))
    push_stack(sc, OP_CATCH_2, args, p);
  else push_stack(sc, OP_CATCH, args, p);      /* args ignored but maybe safer for GC? */

  if (is_closure(proc))                        /* not also lambda* here because we need to handle the arg defaults */
    {
      sc->code = closure_body(proc);
      NEW_FRAME(sc, closure_let(proc), sc->envir);
      push_stack(sc, OP_BEGIN_UNCHECKED, sc->args, sc->code);
    }
  else 
    {
      if (is_any_macro(proc))
	{
	  push_stack(sc, OP_EVAL_MACRO, sc->NIL, args);
	  push_stack(sc, OP_APPLY, list_1(sc, list_1(sc, proc)), proc);
	}
      else push_stack(sc, OP_APPLY, sc->NIL, proc);
    }
  return(sc->F);
}


/* error reporting info -- save filename and line number */

#define remembered_line_number(Line) (Line & 0xfffff)
#define remembered_file_name(Line)   (((Line >> 20) <= sc->file_names_top) ? sc->file_names[Line >> 20] : sc->F)
/* this gives room for 4000 files each of 1000000 lines */


static int remember_file_name(s7_scheme *sc, const char *file)
{
  int i;

  for (i = 0; i <= sc->file_names_top; i++)
    if (safe_strcmp(file, string_value(sc->file_names[i])) == 0)
      return(i);

  sc->file_names_top++;
  if (sc->file_names_top >= sc->file_names_size)
    {
      int old_size = 0;
      if (sc->file_names_size == 0)
	{
	  sc->file_names_size = INITIAL_FILE_NAMES_SIZE;
	  sc->file_names = (s7_pointer *)calloc(sc->file_names_size, sizeof(s7_pointer));
	}
      else
	{
	  old_size = sc->file_names_size;
	  sc->file_names_size *= 2;
	  sc->file_names = (s7_pointer *)realloc(sc->file_names, sc->file_names_size * sizeof(s7_pointer));
	}
      for (i = old_size; i < sc->file_names_size; i++)
	sc->file_names[i] = sc->F;
    }
  sc->file_names[sc->file_names_top] = s7_make_permanent_string(file);

  return(sc->file_names_top);
}


static s7_pointer init_owlet(s7_scheme *sc)
{
  s7_pointer e;
  e = new_frame_in_env(sc, sc->rootlet);
  sc->temp3 = e;
  sc->error_type = s7_make_slot(sc, e, make_symbol(sc, "error-type"), sc->F);  /* the error type or tag ('division-by-zero) */
  sc->error_data = s7_make_slot(sc, e, make_symbol(sc, "error-data"), sc->F);  /* the message or information passed by the error function */
  sc->error_code = s7_make_slot(sc, e, make_symbol(sc, "error-code"), sc->F);  /* the code that s7 thinks triggered the error */
  sc->error_line = s7_make_slot(sc, e, make_symbol(sc, "error-line"), sc->F);  /* the line number of that code */
  sc->error_file = s7_make_slot(sc, e, make_symbol(sc, "error-file"), sc->F);  /* the file name of that code */
  return(e);
}


static s7_pointer g_owlet(s7_scheme *sc, s7_pointer args)
{
  #define H_owlet "(owlet) returns the environment at the point of the last error. \
It has the additional local variables: error-type, error-data, error-code, error-line, and error-file."
  /* if owlet is not copied, (define e (owlet)), e changes as owlet does!
   */
  return(let_copy(sc, sc->owlet));
}


static s7_pointer active_catches(s7_scheme *sc)
{
  int i;
  s7_pointer x, lst;
  lst = sc->NIL;
  for (i = s7_stack_top(sc) - 1; i >= 3; i -= 4)
    switch (stack_op(sc->stack, i))
      {
      case OP_CATCH_ALL:
	lst = cons(sc, sc->T, lst);
	break;

      case OP_CATCH_2:
      case OP_CATCH_1:
      case OP_CATCH:
	x = stack_code(sc->stack, i);
	lst = cons(sc, catch_tag(x), lst);
	break;
      }
  return(reverse_in_place_unchecked(sc, sc->NIL, lst));
}

static s7_pointer active_exits(s7_scheme *sc)
{
  /* (call-with-exit (lambda (exiter) (*s7* 'exits))) */
  int i;
  s7_pointer lst;
  lst = sc->NIL;
  for (i = s7_stack_top(sc) - 1; i >= 3; i -= 4)
    if (stack_op(sc->stack, i) == OP_DEACTIVATE_GOTO)
      {
	s7_pointer func, jump;
	func = stack_code(sc->stack, i);  /* presumably this has the goto name */
	jump = stack_args(sc->stack, i);  /* call this to jump */
	
	if (is_any_closure(func))
	  lst = cons(sc, cons(sc, car(closure_args(func)), jump), lst);
	else
	  {
	    if ((is_pair(func)) && (car(func) == sc->CALL_WITH_EXIT))
	      lst = cons(sc, cons(sc, car(cadr(cadr(func))), jump), lst); /* (call-with-exit (lambda (three) ...)) */
	    else lst = cons(sc, cons(sc, sc->UNSPECIFIED, jump), lst);
	  }
	sc->w = lst;
      }
  return(reverse_in_place_unchecked(sc, sc->NIL, lst));
}

static s7_pointer stack_entries(s7_scheme *sc)
{
  int i;
  s7_pointer lst;
  lst = sc->NIL;
  for (i = s7_stack_top(sc) - 1; i >= 3; i -= 4)
    {
      s7_pointer func, args, e;
      opcode_t op;
      func = stack_code(sc->stack, i);
      args = stack_args(sc->stack, i);
      e = stack_let(sc->stack, i);
      op = stack_op(sc->stack, i);
      if ((s7_is_valid(sc, func)) &&
	  (s7_is_valid(sc, args)) &&
	  (s7_is_valid(sc, e)) &&
	  (op < OP_MAX_DEFINED))
	{
	  lst = cons(sc, list_4(sc, func, args, e, make_string_wrapper(sc, real_op_names[op])), lst);
	  sc->w = lst;
	}
    }
  return(reverse_in_place_unchecked(sc, sc->NIL, lst));
}


/* catch handlers */

typedef bool (*catch_function)(s7_scheme *sc, int i, s7_pointer type, s7_pointer info, bool *reset_hook);
static catch_function catchers[OP_MAX_DEFINED + 1];

static bool catch_all_function(s7_scheme *sc, int i, s7_pointer type, s7_pointer info, bool *reset_hook)
{
  s7_pointer catcher;
  catcher = stack_let(sc->stack, i);
  sc->op_stack_now = (s7_pointer *)(sc->op_stack + catch_all_op_loc(catcher));
  sc->stack_end = (s7_pointer *)(sc->stack_start + catch_all_goto_loc(catcher));
  pop_stack(sc);
  sc->value = catch_all_result(catcher);
  return(true);
}

static bool catch_2_function(s7_scheme *sc, int i, s7_pointer type, s7_pointer info, bool *reset_hook)
{
  /* this is the macro-error-handler case from g_catch
   *    (let () (define-macro (m . args) (apply (car args) (cadr args))) (catch #t (lambda () (error abs -1)) m))
   */ 
  s7_pointer x;
  x = stack_code(sc->stack, i);
  if ((catch_tag(x) == sc->T) ||
      (catch_tag(x) == type) ||
      (type == sc->T))
    {
      int loc;
      loc = catch_goto_loc(x);
      sc->op_stack_now = (s7_pointer *)(sc->op_stack + catch_op_loc(x));
      sc->stack_end = (s7_pointer *)(sc->stack_start + loc);
      sc->code = catch_handler(x);
      
      car(sc->T3_1) = sc->code;
      car(sc->T3_2) = type;
      car(sc->T3_3) = info;
      sc->args = list_1(sc, sc->T3_1);
      
      push_stack(sc, OP_EVAL_MACRO, sc->NIL, sc->args);
      sc->op = OP_APPLY;
      return(true);
    }
  return(false);
}

static bool catch_1_function(s7_scheme *sc, int i, s7_pointer type, s7_pointer info, bool *reset_hook)
{
  s7_pointer x;
  x = stack_code(sc->stack, i);
  if ((catch_tag(x) == sc->T) ||
      (catch_tag(x) == type) ||
      (type == sc->T))
    {
      int loc;
      opcode_t op;
      s7_pointer catcher, error_func, body;
      
      op = stack_op(sc->stack, i);
      catcher = x;
      loc = catch_goto_loc(catcher);
      sc->op_stack_now = (s7_pointer *)(sc->op_stack + catch_op_loc(catcher));
      sc->stack_end = (s7_pointer *)(sc->stack_start + loc);
      error_func = catch_handler(catcher);
      
      /* very often the error handler just returns either a constant ('error or #f), or
       *   the args passed to it, so there's no need to laboriously make a closure,
       *   and apply it -- just set sc->value to the closure body (or the args) and
       *   return.
       *
       * so first examine closure_body(error_func)
       *   if it is a constant, or quoted symbol, return that,
       *   if it is the args symbol, return (list type info)
       */
      
      /* if OP_CATCH_1, we deferred making the error handler until it is actually needed
       */
      if (op == OP_CATCH_1)
	body = cdr(error_func);
      else 
	{
	  if (is_closure(error_func))
	    body = closure_body(error_func);
	  else body = NULL;
	}
      
      if ((body) && (is_null(cdr(body))))
	{
	  s7_pointer y = NULL;
	  body = car(body);
	  if (is_pair(body))
	    {
	      if (car(body) == sc->QUOTE)
		y = cadr(body);
	      else
		{
		  if ((car(body) == sc->CAR) &&
		      (cadr(body) == car(error_func)))
		    y = type;
		}
	    }
	  else
	    {
	      if (is_symbol(body))
		{
		  if (body == car(error_func))
		    y = list_2(sc, type, info);
		}
	      else y = body;
	    }
	  if (y)
	    {
	      pop_stack(sc);
	      sc->value = y;
	      return(true);
	    }
	}
      if (op == OP_CATCH_1)
	{
	  s7_pointer y;
	  make_closure_without_capture(sc, y, car(error_func), cdr(error_func), stack_let(sc->stack, i));
	  sc->code = y;
	}
      else sc->code = error_func;
      
      /* if user (i.e. yers truly!) copies/pastes the preceding lambda () into the
       *   error handler portion of the catch, he gets the inexplicable message:
       *       ;(): too many arguments: (a1 ())
       *   when this apply tries to call the handler.  So, we need a special case
       *   error check here!
       */
      
      if (!s7_is_aritable(sc, sc->code, 2))
	{
	  s7_wrong_number_of_args_error(sc, "catch error handler should accept 2 args: ~S", sc->code);
	  return(false);
	}
      
      /* since make_closure_with_let sets needs_copied_args and we're going to OP_APPLY,
       *   we don't need a new list here.
       */
      car(sc->T2_1) = type;
      car(sc->T2_2) = info;
      sc->args = sc->T2_1;
      sc->op = OP_APPLY;
      
      /* explicit eval needed if s7_call called into scheme where a caught error occurred (ex6 in exs7.c)
       *  but putting it here (via eval(sc, OP_APPLY)) means the C stack is not cleared correctly in non-s7-call cases, 
       *  so defer it until s7_call 
       */
      return(true);
    }
  return(false);
}

static bool catch_dw_function(s7_scheme *sc, int i, s7_pointer type, s7_pointer info, bool *reset_hook)
{
  s7_pointer x;
  x = stack_code(sc->stack, i);
  if (dynamic_wind_state(x) == DWIND_BODY)
    {
      dynamic_wind_state(x) = DWIND_FINISH;    /* make sure an uncaught error in the exit thunk doesn't cause us to loop */
      if (dynamic_wind_out(x) != sc->F)
	{
	  push_stack(sc, OP_EVAL_DONE, sc->args, sc->code); 
	  sc->code = dynamic_wind_out(x);
	  if (is_any_macro(sc->code))
	    {
	      push_stack(sc, OP_EVAL_MACRO, sc->NIL, sc->NIL);
	      sc->args = list_1(sc, list_1(sc, sc->code));
	    }
	  else sc->args = sc->NIL;
	  eval(sc, OP_APPLY);                  /* I guess this means no call/cc out of the exit thunk in an error-catching context */
	}
    }
  return(false);
}

static bool catch_out_function(s7_scheme *sc, int i, s7_pointer type, s7_pointer info, bool *reset_hook)
{
  s7_pointer x;
  x = stack_code(sc->stack, i);                /* "code" = port that we opened */
  s7_close_output_port(sc, x);
  x = stack_args(sc->stack, i);                /* "args" = port that we shadowed, if not #f */
  if (x != sc->F)
    sc->output_port = x;
  return(false);
}

static bool catch_in_function(s7_scheme *sc, int i, s7_pointer type, s7_pointer info, bool *reset_hook)
{
  s7_close_input_port(sc, stack_code(sc->stack, i)); /* "code" = port that we opened */
  sc->input_port = stack_args(sc->stack, i);         /* "args" = port that we shadowed */
  return(false);
}

static bool catch_read_function(s7_scheme *sc, int i, s7_pointer type, s7_pointer info, bool *reset_hook)
{
  pop_input_port(sc);  
  return(false);
}

static bool catch_eval_function(s7_scheme *sc, int i, s7_pointer type, s7_pointer info, bool *reset_hook)
{
  s7_close_input_port(sc, sc->input_port);
  pop_input_port(sc);
  return(false);
}

static bool catch_barrier_function(s7_scheme *sc, int i, s7_pointer type, s7_pointer info, bool *reset_hook)
{
  if (is_input_port(stack_args(sc->stack, i)))      /* (eval-string "'(1 .)") */
    {
      if (sc->input_port == stack_args(sc->stack, i))
	pop_input_port(sc);
      s7_close_input_port(sc, stack_args(sc->stack, i));
    }
  return(false);
}

static bool catch_hook_function(s7_scheme *sc, int i, s7_pointer type, s7_pointer info, bool *reset_hook)
{
  sc->error_hook = stack_code(sc->stack, i);
  /* apparently there was an error during *error-hook* evaluation, but Rick wants the hook re-established anyway */
  (*reset_hook) = true;
  /* avoid infinite loop -- don't try to (re-)evaluate (buggy) *error-hook*! */
  return(false);
}

static bool catch_goto_function(s7_scheme *sc, int i, s7_pointer type, s7_pointer info, bool *reset_hook)
{
  call_exit_active(stack_args(sc->stack, i)) = false;
  return(false);
}

static void init_catchers(void)
{
  int i;
  for (i = 0; i <= OP_MAX_DEFINED; i++) catchers[i] = NULL;
  catchers[OP_CATCH_ALL] = catch_all_function;
  catchers[OP_CATCH_2] = catch_2_function;
  catchers[OP_CATCH_1] = catch_1_function;
  catchers[OP_CATCH] = catch_1_function;
  catchers[OP_DYNAMIC_WIND] = catch_dw_function;
  catchers[OP_GET_OUTPUT_STRING_1] = catch_out_function;
  catchers[OP_UNWIND_OUTPUT] = catch_out_function;
  catchers[OP_UNWIND_INPUT] = catch_in_function;
  catchers[OP_READ_DONE] = catch_read_function;      /* perhaps an error during (read) */
  catchers[OP_EVAL_STRING_1] = catch_eval_function;  /* perhaps an error happened before we could push the OP_EVAL_STRING_2 */
  catchers[OP_EVAL_STRING_2] = catch_eval_function;
  catchers[OP_BARRIER] = catch_barrier_function;
  catchers[OP_DEACTIVATE_GOTO] = catch_goto_function;
  catchers[OP_ERROR_HOOK_QUIT] = catch_hook_function;
}

static s7_pointer g_throw(s7_scheme *sc, s7_pointer args)
{
  #define H_throw "(throw tag . info) is like (error ...) but it does not affect the owlet. \
It looks for an existing catch with a matching tag, and jumps to it if found.  Otherwise it raises an error."

  bool ignored_flag = false;
  int i;
  s7_pointer type, info;

  type = car(args);
  info = cdr(args);
  /* look for a catcher */

  for (i = s7_stack_top(sc) - 1; i >= 3; i -= 4)
    {
      catch_function catcher;
      catcher = catchers[stack_op(sc->stack, i)];
      if ((catcher) &&
	  (catcher(sc, i, type, info, &ignored_flag)))
	{
	  if (sc->longjmp_ok) longjmp(sc->goto_start, 1);
	  return(sc->value);
	}
    }
  if (is_let(car(args))) check_method(sc, car(args), sc->THROW, args);
  return(s7_error(sc, make_symbol(sc, "uncaught-throw"), args));
}


static void s7_warn(s7_scheme *sc, int len, const char *ctrl, ...)
{
  va_list ap;
  char *str;

  str = (char *)malloc(len * sizeof(char));
  va_start(ap, ctrl);
  len = vsnprintf(str, len, ctrl, ap);
  va_end(ap);

  s7_display(sc, make_string_uncopied_with_length(sc, str, len), sc->error_port);
}


s7_pointer s7_error(s7_scheme *sc, s7_pointer type, s7_pointer info)
{
  static int last_line = -1;
  bool reset_error_hook = false;
  s7_pointer cur_code;

  /* type is a symbol normally, and info is compatible with format: (apply format #f info) -- 
   *    car(info) is the control string, cdr(info) its args
   *    type/range errors have cadr(info)=caller, caddr(info)=offending arg number
   *    null info can mean symbol table is locked so make-symbol uses s7_error to get out 
   *
   * set up (owlet), look for a catch that matches 'type', if found
   *   call its error-handler, else if *error-hook* is bound, call it,
   *   else send out the error info ourselves.
   */
  sc->no_values = 0; 
  sc->format_depth = -1;
  sc->gc_off = false;  /* this is in case we were triggered from the sort function -- clumsy! */

  slot_set_value(sc->error_type, type);
  slot_set_value(sc->error_data, info);
  outlet(sc->owlet) = sc->envir;

  cur_code = sc->cur_code;
  slot_set_value(sc->error_code, cur_code);
  if (has_line_number(cur_code)) /* could it ever be a function-env-with-line-info?  is it worth checking is_pair? */
    {
      int line;
      line = pair_line_number(cur_code);
      if (line != last_line)
	{
	  last_line = line;
	  if ((line > 0) &&
	      (is_pair(cur_code))) /* see above */
	    {
	      slot_set_value(sc->error_line, make_integer(sc, remembered_line_number(line))); 
	      slot_set_value(sc->error_file, remembered_file_name(line));	  
	    }
	  else
	    {
	      slot_set_value(sc->error_line, sc->F);
	      slot_set_value(sc->error_file, sc->F);
	    }
	}
    }
  else
    {
      slot_set_value(sc->error_line, sc->F);
      slot_set_value(sc->error_file, sc->F);
    }

  { /* look for a catcher */
    int i;
    /* top is 1 past actual top, top - 1 is op, if op = OP_CATCH, top - 4 is the cell containing the catch struct */

    for (i = s7_stack_top(sc) - 1; i >= 3; i -= 4)
      {
	catch_function catcher;
	catcher = catchers[stack_op(sc->stack, i)];
	if ((catcher) &&
	    (catcher(sc, i, type, info, &reset_error_hook)))
	  {
	    if (sc->longjmp_ok) longjmp(sc->goto_start, 1);
	    return(type);
	  }
      }
  }

  /* error not caught */
  /* (set! *error-hook* (list (lambda (hook) (apply format #t (hook 'args))))) */
  
  if ((!reset_error_hook) && 
      (is_procedure(sc->error_hook)) &&
      (is_pair(s7_hook_functions(sc, sc->error_hook))))
    {
      s7_pointer error_list;
      /* (set! (hook-functions *error-hook*) (list (lambda (h) (format *stderr* "got error ~A~%" (h 'args))))) */
      
      error_list = sc->error_hook;
      sc->error_hook = sc->F;
      /* if the *error-hook* functions trigger an error, we had better not have *error-hook* still set! */
      
      push_stack(sc, OP_ERROR_HOOK_QUIT, sc->NIL, error_list); /* restore *error-hook* upon successful (or any!) evaluation */
      sc->args = list_2(sc, type, info);
      sc->code = error_list;
      
      /* if we drop into the longjmp below, the hook functions are not called!
       *   OP_ERROR_HOOK_QUIT performs the longjmp, so it should be safe to go to eval.
       */
      eval(sc, OP_APPLY);
    }
  else
    {
      /* if info is not a list, send object->string to current error port,
       *   else assume car(info) is a format control string, and cdr(info) are its args
       *
       * if at all possible, get some indication of where we are!
       */
      s7_pointer error_port;
      error_port = s7_current_error_port(sc);
      
      if ((!s7_is_list(sc, info)) ||
	  (!is_string(car(info))))
	format_to_port(sc, error_port, "\n;~S ~S", list_2(sc, type, info), NULL, false, 7);
      else
	{
	  const char *carstr;
	  int i, len;
	  bool use_format = false;
	  
	  /* it's possible that the error string is just a string -- not intended for format */
	  carstr = string_value(car(info));
	  len = string_length(car(info));
	  for (i = 0; i < len; i++)
	    if (carstr[i] == '~')
	      {
		use_format = true;
		break;
	      }
	  
	  if (use_format)
	    {
	      char *errstr;
	      int str_len;
	      len += 8;
	      errstr = (char *)malloc(len * sizeof(char));
	      str_len = snprintf(errstr, len, "\n;%s", string_value(car(info)));
	      format_to_port(sc, error_port, errstr, cdr(info), NULL, false, str_len);
	      free(errstr);
	    }
	  else format_to_port(sc, error_port, "\n;~S ~S", list_2(sc, type, info), NULL, false, 7);
	}
      
      /* now display location at end */
      
      if ((is_input_port(sc->input_port)) &&
	  (port_file(sc->input_port) != stdin) &&
	  (!port_is_closed(sc->input_port)))
	{
	  const char *filename = NULL;
	  int line;
	  
	  filename = port_filename(sc->input_port);
	  line = port_line_number(sc->input_port);
	  
	  if (filename)
	    format_to_port(sc, error_port, "\n;  ~A[~D]", list_2(sc, make_string_wrapper(sc, filename), make_integer(sc, line)), NULL, false, 10);
	  else 
	    {
	      if ((line > 0) &&
		  (slot_value(sc->error_line) != sc->F))
		format_to_port(sc, error_port, "\n;  line ~D", list_1(sc, make_integer(sc, line)), NULL, false, 11);
	      else
		{
		  if (is_pair(sc->input_port_stack))
		    {
		      s7_pointer p;
		      p = car(sc->input_port_stack);
		      if ((is_input_port(p)) &&
			  (port_file(p) != stdin) &&
			  (!port_is_closed(p)))
			{
			  filename = port_filename(p);
			  line = port_line_number(p);
			  if (filename)
			    format_to_port(sc, error_port, "\n;  ~A[~D]", list_2(sc, make_string_wrapper(sc, filename), make_integer(sc, line)), NULL, false, 10);
			}
		    }
		}
	    }
	}
      else
	{
	  const char *call_name;
	  call_name = sc->s7_call_name;
	  /* sc->s7_call_name = NULL; */
	  if (call_name)
	    {
	      sc->s7_call_name = NULL;
	      if ((sc->s7_call_file != NULL) &&
		  (sc->s7_call_line >= 0))
		{
		  format_to_port(sc, error_port, "\n;  ~A ~A[~D]",
				 list_3(sc, 
					make_string_wrapper(sc, call_name), 
					make_string_wrapper(sc, sc->s7_call_file), 
					make_integer(sc, sc->s7_call_line)), 
				 NULL, false, 13);
		}
	    }
	}
      s7_newline(sc, error_port);
      
      if (is_string(slot_value(sc->error_file)))
	{
	  format_to_port(sc, error_port, ";    ~S, line ~D",
			 list_2(sc, slot_value(sc->error_file),	slot_value(sc->error_line)),
			 NULL, false, 16);
	  s7_newline(sc, error_port);
	}
      
      /* look for __func__ in the error environment etc
       */
      if (error_port != sc->F)
	{
	  char *errstr;
	  errstr = stacktrace_1(sc, 
				s7_integer(let_ref_1(sc, sc->stacktrace_env, s7_make_symbol(sc, "max-frames"))),
				s7_integer(let_ref_1(sc, sc->stacktrace_env, s7_make_symbol(sc, "code-cols"))),
				s7_integer(let_ref_1(sc, sc->stacktrace_env, s7_make_symbol(sc, "total-cols"))),
				s7_integer(let_ref_1(sc, sc->stacktrace_env, s7_make_symbol(sc, "notes-start-col"))),
				s7_boolean(sc, let_ref_1(sc, sc->stacktrace_env, s7_make_symbol(sc, "as-comment"))));
	  if (errstr)
	    {
	      port_write_string(error_port)(sc, ";\n", 2, error_port);
	      port_write_string(error_port)(sc, errstr, strlen(errstr), error_port);
	      free(errstr);
	      port_write_character(error_port)(sc, '\n', error_port);
	    }
	}
      else
	{
	  if (is_pair(slot_value(sc->error_code)))
	    {
	      format_to_port(sc, error_port, ";    ~S", list_1(sc, slot_value(sc->error_code)), NULL, false, 7);
	      s7_newline(sc, error_port);
	    }
	}
      
      /* if (is_continuation(type))
       *   go into repl here with access to continuation?  Or expect *error-handler* to deal with it?
       */
      sc->value = type;
      stack_reset(sc);
      sc->op = OP_ERROR_QUIT;
    }

  if (sc->longjmp_ok) longjmp(sc->goto_start, 1);
  return(type);
}


static s7_pointer apply_error(s7_scheme *sc, s7_pointer obj, s7_pointer args)
{
  /* the operator type is needed here else the error message is confusing:
   *    (apply '+ (list 1 2))) -> ;attempt to apply + to (1 2)?
   */
  static s7_pointer errstr = NULL;
  if (is_null(obj))
    return(s7_error(sc, sc->SYNTAX_ERROR, list_2(sc, make_string_wrapper(sc, "attempt to apply nil to ~S?"), args)));
  if (!errstr)
    errstr = s7_make_permanent_string("attempt to apply ~A ~S to ~S?");
  return(s7_error(sc, sc->SYNTAX_ERROR, 
		  list_4(sc, errstr, make_string_wrapper(sc, type_name(sc, obj, DEFINITE_ARTICLE)), obj, args)));
}


static s7_pointer eval_error(s7_scheme *sc, const char *errmsg, s7_pointer obj)
{
  return(s7_error(sc, sc->SYNTAX_ERROR, list_2(sc, make_string_wrapper(sc, errmsg), obj)));
}


static s7_pointer eval_error_with_string(s7_scheme *sc, s7_pointer errmsg, s7_pointer obj)
{
  return(s7_error(sc, sc->SYNTAX_ERROR, list_2(sc, errmsg, obj)));
}


static s7_pointer eval_error_with_name_and_string(s7_scheme *sc, s7_pointer errmsg, s7_pointer obj)
{
  int ind;
  ind = (int)(sc->op);
  if (!sc->op_names_saved)
    sc->op_names_saved = (s7_pointer *)calloc(OP_MAX_DEFINED + 1, sizeof(s7_pointer));
  if (!sc->op_names_saved[ind])
    sc->op_names_saved[ind] = s7_make_permanent_string(op_names[ind]);
  return(s7_error(sc, sc->SYNTAX_ERROR, list_3(sc, errmsg, sc->op_names_saved[ind], obj)));
}


static s7_pointer eval_error_with_name(s7_scheme *sc, const char *errmsg, s7_pointer obj)
{
  return(eval_error_with_name_and_string(sc, make_string_wrapper(sc, errmsg), obj));
}


static s7_pointer eval_type_error(s7_scheme *sc, const char *errmsg, s7_pointer obj)
{
  return(s7_error(sc, sc->WRONG_TYPE_ARG, list_2(sc, make_string_wrapper(sc, errmsg), obj)));
}


static s7_pointer eval_range_error(s7_scheme *sc, const char *errmsg, s7_pointer obj)
{
  return(s7_error(sc, sc->OUT_OF_RANGE, list_2(sc, make_string_wrapper(sc, errmsg), obj)));
}


static s7_pointer read_error_1(s7_scheme *sc, const char *errmsg, bool string_error)
{
  /* reader errors happen before the evaluator gets involved, so forms such as:
   *   (catch #t (lambda () (car '( . ))) (lambda arg 'error))
   * do not catch the error if we simply signal an error when we encounter it.
   */
  char *msg;
  int len;
  s7_pointer pt;
  pt = sc->input_port;

  if (!string_error)
    {
      /* make an heroic effort to find where we slid off the tracks */
      
      if (is_string_port(sc->input_port))
	{
          #define QUOTE_SIZE 40
	  unsigned int i, j, start = 0, end, slen;
	  char *recent_input = NULL;
	  
	  /* we can run off the end in cases like (eval-string "(. . ,.)") or (eval-string " (@ . ,.)") */
	  if (port_position(pt) >= port_data_size(pt))        
	    port_position(pt) = port_data_size(pt) - 1;
	  
	  /* start at current position and look back a few chars */
	  for (i = port_position(pt), j = 0; (i > 0) && (j < QUOTE_SIZE); i--, j++)
	    if ((port_data(pt)[i] == '\0') ||
		(port_data(pt)[i] == '\n') ||
		(port_data(pt)[i] == '\r'))
	      break;
	  start = i;
	  
	  /* start at current position and look ahead a few chars */
	  for (i = port_position(pt), j = 0; (i < port_data_size(pt)) && (j < QUOTE_SIZE); i++, j++)
	    if ((port_data(pt)[i] == '\0') ||
		(port_data(pt)[i] == '\n') ||
		(port_data(pt)[i] == '\r'))
	      break;
	  
	  end = i;
	  slen = end - start;
	  /* hopefully this is more or less the current line where the read error happened */
	  
	  if (slen > 0)
	    {
	      recent_input = (char *)calloc((slen + 9), sizeof(char));
	      for (i = 0; i < (slen + 8); i++) recent_input[i] = '.';
	      recent_input[3] = ' ';
	      recent_input[slen + 4] = ' ';
	      for (i = 0; i < slen; i++) recent_input[i + 4] = port_data(pt)[start + i];
	    }
	  
	  if ((port_line_number(pt) > 0) &&
	      (port_filename(pt)))
	    {
	      len = safe_strlen(recent_input) + safe_strlen(errmsg) + port_filename_length(pt) + safe_strlen(sc->current_file) + 64;
	      msg = (char *)malloc(len * sizeof(char));
	      len = snprintf(msg, len, "%s: %s %s[%d], last top-level form at: %s[%d]", 
			     errmsg, (recent_input) ? recent_input : "", port_filename(pt), port_line_number(pt),
			     sc->current_file, sc->current_line);
	    }
	  else
	    {
	      len = safe_strlen(recent_input) + safe_strlen(errmsg) + safe_strlen(sc->current_file) + 64;
	      msg = (char *)malloc(len * sizeof(char));
	      
	      if ((sc->current_file) &&
		  (sc->current_line >= 0))
		len = snprintf(msg, len, "%s: %s, last top-level form at %s[%d]", 
			       errmsg, (recent_input) ? recent_input : "",
			       sc->current_file, sc->current_line);
	      else len = snprintf(msg, len, "%s: %s", errmsg, (recent_input) ? recent_input : "");
	    }
	  
	  if (recent_input) free(recent_input);
	  return(s7_error(sc, sc->READ_ERROR, 
			  list_1(sc, make_string_uncopied_with_length(sc, msg, len))));
	}
    }

  if ((port_line_number(pt) > 0) &&
      (port_filename(pt)))
    {
      len = safe_strlen(errmsg) + port_filename_length(pt) + safe_strlen(sc->current_file) + 128;
      msg = (char *)malloc(len * sizeof(char));

      if (string_error)
	len = snprintf(msg, len, "%s %s[%d],\n;  possible culprit: \"%s...\"\n;  last top-level form at %s[%d]", 
		       errmsg, port_filename(pt), port_line_number(pt), 
		       sc->strbuf,
		       sc->current_file, sc->current_line);
      else len = snprintf(msg, len, "%s %s[%d], last top-level form at %s[%d]", 
			  errmsg, port_filename(pt), port_line_number(pt), 
			  sc->current_file, sc->current_line);
      return(s7_error(sc, sc->READ_ERROR, 
		      list_1(sc, make_string_uncopied_with_length(sc, msg, len))));
    }

  return(s7_error(sc, sc->READ_ERROR, 
		  list_1(sc, make_string_wrapper(sc, (char *)errmsg))));
}

static s7_pointer read_error(s7_scheme *sc, const char *errmsg)
{
  return(read_error_1(sc, errmsg, false));
}

static s7_pointer string_read_error(s7_scheme *sc, const char *errmsg)
{
  return(read_error_1(sc, errmsg, true));
}


static s7_pointer g_error(s7_scheme *sc, s7_pointer args)
{
  #define H_error "(error type ...) signals an error.  The 'type' can be used with catch to trap \
particular errors.  If the error is not caught, s7 treats the second argument as a format control string, \
and applies it to the rest of the arguments."

  if (is_not_null(args))
    {
      if (is_string(car(args)))                    /* CL-style error? -- use tag = 'no-catch */
	{
	  s7_error(sc, sc->NO_CATCH, args);        /* this can have trailing args (implicit format) */
	  return(sc->UNSPECIFIED);
	}
      return(s7_error(sc, car(args), cdr(args)));
    }
  return(s7_error(sc, sc->NIL, sc->NIL));
}


static char *truncate_string(char *form, int len, use_write_t use_write, int *form_len)
{
  if (use_write != USE_DISPLAY)
    {
      /* I guess we need to protect the outer double quotes in this case */
      int i;
      for (i = len - 4; i >= (len / 2); i--)
	if (is_white_space((int)form[i]))
	  {
	    form[i] = '.';
	    form[i + 1] = '.';
	    form[i + 2] = '.';
	    form[i + 3] = '"';
	    form[i + 4] = '\0';
	    (*form_len) = i + 4;
	    return(form);
	  }
      i = len - 4;
      if (i > 0)
	{
	  form[i] = '.';
	  form[i + 1] = '.';
	  form[i + 2] = '.';
	  form[i + 3] = '"';
	  form[i + 4] = '\0';
	}
      else 
	{
	  if (len >= 2)
	    {
	      form[len - 1] = '"';
	      form[len] = '\0';
	    }
	}
    }
  else
    {
      int i;
      for (i = len - 3; i >= (len / 2); i--)
	if (is_white_space((int)form[i]))
	  {
	    form[i] = '.';
	    form[i + 1] = '.';
	    form[i + 2] = '.';
	    form[i + 3] = '\0';
	    (*form_len) = i + 3;
	    return(form);
	  }
      i = len - 3;
      if (i >= 0)
	{
	  form[i] = '.';
	  form[i + 1] = '.';
	  form[i + 2] = '.';
	  form[i + 3] = '\0';
	}
      else form[len] = '\0';
    }
  return(form);
}


static char *object_to_truncated_string(s7_scheme *sc, s7_pointer p, int len)
{
  char *s;
  int s_len;
  s = s7_object_to_c_string(sc, p);
  s_len = safe_strlen(s);
  if (s_len > len)
    return(truncate_string(s, len, USE_DISPLAY, &s_len));
  return(s);
}


static char *missing_close_paren_syntax_check(s7_scheme *sc, s7_pointer lst)
{
  s7_pointer p, old_hook;
  int old_hook_loc;
  char *msg = NULL;
  
  /* this can get into an infinite loop if unbound variable hook gets involved so...
   */
  old_hook = s7_hook_functions(sc, sc->unbound_variable_hook);
  old_hook_loc = s7_gc_protect(sc, old_hook);
  s7_hook_set_functions(sc, sc->unbound_variable_hook, sc->NIL);

  for (p = lst; is_pair(p); p = cdr(p))
    {
      if (is_pair(car(p)))
	{
	  if (is_symbol(caar(p)))
	    {
	      int len;
	      
	      len = s7_list_length(sc, car(p));
	      if (((caar(p) == make_symbol_with_length(sc, "if", 2)) &&
		   (len > 4)) ||
		  /* some care is needed -- we can't risk autoloading the very same file we're complaining about! 
		   */
		  ((s7_is_defined(sc, symbol_name(caar(p)))) &&
		   (s7_is_procedure(find_global_symbol_checked(sc, caar(p)))) &&
		   (!s7_is_aritable(sc, find_global_symbol_checked(sc, caar(p)), len))) ||
		  ((caar(p) == make_symbol_with_length(sc, "define", 6)) &&
		   (is_pair(cdr(p))) &&
		   (is_symbol(cadr(p))) &&
		   (len > 3)))
		{
		  int msg_len, form_len;
		  char *form;
		  /* it's very tricky to try to see other errors here, especially because 'case'
		   *   can have syntax names in its key lists.  Even this may get fooled, but
		   *   I'm hoping that more often than not, it will help track down the missing
		   *   close paren.
		   */

		  form = object_to_truncated_string(sc, car(p), 80);
		  form_len = safe_strlen(form);
		  msg_len = form_len + 128;
		  msg = (char *)malloc(msg_len * sizeof(char));
		  snprintf(msg, msg_len, ";  this looks bogus: %s", form);
		  free(form);

		  s7_hook_set_functions(sc, sc->unbound_variable_hook, old_hook);
		  s7_gc_unprotect_at(sc, old_hook_loc);

		  return(msg);
		}
	    }
	  msg = missing_close_paren_syntax_check(sc, car(p));
	  if (msg) 
	    {
	      s7_hook_set_functions(sc, sc->unbound_variable_hook, old_hook);
	      s7_gc_unprotect_at(sc, old_hook_loc);
	      return(msg);
	    }
	}
    }
  s7_hook_set_functions(sc, sc->unbound_variable_hook, old_hook);
  s7_gc_unprotect_at(sc, old_hook_loc);

  return(NULL);
}

static s7_pointer missing_close_paren_error(s7_scheme *sc)
{
  int len;
  char *msg, *syntax_msg = NULL;
  s7_pointer pt;
  pt = sc->input_port;

  /* it's hard to give a good idea here of where the missing paren is because we've already
   *   popped off all the stacked info, following ')' until eof.
   * but the current incoming program code is in sc->args, reversed at its top level,
   *   so it's worth looking for some problem involving too many clauses (if) or arguments, etc.
   * this can be a hard bug to track down in a large program, so s7 really has to make an effort to help.
   */

  if (is_pair(sc->args))
    {
      s7_pointer lx;
      lx = s7_reverse(sc, sc->args);
      syntax_msg = missing_close_paren_syntax_check(sc, lx);
      /* if syntax_msg is null, we didn't find the problem, so perhaps show it indented?
       */
    }

  if ((port_line_number(pt) > 0) &&
      (port_filename(pt)))
    {
      len = port_filename_length(pt) + safe_strlen(sc->current_file) + safe_strlen(syntax_msg) + 128;
      msg = (char *)malloc(len * sizeof(char));
      if (syntax_msg)
	{
	  len = snprintf(msg, len, "missing close paren, %s[%d], last top-level form at %s[%d]\n%s", 
			 port_filename(pt), port_line_number(pt), 
			 sc->current_file, sc->current_line, syntax_msg);
	  free(syntax_msg);
	}
      else len = snprintf(msg, len, "missing close paren, %s[%d], last top-level form at %s[%d]", 
			  port_filename(pt), port_line_number(pt), 
			  sc->current_file, sc->current_line);
      return(s7_error(sc, sc->READ_ERROR, make_string_uncopied_with_length(sc, msg, len)));
    }

  if (syntax_msg)
    {
      len = safe_strlen(syntax_msg) + 128;
      msg = (char *)malloc(len * sizeof(char));
      len = snprintf(msg, len, "missing close paren\n%s\n", syntax_msg);
      free(syntax_msg);
      return(s7_error(sc, sc->READ_ERROR, make_string_uncopied_with_length(sc, msg, len)));
    }
  return(s7_error(sc, sc->READ_ERROR, 
		  list_1(sc, make_string_wrapper(sc, "missing close paren"))));
}


static void improper_arglist_error(s7_scheme *sc)
{
  /* sc->code is the last (dotted) arg, sc->args is the arglist reversed not including sc->code
   *   the original was `(,@(reverse args) . ,code) essentially
   */
  if (sc->args == sc->NIL)               /* (abs . 1) */
    s7_error(sc, sc->SYNTAX_ERROR, 
	     list_1(sc, make_string_wrapper(sc, "function call is a dotted list?")));
  else s7_error(sc, sc->SYNTAX_ERROR, 
		list_2(sc, make_string_wrapper(sc, "improper list of arguments: ~S"),
		       append_in_place(sc, sc->args = safe_reverse_in_place(sc, sc->args), sc->code)));
}



/* -------------------------------- leftovers -------------------------------- */


void (*s7_begin_hook(s7_scheme *sc))(s7_scheme *sc, bool *val)
{
  return(sc->begin_hook);
}


void s7_set_begin_hook(s7_scheme *sc, void (*hook)(s7_scheme *sc, bool *val))
{
  sc->begin_hook = hook;
}


static bool call_begin_hook(s7_scheme *sc)
{
  bool result = false;
  /* originally the begin_hook was bool (*hook)(s7_scheme *sc): the value was returned directly,
   *   rather than going through a *bool arg (&result below).  That works in gcc (Linux/OSX),
   *   but does not work in MS Visual C++.  In the latter, the compiler apparently completely
   *   eliminates any local, returning (for example) a thread-relative stack-allocated value
   *   directly, but then by the time we get here, that variable has vanished, and we get
   *   garbage.  We had to thwart the optimization by adding if ((flag) && (!flag)) fprintf(...);
   *   So, in the new form (26-Jun-13), the value is passed directly into an s7 variable
   *   that I hope can't be optimized out of existence.
   */
  opcode_t op;
  op = sc->op;
  push_stack(sc, OP_BARRIER, sc->args, sc->code);
  sc->begin_hook(sc, &result);
  if (result)
    {
      /* set (owlet) in case we were interrupted and need to see why something was hung */
      slot_set_value(sc->error_type, sc->F);
      slot_set_value(sc->error_data, sc->value); /* was sc->F but we now clobber this below */
      slot_set_value(sc->error_code, sc->cur_code);
      slot_set_value(sc->error_line, sc->F);
      slot_set_value(sc->error_file, sc->F);
      outlet(sc->owlet) = sc->envir;

      sc->value = s7_make_symbol(sc, "begin-hook-interrupt"); 
      /* otherwise the evaluator returns whatever random thing is in sc->value (normally #<closure>)
       *   which makes debugging unnecessarily difficult.
       */
      s7_quit(sc);     /* don't call gc here -- perhaps at restart somehow? */
      return(true);
    }
  pop_stack_no_op(sc);
  sc->op = op;         /* for better error handling.  otherwise we get "barrier" as the offending function name in eval_error_with_name */
  return(false);
}


void s7_quit(s7_scheme *sc)
{
  sc->longjmp_ok = false;
  pop_input_port(sc);

  stack_reset(sc);
  push_stack(sc, OP_EVAL_DONE, sc->NIL, sc->NIL);
}


static s7_pointer apply_list_star(s7_scheme *sc, s7_pointer d) 
{
  s7_pointer p, q;
  /* we check this ahead of time: if (is_null(cdr(d))) return(car(d)); */
  p = cons(sc, car(d), cdr(d));
  q = p;
  while (is_not_null(cdr(cdr(p))))
    {
      d = cons(sc, car(p), cdr(p));
      p = cdr(p);
    }
  cdr(p) = car(cdr(p));
  return(q);
}


static s7_pointer apply_list_error(s7_scheme *sc, s7_pointer lst)
{
  return(s7_error(sc, sc->WRONG_TYPE_ARG, list_2(sc, make_string_wrapper(sc, "apply's last argument should be a proper list: ~S"), lst)));
}

static s7_pointer g_apply(s7_scheme *sc, s7_pointer args)
{
  #define H_apply "(apply func ...) applies func to the rest of the arguments"

  /* can apply always be replaced with apply values?
   *   (apply + '(1 2 3)) is the same as (+ (apply values '(1 2 3)))
   * not if apply* in disguise, I think:
   *   (apply + 1 2 ()) -> 3
   *   (apply + 1 2 (apply values ())) -> error
   */

  sc->code = car(args);
  if (is_null(cdr(args)))
    sc->args = sc->NIL;
  else 
    {
      if (is_safe_procedure(sc->code))
	{
	  s7_pointer p, q;

	  for (q = args, p = cdr(args); is_not_null(cdr(p)); q = p, p = cdr(p));
	  /* the last arg is supposed to be a list, it will be spliced onto the end of the previous arg list (if any) below */

	  if (!is_proper_list(sc, car(p)))        /* (apply + #f) etc */
	    return(apply_list_error(sc, args));
	  cdr(q) = car(p);   
	  /* this would work: if (is_c_function(sc->code)) return(c_function_call(sc->code)(sc, cdr(args)));
	   *   but it omits the arg number check 
	   */
	  push_stack(sc, OP_APPLY, cdr(args), sc->code);
	  return(sc->NIL);
	}
      else
	{
	  /* here we have to copy the arg list */
	  if (is_null(cddr(args)))
	    sc->args = cadr(args);
	  else sc->args = apply_list_star(sc, cdr(args));

	  if (!is_proper_list(sc, sc->args))        /* (apply + #f) etc */
	    return(apply_list_error(sc, args));
	}
    }

  if (is_any_macro(sc->code))                   /* (apply mac '(3)) -> (apply mac '((mac 3))) */
    {
      push_stack(sc, OP_EVAL_MACRO, sc->NIL, sc->args);
      sc->args = list_1(sc, cons(sc, sc->code, sc->args));
    }
  push_stack(sc, OP_APPLY, sc->args, sc->code);
  return(sc->NIL);
}


s7_pointer s7_apply_function(s7_scheme *sc, s7_pointer fnc, s7_pointer args)
{
  if (is_c_function(fnc))
    return(c_function_call(fnc)(sc, args));

  push_stack(sc, OP_EVAL_DONE, sc->args, sc->code); 
  sc->args = args;
  sc->code = fnc;
  eval(sc, OP_APPLY);
  return(sc->value);
}


s7_pointer s7_eval(s7_scheme *sc, s7_pointer code, s7_pointer e)
{
  push_stack(sc, OP_EVAL_DONE, sc->args, sc->code);
  sc->code = code;
  if ((e != sc->rootlet) &&
      (is_let(e)))
    sc->envir = e; 
  else sc->envir = sc->NIL; /* can't check is_let(e) because sc->rootlet sets its type to t_env! */
  eval(sc, OP_BEGIN);
  return(sc->value);
}


s7_pointer s7_eval_form(s7_scheme *sc, s7_pointer form, s7_pointer e)
{
  push_stack(sc, OP_EVAL_DONE, sc->args, sc->code);
  sc->code = form;
  if ((e != sc->rootlet) &&
      (is_let(e)))
    sc->envir = e; 
  else sc->envir = sc->NIL;
  eval(sc, OP_EVAL);
  return(sc->value);
}


static s7_pointer g_eval(s7_scheme *sc, s7_pointer args)
{
  #define H_eval "(eval code (env (curlet))) evaluates code in the environment env. 'env' \
defaults to the curlet; to evaluate something in the top-level environment instead, \
pass (rootlet):\n\
\n\
  (define x 32) \n\
  (let ((x 3))\n\
    (eval 'x (rootlet)))\n\
\n\
  returns 32"
  
  if (is_not_null(cdr(args)))
    {
      s7_pointer e;
      e = cadr(args);
      if (!is_let(e))
	return(wrong_type_argument_with_type(sc, sc->EVAL, small_int(2), e, AN_ENVIRONMENT));
      if (e == sc->rootlet)
	sc->envir = sc->NIL;
      else sc->envir = e;
    }
  sc->code = car(args);

  if (s7_stack_top(sc) < 12)
    push_stack(sc, OP_BARRIER, sc->NIL, sc->NIL);
  push_stack(sc, OP_EVAL, sc->args, sc->code);

  return(sc->NIL);
}


s7_pointer s7_call(s7_scheme *sc, s7_pointer func, s7_pointer args)
{
  bool old_longjmp;
  jmp_buf old_goto_start;

  /* this can be called while we are in the eval loop (within eval_c_string for instance),
   *   and if we reset the stack, the previously running evaluation steps off the end
   *   of the stack == segfault. 
   */
  if (is_c_function(func))
    return(c_function_call(func)(sc, args));  /* no check for wrong-number-of-args -- is that reasonable? */

  sc->temp1 = func; /* this is just GC protection */
  sc->temp2 = args; 

  old_longjmp = sc->longjmp_ok;
  memcpy((void *)old_goto_start, (void *)(sc->goto_start), sizeof(jmp_buf));

  /* if an error occurs during s7_call, and it is caught, catch (above) wants to longjmp
   *   to its caller to complete the error handler evaluation so that the C stack is
   *   cleaned up -- this means we have to have the longjmp location set here, but
   *   we could get here from anywhere, so we need to save and restore the incoming
   *   longjmp location.
   */

  sc->longjmp_ok = true;
  if (setjmp(sc->goto_start) != 0) /* returning from s7_error catch handler */
    {
      sc->longjmp_ok = old_longjmp;
      memcpy((void *)(sc->goto_start), (void *)old_goto_start, sizeof(jmp_buf));
      
      if ((sc->op == OP_ERROR_QUIT) &&
	  (sc->longjmp_ok))
	longjmp(sc->goto_start, 1); /* this is trying to clear the C stack back to some clean state */

      eval(sc, sc->op); 
      /* sc->op can be OP_APPLY if s7_call raised an error that was caught (via catch) -- we're about to go to the error handler */
      return(sc->value);
    }

  push_stack(sc, OP_EVAL_DONE, sc->args, sc->code); /* this saves the current evaluation and will eventually finish this (possibly) nested call */
  sc->args = args; 
  sc->code = func; 

  /* besides a closure, "func" can also be an object (T_C_OBJECT) -- in Snd, a generator for example
   */

  eval(sc, OP_APPLY);

  sc->longjmp_ok = old_longjmp;
  memcpy((void *)(sc->goto_start), (void *)old_goto_start, sizeof(jmp_buf));

  return(sc->value);
} 


s7_pointer s7_call_with_location(s7_scheme *sc, s7_pointer func, s7_pointer args, const char *caller, const char *file, int line)
{ 
  s7_pointer result;

  if (caller)
    {
      sc->s7_call_name = caller;
      sc->s7_call_file = file;
      sc->s7_call_line = line;
    }

  result = s7_call(sc, func, args);
  
  if (caller)
    {
      sc->s7_call_name = NULL;
      sc->s7_call_file = NULL;
      sc->s7_call_line = -1;
    }
  return(result);
}


static s7_pointer g_s7_version(s7_scheme *sc, s7_pointer args)
{
  #define H_s7_version "(s7-version) returns some string describing the current s7"
#if WITH_COUNTS
  report_counts(sc);
#endif
  return(s7_make_string(sc, "s7 " S7_VERSION ", " S7_DATE));
}


static s7_pointer g_emergency_exit(s7_scheme *sc, s7_pointer args)
{
  #define H_emergency_exit "(emergency-exit obj) exits s7 immediately"
  s7_pointer obj;
#ifndef EXIT_SUCCESS 
  #define EXIT_SUCCESS 0
  #define EXIT_FAILURE 1
#endif
  if (is_null(args))
    _exit(EXIT_SUCCESS);          /* r7rs spec says use _exit here */
  obj = car(args);
  if (obj == sc->F)
    _exit(EXIT_FAILURE);
  if ((obj == sc->T) || (!s7_is_integer(obj)))
    _exit(EXIT_SUCCESS);
  _exit((int)s7_integer(obj));
  return(sc->F);
}


static s7_pointer g_exit(s7_scheme *sc, s7_pointer args)
{
  #define H_exit "(exit obj) exits s7"
  s7_quit(sc);
  return(g_emergency_exit(sc, args));
}


#if DEBUGGING
static s7_pointer g_abort(s7_scheme *sc, s7_pointer args) {abort();}
#endif



/* ---------------------------------------- map and for-each ---------------------------------------- */

static s7_Int applicable_length(s7_scheme *sc, s7_pointer obj)
{
  switch (type(obj))
    {
    case T_PAIR:
      {
	s7_Int len;
	len = s7_list_length(sc, obj);
	if (len < 0)             /* dotted (does not include the final cdr -- perhaps this is a bug) */
	  return(-len);          /*         it means that (map abs '(1 . "hi")) returns '(1) */
	if (len == 0)            /* circular */
	  return(S7_LONG_MAX);
	return(len);
      }

    case T_C_OBJECT:
      return(object_length_to_int(sc, obj));

    case T_STRING:
      return(string_length(obj));

    case T_INT_VECTOR:
    case T_FLOAT_VECTOR:
    case T_VECTOR:
      return(vector_length(obj));

    case T_HASH_TABLE:
      return(hash_table_entries(obj));

    case T_ENVIRONMENT:
      return(let_length(sc, obj));

    case T_CLOSURE:
    case T_CLOSURE_STAR:
      if (has_methods(obj))
	return(closure_length(sc, obj));
      return(-1);

    case T_NIL:
      return(0);
    }
  return(-1);
}


static bool next_for_each(s7_scheme *sc)
{
  s7_pointer x, y, z, vargs, fargs;
  unsigned int loc;

  z = sc->NIL;

  vargs = counter_list(sc->args);
  fargs = counter_result(sc->args); /* this is the saved list for args */
  loc = counter_count(sc->args);

  /* for-each func ... -- each trailing sequence arg contributes one arg to the current call on func, 
   *   so in the next loop, gather one arg from each sequence.
   *   environments are turned into lists at the start.
   */

  for (x = fargs, y = vargs; is_pair(x); x = cdr(x), y = cdr(y))
    {
      s7_pointer car_y;
      car_y = car(y);
      switch (type(car_y))
	{
	case T_PAIR:
	  car(x) = car(car_y);
	  car(y) = cdr(car_y);
	  break;
	  
	case T_INT_VECTOR:
	case T_FLOAT_VECTOR:
	case T_VECTOR:
	  car(x) = vector_getter(car_y)(sc, car_y, loc); 
	  break;
	  
	case T_STRING:
	  /* in all these small_int cases, don't cast to unsigned int -- the compiler apparently in some cases, first
	   *   extends the char to a full integer (-1 possibly), then unsigns that!
	   */
	  if (is_bytevector(car_y))
	    car(x) = small_int((unsigned char)string_value(car_y)[loc]);
	  else car(x) = s7_make_character(sc, ((unsigned char)(string_value(car_y)[loc])));
	  break;
	  
	case T_C_OBJECT:
	case T_CLOSURE:
	case T_CLOSURE_STAR:
	  {
	    int save_x = -1, save_z = -1;
	    if (is_null(z))
	      {
		counter_args(sc->args) = list_1(sc, make_integer(sc, loc));
		z = counter_args(sc->args);
		/* we can't use car(sc->args) directly here -- it is a counter.
		 *   the object application (the getter function) might return the index.
		 *   Then, if we pre-increment, the for-each application sees the incremented value.
		 */
	      }
	    
	    SAVE_X_Z(save_x, save_z);
	    if (type(car_y) == T_C_OBJECT)
	      car(x) = (*(c_object_ref(car_y)))(sc, car_y, z);
	    else car(x) = s7_apply_function(sc, car_y, z);
	    RESTORE_X_Z(save_x, save_z);
	  }
	  break;
	  
	case T_SLOT:
	  car(x) = cons(sc, slot_symbol(car_y), slot_value(car_y));
	  car(y) = next_slot(car_y);
	  break;

	default:           /* see comment in next_map: (let ((L (list 1 2 3 4 5))) (for-each (lambda (x) (set-cdr! (cddr L) 5) (display x)) L)) */
	  return(false);
	  break;
	}
    }

  counter_count(sc->args) = loc + 1;
  push_stack(sc, OP_FOR_EACH, sc->args, sc->code);
  sc->args = fargs;

  if (is_any_macro(sc->code))
    {
      push_stack(sc, OP_EVAL_MACRO, sc->NIL, sc->args);
      /* we can use a temp here because the arg list will be copied in OP_FOR_EACH */
      car(sc->temp_cell_1) = sc->code;
      cdr(sc->temp_cell_1) = sc->args;
      sc->args = sc->temp_cell;
    }
  return(true);
}


static s7_pointer make_counter(s7_scheme *sc, s7_pointer result, s7_pointer lst, int count, int length)
{
  s7_pointer x;
  NEW_CELL(sc, x);
  counter_result(x) = result;
  counter_list(x) = lst;
  counter_count(x) = count;
  counter_length(x) = length;
  counter_let(x) = sc->NIL;
  counter_args(x) = sc->NIL;
  set_type(x, T_COUNTER);
  return(x);
}


static s7_pointer make_simple_counter(s7_scheme *sc, s7_pointer lst)
{
  s7_pointer x;
  NEW_CELL(sc, x);
  counter_result(x) = sc->NIL;
  counter_list(x) = lst;        /* cdr-ing arg list */
  counter_count(x) = 0;
  counter_length(x) = 0;        /* will be capture_env_counter */
  counter_let(x) = sc->NIL;     /* will be the saved env */
  counter_args(x) = sc->NIL;
  set_type(x, T_COUNTER);
  return(x);
}


static s7_pointer rootlet_vector_getter(s7_scheme *sc, s7_pointer vec, s7_Int loc) 
{
  s7_pointer p;
  p = vector_element(vec, loc);
  return(cons(sc, slot_symbol(p), slot_value(p)));
}

static s7_pointer make_let_iterator(s7_scheme *sc, s7_pointer let)
{
  if (let == sc->rootlet)
    {
      s7_pointer p;
      p = make_vector_wrapper(sc, sc->rootlet_entries, vector_elements(sc->rootlet));
      vector_getter(p) = rootlet_vector_getter;
      return(p);
    }
  return(let_slots(let));
}


static s7_pointer g_for_each(s7_scheme *sc, s7_pointer args)
{
  #define H_for_each "(for-each proc object . objects) applies proc to each element of the objects traversed in parallel. \
Each object can be a list, string, vector, hash-table, or any other sequence."

  s7_Int len = 0; 
  /* "int" here is unhappy on 64-bit machines, and "long int" is unhappy on 32-bit machines, so try long long int.
   *    string_length is an int.
   */
  s7_pointer obj, x;

  sc->code = car(args); /* the function */
  if (!is_applicable(sc->code))
    {
      check_method(sc, sc->code, sc->FOR_EACH, args);
      return(wrong_type_argument_with_type(sc, sc->FOR_EACH, small_int(1), sc->code, SOMETHING_APPLICABLE));
    }

  /* macro application requires the entire call as the argument to apply, but apply itself fixes this up.
   *  that is, g_apply checks, then goes to OP_EVAL_MACRO after OP_APPLY with the fixed up list,
   *  but here we are simply sending it to OP_APPLY, so
   *
   *    (define-macro (hi a) `(+ ,a 1))
   *    (apply hi '(1))                 ; internally rewritten as (apply hi '((hi 1)))
   *    2                               ; apply adds the evaluation if its first arg is a macro
   *    (map hi '((hi 1) (hi 2)))       ; here we've rewritten the arg lists by hand
   *    ((+ 1 1) (+ 2 1))               ; but no evaluation
   *
   * ideally I think it should be
   *
   *    (map hi '(1 2))
   *    (2 3)
   *
   * OP_APPLY knows this is happening (in the second case) and raises an error -- what would break
   *   if we handle it locally instead?  This actually affects only T_C_MACRO (quasiquote) --
   *   normal macros/bacros would still be broken.
   * or could we rewrite the args to fit just as in apply? (also need the evaluation)
   */

  /* before checking len=0, we need to check that the arguments are all sequences (this is like our handling of args to + for example)
   *   otherwise (for-each = "" 123) -> #<unspecified> 
   * the function may not actually be applicable to its sequence elements, but that isn't an error:
   *   (map abs "") -> ()
   * and the function's max arity might be less than the number of sequences, but that also isn't an error!
   *   (let ((x 0)) (for-each (lambda* (a) (set! x (+ x a))) (list :a :a :a) (list 1 2 3)) x)
   *   where :a is the keyword argname (hi :a 1) etc
   */
  obj = cadr(args); 

  if ((is_pair(obj)) &&                                               /* arg is a list */
      (is_null(cddr(args))))                                          /* only one list arg */
    {
      len = s7_list_length(sc, obj);
      if (len > 0)                                                    /* a proper list arg */
	{
	  if ((type(sc->code) == T_CLOSURE) &&                        /* not lambda* that might get confused about arg names */
	      (closure_arity_to_int(sc, sc->code) == 1) &&            /* not a rest arg: not is_pair: (lambda (x . args) arg) */
	      (!is_immutable(car(closure_args(sc->code)))))           /* not a bad arg name! */
	    {
	      /* one list arg -- special, but very common case
	       */
	      obj = make_simple_counter(sc, obj);                     /* counter_list(sc->args) => original list (cadr(args)) */
	      push_stack(sc, OP_FOR_EACH_SIMPLE, obj, sc->code);
	      return(sc->UNSPECIFIED);

	      /* this, and map, across a string (say) or other sequence involving
	       *   only 1 arg could be optimized in the same way. OP_FOR_EACH|MAP_STRING|VECTOR|C_OBJECT
	       */
	    }

	  if ((is_safe_procedure(sc->code)) &&
	      (is_c_function(sc->code)) &&
	      (s7_is_aritable(sc, sc->code, 1)))
	    {
	      s7_function func;
	      s7_pointer p;
	      func = c_function_call(sc->code);
	      for (p = obj; is_pair(p); p = cdr(p))
		{
		  car(sc->T1_1) = car(p);
		  (*func)(sc, sc->T1_1);
		}
	      return(sc->UNSPECIFIED);
	    }
	}

      if (len < 0)
	len = -len; 
      else
	if (len == 0) 
	  len = S7_LONG_MAX;
    }
  else 
    {
      for (x = cdr(args); is_not_null(x); x = cdr(x))
	{
	  s7_Int nlen;
	  if (!is_sequence(car(x)))
	    return(wrong_type_argument_n_with_type(sc, sc->FOR_EACH, position_of(x, args), car(x), A_SEQUENCE));

	  nlen = applicable_length(sc, car(x));
	  if (nlen < 0)
	    return(wrong_type_argument_n_with_type(sc, sc->FOR_EACH, position_of(x, args), car(x), A_SEQUENCE));

	  if ((x == cdr(args)) || (nlen < len)) /* i.e. either the first arg or one with lesser length than previous */
	    len = nlen;
	  if (len == 0) 
	    {
	      for (x = cdr(x); is_not_null(x); x = cdr(x))
		if (!is_sequence(car(x)))
		  return(wrong_type_argument_n_with_type(sc, sc->FOR_EACH, position_of(x, args), car(x), A_SEQUENCE));
	      break;   /* need error check below */
	    }
	}
    }

  if (len != 0)
    {
      sc->args = list_1(sc, sc->NIL);
      if (is_hash_table(obj))
	sc->z = list_1(sc, g_make_hash_table_iterator(sc, cdr(args)));
      else 
	{ 
	  /* (let ((a 1) (b 2)) (for-each (lambda (slot) (format #t "~A~%" slot)) (curlet))) */
	  if (is_let(obj))
	    {
	      check_method(sc, obj, sc->FOR_EACH, args);
	      sc->z = list_1(sc, make_let_iterator(sc, obj));
	    }
	  else
	    {
	      if (is_c_object(obj))
		check_method(sc, obj, sc->MAP, args);
	      sc->z = list_1(sc, obj);
	    }
	}

      if (is_not_null(cddr(args)))
	{
	  for (x = cddr(args); is_not_null(x); x = cdr(x))
	    {
	      sc->args = cons(sc, sc->NIL, sc->args); /* we're making a list to be filled in later with the individual args */

	      if (is_hash_table(car(x)))
		sc->z = cons_unchecked(sc, g_make_hash_table_iterator(sc, x), sc->z);
	      else
		{
		  if (is_let(car(x)))                 /* (let ((a 1) (b 2)) (for-each (lambda (x) (format *stderr* "~A~%" x)) (curlet))) -> (b . 2) (a .1) */
		    sc->z = cons(sc, make_let_iterator(sc, car(x)), sc->z);
		  else sc->z = cons_unchecked(sc, car(x), sc->z);
		}
	    }
	}
    }
  else /* len == 0 and  we checked for non-applicable first arg above */
    return(sc->UNSPECIFIED);    /* circular -> S7_LONG_MAX in this case, so 0 -> nil */

  if ((len == S7_LONG_MAX) &&
      (sc->safety != 0))
    {
      /* if at this point len == S7_LONG_MAX, then all args are circular lists, assuming that
       *    we're not looking at some enormous vector or string -- perhaps -1 would be a
       *    better marker.  This actually might not be an error (the for-each function could
       *    have call-with-exit).
       */
      for (x = cdr(args); (is_pair(x)) && (is_pair(car(x))); x = cdr(x)) {}
      if (!is_pair(x)) /* i.e. all args are lists */
	return(s7_error(sc, sc->WRONG_TYPE_ARG, 
			list_2(sc, make_string_wrapper(sc, "for-each's arguments are circular lists! ~S"), cdr(args))));
    }

  if ((is_closure(sc->code)) &&
      (!s7_is_aritable(sc, sc->code, safe_list_length(sc, cdr(args)))))
    return(s7_wrong_number_of_args_error(sc, "for-each function: ~A args?", small_int(safe_list_length(sc, cdr(args)))));

  sc->args = make_counter(sc, sc->args, sc->z = safe_reverse_in_place(sc, sc->z), 0, len);
  /* counter_result(sc->x): func-arglist, (this is a saved list used for args on each func application)
   * counter_list(sc->z reversed): objects, 
   * counter_count(0): current count, 
   * counter_length(len): applicable length 
   */

  sc->z = sc->NIL;
  push_stack(sc, OP_FOR_EACH, sc->args, sc->code);
  return(sc->UNSPECIFIED);
}


static bool is_lambda(s7_scheme *sc, s7_pointer sym)
{
  return((sym == sc->LAMBDA) && (symbol_id(sym) == 0));
  /* symbol_id==0 means it has never been rebound (T_GLOBAL might not be set for initial stuff) */
}


/* also call-with-exit/catch/map etc */

static s7_pointer for_each_1;
static s7_pointer g_for_each_1(s7_scheme *sc, s7_pointer args)
{
  /* first arg is (lambda (sym) ...) when sym has no complications, only 2 args passed
   */
  s7_pointer obj;
  s7_Int len;

  obj = cadr(args);
  if (!is_pair(obj))
    return(g_for_each(sc, args));
  
  len = s7_list_length(sc, obj);
  if (len == 0)                   /* circular list */
    return(g_for_each(sc, args)); /* this is faster than calling s7_error here -- weird! */
  obj = make_simple_counter(sc, obj);

  push_stack(sc, OP_FOR_EACH_SIMPLE, obj, car(args));
  return(sc->UNSPECIFIED);
}

static s7_pointer for_each_2;
static s7_pointer g_for_each_2(s7_scheme *sc, s7_pointer args)
{
  /* first arg is (lambda (sym) (...)) -- one pair in lambda, when sym has no complications, only 2 args passed
   */
  s7_pointer obj;
  s7_Int len;

  obj = cadr(args);
  if (!is_pair(obj))
    return(g_for_each(sc, args));
  
  len = s7_list_length(sc, obj);
  if (len == 0)                   /* circular list */
    return(g_for_each(sc, args)); /* this is faster than calling s7_error here -- weird! */
  obj = make_simple_counter(sc, obj);

  push_stack(sc, OP_FOR_EACH_SIMPLER, obj, car(args));
  return(sc->UNSPECIFIED);
}

/* for_each_2: (let () (define (f b) b) (define (hi) (let ((lst '(1 2 3))) (for-each (lambda (a) (f a)) lst))) (hi))
 * for_each_1: (let () (define (f b) b) (define (hi) (let ((lst '(1 2 3))) (for-each (lambda (a) 123 (f a)) lst))) (hi))
 */

static s7_pointer for_each_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if (args == 2)
    {
      s7_pointer lexpr;
      lexpr = cadr(expr);

      if (is_lambda(sc, car(lexpr)))
	{
	  s7_pointer largs, lbody;

	  largs = cadr(lexpr);
	  lbody = cddr(lexpr);

      	  if ((is_pair(largs)) &&
	      (is_null(cdr(largs))) &&
	      (!is_immutable(car(largs))))
	    {
	      if ((is_pair(car(lbody))) &&
		  (is_null(cdr(lbody))))
		return(for_each_2);
	      return(for_each_1); 
	    }
	}
      /* (define (hi) (for-each (lambda (a) a) (values '(1 2 3) '(3 2 1))))
       *   which currently won't be optimized
       */
    }
  return(f);
}


static bool next_map(s7_scheme *sc)
{
  s7_pointer y, z, vargs;
  unsigned int loc;

  z = sc->NIL;
  vargs = counter_list(sc->args);
  loc = counter_count(sc->args);
  sc->x = sc->NIL;                     /* can't use preset args list here (as in for-each): (map list '(a b c)) */

  for (y = vargs; is_pair(y); y = cdr(y))
    {
      s7_pointer x, car_y;
      car_y = car(y);
      switch (type(car_y))
	{
	case T_PAIR:
	  x = car(car_y);
	  car(y) = cdr(car_y);
	  break;
	  
	case T_INT_VECTOR:
	case T_FLOAT_VECTOR:
	case T_VECTOR:
	  x = vector_getter(car_y)(sc, car_y, loc); 
	  break;

	case T_STRING:
	  if (is_bytevector(car_y))
	    x = small_int((unsigned char)string_value(car_y)[loc]); /* don't cast to unsigned int here! */
	  else x = s7_make_character(sc, ((unsigned char)(string_value(car_y)[loc])));
	  break;

	case T_C_OBJECT: 
	case T_CLOSURE:
	case T_CLOSURE_STAR:
	  {
	    int save_x = -1, save_z = -1;
	    if (is_null(z))
	      {
		counter_args(sc->args) = list_1(sc, make_integer(sc, loc));
		z = counter_args(sc->args);
	      }
	    SAVE_X_Z(save_x, save_z);
	    if (type(car_y) == T_C_OBJECT)
	      x = (*(c_object_ref(car_y)))(sc, car_y, z);
	    else x = s7_apply_function(sc, car_y, z);
	    RESTORE_X_Z(save_x, save_z);
	  }
	  break;

	case T_SLOT:
	  x = cons(sc, slot_symbol(car_y), slot_value(car_y));
	  car(y) = next_slot(car_y);
	  break;

	default: 
	  /* this can happen if one of the args is clobbered by the map function, so our initial
	   *   length is messed up:
	   *   (let ((L (list 1 2 3 4 5))) (map (lambda (x) (set-cdr! (cddr L) 5) x) L))
	   */
	  return(false);                  /* this stops the map process, so the code mentioned above returns '(1 2 3) */
	  break;
	}
      sc->x = cons(sc, x, sc->x);
    }

  sc->x = safe_reverse_in_place(sc, sc->x);

  counter_count(sc->args) = loc + 1;
  push_stack(sc, OP_MAP, sc->args, sc->code);
  sc->args = sc->x;
  sc->x = sc->NIL;

  if (is_any_macro(sc->code))
    {
      /* (let ((lst '(1 2 3))) 
       *   (define-macro (hiho a) `(+ 1 ,a)) 
       *   (map hiho lst)) 
       * -> '(2 3 4)
       *
       * quasiquote is a C macro (T_C_MACRO) so is_macro omits it, as well as bacros
       */
      push_stack(sc, OP_EVAL_MACRO, sc->NIL, sc->args);
      car(sc->temp_cell_1) = sc->code;
      cdr(sc->temp_cell_1) = sc->args;
      sc->args = sc->temp_cell;
    }
  return(true);
}


static s7_pointer g_map(s7_scheme *sc, s7_pointer args)
{
  #define H_map "(map proc object . objects) applies proc to a list made up of the next element of each of its arguments, returning \
a list of the results.  Its arguments can be lists, vectors, strings, hash-tables, or any applicable objects."

  s7_Int len;
  s7_pointer obj, x;

  sc->code = car(args);
  if (!is_applicable(sc->code))
    {
      check_method(sc, car(args), sc->MAP, args);
      return(wrong_type_argument_with_type(sc, sc->MAP, small_int(1), sc->code, SOMETHING_APPLICABLE));
    }
  obj = cadr(args); 

  /* there are two simple cases here: a safe function can be handled immediately, and a closure
   *   with one arg called with one list can be handled without all the elaborate run-time checks.
   */

  if ((is_pair(obj)) &&                                               /* arg is a list */
      (is_null(cddr(args))))                                          /* only one list arg */
    {
      len = s7_list_length(sc, obj);
      if (len > 0)                                                    /* a proper list arg */
	{
	  if ((type(sc->code) == T_CLOSURE) &&                        /* not lambda* that might get confused about arg names */
	      (closure_arity_to_int(sc, sc->code) == 1) &&            /* not a rest arg, not is_pair here: (lambda (x . args) args) */
	      (!is_immutable(car(closure_args(sc->code)))))           /* not a bad arg name! */
	    {
	      push_stack(sc, OP_MAP_SIMPLE, make_counter(sc, sc->NIL, obj, len, 0), sc->code);
	      return(sc->NO_VALUE);
	    }

	  if ((is_safe_procedure(sc->code)) &&
	      (is_c_function(sc->code)) &&
	      (s7_is_aritable(sc, sc->code, 1)))
	    {
	      s7_pointer p;
	      s7_function func;

	      func = c_function_call(sc->code);
	      sc->args = sc->NIL;                 /* can't use sc->x here, and can't assume func won't hit an error, so... */
	      for (p = obj; is_pair(p); p = cdr(p))
		{
		  s7_pointer x;
		  car(sc->T1_1) = car(p);
		  x = (*func)(sc, sc->T1_1);
		  if (x != sc->NO_VALUE)
		    sc->args = cons(sc, x, sc->args); /* can we assume a safe function won't return multiple values? */
		}
	      p = safe_reverse_in_place(sc, sc->args);
	      return(p);
	    }
	}

      if (len < 0)
	len = -len; 
      else
	if (len == 0) 
	  len = S7_LONG_MAX;
    }
  else len = applicable_length(sc, obj);

  if (len < 0)
    return(wrong_type_argument_with_type(sc, sc->MAP, small_int(2), obj, A_SEQUENCE));

  for (x = cddr(args); is_not_null(x); x = cdr(x))
    if (!is_sequence(car(x)))
      return(wrong_type_argument_n_with_type(sc, sc->MAP, position_of(x, args), car(x), A_SEQUENCE));

  sc->z = sc->NIL;
  if (len != 0)
    {
      if (is_hash_table(obj))
	sc->z = list_1(sc, g_make_hash_table_iterator(sc, cdr(args)));
      else 
	{
	  if (is_let(obj))
	    {
	      check_method(sc, obj, sc->MAP, args);
	      sc->z = list_1(sc, make_let_iterator(sc, obj));
	    }
	  else 
	    {
	      if (is_c_object(obj))
		check_method(sc, obj, sc->MAP, args);
	      sc->z = list_1(sc, obj);
	    }
	}

      /* we have to copy the args if any of them is a list:
       * (let* ((x (list (list 1 2 3))) (y (apply map abs x))) (list x y))
       */
      
      if (is_not_null(cddr(args)))
	{
	  for (x = cddr(args); is_not_null(x); x = cdr(x))
	    {
	      s7_Int nlen;
	      
	      nlen = applicable_length(sc, car(x));
	      if (nlen < 0)
		return(wrong_type_argument_n_with_type(sc, sc->MAP, position_of(x, args), car(x), A_SEQUENCE));
	      if (nlen < len) len = nlen;
	      if (len == 0) break; /* need error check below */
	      
	      if (is_hash_table(car(x)))
		sc->z = cons(sc, g_make_hash_table_iterator(sc, x), sc->z);
	      else
		{
		  if (is_let(car(x)))
		    sc->z = cons_unchecked(sc, make_let_iterator(sc, car(x)), sc->z);
		  else sc->z = cons_unchecked(sc, car(x), sc->z);
		}
	    }
	}
    }

  if (len == 0)         /* (map 1 "hi" ()) */
    return(sc->NIL);    /* obj has no elements (the circular list case will return S7_LONG_MAX here) */

  if ((len == S7_LONG_MAX) &&
      (sc->safety != 0))
    {
      /* all args are circular lists, or perhaps an odd scheme type (see comment under for-each) */
      for (x = cdr(args); (is_pair(x)) && (is_pair(car(x))); x = cdr(x)) {}
      if (!is_pair(x))
	return(s7_error(sc, sc->WRONG_TYPE_ARG, 
			list_2(sc, make_string_wrapper(sc, "map's arguments are circular lists! ~S"), cdr(args))));
    }

  if ((is_closure(sc->code)) &&
      (!s7_is_aritable(sc, sc->code, safe_list_length(sc, cdr(args)))))
    return(s7_wrong_number_of_args_error(sc, "map function: ~A args?", small_int(safe_list_length(sc, cdr(args)))));

  sc->args = make_counter(sc, sc->NIL, sc->z = safe_reverse_in_place(sc, sc->z), 0, len);
  sc->z = sc->NIL;
  if (next_map(sc))
    push_stack(sc, OP_APPLY, sc->args, sc->code);

  return(sc->NIL);
}



/* -------------------------------- multiple-values -------------------------------- */

static s7_pointer splice_in_values(s7_scheme *sc, s7_pointer args)
{
  if (sc->stack_end > sc->stack_start)
    {
      int top;
      s7_pointer x;
      top = s7_stack_top(sc) - 1;

      switch (stack_op(sc->stack, top))
	{
	  /* the normal case -- splice values into caller's args */
	case OP_EVAL_ARGS1:
	case OP_EVAL_ARGS2:
	case OP_EVAL_ARGS3:
	case OP_EVAL_ARGS4:
	  /* code = args yet to eval in order, args = evalled args reversed 
	   *
	   * it's not safe to simply reverse args and tack the current stacked args onto its (new) end,
	   *   setting stacked args to cdr of reversed-args and returning car because the list (args)
	   *   can be some variable's value in a macro expansion via ,@ and reversing it in place
	   *   (all this to avoid consing), clobbers the variable's value.
	   */
	  for (x = args; is_not_null(cdr(x)); x = cdr(x))
	    stack_args(sc->stack, top) = cons(sc, car(x), stack_args(sc->stack, top));
	  return(car(x));

	  /* in the next set, the main evaluator branches blithely assume no multiple-values,
	   *   and if it happens anyway, we vector to a different branch here 
	   */
	case OP_SAFE_C_opSq_P_1:
	  vector_element(sc->stack, top) = (s7_pointer)OP_SAFE_C_opSq_P_MV;
	  return(args);

	case OP_EVAL_ARGS_P_1:
	  vector_element(sc->stack, top) = (s7_pointer)OP_EVAL_ARGS_P_1_MV;
	  return(args);

	case OP_EVAL_ARGS_SSP_1:
	  vector_element(sc->stack, top) = (s7_pointer)OP_EVAL_ARGS_SSP_MV;
	  return(args);

	case OP_EVAL_ARGS_P_2:
	  vector_element(sc->stack, top) = (s7_pointer)OP_EVAL_ARGS_P_2_MV;
	  return(args);

	case OP_EVAL_ARGS_P_3:
	  vector_element(sc->stack, top) = (s7_pointer)OP_EVAL_ARGS_P_3_MV;
	  return(args);

	case OP_EVAL_ARGS_P_4:
	  vector_element(sc->stack, top) = (s7_pointer)OP_EVAL_ARGS_P_4_MV;
	  return(args);

	case OP_C_P_1:
	  vector_element(sc->stack, top) = (s7_pointer)OP_C_P_2;
	  return(args);

	case OP_SAFE_CLOSURE_P_1:
	case OP_CLOSURE_P_1:
	  vector_element(sc->stack, top) = (s7_pointer)OP_CLOSURE_P_2;
	  return(args);

	case OP_C_SP_1:
	  vector_element(sc->stack, top) = (s7_pointer)OP_C_SP_2;
	  return(args);

	case OP_SAFE_C_PP_1:
	  vector_element(sc->stack, top) = (s7_pointer)OP_SAFE_C_PP_3;
	  return(args);

	case OP_SAFE_C_PP_2:
	  vector_element(sc->stack, top) = (s7_pointer)OP_SAFE_C_PP_4;
	  return(args);

	case OP_SAFE_C_PP_5:
	  vector_element(sc->stack, top) = (s7_pointer)OP_SAFE_C_PP_6;
	  return(args);

	case OP_EVAL_ARGS5:
	  /* code = previous arg saved, args = ante-previous args reversed
	   *   we'll take value->code->args and reverse in args5
	   *   if one value, return it, else
	   *      put code onto args, splice as above until there are 2 left
	   *      set code to first and value to last
	   */
	  if (is_null(args))
	    return(sc->UNSPECIFIED);

	  if (is_null(cdr(args)))
	    return(car(args));

	  stack_args(sc->stack, top) = cons(sc, stack_code(sc->stack, top), stack_args(sc->stack, top));
	  for (x = args; is_not_null(cddr(x)); x = cdr(x))
	    stack_args(sc->stack, top) = cons(sc, car(x), stack_args(sc->stack, top));
	  stack_code(sc->stack, top) = car(x);
	  return(cadr(x));

	  /* look for errors here rather than glomming up the set! and let code */
	case OP_SET_SAFE:
	case OP_SET1:                                             /* (set! var (values 1 2 3)) */
	  set_multiple_value(args);
	  return(eval_error(sc, "can't set! some variable to ~S", args));

	case OP_SET_PAIR_P_1:
	case OP_SET_PAIR_C_P_1:
	  set_multiple_value(args);
	  return(eval_error(sc, "too many values to set! ~S", args));

	case OP_LET1:                                             /* (let ((var (values 1 2 3))) ...) */
	case OP_LET_ONE_1:
	case OP_LET_Z_1:
	case OP_LET_STAR1:
	case OP_LETREC1:
	case OP_LETREC_STAR1:
	  set_multiple_value(args);
	  return(eval_error_with_name_and_string(sc, BAD_BINDING, args)); /* "~A: can't bind some variable to ~S" */

	  /* handle 'and' and 'or' specially */
	case OP_AND1:
	  for (x = args; is_not_null(cdr(x)); x = cdr(x))
	    if (car(x) == sc->F)
	      return(sc->F);
	  return(car(x));

	case OP_OR1:
	  for (x = args; is_not_null(cdr(x)); x = cdr(x))
	    if (car(x) != sc->F)
	      return(car(x));
	  return(car(x));

	case OP_BARRIER: 
	  pop_stack(sc);
	  return(splice_in_values(sc, args));

	case OP_BEGIN1:
	  /* here we have a values call with nothing to splice into.  So flush it...
	   *   otherwise the multiple-values bit gets set in some innocent list and never unset:
	   *   :(let ((x '((1 2)))) (eval `(apply apply values x)) x)
           *   ((values 1 2))
	   * other cases: (+ 1 (begin (values 5 6) (values 2 3)) 4) -> 10 -- the (5 6) is dropped
	   *              (let () (values 1 2 3) 4) but (+ (let () (values 1 2))) -> 3
	   */
	  return(args);

	case OP_CATCH: 
	case OP_CATCH_1: 
	case OP_CATCH_2: 
	  /* (+ (catch #t (lambda () (values 3 4)) (lambda args args))) */
	  pop_stack(sc);
	  return(splice_in_values(sc, args));

	default:
	  break;
	}
    }

  /* let it meander back up the call chain until someone knows where to splice it */
  set_multiple_value(args);
  return(args);
}


static s7_pointer g_values(s7_scheme *sc, s7_pointer args)
{
  #define H_values "(values obj ...) splices its arguments into whatever list holds it (its 'continuation')"

  if (is_null(args))         /* ((lambda () (let ((x 1)) (set! x (boolean? (values)))))) */
    return(sc->NO_VALUE); 

  /* this was sc->NIL until 16-Jun-10, 
   *   nil is consistent with the implied values call in call/cc (if no args, the continuation function returns ())
   *   hmmm... 
   *   Guile complains ("too few values returned to continuation") in the call/cc case, and
   *   (equal? (if #f #f) (* (values))) complains "Zero values returned to single-valued continuation"
   *   so perhaps call/cc should also return #<unspecified> -- I don't know what is best.
   *
   * a note in the scheme bboard:
   *
   *  This would work in s7:
   *
   *  (define (print-concat . args) 
   *    (if (or (null? args)               ; (print-concat)
   *            (eq? (car args) (values))) ; (print-concat arg1 ...)
   *        (newline) 
   *      (begin 
   *        (display (car args)) 
   *        (print-concat (apply values (cdr args))))))
   *  
   *  but it's a bit ugly.  I think (values) should be the same as
   *  (apply values ()). It's currently #<unspecified>, mainly for
   *  historical reasons (a lot of the code s7 is used with
   *  assumes that behavior).  If (values) simply vanished,
   *  then code like (abs -1 (values)) is not an error.
   *  If it returned nil, you get inconsistencies elsewhere:
   *  (values) now is the same as (values ()) which is
   *  (apply values '(())).  This also affects quasiquote 
   *  (s7 uses (apply values ...) for unquote-splicing).
   *
   * But that makes me think that (eval) should return #<unspecified>. What about (eval-string)?
   */
  
  if (is_null(cdr(args)))
    return(car(args));

  return(splice_in_values(sc, args));
}


/* -------------------------------- quasiquote -------------------------------- */

static s7_pointer g_qq_list(s7_scheme *sc, s7_pointer args)
{
  #define H_qq_list "({list} ...) returns its arguments in a list (internal to quasiquote)"

  s7_pointer x, y, px;

  if (sc->no_values == 0)
    return(args);

  for (x = args; is_pair(x); x = cdr(x))
    if (car(x) == sc->NO_VALUE) 
      break;
  
  if (is_null(x))
    return(args);

  /* this is not maximally efficient, but it's not important:
   *   we've hit the rare special case where ({apply_values} ())) needs to be ignored
   *   in the splicing process (i.e. the arglist acts as if the thing never happened)
   */
  px = sc->NIL;
  for (x = args, y = args; is_pair(y); y = cdr(y))
    if (car(y) != sc->NO_VALUE)
      {
	car(x) = car(y);
	px = x;
	x = cdr(x);
      }

  if ((is_not_null(y)) &&
      (y != sc->NO_VALUE))
    cdr(x) = cdr(y);
  else 
    {
      sc->no_values--;
      if (is_null(px))
	return(sc->NIL);
      cdr(px) = sc->NIL;
    }
  return(args);
}


static s7_pointer g_apply_values(s7_scheme *sc, s7_pointer args)
{
  #define H_apply_values "({apply_values} var) applies values to var.  This is an internal function."
  s7_pointer x;

  if (is_null(args))
    {
      sc->no_values++;
      return(sc->NO_VALUE);
    }
  if (is_null(cdr(args)))
    x = car(args);
  else x = apply_list_star(sc, args);

  if (!is_proper_list(sc, x))
    return(apply_list_error(sc, args));
  if (is_null(x))
    {
      sc->no_values++;
      return(sc->NO_VALUE);
    }
  return(g_values(sc, x));
}

/* (apply values ...) replaces (unquote_splicing ...)
 *
 * (define-macro (hi a) `(+ 1 ,a) == (list '+ 1 a)
 * (define-macro (hi a) ``(+ 1 ,,a) == (list list '+ 1 (list quote a)))
 *
 * (define-macro (hi a) `(+ 1 ,@a) == (list '+ 1 (apply values a))
 * (define-macro (hi a) ``(+ 1 ,,@a) == (list list '+ 1 (apply values a))
 *
 * this is not the same as CL's quasiquote; for example:
 *   [1]> (let ((a 1) (b 2)) `(,a ,@b))
 *   (1 . 2)
 *   in s7 this is an error.  
 *
 * also in CL the target of ,@ can apparently be a circular list
 */

static bool is_simple_code(s7_scheme *sc, s7_pointer form)
{
  s7_pointer tmp;
  for (tmp = form; is_pair(tmp); tmp = cdr(tmp))
    if (is_pair(car(tmp)))
      {
	if (!is_simple_code(sc, car(tmp)))
	  return(false);
      }
    else
      {
	if ((car(tmp) == sc->UNQUOTE) ||
	    ((is_null(car(tmp))) && (is_null(cdr(tmp)))))
	  return(false);
      }
  return(is_null(tmp));
}


static s7_pointer g_quasiquote_1(s7_scheme *sc, s7_pointer form)
{
  #define H_quasiquote "(quasiquote arg) is the same as `arg.  If arg is a list, it can contain \
comma (\"unquote\") and comma-atsign (\"unquote-splicing\") to pre-evaluate portions of the list. \
unquoted expressions are evaluated and plugged into the list, unquote-splicing evaluates the expression \
and splices the resultant list into the outer list. `(1 ,(+ 1 1) ,@(list 3 4)) -> (1 2 3 4)."

  if (!is_pair(form))
    {
      if (!is_symbol(form))
	{
	  /* things that evaluate to themselves don't need to be quoted. 
	   *    but this means `() -> () whereas below `(1) -> '(1) -- should nil here return ()?
	   *    (this also affects vector constants since they call g_quasiquote at run time in OP_READ_QUASIQUOTE_VECTOR)
	   *
	   * format ~<~> innards are ignored by quasiquote expansion -- it sees a string constant
	   */
	  return(form);
	}
      return(list_2(sc, sc->QUOTE, form));
    }

  if (car(form) == sc->UNQUOTE)
    {
      if (is_not_null(cddr(form)))
	return(eval_error(sc, "unquote: too many arguments, ~S", form));
      return(cadr(form));
    }

  /* it's a list, so return the list with each element handled as above.
   *    we try to support dotted lists which makes the code much messier.
   */
  
  /* if no element of the list is a list or unquote, just return the original quoted */
  if (is_simple_code(sc, form))
    return(list_2(sc, sc->QUOTE, form));

  {
    int len, i, loc;
    s7_pointer orig, bq, old_scw;
    bool dotted = false;

    len = s7_list_length(sc, form);
    if (len == 0)
      {
	/* a circular form, apparently */
	return(list_2(sc, sc->QUOTE, form));
      }
    if (len < 0)
      {
	len = -len;
	dotted = true;
      }

    old_scw = sc->w;
    loc = s7_gc_protect(sc, old_scw);

    sc->w = sc->NIL;
    for (i = 0; i <= len; i++)
      sc->w = cons(sc, sc->NIL, sc->w);

    car(sc->w) = sc->QQ_List;
    
    if (!dotted)
      {
	for (orig = form, bq = cdr(sc->w), i = 0; i < len; i++, orig = cdr(orig), bq = cdr(bq))
	  {
	    if ((is_pair(cdr(orig))) &&            /* this was is_pair(orig) which seems to be always the case */
		(cadr(orig) == sc->UNQUOTE))
	      {
		/* `(1 . ,(+ 1 1)) -> '(1 unquote (+ 1 1)) -> '(1 . 2) 
		 * `(1 . ,@'((2 3))) -> (1 unquote ({apply_values} '((2 3)))) -> ({append} ({list} 1) ({apply_values} '((2 3)))) -> '(1 2 3)
		 * this used to be `(1 . ,@('(2 3))).  
		 *     This now becomes (1 unquote ({apply_values} ('(2 3)))) -> ({append} ({list} 1) ({apply_values} ('(2 3)))) -> error
		 * `(1 . (,@'(2 3))) works in both cases, and `(1 . (,(+ 1 1)))
		 */
		car(bq) = g_quasiquote_1(sc, car(orig));
		cdr(bq) = sc->NIL;
		sc->w = list_3(sc, sc->QQ_Append, sc->w, caddr(orig));
		break;
	      }
	    else car(bq) = g_quasiquote_1(sc, car(orig));
	  }
      }
    else
      {
	/* `(1 2 . 3) */
	len--;
	for (orig = form, bq = cdr(sc->w), i = 0; i < len; i++, orig = cdr(orig), bq = cdr(bq))
	  car(bq) = g_quasiquote_1(sc, car(orig));
	car(bq) = g_quasiquote_1(sc, car(orig));

	sc->w = list_3(sc, sc->QQ_Append, sc->w, g_quasiquote_1(sc, cdr(orig)));
	/* quasiquote might quote a symbol in cdr(orig), so it's not completely pointless */
      }

    bq = sc->w;
    sc->w = old_scw;
    s7_gc_unprotect_at(sc, loc);
    return(bq);
  }
}


static s7_pointer g_quasiquote(s7_scheme *sc, s7_pointer args)
{
  /* this is for explicit quasiquote support, not the backquote stuff in macros */
  return(g_quasiquote_1(sc, car(args)));
}



/* ---------------- reader funcs for eval ---------------- */

static void back_up_stack(s7_scheme *sc)
{
  opcode_t top_op;
  top_op = stack_op(sc->stack, s7_stack_top(sc) - 1);
  if (top_op == OP_READ_DOT)
    {
      pop_stack(sc); /* perhaps pop_main_stack here and below? */
      top_op = stack_op(sc->stack, s7_stack_top(sc) - 1);
    }
  if ((top_op == OP_READ_VECTOR) ||
      (top_op == OP_READ_BYTEVECTOR))
    {
      pop_stack(sc);
      top_op = stack_op(sc->stack, s7_stack_top(sc) - 1);
    }
  if (top_op == OP_READ_QUOTE)
    pop_stack(sc);
}


static token_t read_sharp(s7_scheme *sc, s7_pointer pt)
{
  int c;
  /* inchar can return EOF, so it can't be used directly as an index into the digits array */
  c = inchar(pt);
  switch (c)
    {
    case EOF:
      s7_error(sc, sc->READ_ERROR,
	       list_1(sc, make_string_wrapper(sc, "unexpected '#' at end of input")));
      break;
      
    case '(':
      sc->w = small_int(1);
      return(TOKEN_VECTOR);
      
    case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
      {
	/* here we can get an overflow: #1231231231231232131D() 
	 *   and we can't shrug it off:
	 *   :#2147483649123D()
	 *   ;#nD(...) dimensions argument 1, -2147483647, is out of range (must be 1 or more)
	 * but
	 *   :#2147483649123D()
	 *   creates a vector with 512 dimensions!
	 * ndims in the vector struct is an unsigned int, so we'll complain if it goes over short max for now
	 */
	s7_Int dims;
	int d, loc = 0;
	sc->strbuf[loc++] = c;
	dims = digits[c];
	
	while (true)
	  {
	    s7_Int dig;
	    d = inchar(pt);
	    if (d == EOF)
	      s7_error(sc, sc->READ_ERROR,
		       list_1(sc, make_string_wrapper(sc, "unexpected end of input while reading #n...")));
	    
	    dig = digits[d];
	    if (dig >= 10) break;
	    dims = dig + (dims * 10);
	    if ((dims <= 0) ||
		(dims > S7_SHORT_MAX))
	      s7_error(sc, sc->READ_ERROR,
		       list_2(sc, make_string_wrapper(sc, "overflow while reading #nD: ~A"), make_integer(sc, dims)));
	    sc->strbuf[loc++] = d;
	  }
	sc->strbuf[loc++] = d;
	if ((d == 'D') || (d == 'd'))
	  {
	    d = inchar(pt);
	    if (d == EOF)
	      s7_error(sc, sc->READ_ERROR,
		       list_1(sc, make_string_wrapper(sc, "unexpected end of input while reading #nD...")));
	    sc->strbuf[loc++] = d;
	    if (d == '(')
	      {
		sc->w = make_integer(sc, dims);
		return(TOKEN_VECTOR);
	      }
	  }
	
	/* try to back out */
	for (d = loc - 1; d > 0; d--)
	  backchar(sc->strbuf[d], pt);
      }
      break;

    case 'u':
      {
	int d;
	d = inchar(pt);
	if (d == EOF)
	  s7_error(sc, sc->READ_ERROR,
		   list_1(sc, make_string_wrapper(sc, "unexpected end of input while reading #u...")));
	if (d == '8')
	  {
	    d = inchar(pt);
	    if (d == EOF)
	      s7_error(sc, sc->READ_ERROR,
		       list_1(sc, make_string_wrapper(sc, "unexpected end of input while reading #u8...")));
	    if (d == '(')
	      return(TOKEN_BYTEVECTOR);
	    backchar(d, pt);
	    backchar('8', pt);
	  }
	else backchar(d, pt);
      }
      break;
      
    case ':':  /* turn #: into : -- this is for compatibility with Guile, #:optional in particular.
		*   I just noticed that Rick is using this -- I'll just leave it alone.
		*   but that means : readers need to handle this case specially.
		* I don't think #! is special anymore -- maybe remove that code?
		*/
      sc->strbuf[0] = ':';
      return(TOKEN_ATOM);

      /* block comments in #! ... !# */
      /* this is needed when an input file is treated as a script:
	 #!/home/bil/cl/snd
	 !#
	 (format #t "a test~%")
	 (exit)
      */
    case '!':
      {
	char last_char;
	last_char = ' ';
	while ((c = inchar(pt)) != EOF)
	  {
	    if ((c == '#') &&
		(last_char == '!'))
	      break;
	    last_char = c;
	  }
	if (c == EOF)
	  s7_error(sc, sc->READ_ERROR,
		   list_1(sc, make_string_wrapper(sc, "unexpected end of input while reading #!")));
	return(token(sc));
      }
      
      /* block comments in #| ... |# 
       *   since we ignore everything until the |#, internal semicolon comments are ignored,
       *   meaning that ;|# is as effective as |#
       */
    case '|':
      {
	if (is_file_port(pt))
	  {
	    char last_char;
	    last_char = ' ';
	    while (true)
	      {
		c = fgetc(port_file(pt));
		if (c == EOF)
		  s7_error(sc, sc->READ_ERROR,
			   list_1(sc, make_string_wrapper(sc, "unexpected end of input while reading #|")));
		if ((c == '#') &&
		    (last_char == '|'))
		  break;
		last_char = c;
		if (c == '\n')
		  port_line_number(pt)++;
	      }
	    return(token(sc));
	  }
	else 
	  {
	    const char *str, *orig_str, *p, *pend;

	    orig_str = (const char *)(port_data(pt) + port_position(pt));
	    pend = (const char *)(port_data(pt) + port_data_size(pt));
	    str = orig_str;

	    while (true)
	      {
		p = strchr(str, (int)'|');
		if ((!p) || (p >= pend))
		  {
		    port_position(pt) = port_data_size(pt);
		    s7_error(sc, sc->READ_ERROR,
			     list_1(sc, make_string_wrapper(sc, "unexpected end of input while reading #|")));
		  }
		if (p[1] == '#')
		  break;
		str = (const char *)(p + 1);
	      }
	    port_position(pt) += (p - orig_str + 2);

	    /* now count newline inside the comment */
	    str = (const char *)orig_str;
	    pend = p;
	    while (true)
	      {
		p = strchr(str, (int)'\n');
		if ((p) && (p < pend))
		  {
		    port_line_number(pt)++;
		    str = (char *)(p + 1);
		  }
		else break;
	      }
	    return(token(sc));
	  }
      }
    }
  sc->strbuf[0] = c; 
  return(TOKEN_SHARP_CONST); /* next stage notices any errors */
}    


static token_t read_comma(s7_scheme *sc, s7_pointer pt)
{
  int c;
  /* here we probably should check for symbol names that start with "@":
     :(define-macro (hi @foo) `(+ ,@foo 1))
     hi
     :(hi 2)
     ;foo: unbound variable
	 
     but
     :(define-macro (hi .foo) `(+ ,.foo 1))
     hi
     :(hi 2)
     3

     and ambiguous:
     :(define-macro (hi @foo . foo) `(list ,@foo))

     what about , @foo -- is the space significant?  We accept ,@ foo.
  */

  if ((c = inchar(pt)) == '@') 
    return(TOKEN_AT_MARK);

  if (c == EOF)
    {
      sc->strbuf[0] = ',';  /* was '@' which doesn't make any sense */
      return(TOKEN_COMMA);  /* was TOKEN_ATOM, which also doesn't seem sensible */
    }
  backchar(c, pt);
  return(TOKEN_COMMA);
}


static token_t read_dot(s7_scheme *sc, s7_pointer pt)
{
  int c;
  c = inchar(pt);
  if (c != EOF)
    {
      backchar(c, pt);

      if ((!char_ok_in_a_name[c]) && (c != 0))
	return(TOKEN_DOT);
    }
  else
    {
      sc->strbuf[0] = '.'; 
      return(TOKEN_DOT);
    }
  sc->strbuf[0] = '.'; 
  return(TOKEN_ATOM);  /* i.e. something that can start with a dot like a number */
}


static token_t token(s7_scheme *sc)
{
  int c;
  c = port_read_white_space(sc->input_port)(sc, sc->input_port);
  switch (c) 
    {
    case '(':
      return(TOKEN_LEFT_PAREN);
      
    case ')':
      return(TOKEN_RIGHT_PAREN);
      
    case '.':
      return(read_dot(sc, sc->input_port));

    case '\'':
      return(TOKEN_QUOTE);
      
    case ';':
      return(port_read_semicolon(sc->input_port)(sc, sc->input_port));

    case '"':
      return(TOKEN_DOUBLE_QUOTE);
      
    case '`':
      return(TOKEN_BACK_QUOTE);
      
    case ',':
      return(read_comma(sc, sc->input_port));
      
    case '#':
      return(read_sharp(sc, sc->input_port));

    case '\0':
    case EOF:
      return(TOKEN_EOF);

    default:
      sc->strbuf[0] = c; /* every TOKEN_ATOM return goes to port_read_name, so we save a backchar/inchar shuffle by starting the read here */
      return(TOKEN_ATOM);
    }
}


#define NOT_AN_X_CHAR -1

static int read_x_char(s7_pointer pt)
{
  /* possible "\xnn" char (write creates these things, so we have to read them) 
   *   but we could have crazy input like "\x -- with no trailing double quote
   */
  int d1, c;

  c = inchar(pt);
  if (c == EOF)
    return(NOT_AN_X_CHAR);

  d1 = digits[c];
  if (d1 < 16)
    {
      int d2;
      c = inchar(pt);
      if (c == EOF)
	return(NOT_AN_X_CHAR);
      d2 = digits[c];
      if (d2 < 16)
	return(16 * d1 + d2);           /* following char can be anything, including a number -- we ignore it */
      /* apparently one digit is also ok */
      backchar(c, pt);
      return(d1);
    }
  return(NOT_AN_X_CHAR);
}


static s7_pointer read_string_constant(s7_scheme *sc, s7_pointer pt)
{
  /* sc->F => error 
   *   no check needed here for bad input port and so on
   */
  unsigned int i = 0;

  if (is_string_port(pt)) 
    {
      /* try the most common case first */
      char *s, *start, *end;
      start = (char *)(port_data(pt) + port_position(pt));
      if (*start == '"')
	{
	  port_position(pt)++;
	  return(make_empty_string(sc, 0, 0));
	}

      end = (char *)(port_data(pt) + port_data_size(pt));
      s = strpbrk(start, "\"\n\\");
      if ((!s) || (s >= end))                                 /* can this read a huge string constant from a file? */
	{
	  memcpy((void *)(sc->strbuf), (void *)start, (end - start > 8) ? 8 : (end - start));
	  sc->strbuf[8] = '\0';
	  return(sc->F);
	}
      if (*s == '"')
	{
	  int len;
	  len = s - start;
	  port_position(pt) += (len + 1);
	  return(s7_make_terminated_string_with_length(sc, start, len));
	}
      
      for (; s < end; s++)
	{
	  if (*s == '"')                         /* switch here no faster */
	    {
	      int len;
	      len = s - start;
	      port_position(pt) += (len + 1);
	      return(s7_make_terminated_string_with_length(sc, start, len));
	    }
	  else
	    {
	      if (*s == '\\')
		{
		  /* all kinds of special cases here (resultant string is not the current string), so drop to loop below (setting "i") */
		  unsigned int len;
		  len = (unsigned int)(s - start);
		  if (len > 0)
		    {
		      if (len >= sc->strbuf_size)
			resize_strbuf(sc, len);
		      /* for (i = 0; i < len; i++) sc->strbuf[i] = port_data(pt)[port_position(pt)++]; */
		      memcpy((void *)(sc->strbuf), (void *)(port_data(pt) + port_position(pt)), len);
		      port_position(pt) += len;
		    }
		  i = len;
		  break;
		}
	      else
		{
		  if (*s == '\n')
		    port_line_number(pt)++; 
		}
	    }
	}
    }

  while (true)
    {
      /* splitting this check out and duplicating the loop was slower?!? */
      int c;
      c = port_read_character(pt)(sc, pt);

      switch (c)
	{
	case '\n': 
	  port_line_number(pt)++; 
	  sc->strbuf[i++] = c;
	  break;

	case EOF:
	  sc->strbuf[(i > 8) ? 8 : i] = '\0';
	  return(sc->F);

	case '"':
	  return(s7_make_terminated_string_with_length(sc, sc->strbuf, i));

	case '\\':
	  c = inchar(pt);

	  if (c == EOF) 
	    {
	      sc->strbuf[(i > 8) ? 8 : i] = '\0';
	      return(sc->F);
	    }

	  if ((c == '\\') || (c == '"') || (c == '|'))
	    sc->strbuf[i++] = c;
	  else
	    {
	      if (c == 'n')
		sc->strbuf[i++] = '\n';
	      else 
		{
		  if (c == 't')                         /* this is for compatibility with other Schemes */
		    sc->strbuf[i++] = '\t';
		  else 
		    {
		      if (c == 'x')
			{
			  c = read_x_char(pt);
			  if (c == NOT_AN_X_CHAR)
			    return(sc->T);
			  sc->strbuf[i++] = (unsigned char)c;
			}
		      else
			{
			  /* if (!is_white_space(c)) */ /* changed 8-Apr-12 */
			  if ((c != '\n') && (c != '\r'))
			    return(sc->T); 
			  
			  /* #f here would give confusing error message "end of input", so return #t=bad backslash.
			   *     this is not optimal. It's easy to forget that backslash needs to be backslashed. 
			   *
			   * the white_space business half-implements Scheme's \<newline>...<eol>... or \<space>...<eol>...
			   *   feature -- the characters after \ are flushed if they're all white space and include a newline.
			   *   (string->number "1\   2") is 12??  Too bizarre.
			   */
			}
		    }
		}
	    }
	  break;

	default:
	  sc->strbuf[i++] = c;
	  break;
	}

      if (i >= sc->strbuf_size)
	resize_strbuf(sc, i);
    }
}


static s7_pointer read_expression(s7_scheme *sc)
{ 
  while (true) 
    {
      int c;
      switch (sc->tok) 
	{
	case TOKEN_EOF:
	  return(sc->EOF_OBJECT);
	  
	case TOKEN_BYTEVECTOR:
	  push_stack_no_code(sc, OP_READ_BYTEVECTOR, sc->NIL);
	  sc->tok = TOKEN_LEFT_PAREN;
	  break;

	case TOKEN_VECTOR:         /* already read #( -- TOKEN_VECTOR is triggered by #( */
	    push_stack_no_code(sc, OP_READ_VECTOR, sc->w);   /* sc->w is the dimensions */
	    /* fall through */
	  
	case TOKEN_LEFT_PAREN:
	  sc->tok = token(sc);

	  if (sc->tok == TOKEN_RIGHT_PAREN)
	    return(sc->NIL);

	  if (sc->tok == TOKEN_DOT) 
	    {
	      back_up_stack(sc);
	      do {c = inchar(sc->input_port);} while ((c != ')') && (c != EOF));
	      return(read_error(sc, "stray dot after '('?"));         /* (car '( . )) */
	    }

	  if (sc->tok == TOKEN_EOF)
	    return(missing_close_paren_error(sc));

	  push_stack_no_code(sc, OP_READ_LIST, sc->NIL);
	  /* here we need to clear args, but code is ignored */

	  CHECK_STACK_SIZE(sc);
	  break;
	  
	case TOKEN_QUOTE:
	  push_stack_no_code(sc, OP_READ_QUOTE, sc->NIL);
	  sc->tok = token(sc);
	  break;
	  
	case TOKEN_BACK_QUOTE:
	  sc->tok = token(sc);
#if WITH_QUASIQUOTE_VECTOR
	  if (sc->tok == TOKEN_VECTOR)
	    {
	      push_stack_no_code(sc, OP_READ_QUASIQUOTE_VECTOR, sc->w);
	      sc->tok = TOKEN_LEFT_PAREN; 
	    } 
	  else 
#endif
	    push_stack_no_code(sc, OP_READ_QUASIQUOTE, sc->NIL);
	  break;
	  
	case TOKEN_COMMA:
	  push_stack_no_code(sc, OP_READ_UNQUOTE, sc->NIL);
	  sc->tok = token(sc);
	  break;
	  
	case TOKEN_AT_MARK:
	  push_stack_no_code(sc, OP_READ_APPLY_VALUES, sc->NIL);
	  sc->tok = token(sc);
	  break;
	  
	case TOKEN_ATOM:
	  return(port_read_name(sc->input_port)(sc, sc->input_port));
	  /* If reading list (from lparen), this will finally get us to op_read_list */
	  
	case TOKEN_DOUBLE_QUOTE:
	  sc->value = read_string_constant(sc, sc->input_port);

	  if (sc->value == sc->F)                                   /* can happen if input code ends in the middle of a string */
	    return(string_read_error(sc, "end of input encountered while in a string"));
	  if (sc->value == sc->T)
	    return(read_error(sc, "unknown backslash usage -- perhaps you meant two backslashes?"));

	  return(sc->value);
	  
	case TOKEN_SHARP_CONST:
	  sc->value = port_read_sharp(sc->input_port)(sc, sc->input_port);

	  /* here we need the following character and form 
	   *   strbuf[0] == '#', false above = # case, not an atom
	   */
	  if (is_null(sc->value))
	    {
	      return(read_error(sc, "undefined # expression"));
	      /* a read error here seems draconian -- this unknown constant doesn't otherwise get in our way
	       *   but how to alert the caller to the problem without stopping the read?
	       */
	    }
	  return(sc->value);

	case TOKEN_DOT:                                             /* (catch #t (lambda () (+ 1 . . )) (lambda args 'hiho)) */
	  back_up_stack(sc);
	  do {c = inchar(sc->input_port);} while ((c != ')') && (c != EOF));
	  return(read_error(sc, "stray dot in list?"));             /* (+ 1 . . ) */

	case TOKEN_RIGHT_PAREN:                                     /* (catch #t (lambda () '(1 2 . )) (lambda args 'hiho)) */
	  back_up_stack(sc);
	  return(read_error(sc, "unexpected close paren"));         /* (+ 1 2)) or (+ 1 . ) */
	}
    }
  /* we never get here */
  return(sc->NIL);
}



/* ---------------- *unbound-variable-hook* ---------------- */

static s7_pointer loaded_library(s7_scheme *sc, const char *file)
{
  s7_pointer p;
  for (p = slot_value(sc->libraries); is_pair(p); p = cdr(p))
    if (strcmp(file, string_value(caar(p))) == 0)
      return(cdar(p));
  return(sc->NIL);
}


static s7_pointer unbound_variable(s7_scheme *sc, s7_pointer sym)
{
  /* this always occurs in a context where we're trying to find anything, so I'll move a couple of those checks here
   */
  if (sym == sc->UNQUOTE)
    return(eval_error(sc, "unquote (',') occurred outside quasiquote: ~S", sc->cur_code));

  if (sym == sc->__FUNC__) /* __func__ is a sort of symbol macro */
    {
      s7_pointer env;
      env = find_closure_let(sc, sc->envir);
      if ((is_let(env)) &&
	  (is_symbol(funclet_function(env))))
	{
	  if (has_line_number(env))
	    return(list_3(sc, funclet_function(env), sc->file_names[let_file(env)], make_integer(sc, let_line(env))));
	  return(funclet_function(env));
	}
      return(sc->UNDEFINED);
    }
  
  if (safe_strcmp(symbol_name(sym), "|#") == 0)
    return(read_error(sc, "unmatched |#"));

  /* check *autoload*, autoload_names, then *unbound-variable-hook* 
   */
  if ((sc->autoload_names) ||
      (is_hash_table(sc->autoload_table)) ||
      (is_not_null(s7_hook_functions(sc, sc->unbound_variable_hook))))
    {
      s7_pointer result, cur_code, value, code, args, cur_env;

      if (sc->ubh_cur_code_loc != -1) s7_gc_unprotect_at(sc, sc->ubh_cur_code_loc);
      if (sc->ubh_value_loc != -1) s7_gc_unprotect_at(sc, sc->ubh_value_loc);
      if (sc->ubh_args_loc != -1) s7_gc_unprotect_at(sc, sc->ubh_args_loc);
      if (sc->ubh_code_loc != -1) s7_gc_unprotect_at(sc, sc->ubh_code_loc);
      if (sc->ubh_hook_loc != -1) s7_gc_unprotect_at(sc, sc->ubh_hook_loc);
      if (sc->ubh_x != -1) s7_gc_unprotect_at(sc, sc->ubh_x);
      if (sc->ubh_z != -1) s7_gc_unprotect_at(sc, sc->ubh_z);
      
      /* sc->args and sc->code are pushed on the stack by s7_call, then
       *   restored by eval, so they are normally protected, but sc->value and sc->cur_code are
       *   not protected (yet).  We need sc->cur_code so that the possible eventual error
       *   call can tell where the error occurred, and we need sc->value because it might
       *   be awaiting addition to sc->args in e.g. OP_EVAL_ARGS5, and then be clobbered
       *   by the hook function.  (+ 1 asdf) will end up evaluating (+ asdf asdf) if sc->value
       *   is not protected.  We also need to save/restore sc->envir in case s7_load is called.
       */

      args = sc->args;
      sc->ubh_args_loc = s7_gc_protect(sc, args);
      code = sc->code;
      sc->ubh_code_loc = s7_gc_protect(sc, code);
      value = sc->value;
      sc->ubh_value_loc = s7_gc_protect(sc, value);
      cur_code = sc->cur_code;
      cur_env = sc->envir;
      result = sc->UNDEFINED;

      if (!is_pair(cur_code))
	{
	  /* isolated typo perhaps -- no pair to hold the position info, so make one.
	   *   sc->cur_code is GC-protected, so this should be safe.
	   */
	  cur_code = cons(sc, sym, sc->NIL);     /* the error will say "(sym)" which is not too misleading */
	  pair_line_number(cur_code) = port_line_number(sc->input_port) | (port_file_number(sc->input_port) << 20);
	  set_has_line_number(cur_code);
	}
      sc->ubh_cur_code_loc = s7_gc_protect(sc, cur_code);   /* we need to save this because it has the file/line number of the unbound symbol */

      SAVE_X_Z(sc->ubh_x, sc->ubh_z); /* this is needed to protect entire expression context */

      /* check sc->autoload_names */
      if (sc->autoload_names)
	{
	  const char *file;
	  bool loaded = false;
	  file = find_autoload_name(sc, sym, &loaded, true);
	  if ((file) && (!loaded))
	    {
	      s7_pointer e;
	      /* if we've already loaded this file, we can get the library (e) from a table [(file lib) ...]
	       * here it was possible to get caught in a loop:
	       *   change file, reload, unbound var seen, check autoload, it says "load file"... (where file does not get added to *libraries*)
	       *   so the "loaded" arg tries to catch such cases
	       */
	      e = loaded_library(sc, file);
	      if (!is_let(e))
		e = s7_load(sc, file);
	      result = s7_symbol_value(sc, sym); /* calls find_symbol, does not trigger unbound_variable search */
	      if ((result == sc->UNDEFINED) &&
		  (is_let(e)))
		{
		  result = s7_let_ref(sc, e, sym);
		  /* I think to be consistent we should add '(sym . result) to the global env
		   */
		  if (result != sc->UNDEFINED)
		    {
		      s7_define(sc, sc->NIL, sym, result);
		    }
		}
	    }
	}

      if (result == sc->UNDEFINED)
	{
	  /* check the *autoload* hash table */
	  if (is_hash_table(sc->autoload_table))
	    {
	      s7_pointer val;
	      /* it was possible to get in a loop here: missing paren in x.scm, checks last symbol, sees
	       *   autoload sym -> x.scm, loads x.scm, missing paren...
	       */
	      val = s7_hash_table_ref(sc, sc->autoload_table, sym);
	      if (is_string(val))
		{
		  /* val should be a filename. *load-path* is searched if necessary. */
		  s7_load(sc, string_value(val));
		}
	      else
		{
		  if (is_closure(val))
		    {
		      /* val should be a function of one argument, the current (calling) environment. */
		      s7_call(sc, val, s7_cons(sc, sc->envir, sc->NIL));
		    }
		}
	      result = s7_symbol_value(sc, sym); /* calls find_symbol, does not trigger unbound_variable search */
	    }
	  
	  /* check *unbound-variable-hook* */
	  if ((result == sc->UNDEFINED) &&
	      (is_not_null(sc->unbound_variable_hook)))
	    {
	      /* (let () (set! (hook-functions *unbound-variable-hook*) (list (lambda (v) _asdf_))) _asdf_)
	       */
	      s7_pointer old_hook;

	      old_hook = sc->unbound_variable_hook;
	      sc->ubh_hook_loc = s7_gc_protect(sc, old_hook);
	      sc->unbound_variable_hook = sc->error_hook;      /* avoid the infinite loop mentioned above */
	      result = s7_call(sc, old_hook, list_1(sc, sym)); /* not s7_apply_function */
	      sc->unbound_variable_hook = old_hook;
	      s7_gc_unprotect_at(sc, sc->ubh_hook_loc);
	      sc->ubh_hook_loc = -1;
	    }
	}

      RESTORE_X_Z(sc->ubh_x, sc->ubh_z);
      sc->ubh_x = -1;
      sc->ubh_z = -1;

      sc->value = value;
      s7_gc_unprotect_at(sc, sc->ubh_value_loc);
      sc->ubh_value_loc = -1;
      sc->cur_code = cur_code;
      s7_gc_unprotect_at(sc, sc->ubh_cur_code_loc);
      sc->ubh_cur_code_loc = -1;
      sc->args = args;
      s7_gc_unprotect_at(sc, sc->ubh_args_loc);
      sc->ubh_args_loc = -1;
      sc->code = code;
      sc->envir = cur_env;
      s7_gc_unprotect_at(sc, sc->ubh_code_loc);
      sc->ubh_code_loc = -1;

      if ((result != sc->UNDEFINED) &&
	  (result != sc->UNSPECIFIED))
	return(result);
    }
  return(eval_error(sc, "~A: unbound variable", sym));
}


static s7_pointer assign_syntax(s7_scheme *sc, const char *name, opcode_t op) 
{
  s7_pointer x, syn;
  unsigned int hash, loc;

  hash = raw_string_hash((const unsigned char *)name, safe_strlen(name));
  loc = hash % SYMBOL_TABLE_SIZE;
  x = new_symbol(sc, name, safe_strlen(name), hash, loc);

  syn = alloc_pointer();
  set_type(syn, T_SYNTAX | T_SYNTACTIC | T_DONT_EVAL_ARGS); 
  syntax_opcode(syn) = op;
  syntax_symbol(syn) = x; 

  global_slot(x) = permanent_slot(x, syn);
  initial_slot(x) = permanent_slot(x, syn);
  typeflag(x) = SYNTACTIC_TYPE;
  symbol_set_local(x, 0LL, sc->NIL);
  symbol_syntax_op(x) = op;
  return(x);
}


static s7_pointer assign_internal_syntax(s7_scheme *sc, const char *name, opcode_t op) 
{
  s7_pointer x, str, syn; 

  str = s7_make_permanent_string(name);

  x = alloc_pointer();
  heap_location(x) = NOT_IN_HEAP;
  symbol_name_cell(x) = str;
  set_type(x, T_SYMBOL);
  symbol_hash(x) = (raw_string_hash((const unsigned char *)name, safe_strlen(name)) % SYMBOL_TABLE_SIZE);
  symbol_set_local(x, 0LL, sc->NIL);
  symbol_syntax_op(x) = op;

  syn = alloc_pointer();
  set_type(syn, T_SYNTAX | T_SYNTACTIC | T_DONT_EVAL_ARGS); 
  syntax_opcode(syn) = op;
  syntax_symbol(syn) = s7_make_symbol(sc, name);

  global_slot(x) = permanent_slot(x, syn);
  initial_slot(x) = permanent_slot(x, syn);
  typeflag(x) = SYNTACTIC_TYPE;
  return(x);
}


static bool direct_memq(s7_pointer symbol, s7_pointer symbols)
{
  s7_pointer x;
  for (x = symbols; is_pair(x); x = cdr(x))
    {
      if (car(x) == symbol)
	return(true);
      x = cdr(x);
      if (car(x) == symbol)
	return(true);
    }
  return(false);
}


bool s7_tree_memq(s7_scheme *sc, s7_pointer symbol, s7_pointer tree)
{
  if (is_null(tree))
    return(false);
  if (symbol == tree)
    return(true);
  if (is_pair(tree))
    return((s7_tree_memq(sc, symbol, car(tree))) ||
	   (s7_tree_memq(sc, symbol, cdr(tree))));
  return(false);
}


static s7_pointer g_pair_line_number(s7_scheme *sc, s7_pointer args)
{
  #define H_pair_line_number "(pair-line-number pair) returns the line number at which it read 'pair'"
  s7_pointer p;
  p = car(args);

  if (!is_pair(p))
    {
      check_method(sc, p, sc->PAIR_LINE_NUMBER, args);
      return(simple_wrong_type_argument(sc, sc->PAIR_LINE_NUMBER, p, T_PAIR));	
    }
  if (has_line_number(p))
    return(make_integer(sc, (s7_Int)(remembered_line_number(pair_line_number(p)))));
  return(small_int(0));
}


static s7_pointer quotify(s7_scheme *sc, s7_pointer pars)
{
  /* the default parameter values of define-macro* and define-bacro* should not be evaluated until
   * the expansion is evaluated.  That is, 
   *
   *   (let ((x 0)) (define-macro* (hi (a (let () (set! x (+ x 1)) x))) `(let ((x -1)) (+ x ,a))) (list (hi) x))
   *
   * should return the same value as the equivalent explicit form:
   *
   *   (let ((x 0)) (define-macro (hi a) `(let ((x -1)) (+ x ,a))) (list (hi (let () (set! x (+ x 1)) x)) x))
   *
   * '(-1 0) in both cases.
   *
   * But at the point in eval where we handle lambda* arguments, we can't easily tell whether we're part of
   * a function or a macro, so at definition time of a macro* we scan the parameter list for an expression
   * as a default value, and replace it with (quote expr).
   *
   * and... (define-macro* ((a x)) ...) should behave the same as (define-macro* ((a (+ x 0))) ...)
   */
  s7_pointer tmp;
  for (tmp = pars; is_pair(tmp); tmp = cdr(tmp))
    if ((is_pair(car(tmp))) &&
	(is_pair(cdar(tmp))) &&
	((is_pair(cadar(tmp))) ||
	 (is_symbol(cadar(tmp))) ||
	 (is_syntax(cadar(tmp)))))
      cadar(tmp) = list_2(sc, sc->QUOTE, cadar(tmp));
  return(pars);
}


static s7_pointer lambda_star_argument_set_value(s7_scheme *sc, s7_pointer sym, s7_pointer val)
{
  s7_pointer x;

  for (x = let_slots(sc->envir) /* presumably the arglist */; is_slot(x); x = next_slot(x))
    if (slot_symbol(x) == sym)
      {
	/* x is our binding (symbol . value) */
	if (is_not_checked(x))
	  set_checked(x); /* this is a special use of this bit, I think */
	else 
	  {
	    return(s7_error(sc, sc->WRONG_TYPE_ARG,
			    list_4(sc,
				   make_string_wrapper(sc, "~A: parameter set twice, ~S in ~S"),
				   closure_name(sc, sc->code), sym, sc->args)));
	  }
	slot_set_value(x, val);
	return(val);
      }
  return(sc->NO_VALUE);
}


static s7_pointer lambda_star_set_args(s7_scheme *sc)
{
  /* sc->code is a closure: ((args body) envir)
   * (define* (hi a (b 1)) (+ a b))
   * (procedure-source hi) -> (lambda* (a (b 1)) (+ a b))
   *
   * so rather than spinning through the args binding names to values in the
   *   procedure's new environment (as in the usual closure case above),
   *   we scan the current args, and match against the
   *   template in the car of the closure, binding as we go.
   *
   * for each actual arg, if it's not a keyword that matches a member of the 
   *   template, bind it to its current (place-wise) arg, else bind it to
   *   that arg.  If it's the symbol :key or :optional, just go on.
   *   If it's :rest bind the next arg to the trailing args at this point.
   *   All args can be accessed by their name as a keyword.
   *   In other words (define* (hi (a 1)) ...) is the same as (define* (hi :key (a 1)) ...) etc.
   *
   * all args are optional, any arg with no default value defaults to #f.
   *   but the rest arg should default to ().
   * I later decided to add two warnings: if a parameter is set twice and if
   *   an unknown keyword is seen in a keyword position and there is no rest arg.
   *
   * :key and :optional are just noise words, so these have already been spliced out of the arg list
   */

  bool allow_other_keys = false;
  s7_pointer lx, cx, zx;

  /* get the current args, re-setting args that have explicit values */
  cx = closure_args(sc->code);
  lx = sc->args; 

  zx = sc->NIL;
  while ((is_pair(cx)) &&
	 (is_pair(lx)))
    {
      if (car(cx) == sc->KEY_ALLOW_OTHER_KEYS)
	{
	  allow_other_keys = true;
	  cx = cdr(cx);
	}
      else
	{
	  if (car(cx) == sc->KEY_REST)           /* the rest arg */
	    {
	      /* next arg is bound to trailing args from this point as a list */
	      zx = sc->KEY_REST;
	      cx = cdr(cx);
	      
	      if (is_pair(car(cx)))
		lambda_star_argument_set_value(sc, caar(cx), lx);
	      else lambda_star_argument_set_value(sc, car(cx), lx);
	      
	      lx = cdr(lx);
	      cx = cdr(cx);
	      if (car(cx) == sc->KEY_ALLOW_OTHER_KEYS)
		break;
	    }
	  else
	    {
	      /* mock-symbols introduce an ambiguity here; if the object's value is a keyword, is that
	       *   intended to be used as an argument name or value?
	       */
	      s7_pointer car_lx;
	      car_lx = car(lx);
	      if (has_methods(car_lx))
		car_lx = check_values(sc, car_lx, lx);
	      if ((is_pair(cdr(lx))) &&
		  (is_keyword(car_lx)))
		{
		  /* char *name; */                      /* found a keyword, check the lambda args via the corresponding symbol */
		  s7_pointer sym;
		  sym = keyword_symbol(car_lx);
		  
		  if (lambda_star_argument_set_value(sc, sym, car(cdr(lx))) == sc->NO_VALUE)
		    {
		      /* if default value is a key, go ahead and use this value.
		       *    (define* (f (a :b)) a) (f :c) 
		       * this has become much trickier than I anticipated...
		       */
		      
		      if ((allow_other_keys) ||
			  (direct_memq(sc->KEY_ALLOW_OTHER_KEYS, cx)))
			{
			  allow_other_keys = true;
			  /* in CL: (defun hi (&key (a 1) &allow-other-keys) a) (hi :b :a :a 3) -> 3 
			   * in s7: (define* (hi (a 1) :allow-other-keys) a)    (hi :b :a :a 3) -> 3
			   */
			  lx = cddr(lx);
			  continue;
			}
		      else
			{
			  if ((is_pair(car(cx))) &&
			      (is_keyword(cadar(cx))))
			    {
			      /* cx is the closure args list, not the copy of it in the curlet */
			      s7_pointer x;
			      
			      x = find_symbol(sc, caar(cx));
			      if (is_slot(x))
				{
				  if (is_not_checked(x))
				    {
				      set_checked(x);
				      slot_set_value(x, car(lx));
				    }
				  else 
				    {
				      /* this case is not caught yet: ((lambda* (a :optional b :allow-other-keys ) a) :b 1 :c :a :a ) */
				      return(s7_error(sc, sc->WRONG_TYPE_ARG,
						      list_4(sc, make_string_wrapper(sc, "~A: parameter set twice, ~S in ~S"),
							     closure_name(sc, sc->code), lx, sc->args)));
				    }
				}
			      else
				{
				  return(s7_error(sc, sc->WRONG_TYPE_ARG,
						  list_4(sc, make_string_wrapper(sc, "~A: unknown key: ~S in ~S"),
							 closure_name(sc, sc->code), lx, sc->args)));
				}
			      /* (define* (f a (b :c)) b) (f :b 1 :d) */
			    }
			  else
			    {
			      return(s7_error(sc, sc->WRONG_TYPE_ARG,
					      list_4(sc, make_string_wrapper(sc, "~A: unknown key: ~S in ~S"),
						     closure_name(sc, sc->code), lx, sc->args)));
			    }
			}
		    }
		  lx = cdr(lx);
		  if (is_pair(lx)) lx = cdr(lx);
		}
	      else                                  /* not a key/value pair */
		{
		  /* this is always a positional (i.e. direct) change, but the closure_args are in the
		   *   definition order whereas currently the environment slots are in reverse order.
		   */
		  if (is_pair(car(cx)))
		    lambda_star_argument_set_value(sc, caar(cx), car(lx));
		  else lambda_star_argument_set_value(sc, car(cx), car(lx));

		  lx = cdr(lx);
		}
	      cx = cdr(cx);
	    }
	}
    }

  /* (let () (define* (hi (a 1) :allow-other-keys) a) (hi :a 2 32)) */
  /* (let () (define* (f (a :b)) a) (list (f) (f 1) (f :c) (f :a :c) (f :a 1) (f))) */

  /* check for trailing args with no :rest arg */
  if (is_not_null(lx))
    {
      if ((is_not_null(cx)) ||
	  (zx == sc->KEY_REST))
	{
	  if (is_symbol(cx))
	    add_slot(sc, cx, lx); 
	}
      else
	{
	  if (!allow_other_keys)                       /* ((lambda* (a) a) :a 1 2) */
	    return(s7_error(sc, sc->WRONG_NUMBER_OF_ARGS, 
			    list_3(sc, sc->TOO_MANY_ARGUMENTS, closure_name(sc, sc->code), sc->args)));
	  else
	    {
	      /* check trailing args for repeated keys or keys with no values or values with no keys */
	      while (is_pair(lx))
		{
		  if ((!is_keyword(car(lx))) ||     /* ((lambda* (a :allow-other-keys) a) :a 1 :b 2 3) */
		      (!is_pair(cdr(lx))))          /* ((lambda* (a :allow-other-keys) a) :a 1 :b) */
		    return(s7_error(sc, sc->WRONG_TYPE_ARG,
				    list_3(sc, make_string_wrapper(sc, "~A: not a key/value pair: ~S"), closure_name(sc, sc->code), lx)));
		  /* errors not caught? 
		   *    ((lambda* (a :allow-other-keys) a) :a 1 :a 2)
		   *    ((lambda* (:allow-other-keys ) #f) :b :a :a :b)
		   *    ((lambda* (:key b :allow-other-keys ) b) 1 :b 2)
		   */
		  lx = cddr(lx);
		}
	    }
	} 
    }
  return(sc->NIL);
}


static s7_pointer find_symbol_unchecked(s7_scheme *sc, s7_pointer symbol) 
{
  s7_pointer x;

  if (let_id(sc->envir) == symbol_id(symbol))
    return(slot_value(local_slot(symbol)));

  for (x = sc->envir; symbol_id(symbol) < let_id(x); x = outlet(x));

  /* this looks redundant, but every attempt to improve it is much slower! */
  if (let_id(x) == symbol_id(symbol))
    return(slot_value(local_slot(symbol)));

  for (; is_let(x); x = outlet(x))
    {
      s7_pointer y;
      for (y = let_slots(x); is_slot(y); y = next_slot(y))
	if (slot_symbol(y) == symbol)
	  return(slot_value(y));
    }

  x = global_slot(symbol);
  if (is_slot(x)) 
    return(slot_value(x));

#if WITH_GCC
  return(NULL);
#else
  return(unbound_variable(sc, symbol));
#endif
}


#define is_h_safe_c_s(P) ((is_optimized(P)) && (optimize_data(P) == HOP_SAFE_C_S))
#define is_safe_c_s(P)   ((is_optimized(P)) && (op_no_hop(P) == OP_SAFE_C_S))

static bool is_h_optimized(s7_pointer p)
{
  return((is_optimized(p)) &&
	 ((optimize_data(p) & 1) != 0) &&
	 (!is_unknown_op(optimize_data(p))));
}


static s7_pointer is_pair_car, is_pair_cdr, is_pair_cadr;
static s7_pointer g_is_pair_car(s7_scheme *sc, s7_pointer args) 
{
  s7_pointer val;
  val = find_symbol_checked(sc, cadar(args));
  if (!is_pair(val))                                               /* (define (tst) (let ((a 123)) (pair? (car a)))) */
    return(g_is_pair(sc, list_1(sc, g_car(sc, list_1(sc, val)))));
  return(make_boolean(sc, is_pair(car(val))));
}

static s7_pointer g_is_pair_cdr(s7_scheme *sc, s7_pointer args) 
{
  s7_pointer val;
  val = find_symbol_checked(sc, cadar(args));
  if (!is_pair(val))
    return(g_is_pair(sc, list_1(sc, g_cdr(sc, list_1(sc, val))))); 
  return(make_boolean(sc, is_pair(cdr(val))));
}

static s7_pointer g_is_pair_cadr(s7_scheme *sc, s7_pointer args) 
{
  s7_pointer val;
  val = find_symbol_checked(sc, cadar(args));
  if (!is_pair(val))
    return(g_is_pair(sc, list_1(sc, g_cadr(sc, list_1(sc, val))))); 
  return(make_boolean(sc, is_pair(cadr(val))));
}

static s7_pointer is_pair_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if (is_optimized(cadr(expr)))
    {
      if (optimize_data(cadr(expr)) == HOP_SAFE_C_S)
	{
	  s7_function g;
	  g = c_call(cadr(expr));
	  if (g == g_car)
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(is_pair_car);
	    }
	  if (g == g_cdr)
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(is_pair_cdr);
	    }
	  if (g == g_cadr)
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(is_pair_cadr);
	    }
	}
    }
  return(f);
}

static s7_pointer is_null_cdr;
static s7_pointer g_is_null_cdr(s7_scheme *sc, s7_pointer args) 
{
  s7_pointer val;
  val = find_symbol_checked(sc, cadar(args));
  if (!is_pair(val))
    return(g_is_null(sc, list_1(sc, g_cdr(sc, list_1(sc, val))))); 
  return(make_boolean(sc, is_null(cdr(val))));
}

static s7_pointer is_null_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if ((is_optimized(cadr(expr))) &&
      (optimize_data(cadr(expr)) == HOP_SAFE_C_S))
    {
      s7_function g;
      g = c_call(cadr(expr));
      if (g == g_cdr)
	{
	  set_optimize_data(expr, HOP_SAFE_C_C);
	  return(is_null_cdr);
	}
    }
  return(f);
}

static s7_pointer cdr_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  return(f);
}

static s7_pointer car_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  return(f);
}


static s7_pointer format_allg, format_allg_no_column, format_just_newline;
static s7_pointer g_format_allg(s7_scheme *sc, s7_pointer args)
{
  return(g_format_1(sc, args));
}

static s7_pointer g_format_just_newline(s7_scheme *sc, s7_pointer args)
{
  s7_pointer pt, str;

  pt = car(args);
  str = cadr(args);

  if (pt == sc->F)
    return(s7_make_string_with_length(sc, string_value(str), string_length(str)));

  if (pt == sc->T)
    {
      port_write_string(sc->output_port)(sc, string_value(str), string_length(str), sc->output_port);
      return(s7_make_string_with_length(sc, string_value(str), string_length(str)));
    }

  if ((!is_output_port(pt)) ||    
      (port_is_closed(pt)))
    {
      check_method(sc, pt, sc->FORMAT, args);
      return(wrong_type_argument_with_type(sc, sc->FORMAT, small_int(1), pt, A_FORMAT_PORT));
    }
  port_write_string(pt)(sc, string_value(str), string_length(str), pt);
  return(sc->F);
}


static s7_pointer g_format_allg_no_column(s7_scheme *sc, s7_pointer args)
{
  s7_pointer pt;
  pt = car(args);
 
  if (!((s7_is_boolean(pt)) ||
	((is_output_port(pt)) &&             /* (current-output-port) or call-with-open-file arg, etc */
	 (!port_is_closed(pt)))))
    {
      check_method(sc, pt, sc->FORMAT, args);
      return(wrong_type_argument_with_type(sc, sc->FORMAT, small_int(1), pt, A_FORMAT_PORT));
    }
  sc->format_column = 0;
  return(format_to_port_1(sc, (pt == sc->T) ? sc->output_port : pt, 
			  string_value(cadr(args)), cddr(args), NULL,
			  !is_output_port(pt),   /* i.e. is boolean port so we're returning a string */
			  false,                 /* we checked in advance that it is not columnized */
			  string_length(cadr(args))));
}


static s7_pointer format_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  s7_pointer port, str_arg;
  port = cadr(expr);
  str_arg = caddr(expr);
  if ((args > 1) &&
      (!is_string(port)) &&
      (is_string(str_arg)))
    {
      if (args == 2) 
	{
	  int len;
	  char *orig;
	  const char *p;

	  orig = string_value(str_arg);
	  p = strchr((const char *)orig, (int)'~');
	  if (!p)
	    {
	      if (s7_is_boolean(port))
		set_optimize_data(expr, HOP_SAFE_C_C);
	      return(format_just_newline);
	    }

	  len = string_length(str_arg);
	  if ((len > 1) &&
	      (orig[len - 1] == '%') &&
	      ((p - orig) == len - 2))
	    {
	      orig[len - 2] = '\n';
	      orig[len - 1] = '\0';
	      string_length(str_arg) = len - 1;
	      if (s7_is_boolean(port))
		set_optimize_data(expr, HOP_SAFE_C_C);
	      return(format_just_newline);
	    }
	}

      /* this used to worry about optimized expr and particular cases -- why? I can't find a broken case */
      if (!is_columnizing(string_value(str_arg)))
	return(format_allg_no_column);
      return(format_allg);
    }
  return(f);
}

static s7_pointer is_eq_car, is_eq_car_q, is_eq_caar_q;
static s7_pointer g_is_eq_car(s7_scheme *sc, s7_pointer args) 
{
  s7_pointer lst, val;
  lst = find_symbol_checked(sc, cadar(args));
  val = find_symbol_checked(sc, cadr(args));
  if (!is_pair(lst))
    return(g_is_eq(sc, list_2(sc, g_car(sc, list_1(sc, lst)), val)));
  return(make_boolean(sc, car(lst) == val));
}

static s7_pointer g_is_eq_car_q(s7_scheme *sc, s7_pointer args) 
{
  s7_pointer lst;
  lst = find_symbol_checked(sc, cadar(args));
  if (!is_pair(lst))
    return(g_is_eq(sc, list_2(sc, g_car(sc, list_1(sc, lst)), cadr(cadr(args)))));
  return(make_boolean(sc, car(lst) == cadr(cadr(args))));
}

static s7_pointer g_is_eq_caar_q(s7_scheme *sc, s7_pointer args) 
{
  s7_pointer lst;
  lst = find_symbol_checked(sc, cadar(args));
  if (!is_pair(lst))
    return(g_is_eq(sc, list_2(sc, g_caar(sc, list_1(sc, lst)), cadr(cadr(args)))));
  return(make_boolean(sc, caar(lst) == cadr(cadr(args))));
}

static s7_pointer is_eq_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if ((is_optimized(cadr(expr))) &&
      (optimize_data(cadr(expr)) == HOP_SAFE_C_S))
    {
      if ((is_symbol(caddr(expr))) &&
	  (c_call(cadr(expr)) == g_car))
	{
	  set_optimize_data(expr, HOP_SAFE_C_C);
	  return(is_eq_car);
	}
      if ((is_pair(caddr(expr))) &&
	  (caaddr(expr) == sc->QUOTE))
	{
	  if (c_call(cadr(expr)) == g_car)
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(is_eq_car_q);
	    }
	  if (c_call(cadr(expr)) == g_caar)
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(is_eq_caar_q);
	    }
	}
    }
  return(f);
}


/* also not-chooser for all the ? procs, ss case for not equal? etc
 */
static s7_pointer not_is_pair, not_is_symbol, not_is_null, not_is_list, not_is_number, not_is_real, not_is_rational, not_is_integer;
static s7_pointer not_is_boolean, not_is_char, not_is_string, not_is_eof, not_is_zero;
static s7_pointer not_is_eq_sq, not_is_eq_ss;

static s7_pointer g_not_is_pair(s7_scheme *sc, s7_pointer args) {check_boolean_not_method(sc, is_pair, sc->IS_PAIR, args);}
static s7_pointer g_not_is_null(s7_scheme *sc, s7_pointer args) {check_boolean_not_method(sc, is_null, sc->IS_NULL, args);}
static s7_pointer g_not_is_symbol(s7_scheme *sc, s7_pointer args) {check_boolean_not_method(sc, is_symbol, sc->IS_SYMBOL, args);}
static s7_pointer g_not_is_number(s7_scheme *sc, s7_pointer args) {check_boolean_not_method(sc, s7_is_number, sc->IS_NUMBER, args);}
static s7_pointer g_not_is_real(s7_scheme *sc, s7_pointer args) {check_boolean_not_method(sc, s7_is_real, sc->IS_REAL, args);}
static s7_pointer g_not_is_integer(s7_scheme *sc, s7_pointer args) {check_boolean_not_method(sc, s7_is_integer, sc->IS_INTEGER, args);}
static s7_pointer g_not_is_rational(s7_scheme *sc, s7_pointer args) {check_boolean_not_method(sc, s7_is_rational, sc->IS_RATIONAL, args);}
static s7_pointer g_not_is_boolean(s7_scheme *sc, s7_pointer args) {check_boolean_not_method(sc, s7_is_boolean, sc->IS_BOOLEAN, args);}
static s7_pointer g_not_is_char(s7_scheme *sc, s7_pointer args) {check_boolean_not_method(sc, s7_is_character, sc->IS_CHAR, args);}
static s7_pointer g_not_is_string(s7_scheme *sc, s7_pointer args) {check_boolean_not_method(sc, is_string, sc->IS_STRING, args);}
static s7_pointer g_not_is_eof(s7_scheme *sc, s7_pointer args) {check_boolean_not_method(sc, is_eof, sc->IS_EOF_OBJECT, args);}
static s7_pointer g_not_is_zero(s7_scheme *sc, s7_pointer args) {check_boolean_not_method(sc, s7_is_zero, sc->IS_ZERO, args);}
#define is_normal_list(p) is_proper_list(sc, p)
static s7_pointer g_not_is_list(s7_scheme *sc, s7_pointer args) {check_boolean_not_method(sc, is_normal_list, sc->IS_LIST, args);}

/* eq? does not check for methods */
static s7_pointer g_not_is_eq_sq(s7_scheme *sc, s7_pointer args) 
{
  return(make_boolean(sc, find_symbol_checked(sc, cadr(car(args))) != cadr(caddr(car(args)))));
}

static s7_pointer g_not_is_eq_ss(s7_scheme *sc, s7_pointer args) 
{
  return(make_boolean(sc, find_symbol_checked(sc, cadr(car(args))) != find_symbol_checked(sc, caddr(car(args)))));
}

/* here the method finder is in either car or cdr */
static s7_pointer not_is_pair_car;
static s7_pointer g_not_is_pair_car(s7_scheme *sc, s7_pointer args) 
{
  s7_pointer val;
  val = find_symbol_checked(sc, cadr(cadar(args)));
  if (!is_pair(val))
    return(g_not(sc, list_1(sc, g_is_pair(sc, list_1(sc, g_car(sc, list_1(sc, val)))))));
  return(make_boolean(sc, !is_pair(car(val))));
}

static s7_pointer not_is_null_cdr;
static s7_pointer g_not_is_null_cdr(s7_scheme *sc, s7_pointer args) 
{
  s7_pointer val;
  val = find_symbol_checked(sc, cadr(cadar(args)));
  if (!is_pair(val))
    return(g_not(sc, list_1(sc, g_is_null(sc, list_1(sc, g_cdr(sc, list_1(sc, val)))))));
  return(make_boolean(sc, !is_null(cdr(val))));
}

static s7_pointer not_c_c;
static s7_pointer g_not_c_c(s7_scheme *sc, s7_pointer args) 
{
  /* args: ( (null? l) ) */
  return(make_boolean(sc, is_false(sc, c_call(car(args))(sc, cdar(args)))));
}

static s7_pointer not_chooser(s7_scheme *sc, s7_pointer g, int args, s7_pointer expr)
{
  if (is_optimized(cadr(expr))) /* cadr(expr) might be a symbol, for example; is_optimized includes is_pair */
    {
      if (optimize_data(cadr(expr)) == HOP_SAFE_C_S)
	{
	  s7_function f;
	  f = c_call(cadr(expr));
	  
	  if (f == g_is_pair)
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(not_is_pair);
	    }
	  if (f == g_is_null)
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(not_is_null);
	    }
	  if (f == g_is_symbol)
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(not_is_symbol);
	    }
	  if (f == g_is_list)
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(not_is_list);
	    }

	  /* g_is_number is c_function_call(slot_value(global_slot(sc->IS_NUMBER)))
	   *   so if this is changed (via openlet??) the latter is perhaps better??
	   * but user might have (#_number? e), so we can't change later and catch this.
	   */

	  if ((f == g_is_number) || (f == g_is_complex))
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(not_is_number);
	    }
	  if (f == g_is_rational)
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(not_is_rational);
	    }
	  if (f == g_is_real)
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(not_is_real);
	    }
	  if (f == g_is_integer)
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(not_is_integer);
	    }
	  if (f == g_is_zero)
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(not_is_zero);
	    }
	  if (f == g_is_char)
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(not_is_char);
	    }
	  if (f == g_is_boolean)
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(not_is_boolean);
	    }
	  if (f == g_is_string)
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(not_is_string);
	    }
	  if (f == g_is_eof_object)
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(not_is_eof);
	    }
	}

      if ((optimize_data(cadr(expr)) == HOP_SAFE_C_SQ) &&
	  (c_call(cadr(expr)) == g_is_eq))
	{
	  set_optimize_data(expr, HOP_SAFE_C_C);
	  return(not_is_eq_sq);
	}

      if (optimize_data(cadr(expr)) == HOP_SAFE_C_SS)
	{
	  if (c_call(cadr(expr)) == g_is_eq)
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(not_is_eq_ss);
	    }
	}

      if (optimize_data(cadr(expr)) == HOP_SAFE_C_C)
	{
	  set_optimize_data(expr, HOP_SAFE_C_C);
	  if (c_call(cadr(expr)) == g_is_pair_car)
	    return(not_is_pair_car);
	  if (c_call(cadr(expr)) == g_is_null_cdr)
	    return(not_is_null_cdr);
	  return(not_c_c);
	}
    }
  return(g);
}


static s7_pointer vector_ref_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if (args == 2)
    {
      if (is_symbol(cadr(expr)))
	{
	  if ((s7_is_integer(caddr(expr))) &&
	      (s7_integer(caddr(expr)) >= 0))
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(vector_ref_ic);
	    }

	  if (is_global(cadr(expr)))
	    {
	      if (is_symbol(caddr(expr)))
		{
		  set_optimize_data(expr, HOP_SAFE_C_C);
		  return(vector_ref_gs);

		  /* (define global_vector (vector 1 2 3))
		   * (define (hi i) (vector-ref global_vector i))
		   */
		}
	    }

	  if ((is_pair(caddr(expr))) &&
	      (is_safely_optimized(caddr(expr))) &&
	      (c_call(caddr(expr)) == g_add_cs1))
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(vector_ref_add1);
	      /* vector_ref_sub1 was not worth the code, and few other easily optimized expressions happen here */
	    }
	}
      return(vector_ref_2);
    }
  return(f);
}


static s7_pointer vector_set_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if (args == 3)
    {
      s7_pointer arg1, arg2, arg3;

      arg1 = cadr(expr);
      arg2 = caddr(expr);
      arg3 = cadddr(expr);
      
      if (is_symbol(arg1))
	{
	  if ((s7_is_integer(arg2)) &&
	      (s7_integer(arg2) >= 0) &&
	      (is_symbol(arg3)))
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(vector_set_ic);
	    }
	  if (is_symbol(arg2))
	    {
	      if ((is_pair(arg3)) &&
		  (is_safely_optimized(arg3)))
		{
		  if ((c_call(arg3) == g_vector_ref_2) &&
		      (arg1 == cadr(arg3)) &&
		      (is_symbol(caddr(arg3))))
		    {
		      set_optimize_data(expr, HOP_SAFE_C_C);
		      return(vector_set_vref);
		    }
		  if (((c_call(arg3) == g_add_2) || (c_call(arg3) == g_subtract_2)) &&
		      (is_symbol(caddr(arg3))) &&
		      (is_optimized(cadr(arg3))) &&
		      (c_call(cadr(arg3)) == g_vector_ref_2) &&
		      (cadr(cadr(arg3)) == arg1))
		    {
		      set_optimize_data(expr, HOP_SAFE_C_C);
		      return(vector_set_vector_ref);
		    }
		}
	    }
	}
      return(vector_set_3);
    }
  return(f);
}


static s7_pointer list_set_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if ((args == 3) &&
      (s7_is_integer(caddr(expr))) &&
      (s7_integer(caddr(expr)) >= 0) &&
      (s7_integer(caddr(expr)) < MAX_LIST_LENGTH))
    return(list_set_ic);
  return(f);
}


static s7_pointer list_ref_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if ((args == 2) &&
      (s7_is_integer(caddr(expr))) &&
      (s7_integer(caddr(expr)) >= 0) &&
      (s7_integer(caddr(expr)) < MAX_LIST_LENGTH))
    return(list_ref_ic);
  return(f);
}


static s7_pointer hash_table_ref_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if (args == 2)
    {
      if ((is_symbol(cadr(expr))) &&
	  (is_symbol(caddr(expr))))
	{
	  set_optimize_data(expr, HOP_SAFE_C_C);
	  return(hash_table_ref_ss);
	}
      if ((is_symbol(cadr(expr))) &&
	  (is_optimized(caddr(expr))) &&
	  (optimize_data(caddr(expr)) == HOP_SAFE_C_S) &&
	  (c_call(caddr(expr)) == g_car))
	{
	  set_optimize_data(expr, HOP_SAFE_C_C);
	  return(hash_table_ref_car);
	}
      return(hash_table_ref_2);
    }
  return(f);
}


#if (!WITH_GMP)
static s7_pointer modulo_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if ((args == 2) &&
      (is_symbol(cadr(expr))) &&
      (is_integer(caddr(expr))) &&
      (integer(caddr(expr)) > 1))
    {
      set_optimize_data(expr, HOP_SAFE_C_C);
      return(mod_si);
    }
  return(f);
}

static s7_pointer abs_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  s7_pointer arg;
  arg = cadr(expr);
  if ((is_optimized(arg)) &&
      (s7_function_returns_temp(sc, arg)))
    {
      if (optimize_data(arg) == HOP_SAFE_C_C)
	{
	  set_optimize_data(expr, HOP_SAFE_C_C);
	  return(abs_direct);
	}
    }
  return(f);
}
#endif


static s7_pointer add_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  /* (+ s f) (+ (* s s) s) (+ s s) (+ s (* s s))
   */
  if (args == 1)
    return(add_1);

#if (!WITH_GMP)
  if (args == 2)
    {
      s7_pointer arg1, arg2;
      arg1 = cadr(expr);
      if (arg1 == small_int(1))
	return(add_1s);

      arg2 = caddr(expr);
      if (arg2 == small_int(1))
	{
	  if (is_symbol(arg1))
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(add_cs1);
	    }
	  return(add_s1);
	}

      if ((s7_is_integer(arg2)) &&
	  (integer_length(integer(arg2)) < 31))
	{
	  if (is_symbol(arg1))
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(add_si);
	    }
	  if ((is_pair(arg1)) &&
	      (is_safely_optimized(arg1)) &&
	      (c_call(arg1) == g_multiply_si))
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(add_si_i);
	    }
	}

      if ((s7_is_integer(arg1)) &&
	  (integer_length(integer(arg1)) < 31) &&
	  (is_symbol(arg2)))
	{
	  set_optimize_data(expr, HOP_SAFE_C_C);
	  return(add_is);
	}

      if ((type(arg2) == T_REAL) &&
	  (is_symbol(arg1)))
	{
	  set_optimize_data(expr, HOP_SAFE_C_C);
	  return(add_sf);
	}

      if (type(arg1) == T_REAL)
	{
	  if (is_symbol(arg2))
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(add_fs);
	    }
	  if ((is_optimized(arg2)) &&
	      (optimize_data(arg2) == HOP_SAFE_C_C) &&
	      (fcdr(arg2) == (s7_pointer)g_multiply_sf))
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(add_f_sf);
	    }
	}

      if ((is_symbol(arg1)) &&
	  (is_optimized(arg2)) &&
	  (optimize_data(arg2) == HOP_SAFE_C_C) &&
	  (s7_function_returns_temp(sc, arg2)))
	{
	  set_optimize_data(expr, HOP_SAFE_C_C);
	  return(add_s_direct);
	}

      if ((is_optimized(arg1)) &&
	  (is_optimized(arg2)))
	{
	  if ((optimize_data(arg1) == HOP_SAFE_C_SS) &&
	      (optimize_data(arg2) == HOP_SAFE_C_C) &&
	      (fcdr(arg1) == (s7_pointer)g_multiply_2) &&
	      (fcdr(arg2) == (s7_pointer)g_mul_1ss) &&
	      (cadr(arg1) == caddr(cadr(arg2))))
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      set_ecdr(cdr(expr), caddr(arg1));
	      set_fcdr(cdr(expr), caddr(arg2));
	      return(add_ss_1ss);
	    }

	  if ((s7_function_returns_temp(sc, arg1)) &&
	      (s7_function_returns_temp(sc, arg2)))
	    return(add_2_temp);
	}

      if ((is_optimized(arg2)) &&
	  (s7_function_returns_temp(sc, arg2)))
	return(add_s_temp);

      if ((is_optimized(arg1)) &&
	  (s7_function_returns_temp(sc, arg1)))
	return(add_temp_s);

      return(add_2);
    }

  if (args == 3)
    {
      s7_pointer arg1, arg2, arg3;
      arg1 = cadr(expr);
      arg2 = caddr(expr);
      arg3 = cadddr(expr);

      if ((is_optimized(arg1)) &&
	  (is_optimized(arg2)) &&
	  (is_optimized(arg3)) &&
	  (s7_function_returns_temp(sc, arg1)) &&
	  (s7_function_returns_temp(sc, arg2)) &&
	  (s7_function_returns_temp(sc, arg3)))
	return(add_3_temp);
    }
#endif
  return(f);
}


static s7_pointer multiply_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  /* it's possible to precompute constant multiplies, use OP_CONSTANT as the optimize_data, 
   *    put the value in fcdr, remove it from the heap, return NULL (checking for ecdr null in 
   *    set_c_function and c_function_is_ok), then simply return fcdr in OP_CONSTANT, but
   *    that sort of thing almost never happens in real code.
   */
#if (!WITH_GMP)
  if (args == 2)
    {
      s7_pointer arg1, arg2;
      arg1 = cadr(expr);
      arg2 = caddr(expr);

      if ((is_symbol(arg1)) &&
	  (s7_is_integer(arg2)) &&
	  (integer_length(integer(arg2)) < 31))
	{
	  set_optimize_data(expr, HOP_SAFE_C_C);
	  return(multiply_si);
	}

      if ((is_symbol(arg2)) &&
	  (s7_is_integer(arg1)) &&
	  (integer_length(integer(arg1)) < 31))
	{
	  set_optimize_data(expr, HOP_SAFE_C_C);
	  return(multiply_is);
	}

      if ((type(arg1) == T_REAL) &&
	  (is_symbol(arg2)))
	{
	  set_optimize_data(expr, HOP_SAFE_C_C);
	  return(multiply_fs);
	}
      if ((type(arg2) == T_REAL) &&
	  (is_symbol(arg1)))
	{
	  set_optimize_data(expr, HOP_SAFE_C_C);
	  return(multiply_sf);
	}
      if ((is_symbol(arg1)) &&
	  (arg1 == arg2))
	{
	  set_optimize_data(expr, HOP_SAFE_C_C);
	  return(sqr_ss);
	}

      /* (* direct ...) here saved a little, but I think not worth the extra code */
      
      if ((is_pair(arg1)) &&
	  (is_symbol(arg2)) &&
	  (car(arg1) == sc->MINUS) &&
	  (type(cadr(arg1)) == T_REAL) &&
	  (real(cadr(arg1)) == 1.0) &&
	  (is_symbol(caddr(arg1))) &&
	  (is_null(cdddr(arg1))))
	{
	  set_optimize_data(expr, HOP_SAFE_C_C);
	  return(mul_1ss);
	}

      if ((is_optimized(arg1)) &&
	  (is_optimized(arg2)) &&
	  (s7_function_returns_temp(sc, arg1)) &&
	  (s7_function_returns_temp(sc, arg2)))
	return(multiply_2_temp);

      if ((is_symbol(arg1)) &&
	  (is_optimized(arg2)) &&
	  (optimize_data(arg2) == HOP_SAFE_C_C) &&
	  (s7_function_returns_temp(sc, arg2)))
	{
	  set_optimize_data(expr, HOP_SAFE_C_C);
	  return(multiply_s_direct);
	}

      if ((is_optimized(arg2)) &&
	  (s7_function_returns_temp(sc, arg2)))
	return(multiply_s_temp);

      if ((is_optimized(arg1)) &&
	  (s7_function_returns_temp(sc, arg1)))
	return(multiply_temp_s);

      if ((is_symbol(arg1)) &&
	  (is_optimized(arg2)) &&
	  ((car(arg2) == sc->SIN) || (car(arg2) == sc->COS)) &&
	  (is_symbol(cadr(arg2))))
	{
	  s7_function_choice_set_direct(sc, expr);
	  if (car(arg2) == sc->SIN)
	    return(mul_s_sin_s);
	  return(mul_s_cos_s);
	}

      /* (* c c) -- types are manifest (as is the result...)
       * (* 1.0 x) -> float(x)
       * (* pi x) special too
       * (* 2 (frames)) -- we know both are ints
       */
      return(multiply_2);
    }

  if (args == 3)
    {
      s7_pointer arg1, arg2, arg3;
      arg1 = cadr(expr);
      arg2 = caddr(expr);
      arg3 = cadddr(expr);

      if ((is_optimized(arg1)) &&
	  (is_optimized(arg2)) &&
	  (is_optimized(arg3)) &&
	  (s7_function_returns_temp(sc, arg1)) &&
	  (s7_function_returns_temp(sc, arg2)) &&
	  (s7_function_returns_temp(sc, arg3)))
	return(multiply_3_temp);

      if ((type(arg1) == T_REAL) &&
	  (is_symbol(arg2)) &&
	  (is_pair(arg3)) &&
	  (car(arg3) == sc->COS) &&
	  (is_symbol(cadr(arg3))))
	{
	  set_optimize_data(expr, HOP_SAFE_C_C);
	  return(multiply_cs_cos);
	}
    }

  if (args > 2)
    {
      s7_pointer arg1;

      arg1 = cadr(expr);
      if ((is_optimized(arg1)) &&
      	  (s7_function_returns_temp(sc, arg1)))
	return(multiply_1_any);
    }
#endif
  return(f);
}

/* (define (hi a) (- a 1.0)) (hi (string->number "#e1."))
 */

static s7_pointer subtract_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
#if (!WITH_GMP)
  if (args == 1)
    return(subtract_1);

  if (args == 2)
    {
      s7_pointer arg1, arg2;
      arg1 = cadr(expr);
      arg2 = caddr(expr);

      if (arg2 == small_int(1))
	{
	  if (is_symbol(arg1))
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(subtract_cs1);
	    }
	  return(subtract_s1);
	}

      if (type(arg2) == T_REAL)
	{
	  if (is_symbol(arg1))
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(subtract_sf);
	    }
	  if ((is_pair(arg1)) &&
	      (is_safely_optimized(arg1)) &&
	      (optimize_data(arg1) == HOP_SAFE_C_C) &&
	      (c_call(arg1) == g_multiply_sf))
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(subtract_sf_f);
	    }
	}

      if (type(arg1) == T_REAL)
	{
	  if (is_symbol(arg2))
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(subtract_fs);
	    }
	  if ((is_optimized(arg2)) &&
	      (optimize_data(arg2) == HOP_SAFE_C_C) &&
	      (fcdr(arg2) == (s7_pointer)g_sqr_ss))
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(subtract_f_sqr);
	    }
	}

      if ((s7_is_integer(arg2)) &&
	  (is_symbol(arg1)))
	{
	  set_optimize_data(expr, HOP_SAFE_C_C);
	  return(subtract_csn);
	}

      if ((is_optimized(arg1)) &&
	  (s7_function_returns_temp(sc, arg1)) &&
	  ((type(arg2) == T_REAL) ||
	   ((is_optimized(arg2)) &&
	    (s7_function_returns_temp(sc, arg2)))))
	return(subtract_2_temp); /* this only cares that arg2 is known to be T_REAL */

      if (type(arg2) == T_REAL)
	return(subtract_2f);

      return(subtract_2);
    }
#endif
  return(f);
}


#if (!WITH_GMP)
static s7_pointer divide_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if (args == 1)
    return(invert_1);

  if (args == 2)
    {
      s7_pointer arg1, arg2;
      arg1 = cadr(expr);

      if ((type(arg1) == T_REAL) &&
	  (real(arg1) == 1.0))
	return(divide_1r);

      if ((is_optimized(arg1)) &&
	  (s7_function_returns_temp(sc, arg1)))
	return(divide_temp_s); /* "s" = anything */

      arg2 = caddr(expr);
      if ((is_optimized(arg2)) &&
	  (s7_function_returns_temp(sc, arg2)))
	return(divide_s_temp);
    }
  return(f);
}


static s7_pointer expt_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if (args == 2)
    {
      s7_pointer arg1;
      arg1 = cadr(expr);
      if ((is_optimized(arg1)) &&
	  (s7_function_returns_temp(sc, arg1)))
	return(expt_temp_s);
    }
  return(f);
}
#endif


static s7_pointer max_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
#if (!WITH_GMP)
  if ((args == 2) &&
      (type(cadr(expr)) == T_REAL) &&
      (!is_NaN(real(cadr(expr)))))
    return(max_f2);
#endif
  return(f);
}


static s7_pointer min_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
#if (!WITH_GMP)
  if ((args == 2) &&
      (type(cadr(expr)) == T_REAL) &&
      (!is_NaN(real(cadr(expr)))))
    return(min_f2);
#endif
  return(f);
}


static s7_pointer is_zero_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
#if (!WITH_GMP)
  if ((args == 1) &&
      (is_safely_optimized(cadr(expr))) &&
      (optimize_data(cadr(expr)) == HOP_SAFE_C_C) &&
      (c_call(cadr(expr)) == g_mod_si))
    {
      set_optimize_data(expr, HOP_SAFE_C_C);
      return(mod_si_is_zero);
    }
#endif
  return(f);
}


static s7_pointer equal_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
#if (!WITH_GMP)
  if (args == 2)
    {
      if (s7_is_integer(caddr(expr)))
	{
	  if (is_safely_optimized(cadr(expr)))
	    {
	      s7_function f;
	      f = c_call(cadr(expr));
	      if (f == g_length)
		{
		  if (optimize_data(cadr(expr)) == HOP_SAFE_C_S)
		    {
		      set_optimize_data(expr, HOP_SAFE_C_C);
		      return(equal_length_ic);
		    }
		}
	      if ((f == g_mod_si) &&
		  (integer(caddr(expr)) == 0))
		{
		  set_optimize_data(expr, HOP_SAFE_C_C);
		  return(mod_si_is_zero);
		}
	    }
	  if (is_symbol(cadr(expr)))
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(equal_s_ic);
	    }
	}
      return(equal_2);
    }
#endif
  return(f);
}


#if (!WITH_GMP)
static s7_pointer less_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if (args == 2)
    {
      s7_pointer arg2;
      arg2 = caddr(expr);
      if (is_integer(arg2))
	{
	  if ((is_optimized(cadr(expr))) &&
	      (optimize_data(cadr(expr)) == HOP_SAFE_C_S))
	    {
	      s7_function f;
	      f = c_call(cadr(expr));
	      if (f == g_length)
		{
		  set_optimize_data(expr, HOP_SAFE_C_C);
		  return(less_length_ic);
		}
	    }
	  if (integer(arg2) == 0)
	    return(less_s0);

	  if ((integer(arg2) < S7_LONG_MAX) &&
	      (integer(arg2) > S7_LONG_MIN))
	    return(less_s_ic);
	}
      return(less_2);
    }
  return(f);
}


static s7_pointer leq_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if (args == 2)
    {
      s7_pointer arg2;
      arg2 = caddr(expr);
      if ((is_integer(arg2)) &&
	  (integer(arg2) < S7_LONG_MAX) &&
	  (integer(arg2) > S7_LONG_MIN))
	return(leq_s_ic);
      return(leq_2);
    }
  return(f);
}


static s7_pointer greater_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if (args == 2)
    {
      s7_pointer arg2;
      arg2 = caddr(expr);
      if ((is_integer(arg2)) &&
	  (integer(arg2) < S7_LONG_MAX) &&
	  (integer(arg2) > S7_LONG_MIN))
	return(greater_s_ic);
      if ((type(arg2) == T_REAL) &&
	  (real(arg2) < S7_LONG_MAX) &&
	  (real(arg2) > S7_LONG_MIN))
	return(greater_s_fc);
      return(greater_2);
    }
  return(f);
}


static s7_pointer geq_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if (args == 2)
    {
      s7_pointer arg2;
      arg2 = caddr(expr);
      if (is_integer(arg2))
	{
	  if ((is_optimized(cadr(expr))) &&
	      (optimize_data(cadr(expr)) == HOP_SAFE_C_S))
	    {
	      s7_function f;
	      f = c_call(cadr(expr));
	      if (f == g_length)
		{
		  set_optimize_data(expr, HOP_SAFE_C_C);
		  return(geq_length_ic);
		}
	    }
	  if ((integer(arg2) < S7_LONG_MAX) &&
	      (integer(arg2) > S7_LONG_MIN))
	    return(geq_s_ic);
	}
      if ((type(arg2) == T_REAL) &&
	  (real(arg2) < S7_LONG_MAX) &&
	  (real(arg2) > S7_LONG_MIN))
	return(geq_s_fc);

      return(geq_2);
    }
  return(f);
}


static s7_pointer is_negative_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if (args == 1)
    {
      if ((is_optimized(cadr(expr))) &&
	  (optimize_data(cadr(expr)) == HOP_SAFE_C_S))
	{
	  s7_function f;
	  f = c_call(cadr(expr));
	  if (f == g_length)
	    {
	      set_optimize_data(expr, HOP_SAFE_C_C);
	      return(is_negative_length);
	    }
	}
    }
  return(f);
}
#endif
/* end (!WITH_GMP) */


static s7_pointer char_equal_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if (args == 2)
    {
      if ((is_symbol(cadr(expr))) &&
	  (s7_is_character(caddr(expr))))
	{
	  set_optimize_data(expr, HOP_SAFE_C_C);
	  return(char_equal_s_ic);
	}
      return(char_equal_2);
    }
  return(f);
}

static s7_pointer char_less_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if (args == 2)
    {
      if (s7_is_character(caddr(expr)))
	return(char_less_s_ic);
      return(char_less_2);
    }
  return(f);
}

static s7_pointer char_greater_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if (args == 2)
    {
      if (s7_is_character(caddr(expr)))
	return(char_greater_s_ic);
      return(char_greater_2);
    }
  return(f);
}

static s7_pointer char_geq_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if (args == 2)
    {
      if (s7_is_character(caddr(expr)))
	return(char_geq_s_ic);
      return(char_geq_2);
    }
  return(f);
}

static s7_pointer char_leq_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if (args == 2)
    {
      if (s7_is_character(caddr(expr)))
	return(char_leq_s_ic);
      return(char_leq_2);
    }
  return(f);
}

#if (!WITH_PURE_S7)
static s7_pointer char_ci_equal_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if (args == 2)
    {
      if (s7_is_character(caddr(expr)))
	return(char_ci_equal_s_ic);
      return(char_ci_equal_2);
    }
  return(f);
}

static s7_pointer char_ci_less_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if (args == 2)
    {
      if (s7_is_character(caddr(expr)))
	return(char_ci_less_s_ic);
      return(char_ci_less_2);
    }
  return(f);
}

static s7_pointer char_ci_greater_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if (args == 2)
    {
      if (s7_is_character(caddr(expr)))
	return(char_ci_greater_s_ic);
      return(char_ci_greater_2);
    }
  return(f);
}

static s7_pointer char_ci_geq_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if (args == 2)
    {
      if (s7_is_character(caddr(expr)))
	return(char_ci_geq_s_ic);
      return(char_ci_geq_2);
    }
  return(f);
}

static s7_pointer char_ci_leq_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if (args == 2)
    {
      if (s7_is_character(caddr(expr)))
	return(char_ci_leq_s_ic);
      return(char_ci_leq_2);
    }
  return(f);
}
#endif /* not pure s7 */


static void check_for_substring_temp(s7_scheme *sc, s7_pointer expr)
{
  s7_pointer p, np = NULL;
  int pairs = 0;
  /* a bit tricky -- accept temp only if there's just one inner expression and it calls substring */
  for (p = cdr(expr); is_pair(p); p = cdr(p))
    {
      s7_pointer arg;
      arg = car(p);
      if (is_pair(arg))
	{
	  pairs++;
	  if ((is_symbol(car(arg))) &&
	      (is_safely_optimized(arg)))
	    {
	      if (c_call(arg) == g_substring)
		np = arg;

	      if (c_call(arg) == g_symbol_to_string)
		set_c_function(arg, symbol_to_string_uncopied);
	    }
	}
    }
  if ((pairs == 1) && (np)) 
    set_c_function(np, substring_to_temp);
}

static s7_pointer char_position_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  if ((args == 2) &&
      (s7_is_character(cadr(expr))))
    return(char_position_cs);
  return(f);
}

static s7_pointer string_equal_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  check_for_substring_temp(sc, expr);
  if (args == 2)
    {
      if (is_string(caddr(expr)))
	return(string_equal_s_ic); 
      return(string_equal_2);
    }
  return(f);
}

static s7_pointer string_less_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  check_for_substring_temp(sc, expr);
  if (args == 2)
    {
      if (is_string(caddr(expr)))
	return(string_less_s_ic);
      return(string_less_2);
    }
  return(f);
}

static s7_pointer string_greater_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  check_for_substring_temp(sc, expr);
  if (args == 2)
    {
      if (is_string(caddr(expr)))
	return(string_greater_s_ic);
      return(string_greater_2);
    }
  return(f);
}

static s7_pointer string_geq_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  check_for_substring_temp(sc, expr);
  if (args == 2)
    {
      if (is_string(caddr(expr)))
	return(string_geq_s_ic);
      return(string_geq_2);
    }
  return(f);
}

static s7_pointer string_leq_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  check_for_substring_temp(sc, expr);
  if (args == 2)
    {
      if (is_string(caddr(expr)))
	return(string_leq_s_ic);
      return(string_leq_2);
    }
  return(f);
}


static s7_pointer string_to_symbol_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  check_for_substring_temp(sc, expr);
  return(f);
}


#if (!WITH_PURE_S7)
static s7_pointer string_ci_equal_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  check_for_substring_temp(sc, expr);
  if (args == 2)
    {
      if (is_string(caddr(expr)))
	return(string_ci_equal_s_ic);
      return(string_ci_equal_2);
    }
  return(f);
}

static s7_pointer string_ci_less_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  check_for_substring_temp(sc, expr);
  if (args == 2)
    {
      if (is_string(caddr(expr)))
	return(string_ci_less_s_ic);
      return(string_ci_less_2);
    }
  return(f);
}

static s7_pointer string_ci_greater_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  check_for_substring_temp(sc, expr);
  if (args == 2)
    {
      if (is_string(caddr(expr)))
	return(string_ci_greater_s_ic);
      return(string_ci_greater_2);
    }
  return(f);
}

static s7_pointer string_ci_geq_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  check_for_substring_temp(sc, expr);
  if (args == 2)
    {
      if (is_string(caddr(expr)))
	return(string_ci_geq_s_ic);
      return(string_ci_geq_2);
    }
  return(f);
}

static s7_pointer string_ci_leq_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  check_for_substring_temp(sc, expr);
  if (args == 2)
    {
      if (is_string(caddr(expr)))
	return(string_ci_leq_s_ic);
      return(string_ci_leq_2);
    }
  return(f);
}
#endif /* not pure s7 */


static s7_pointer string_ref_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  check_for_substring_temp(sc, expr);
  return(f);
}

static s7_pointer string_set_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  return(f);
}

static s7_pointer string_append_chooser(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr)
{
  check_for_substring_temp(sc, expr);
  return(f);
}


static s7_pointer or_direct;
static s7_pointer g_or_direct(s7_scheme *sc, s7_pointer args)
{
  s7_pointer p, x;
  for (p = args; is_pair(p); p = cdr(p))
    {
      x = car(p);
      if (is_symbol(x))
	x = find_symbol_checked(sc, x);
      if (is_true(sc, x))
	return(x);
    }
  return(sc->F);
}


static s7_pointer and_direct;
static s7_pointer g_and_direct(s7_scheme *sc, s7_pointer args)
{
  s7_pointer p, x;
  x = sc->T;
  for (p = args; is_pair(p); p = cdr(p))
    {
      x = car(p);
      if (is_symbol(x))
	x = find_symbol_checked(sc, x);
      if (is_false(sc, x))
	return(x);
    }
  return(x);
}


static s7_pointer if_direct;
static s7_pointer g_if_direct(s7_scheme *sc, s7_pointer args)
{
  s7_pointer p;
  p = car(args);
  if (is_symbol(p))
    p = find_symbol_checked(sc, p);
  if (is_true(sc, p))
    p = cadr(args);
  else 
    {
      if (!is_null(cddr(args)))
	p = caddr(args);
      else return(sc->UNSPECIFIED);
    }
  if (is_symbol(p))
    return(find_symbol_checked(sc, p));
  return(p);
}


static s7_pointer or_all_x;
static s7_pointer g_or_all_x(s7_scheme *sc, s7_pointer args)
{
  s7_pointer p, x;
  for (p = args; is_pair(p); p = cdr(p))
    {
      x = ((s7_function)fcdr(p))(sc, car(p));
      if (is_true(sc, x))
	return(x);
    }
  return(sc->F);
}


static s7_pointer and_all_x;
static s7_pointer g_and_all_x(s7_scheme *sc, s7_pointer args)
{
  s7_pointer p, x = sc->T;
  for (p = args; is_pair(p); p = cdr(p))
    {
      x = ((s7_function)fcdr(p))(sc, car(p));
      if (is_false(sc, x))
	return(x);
    }
  return(x);
}


static s7_pointer if_all_x1;
static s7_pointer g_if_all_x1(s7_scheme *sc, s7_pointer args)
{
  s7_pointer p;
  if (is_true(sc, ((s7_function)fcdr(args))(sc, car(args))))
    p = cdr(args);
  else return(sc->UNSPECIFIED);
  return(((s7_function)fcdr(p))(sc, car(p)));
}

static s7_pointer if_all_x2;
static s7_pointer g_if_all_x2(s7_scheme *sc, s7_pointer args)
{
  s7_pointer p;
  if (is_true(sc, ((s7_function)fcdr(args))(sc, car(args))))
    p = cdr(args);
  else p = cddr(args);
  return(((s7_function)fcdr(p))(sc, car(p)));
}


static s7_pointer if_all_x_qq;
static s7_pointer g_if_all_x_qq(s7_scheme *sc, s7_pointer args)
{
  if (is_true(sc, ((s7_function)fcdr(args))(sc, car(args))))
    return(cadr(cadr(args)));
  return(cadr(caddr(args)));
}


static s7_pointer if_all_x_qa;
static s7_pointer g_if_all_x_qa(s7_scheme *sc, s7_pointer args)
{
  if (is_true(sc, ((s7_function)fcdr(args))(sc, car(args))))
    return(cadr(cadr(args)));
  return(((s7_function)fcdr(cddr(args)))(sc, caddr(args)));
}


static s7_pointer or_s_direct;
static s7_pointer g_or_s_direct(s7_scheme *sc, s7_pointer args)
{
  s7_pointer p, x;
  car(sc->T1_1) = find_symbol_checked(sc, cadar(args));
  for (p = args; is_pair(p); p = cdr(p))
    {
      x = c_call(car(p))(sc, sc->T1_1);
      if (is_true(sc, x))
	return(x);
    }
  return(sc->F);
}


static s7_pointer and_s_direct;
static s7_pointer g_and_s_direct(s7_scheme *sc, s7_pointer args)
{
  s7_pointer p, x = sc->T;
  car(sc->T1_1) = find_symbol_checked(sc, cadar(args));
  for (p = args; is_pair(p); p = cdr(p))
    {
      x = c_call(car(p))(sc, sc->T1_1);
      if (is_false(sc, x))
	return(x);
    }
  return(x);
}


static s7_pointer if_s_direct;
static s7_pointer g_if_s_direct(s7_scheme *sc, s7_pointer args)
{
  s7_pointer p;
  car(sc->T1_1) = find_symbol_checked(sc, cadar(args));
  if (is_true(sc, c_call(car(args))(sc, sc->T1_1)))
    p = cdr(args);
  else 
    {
      p = cddr(args);
      if (is_null(p))
	return(sc->UNSPECIFIED);
    }
  return(c_call(car(p))(sc, sc->T1_1));
}


static s7_pointer make_function_with_class(s7_scheme *sc, s7_pointer cls, const char *name, s7_function f, int required_args, int optional_args, bool rest_arg, const char *doc)
{
  s7_pointer uf;
  uf = s7_make_function(sc, name, f, required_args, optional_args, rest_arg, doc);
  s7_function_set_class(uf, cls);
  return(uf);
}

static s7_pointer make_temp_function_with_class(s7_scheme *sc, s7_pointer cls, const char *name, s7_function f, int required_args, int optional_args, bool rest_arg, const char *doc)
{
  s7_pointer uf;
  uf = s7_make_function(sc, name, f, required_args, optional_args, rest_arg, doc);
  s7_function_set_class(uf, cls);
  s7_function_set_returns_temp(uf);
  return(uf);
}

static s7_pointer set_function_chooser(s7_scheme *sc, s7_pointer sym, s7_pointer (*chooser)(s7_scheme *sc, s7_pointer f, int args, s7_pointer expr))
{
  s7_pointer f;
  f = slot_value(global_slot(sym));
  c_function_chooser(f) = chooser;
  return(f);
}


static void init_choosers(s7_scheme *sc)
{
  s7_pointer f;

  /* + */
  f = set_function_chooser(sc, sc->ADD, add_chooser);
  sc->add_class = c_function_class(f);

  add_1 = make_function_with_class(sc, f, "+", g_add_1, 1, 0, false, "+ opt");
  add_2 = make_function_with_class(sc, f, "+", g_add_2, 2, 0, false, "+ opt");

  add_2_temp = make_temp_function_with_class(sc, f, "+", g_add_2_temp, 2, 0, false, "+ opt");
  add_s_temp = make_temp_function_with_class(sc, f, "+", g_add_s_temp, 2, 0, false, "+ opt");
  add_s_direct = make_temp_function_with_class(sc, f, "+", g_add_s_direct, 2, 0, false, "+ opt");
  add_temp_s = make_temp_function_with_class(sc, f, "+", g_add_temp_s, 2, 0, false, "+ opt");
  add_3_temp = make_temp_function_with_class(sc, f, "+", g_add_3_temp, 3, 0, false, "+ opt");

  add_1s = make_function_with_class(sc, f, "+", g_add_1s, 2, 0, false, "+ opt");
  add_s1 = make_function_with_class(sc, f, "+", g_add_s1, 2, 0, false, "+ opt");
  add_cs1 = make_function_with_class(sc, f, "+", g_add_cs1, 2, 0, false, "+ opt");
  add_si_i = make_function_with_class(sc, f, "+", g_add_si_i, 2, 0, false, "+ opt");
  add_si = make_function_with_class(sc, f, "+", g_add_si, 2, 0, false, "+ opt");
  add_is = make_function_with_class(sc, f, "+", g_add_is, 2, 0, false, "+ opt");
  add_sf = make_function_with_class(sc, f, "+", g_add_sf, 2, 0, false, "+ opt");
  add_fs = make_function_with_class(sc, f, "+", g_add_fs, 2, 0, false, "+ opt");
  add_ss_1ss = make_temp_function_with_class(sc, f, "+", g_add_ss_1ss, 2, 0, false, "+ opt");
  add_f_sf = make_function_with_class(sc, f, "+", g_add_f_sf, 2, 0, false, "+ opt");

  /* - */
  f = set_function_chooser(sc, sc->MINUS, subtract_chooser);

  subtract_1 = make_function_with_class(sc, f, "-", g_subtract_1, 1, 0, false, "- opt");
  subtract_2 = make_function_with_class(sc, f, "-", g_subtract_2, 2, 0, false, "- opt");
  subtract_s1 = make_function_with_class(sc, f, "-", g_subtract_s1, 2, 0, false, "- opt");
  subtract_cs1 = make_function_with_class(sc, f, "-", g_subtract_cs1, 2, 0, false, "- opt");
  subtract_csn = make_function_with_class(sc, f, "-", g_subtract_csn, 2, 0, false, "- opt");
  subtract_sf = make_function_with_class(sc, f, "-", g_subtract_sf, 2, 0, false, "- opt");
  subtract_2f = make_function_with_class(sc, f, "-", g_subtract_2f, 2, 0, false, "- opt");
  subtract_fs = make_function_with_class(sc, f, "-", g_subtract_fs, 2, 0, false, "- opt");
  subtract_sf_f = make_function_with_class(sc, f, "-", g_subtract_sf_f, 2, 0, false, "- opt");
  subtract_2_temp = make_temp_function_with_class(sc, f, "-", g_subtract_2_temp, 2, 0, false, "- opt");
  subtract_f_sqr = make_function_with_class(sc, f, "-", g_subtract_f_sqr, 2, 0, false, "- opt");

  /* * */
  f = set_function_chooser(sc, sc->MULTIPLY, multiply_chooser);

  multiply_2 = make_function_with_class(sc, f, "*", g_multiply_2, 2, 0, false, "* opt");

  multiply_2_temp = make_temp_function_with_class(sc, f, "*", g_multiply_2_temp, 2, 0, false, "* opt");
  multiply_3_temp = make_temp_function_with_class(sc, f, "*", g_multiply_3_temp, 3, 0, false, "* opt");
  multiply_1_any = make_temp_function_with_class(sc, f, "*", g_multiply_1_any, 3, 0, true, "* opt");
  multiply_s_temp = make_temp_function_with_class(sc, f, "*", g_multiply_s_temp, 2, 0, false, "* opt");
  multiply_temp_s = make_temp_function_with_class(sc, f, "*", g_multiply_temp_s, 2, 0, false, "* opt");
  multiply_s_direct = make_temp_function_with_class(sc, f, "*", g_multiply_s_direct, 2, 0, false, "* opt");

  multiply_is = make_function_with_class(sc, f, "*", g_multiply_is, 2, 0, false, "* opt");
  multiply_si = make_function_with_class(sc, f, "*", g_multiply_si, 2, 0, false, "* opt");
  multiply_fs = make_function_with_class(sc, f, "*", g_multiply_fs, 2, 0, false, "* opt");
  multiply_sf = make_function_with_class(sc, f, "*", g_multiply_sf, 2, 0, false, "* opt");
#if (!WITH_GMP)
  sqr_ss = make_function_with_class(sc, f, "*", g_sqr_ss, 2, 0, false, "* opt");
  mul_1ss = make_function_with_class(sc, f, "*", g_mul_1ss, 2, 0, false, "* opt");
  multiply_cs_cos = make_function_with_class(sc, f, "*", g_multiply_cs_cos, 3, 0, false, "* opt");
  mul_s_sin_s = make_function_with_class(sc, f, "*", g_mul_s_sin_s, 2, 0, false, "* opt");
  mul_s_cos_s = make_function_with_class(sc, f, "*", g_mul_s_cos_s, 2, 0, false, "* opt");
#endif

  /* / */
#if (!WITH_GMP)
  f = set_function_chooser(sc, sc->DIVIDE, divide_chooser);
  invert_1 = make_function_with_class(sc, f, "/", g_invert_1, 1, 0, false, "/ opt");
  divide_1r = make_function_with_class(sc, f, "/", g_divide_1r, 2, 0, false, "/ opt");
  divide_temp_s = make_temp_function_with_class(sc, f, "/", g_divide_temp_s, 2, 0, false, "/ opt");
  divide_s_temp = make_temp_function_with_class(sc, f, "/", g_divide_s_temp, 2, 0, false, "/ opt");
#endif

#if (!WITH_GMP)
  /* modulo */
  f = set_function_chooser(sc, sc->MODULO, modulo_chooser);
  mod_si = make_function_with_class(sc, f, "modulo", g_mod_si, 2, 0, false, "modulo opt");

  /* abs */
  f = set_function_chooser(sc, sc->ABS, abs_chooser);
  abs_direct = make_temp_function_with_class(sc, f, "abs", g_abs_direct, 1, 0, false, "abs opt");

  /* expt */
  f = set_function_chooser(sc, sc->EXPT, expt_chooser);
  expt_temp_s = make_temp_function_with_class(sc, f, "expt", g_expt_temp_s, 2, 0, false, "expt opt");
#endif

  /* max */
  f = set_function_chooser(sc, sc->MAX, max_chooser);
  max_f2 = make_function_with_class(sc, f, "max", g_max_f2, 2, 0, false, "max opt");

  /* min */
  f = set_function_chooser(sc, sc->MIN, min_chooser);
  min_f2 = make_function_with_class(sc, f, "min", g_min_f2, 2, 0, false, "min opt");

  /* zero? */
  set_function_chooser(sc, sc->IS_ZERO, is_zero_chooser);

  /* = */
  f = set_function_chooser(sc, sc->EQ, equal_chooser);
  sc->equal_class = c_function_class(f);

  equal_s_ic = make_function_with_class(sc, f, "=", g_equal_s_ic, 2, 0, false, "= opt");
  equal_length_ic = make_function_with_class(sc, f, "=", g_equal_length_ic, 2, 0, false, "= opt");
  equal_2 = make_function_with_class(sc, f, "=", g_equal_2, 2, 0, false, "= opt");
#if (!WITH_GMP)
  mod_si_is_zero = make_function_with_class(sc, f, "=", g_mod_si_is_zero, 2, 0, false, "= opt");
#endif

#if (!WITH_GMP)
  /* negative? */
  f = set_function_chooser(sc, sc->IS_NEGATIVE, is_negative_chooser);
  is_negative_length = make_function_with_class(sc, f, "negative?", g_is_negative_length, 1, 0, false, "negative? opt");

  /* < */
  f = set_function_chooser(sc, sc->LT, less_chooser);

  less_s_ic = make_function_with_class(sc, f, "<", g_less_s_ic, 2, 0, false, "< opt");
  less_s0 = make_function_with_class(sc, f, "<", g_less_s0, 2, 0, false, "< opt");
  less_2 = make_function_with_class(sc, f, "<", g_less_2, 2, 0, false, "< opt");
  less_length_ic = make_function_with_class(sc, f, "<", g_less_length_ic, 2, 0, false, "< opt");

  /* > */
  f = set_function_chooser(sc, sc->GT, greater_chooser);

  greater_s_ic = make_function_with_class(sc, f, ">", g_greater_s_ic, 2, 0, false, "> opt");
  greater_s_fc = make_function_with_class(sc, f, ">", g_greater_s_fc, 2, 0, false, "> opt");
  greater_2 = make_function_with_class(sc, f, ">", g_greater_2, 2, 0, false, "> opt");

  /* <= */
  f = set_function_chooser(sc, sc->LEQ, leq_chooser);

  leq_s_ic = make_function_with_class(sc, f, "<=", g_leq_s_ic, 2, 0, false, "<= opt");
  leq_2 = make_function_with_class(sc, f, "<=", g_leq_2, 2, 0, false, "<= opt");

  /* >= */
  f = set_function_chooser(sc, sc->GEQ, geq_chooser);

  geq_s_ic = make_function_with_class(sc, f, ">=", g_geq_s_ic, 2, 0, false, ">= opt");
  geq_s_fc = make_function_with_class(sc, f, ">=", g_geq_s_fc, 2, 0, false, ">= opt");
  geq_2 = make_function_with_class(sc, f, ">=", g_geq_2, 2, 0, false, ">= opt");
  geq_length_ic = make_function_with_class(sc, f, ">=", g_geq_length_ic, 2, 0, false, ">= opt");

  /* random */
  f = set_function_chooser(sc, sc->RANDOM, random_chooser);

  random_ic = make_function_with_class(sc, f, "random", g_random_ic, 1, 0, false, "random opt");
  random_rc = make_function_with_class(sc, f, "random", g_random_rc, 1, 0, false, "random opt");
  set_returns_temp(random_rc);
#endif

  /* list */
  f = set_function_chooser(sc, sc->LIST, list_chooser);
  list_0 = make_function_with_class(sc, f, "list", g_list_0, 0, 0, false, "list opt");
  list_1 = make_function_with_class(sc, f, "list", g_list_1, 1, 0, false, "list opt");
  list_2 = make_function_with_class(sc, f, "list", g_list_2, 2, 0, false, "list opt");

  /* aritable? */
  f = set_function_chooser(sc, sc->IS_ARITABLE, is_aritable_chooser);
  is_aritable_ic = make_function_with_class(sc, f, "aritable?", g_is_aritable_ic, 2, 0, false, "aritable? opt");

  /* char=? */
  f = set_function_chooser(sc, sc->CHAR_EQ, char_equal_chooser);

  char_equal_s_ic = make_function_with_class(sc, f, "char=?", g_char_equal_s_ic, 2, 0, false, "char=? opt");
  char_equal_2 = make_function_with_class(sc, f, "char=?", g_char_equal_2, 2, 0, false, "char=? opt");

  /* char>? */
  f = set_function_chooser(sc, sc->CHAR_GT, char_greater_chooser);

  char_greater_s_ic = make_function_with_class(sc, f, "char>?", g_char_greater_s_ic, 2, 0, false, "char>? opt");
  char_greater_2 = make_function_with_class(sc, f, "char>?", g_char_greater_2, 2, 0, false, "char>? opt");

  /* char<? */
  f = set_function_chooser(sc, sc->CHAR_LT, char_less_chooser);

  char_less_s_ic = make_function_with_class(sc, f, "char<?", g_char_less_s_ic, 2, 0, false, "char<? opt");
  char_less_2 = make_function_with_class(sc, f, "char<?", g_char_less_2, 2, 0, false, "char<? opt");

  /* char<=? */
  f = set_function_chooser(sc, sc->CHAR_LEQ, char_leq_chooser);

  char_leq_s_ic = make_function_with_class(sc, f, "char<=?", g_char_leq_s_ic, 2, 0, false, "char<=? opt");
  char_leq_2 = make_function_with_class(sc, f, "char<=?", g_char_leq_2, 2, 0, false, "char<=? opt");

  /* char>=? */
  f = set_function_chooser(sc, sc->CHAR_GEQ, char_geq_chooser);

  char_geq_s_ic = make_function_with_class(sc, f, "char>=?", g_char_geq_s_ic, 2, 0, false, "char>=? opt");
  char_geq_2 = make_function_with_class(sc, f, "char>=?", g_char_geq_2, 2, 0, false, "char>=? opt");

#if (!WITH_PURE_S7)
  /* char-ci=? */
  f = set_function_chooser(sc, sc->CHAR_CI_EQ, char_ci_equal_chooser);

  char_ci_equal_s_ic = make_function_with_class(sc, f, "char-ci=?", g_char_ci_equal_s_ic, 2, 0, false, "char-ci=? opt");
  char_ci_equal_2 = make_function_with_class(sc, f, "char-ci=?", g_char_ci_equal_2, 2, 0, false, "char-ci=? opt");

  /* char-ci>? */
  f = set_function_chooser(sc, sc->CHAR_CI_GT, char_ci_greater_chooser);

  char_ci_greater_s_ic = make_function_with_class(sc, f, "char-ci>?", g_char_ci_greater_s_ic, 2, 0, false, "char-ci>? opt");
  char_ci_greater_2 = make_function_with_class(sc, f, "char-ci>?", g_char_ci_greater_2, 2, 0, false, "char-ci>? opt");

  /* char-ci<? */
  f = set_function_chooser(sc, sc->CHAR_CI_LT, char_ci_less_chooser);

  char_ci_less_s_ic = make_function_with_class(sc, f, "char-ci<?", g_char_ci_less_s_ic, 2, 0, false, "char-ci<? opt");
  char_ci_less_2 = make_function_with_class(sc, f, "char-ci<?", g_char_ci_less_2, 2, 0, false, "char-ci<? opt");

  /* char-ci<=? */
  f = set_function_chooser(sc, sc->CHAR_CI_LEQ, char_ci_leq_chooser);

  char_ci_leq_s_ic = make_function_with_class(sc, f, "char-ci<=?", g_char_ci_leq_s_ic, 2, 0, false, "char-ci<=? opt");
  char_ci_leq_2 = make_function_with_class(sc, f, "char-ci<=?", g_char_ci_leq_2, 2, 0, false, "char-ci<=? opt");

  /* char-ci>=? */
  f = set_function_chooser(sc, sc->CHAR_CI_GEQ, char_ci_geq_chooser);

  char_ci_geq_s_ic = make_function_with_class(sc, f, "char-ci>=?", g_char_ci_geq_s_ic, 2, 0, false, "char-ci>=? opt");
  char_ci_geq_2 = make_function_with_class(sc, f, "char-ci>=?", g_char_ci_geq_2, 2, 0, false, "char-ci>=? opt");
#endif

  /* char-position */
  f = set_function_chooser(sc, sc->CHAR_POSITION, char_position_chooser);
  char_position_cs = make_function_with_class(sc, f, "char-position", g_char_position_cs, 2, 0, false, "char-position opt");

  /* string->symbol */
  f = set_function_chooser(sc, sc->STRING_TO_SYMBOL, string_to_symbol_chooser);

  /* string=? */
  f = set_function_chooser(sc, sc->STRING_EQ, string_equal_chooser);

  string_equal_s_ic = make_function_with_class(sc, f, "string=?", g_string_equal_s_ic, 2, 0, false, "string=? opt");
  string_equal_2 = make_function_with_class(sc, f, "string=?", g_string_equal_2, 2, 0, false, "string=? opt");

  /* substring */
  substring_to_temp = s7_make_function(sc, "substring", g_substring_to_temp, 2, 1, false, "substring opt");
  s7_function_set_class(substring_to_temp, slot_value(global_slot(sc->SUBSTRING)));

  /* string>? */
  f = set_function_chooser(sc, sc->STRING_GT, string_greater_chooser);

  string_greater_s_ic = make_function_with_class(sc, f, "string>?", g_string_greater_s_ic, 2, 0, false, "string>? opt");
  string_greater_2 = make_function_with_class(sc, f, "string>?", g_string_greater_2, 2, 0, false, "string>? opt");

  /* string<? */
  f = set_function_chooser(sc, sc->STRING_LT, string_less_chooser);

  string_less_s_ic = make_function_with_class(sc, f, "string<?", g_string_less_s_ic, 2, 0, false, "string<? opt");
  string_less_2 = make_function_with_class(sc, f, "string<?", g_string_less_2, 2, 0, false, "string<? opt");

  /* string<=? */
  f = set_function_chooser(sc, sc->STRING_LEQ, string_leq_chooser);

  string_leq_s_ic = make_function_with_class(sc, f, "string<=?", g_string_leq_s_ic, 2, 0, false, "string<=? opt");
  string_leq_2 = make_function_with_class(sc, f, "string<=?", g_string_leq_2, 2, 0, false, "string<=? opt");

  /* string>=? */
  f = set_function_chooser(sc, sc->STRING_GEQ, string_geq_chooser);

  string_geq_s_ic = make_function_with_class(sc, f, "string>=?", g_string_geq_s_ic, 2, 0, false, "string>=? opt");
  string_geq_2 = make_function_with_class(sc, f, "string>=?", g_string_geq_2, 2, 0, false, "string>=? opt");

#if (!WITH_PURE_S7)
  /* string-ci=? */
  f = set_function_chooser(sc, sc->STRING_CI_EQ, string_ci_equal_chooser);

  string_ci_equal_s_ic = make_function_with_class(sc, f, "string-ci=?", g_string_ci_equal_s_ic, 2, 0, false, "string-ci=? opt");
  string_ci_equal_2 = make_function_with_class(sc, f, "string-ci=?", g_string_ci_equal_2, 2, 0, false, "string-ci=? opt");

  /* string-ci>? */
  f = set_function_chooser(sc, sc->STRING_CI_GT, string_ci_greater_chooser);

  string_ci_greater_s_ic = make_function_with_class(sc, f, "string-ci>?", g_string_ci_greater_s_ic, 2, 0, false, "string-ci>? opt");
  string_ci_greater_2 = make_function_with_class(sc, f, "string-ci>?", g_string_ci_greater_2, 2, 0, false, "string-ci>? opt");

  /* string-ci<? */
  f = set_function_chooser(sc, sc->STRING_CI_LT, string_ci_less_chooser);

  string_ci_less_s_ic = make_function_with_class(sc, f, "string-ci<?", g_string_ci_less_s_ic, 2, 0, false, "string-ci<? opt");
  string_ci_less_2 = make_function_with_class(sc, f, "string-ci<?", g_string_ci_less_2, 2, 0, false, "string-ci<? opt");

  /* string-ci<=? */
  f = set_function_chooser(sc, sc->STRING_CI_LEQ, string_ci_leq_chooser);

  string_ci_leq_s_ic = make_function_with_class(sc, f, "string-ci<=?", g_string_ci_leq_s_ic, 2, 0, false, "string-ci<=? opt");
  string_ci_leq_2 = make_function_with_class(sc, f, "string-ci<=?", g_string_ci_leq_2, 2, 0, false, "string-ci<=? opt");

  /* string-ci>=? */
  f = set_function_chooser(sc, sc->STRING_CI_GEQ, string_ci_geq_chooser);

  string_ci_geq_s_ic = make_function_with_class(sc, f, "string-ci>=?", g_string_ci_geq_s_ic, 2, 0, false, "string-ci>=? opt");
  string_ci_geq_2 = make_function_with_class(sc, f, "string-ci>=?", g_string_ci_geq_2, 2, 0, false, "string-ci>=? opt");
#endif

  /* string-ref */
  set_function_chooser(sc, sc->STRING_REF, string_ref_chooser);

  /* string-set! */
  set_function_chooser(sc, sc->STRING_SET, string_set_chooser);

  /* string-append */
  set_function_chooser(sc, sc->STRING_APPEND, string_append_chooser);

  /* symbol->string */
  f = slot_value(global_slot(sc->SYMBOL_TO_STRING));
  symbol_to_string_uncopied = s7_make_function(sc, "symbol->string", g_symbol_to_string_uncopied, 1, 0, false, "symbol->string opt");
  s7_function_set_class(symbol_to_string_uncopied, f);

  /* for-each */
  f = set_function_chooser(sc, sc->FOR_EACH, for_each_chooser);

  for_each_1 = make_function_with_class(sc, f, "for-each", g_for_each_1, 2, 0, false, "for-each opt");
  for_each_2 = make_function_with_class(sc, f, "for-each", g_for_each_2, 2, 0, false, "for-each opt");
  
  /* vector-ref */
  f = set_function_chooser(sc, sc->VECTOR_REF, vector_ref_chooser);

  vector_ref_ic = make_function_with_class(sc, f, "vector-ref", g_vector_ref_ic, 2, 0, false, "vector-ref opt");
  vector_ref_ic_0 = make_function_with_class(sc, f, "vector-ref", g_vector_ref_ic_0, 1, 0, false, "vector-ref opt");
  vector_ref_ic_1 = make_function_with_class(sc, f, "vector-ref", g_vector_ref_ic_1, 1, 0, false, "vector-ref opt");
  vector_ref_ic_2 = make_function_with_class(sc, f, "vector-ref", g_vector_ref_ic_2, 1, 0, false, "vector-ref opt");
  vector_ref_ic_3 = make_function_with_class(sc, f, "vector-ref", g_vector_ref_ic_3, 1, 0, false, "vector-ref opt");
  vector_ref_add1 = make_function_with_class(sc, f, "vector-ref", g_vector_ref_add1, 2, 0, false, "vector-ref opt");
  vector_ref_2 = make_function_with_class(sc, f, "vector-ref", g_vector_ref_2, 2, 0, false, "vector-ref opt");
  vector_ref_gs = make_function_with_class(sc, f, "vector-ref", g_vector_ref_gs, 2, 0, false, "vector-ref opt");

  /* vector-set! */
  f = set_function_chooser(sc, sc->VECTOR_SET, vector_set_chooser);

  vector_set_ic = make_function_with_class(sc, f, "vector-set!", g_vector_set_ic, 3, 0, false, "vector-set! opt");
  vector_set_vref = make_function_with_class(sc, f, "vector-set!", g_vector_set_vref, 3, 0, false, "vector-set! opt");
  vector_set_vector_ref = make_function_with_class(sc, f, "vector-set!", g_vector_set_vector_ref, 3, 0, false, "vector-set! opt");
  vector_set_3 = make_function_with_class(sc, f, "vector-set!", g_vector_set_3, 3, 0, false, "vector-set! opt");

  /* list-ref */
  f = set_function_chooser(sc, sc->LIST_REF, list_ref_chooser);
  list_ref_ic = make_function_with_class(sc, f, "list-ref", g_list_ref_ic, 2, 0, false, "list-ref opt");

  /* list-set! */
  f = set_function_chooser(sc, sc->LIST_SET, list_set_chooser);
  list_set_ic = make_function_with_class(sc, f, "list-set!", g_list_set_ic, 3, 0, false, "list-set! opt");

  /* hash-table-ref */
  f = set_function_chooser(sc, sc->HASH_TABLE_REF, hash_table_ref_chooser);

  hash_table_ref_2 = make_function_with_class(sc, f, "hash-table-ref", g_hash_table_ref_2, 2, 0, false, "hash-table-ref opt");
  hash_table_ref_ss = make_function_with_class(sc, f, "hash-table-ref", g_hash_table_ref_ss, 2, 0, false, "hash-table-ref opt");
  hash_table_ref_car = make_function_with_class(sc, f, "hash-table-ref", g_hash_table_ref_car, 2, 0, false, "hash-table-ref opt");

  /* car */
  set_function_chooser(sc, sc->CAR, car_chooser);

  /* cdr */
  set_function_chooser(sc, sc->CDR, cdr_chooser);

  /* format */
  f = set_function_chooser(sc, sc->FORMAT, format_chooser);

  format_allg = make_function_with_class(sc, f, "format", g_format_allg, 1, 0, true, "format opt");
  format_allg_no_column = make_function_with_class(sc, f, "format", g_format_allg_no_column, 1, 0, true, "format opt");
  format_just_newline = make_function_with_class(sc, f, "format", g_format_just_newline, 2, 0, false, "format opt");
  set_type(format_just_newline, type(format_just_newline) | T_SAFE_PROCEDURE);

  /* not */
  f = set_function_chooser(sc, sc->NOT, not_chooser);

  not_is_pair = make_function_with_class(sc, f, "not", g_not_is_pair, 1, 0, false, "not opt");
  not_is_null = make_function_with_class(sc, f, "not", g_not_is_null, 1, 0, false, "not opt");
  not_is_list = make_function_with_class(sc, f, "not", g_not_is_list, 1, 0, false, "not opt");
  not_is_symbol = make_function_with_class(sc, f, "not", g_not_is_symbol, 1, 0, false, "not opt");
  not_is_number = make_function_with_class(sc, f, "not", g_not_is_number, 1, 0, false, "not opt");
  not_is_zero = make_function_with_class(sc, f, "not", g_not_is_zero, 1, 0, false, "not opt");
  not_is_real = make_function_with_class(sc, f, "not", g_not_is_real, 1, 0, false, "not opt");
  not_is_rational = make_function_with_class(sc, f, "not", g_not_is_rational, 1, 0, false, "not opt");
  not_is_integer = make_function_with_class(sc, f, "not", g_not_is_integer, 1, 0, false, "not opt");
  not_is_boolean = make_function_with_class(sc, f, "not", g_not_is_boolean, 1, 0, false, "not opt");
  not_is_string = make_function_with_class(sc, f, "not", g_not_is_string, 1, 0, false, "not opt");
  not_is_char = make_function_with_class(sc, f, "not", g_not_is_char, 1, 0, false, "not opt");
  not_is_eof = make_function_with_class(sc, f, "not", g_not_is_eof, 1, 0, false, "not opt");
  not_is_eq_ss = make_function_with_class(sc, f, "not", g_not_is_eq_ss, 1, 0, false, "not opt");
  not_is_eq_sq = make_function_with_class(sc, f, "not", g_not_is_eq_sq, 1, 0, false, "not opt");
  not_is_pair_car = make_function_with_class(sc, f, "not", g_not_is_pair_car, 1, 0, false, "not opt");
  not_is_null_cdr = make_function_with_class(sc, f, "not", g_not_is_null_cdr, 1, 0, false, "not opt");
  not_c_c = make_function_with_class(sc, f, "not", g_not_c_c, 1, 0, false, "not opt");

  /* pair? */
  f = set_function_chooser(sc, sc->IS_PAIR, is_pair_chooser);

  is_pair_car = make_function_with_class(sc, f, "pair?", g_is_pair_car, 1, 0, false, "pair? opt");
  is_pair_cdr = make_function_with_class(sc, f, "pair?", g_is_pair_cdr, 1, 0, false, "pair? opt");
  is_pair_cadr = make_function_with_class(sc, f, "pair?", g_is_pair_cadr, 1, 0, false, "pair? opt");

  /* null? */
  f = set_function_chooser(sc, sc->IS_NULL, is_null_chooser);
  is_null_cdr = make_function_with_class(sc, f, "null?", g_is_null_cdr, 1, 0, false, "null? opt");

  /* eq? */
  f = set_function_chooser(sc, sc->IS_EQ, is_eq_chooser);

  is_eq_car = make_function_with_class(sc, f, "eq?", g_is_eq_car, 2, 0, false, "eq? opt");
  is_eq_car_q = make_function_with_class(sc, f, "eq?", g_is_eq_car_q, 2, 0, false, "eq? opt");
  is_eq_caar_q = make_function_with_class(sc, f, "eq?", g_is_eq_caar_q, 2, 0, false, "eq? opt");

  /* memq */
  f = set_function_chooser(sc, sc->MEMQ, memq_chooser);
  
  memq_3 = make_function_with_class(sc, f, "memq", g_memq_3, 2, 0, false, "memq opt");
  memq_4 = make_function_with_class(sc, f, "memq", g_memq_4, 2, 0, false, "memq opt");
  memq_any = make_function_with_class(sc, f, "memq", g_memq_any, 2, 0, false, "memq opt");
  memq_car = make_function_with_class(sc, f, "memq", g_memq_car, 2, 0, false, "memq opt");

  /* member */
  f = set_function_chooser(sc, sc->MEMBER, member_chooser);
  
  member_ss = make_function_with_class(sc, f, "member", g_member_ss, 2, 0, false, "member opt");
  member_sq = make_function_with_class(sc, f, "member", g_member_sq, 2, 0, false, "member opt");
  member_sym_s = make_function_with_class(sc, f, "member", g_member_sym_s, 2, 0, false, "member opt");
  member_num_s = make_function_with_class(sc, f, "member", g_member_num_s, 2, 0, false, "member opt");

  /* read-char */
  f = set_function_chooser(sc, sc->READ_CHAR, read_char_chooser);
  
  read_char_0 = make_function_with_class(sc, f, "read-char", g_read_char_0, 0, 0, false, "read-char opt");
  read_char_1 = make_function_with_class(sc, f, "read-char", g_read_char_1, 1, 0, false, "read-char opt");

  /* write-char */
  f = set_function_chooser(sc, sc->WRITE_CHAR, write_char_chooser);
  write_char_1 = make_function_with_class(sc, f, "write-char", g_write_char_1, 1, 0, false, "write-char opt");

  /* or and if simple cases */
  or_direct = s7_make_function(sc, "or", g_or_direct, 0, 0, true, "or opt");
  and_direct = s7_make_function(sc, "and", g_and_direct, 0, 0, true, "and opt");
  if_direct = s7_make_function(sc, "if", g_if_direct, 2, 1, false, "if opt");

  or_all_x = s7_make_function(sc, "or", g_or_all_x, 0, 0, true, "or opt");
  and_all_x = s7_make_function(sc, "and", g_and_all_x, 0, 0, true, "and opt");
  if_all_x1 = s7_make_function(sc, "if", g_if_all_x1, 2, 0, false, "if opt");
  if_all_x2 = s7_make_function(sc, "if", g_if_all_x2, 3, 0, false, "if opt");
  if_all_x_qq = s7_make_function(sc, "if", g_if_all_x_qq, 3, 0, false, "if opt");
  if_all_x_qa = s7_make_function(sc, "if", g_if_all_x_qa, 3, 0, false, "if opt");

  or_s_direct = s7_make_function(sc, "or", g_or_s_direct, 0, 0, true, "or opt");
  and_s_direct = s7_make_function(sc, "and", g_and_s_direct, 0, 0, true, "and opt");
  if_s_direct = s7_make_function(sc, "if", g_if_s_direct, 2, 1, false, "if opt");
}


static bool optimize_expression(s7_scheme *sc, s7_pointer x, int hop, s7_pointer e);

static s7_pointer collect_collisions(s7_scheme *sc, s7_pointer lst, s7_pointer e)
{
  s7_pointer p;
  sc->w = e;
  for (p = lst; is_pair(p); p = cdr(p))
    {
      s7_pointer car_p;
      car_p = car(p);
      if (is_pair(car_p))
	{
	  if ((is_symbol(car(car_p))) &&
	      (global_slot(car(car_p)) != sc->NIL))
	    sc->w = cons(sc, add_sym_to_list(sc, car(car_p)), sc->w);
	}
      else
	{
	  if ((is_symbol(car_p)) &&
	      (global_slot(car_p) != sc->NIL))
	    sc->w = cons(sc, add_sym_to_list(sc, car_p), sc->w);
	}
    }
  return(sc->w);
}


static s7_function all_x_function[OPT_MAX_DEFINED];
#define is_all_x_op(Op) (all_x_function[Op] != NULL)

static bool is_all_x_safe(s7_scheme *sc, s7_pointer p)
{
  if ((!is_pair(p)) ||
      ((car(p) == sc->QUOTE) &&
       (is_pair(cdr(p)))))   /* (if #t (quote . -1)) */
    return(true);
  if (!is_optimized(p))
    return(false);
  return(is_all_x_op(optimize_data(p)));
}


static int all_x_count(s7_pointer x)
{
  int count = 0;
  s7_pointer p;
  for (p = cdr(x); is_pair(p); p = cdr(p))
    if ((is_optimized(car(p))) &&
	(is_all_x_op(optimize_data(car(p)))))
      count++;
  return(count);
}


/* arg here is the full expression */

static s7_pointer all_x_else(s7_scheme *sc, s7_pointer arg)
{
  return(sc->T); /* used in cond_all_x */
}

static s7_pointer all_x_c(s7_scheme *sc, s7_pointer arg)
{
  return(arg);
}

static s7_pointer all_x_q(s7_scheme *sc, s7_pointer arg)
{
  return(cadr(arg));
}

static s7_pointer all_x_s(s7_scheme *sc, s7_pointer arg)
{
  return(find_symbol_checked(sc, arg));
}

static s7_pointer all_x_k(s7_scheme *sc, s7_pointer arg)
{
  return(arg);
}

static s7_pointer all_x_c_c(s7_scheme *sc, s7_pointer arg)
{
  return(c_call(arg)(sc, cdr(arg)));
}

static s7_pointer all_x_c_q(s7_scheme *sc, s7_pointer arg)
{
  car(sc->T1_1) = cadr(cadr(arg));
  return(c_call(arg)(sc, sc->T1_1));
}

static s7_pointer all_x_c_s(s7_scheme *sc, s7_pointer arg)
{
  car(sc->T1_1) = find_symbol_checked(sc, cadr(arg));
  return(c_call(arg)(sc, sc->T1_1));
}
		    
static s7_pointer all_x_cdr_s(s7_scheme *sc, s7_pointer arg)
{
  s7_pointer val;
  val = find_symbol_checked(sc, cadr(arg));
  return((is_pair(val)) ? cdr(val) : g_cdr(sc, list_1(sc, val)));
}
		    
static s7_pointer all_x_car_s(s7_scheme *sc, s7_pointer arg)
{
  s7_pointer val;
  val = find_symbol_checked(sc, cadr(arg));
  return((is_pair(val)) ? car(val) : g_car(sc, list_1(sc, val)));
}
		    
static s7_pointer all_x_null_s(s7_scheme *sc, s7_pointer arg) 
{
  return(make_boolean(sc, is_null(find_symbol_checked(sc, cadr(arg)))));
}
		    
static s7_pointer all_x_c_sc(s7_scheme *sc, s7_pointer arg)
{
  car(sc->T2_1) = find_symbol_checked(sc, cadr(arg));
  car(sc->T2_2) = caddr(arg);
  return(c_call(arg)(sc, sc->T2_1));
}

static s7_pointer all_x_c_cs(s7_scheme *sc, s7_pointer arg)
{
  car(sc->T2_2) = find_symbol_checked(sc, caddr(arg));
  car(sc->T2_1) = cadr(arg);
  return(c_call(arg)(sc, sc->T2_1));
}

static s7_pointer all_x_c_ss(s7_scheme *sc, s7_pointer arg)
{
  car(sc->T2_1) = find_symbol_checked(sc, cadr(arg));
  car(sc->T2_2) = find_symbol_checked(sc, caddr(arg));
  return(c_call(arg)(sc, sc->T2_1));
}		    

static s7_pointer all_x_c_sss(s7_scheme *sc, s7_pointer arg)
{
  car(sc->T3_1) = find_symbol_checked(sc, cadr(arg));
  car(sc->T3_2) = find_symbol_checked(sc, caddr(arg));
  car(sc->T3_3) = find_symbol_checked(sc, cadddr(arg));
  return(c_call(arg)(sc, sc->T3_1));
}		    

static s7_pointer all_x_c_scs(s7_scheme *sc, s7_pointer arg)
{
  car(sc->T3_1) = find_symbol_checked(sc, cadr(arg));
  car(sc->T3_3) = find_symbol_checked(sc, cadddr(arg));
  car(sc->T3_2) = caddr(arg);
  return(c_call(arg)(sc, sc->T3_1));
}		    

static s7_pointer all_x_c_scc(s7_scheme *sc, s7_pointer arg)
{
  car(sc->T3_1) = find_symbol_checked(sc, cadr(arg));
  car(sc->T3_2) = caddr(arg);
  car(sc->T3_3) = cadddr(arg);
  return(c_call(arg)(sc, sc->T3_1));
}		    

static s7_pointer all_x_c_css(s7_scheme *sc, s7_pointer arg)
{
  car(sc->T3_2) = find_symbol_checked(sc, caddr(arg));
  car(sc->T3_3) = find_symbol_checked(sc, cadddr(arg));
  car(sc->T3_1) = cadr(arg);
  return(c_call(arg)(sc, sc->T3_1));
}		    

static s7_pointer all_x_c_csc(s7_scheme *sc, s7_pointer arg)
{
  car(sc->T3_2) = find_symbol_checked(sc, caddr(arg));
  car(sc->T3_1) = cadr(arg);
  car(sc->T3_3) = cadddr(arg);
  return(c_call(arg)(sc, sc->T3_1));
}		    

static s7_pointer all_x_c_ssc(s7_scheme *sc, s7_pointer arg)
{
  car(sc->T3_1) = find_symbol_checked(sc, cadr(arg));
  car(sc->T3_2) = find_symbol_checked(sc, caddr(arg));
  car(sc->T3_3) = cadddr(arg);
  return(c_call(arg)(sc, sc->T3_1));
}

static s7_pointer all_x_c_sq(s7_scheme *sc, s7_pointer arg)
{
  car(sc->T2_1) = find_symbol_checked(sc, cadr(arg));
  car(sc->T2_2) = cadr(caddr(arg));
  return(c_call(arg)(sc, sc->T2_1));
}

static s7_pointer all_x_c_opcq(s7_scheme *sc, s7_pointer arg)
{
  s7_pointer largs;
  largs = cadr(arg);
  car(sc->T1_1) = c_call(largs)(sc, cdr(largs));
  return(c_call(arg)(sc, sc->T1_1));
}
		    
static s7_pointer all_x_c_s_opcq(s7_scheme *sc, s7_pointer arg)
{
  s7_pointer largs;
  largs = caddr(arg);
  car(sc->T2_2) = c_call(largs)(sc, cdr(largs));
  car(sc->T2_1) = find_symbol_checked(sc, cadr(arg));
  return(c_call(arg)(sc, sc->T2_1));
}
		    
static s7_pointer all_x_c_c_opcq(s7_scheme *sc, s7_pointer arg)
{
  s7_pointer largs;
  largs = caddr(arg);
  car(sc->T2_2) = c_call(largs)(sc, cdr(largs));
  car(sc->T2_1) = cadr(arg);
  return(c_call(arg)(sc, sc->T2_1));
}
		    
static s7_pointer all_x_c_opcq_s(s7_scheme *sc, s7_pointer arg)
{
  s7_pointer largs;
  largs = cadr(arg);
  car(sc->T2_1) = c_call(largs)(sc, cdr(largs));
  car(sc->T2_2) = find_symbol_checked(sc, caddr(arg));
  return(c_call(arg)(sc, sc->T2_1));
}
		    
static s7_pointer all_x_c_opcq_c(s7_scheme *sc, s7_pointer arg)
{
  s7_pointer largs;
  largs = cadr(arg);
  car(sc->T2_1) = c_call(largs)(sc, cdr(largs));
  car(sc->T2_2) = caddr(arg);
  return(c_call(arg)(sc, sc->T2_1));
}
		    
static s7_pointer all_x_c_opcq_opcq(s7_scheme *sc, s7_pointer arg)
{
  s7_pointer largs;
  largs = cadr(arg);
  car(sc->T2_1) = c_call(largs)(sc, cdr(largs));
  largs = caddr(arg);
  car(sc->T2_2) = c_call(largs)(sc, cdr(largs));
  return(c_call(arg)(sc, sc->T2_1));
}
		    
static s7_pointer all_x_c_opsq(s7_scheme *sc, s7_pointer arg)
{
  s7_pointer largs;
  largs = cadr(arg);
  car(sc->T1_1) = find_symbol_checked(sc, cadr(largs));
  car(sc->T1_1) = c_call(largs)(sc, sc->T1_1);
  return(c_call(arg)(sc, sc->T1_1));
}
		    
static s7_pointer all_x_c_opssq(s7_scheme *sc, s7_pointer arg)
{
  s7_pointer largs;
  largs = cadr(arg);
  car(sc->T2_1) = find_symbol_checked(sc, cadr(largs));
  car(sc->T2_2) = find_symbol_checked(sc, caddr(largs));
  car(sc->T1_1) = c_call(largs)(sc, sc->T2_1);
  return(c_call(arg)(sc, sc->T1_1));
}
		    
static s7_pointer all_x_c_opscq(s7_scheme *sc, s7_pointer arg)
{
  s7_pointer largs;
  largs = cadr(arg);
  car(sc->T2_1) = find_symbol_checked(sc, cadr(largs));
  car(sc->T2_2) = caddr(largs);
  car(sc->T1_1) = c_call(largs)(sc, sc->T2_1);
  return(c_call(arg)(sc, sc->T1_1));
}
		    
static s7_pointer all_x_c_opsqq(s7_scheme *sc, s7_pointer arg)
{
  s7_pointer largs;
  largs = cadr(arg);
  car(sc->T2_1) = find_symbol_checked(sc, cadr(largs));
  car(sc->T2_2) = cadr(caddr(largs));
  car(sc->T1_1) = c_call(largs)(sc, sc->T2_1);
  return(c_call(arg)(sc, sc->T1_1));
}
		    
static s7_pointer all_x_c_opssq_s(s7_scheme *sc, s7_pointer arg)
{
  s7_pointer largs;
  largs = cadr(arg);
  car(sc->T2_1) = find_symbol_checked(sc, cadr(largs));
  car(sc->T2_2) = find_symbol_checked(sc, caddr(largs));
  car(sc->T2_1) = c_call(largs)(sc, sc->T2_1);
  car(sc->T2_2) = find_symbol_checked(sc, caddr(arg));
  return(c_call(arg)(sc, sc->T2_1));
}
		    
static s7_pointer all_x_c_opssq_c(s7_scheme *sc, s7_pointer arg)
{
  s7_pointer largs;
  largs = cadr(arg);
  car(sc->T2_1) = find_symbol_checked(sc, cadr(largs));
  car(sc->T2_2) = find_symbol_checked(sc, caddr(largs));
  car(sc->T2_1) = c_call(largs)(sc, sc->T2_1);
  car(sc->T2_2) = caddr(arg);
  return(c_call(arg)(sc, sc->T2_1));
}
		    
static s7_pointer all_x_c_opsq_s(s7_scheme *sc, s7_pointer arg)
{
  s7_pointer largs;
  largs = cadr(arg);
  car(sc->T1_1) = find_symbol_checked(sc, cadr(largs));
  car(sc->T2_1) = c_call(largs)(sc, sc->T1_1);
  car(sc->T2_2) = find_symbol_checked(sc, caddr(arg));
  return(c_call(arg)(sc, sc->T2_1));
}
		    
static s7_pointer all_x_c_opsq_c(s7_scheme *sc, s7_pointer arg)
{
  s7_pointer largs;
  largs = cadr(arg);
  car(sc->T1_1) = find_symbol_checked(sc, cadr(largs));
  car(sc->T2_1) = c_call(largs)(sc, sc->T1_1);
  car(sc->T2_2) = caddr(arg);
  return(c_call(arg)(sc, sc->T2_1));
}
		    
static s7_pointer all_x_c_s_opssq(s7_scheme *sc, s7_pointer arg)
{
  s7_pointer largs;
  largs = caddr(arg);
  car(sc->T2_1) = find_symbol_checked(sc, cadr(largs));
  car(sc->T2_2) = find_symbol_checked(sc, caddr(largs));
  car(sc->T2_2) = c_call(largs)(sc, sc->T2_1);
  car(sc->T2_1) = find_symbol_checked(sc, cadr(arg));
  return(c_call(arg)(sc, sc->T2_1));
}
		    
static s7_pointer all_x_c_s_opsq(s7_scheme *sc, s7_pointer arg)
{
  s7_pointer largs;
  largs = caddr(arg);
  car(sc->T1_1) = find_symbol_checked(sc, cadr(largs));
  car(sc->T2_2) = c_call(largs)(sc, sc->T1_1);
  car(sc->T2_1) = find_symbol_checked(sc, cadr(arg));
  return(c_call(arg)(sc, sc->T2_1));
}
		    
static s7_pointer all_x_c_c_opsq(s7_scheme *sc, s7_pointer arg)
{
  s7_pointer largs;
  largs = caddr(arg);
  car(sc->T1_1) = find_symbol_checked(sc, cadr(largs));
  car(sc->T2_2) = c_call(largs)(sc, sc->T1_1);
  car(sc->T2_1) = cadr(arg);
  return(c_call(arg)(sc, sc->T2_1));
}
		    
static s7_pointer all_x_c_opsq_opsq(s7_scheme *sc, s7_pointer arg)
{
  s7_pointer largs;
  largs = cdr(arg);
  car(sc->T1_1) = find_symbol_checked(sc, cadr(car(largs)));
  sc->temp3 = c_call(car(largs))(sc, sc->T1_1);
  largs = cadr(largs);
  car(sc->T1_1) = find_symbol_checked(sc, cadr(largs));
  car(sc->T2_2) = c_call(largs)(sc, sc->T1_1);
  car(sc->T2_1) = sc->temp3;
  return(c_call(arg)(sc, sc->T2_1));
}

static s7_pointer all_x_c_opssq_opssq(s7_scheme *sc, s7_pointer arg)
{
  s7_pointer largs;
  largs = cdr(arg);
  car(sc->T2_1) = find_symbol_checked(sc, cadr(car(largs)));
  car(sc->T2_2) = find_symbol_checked(sc, caddr(car(largs)));
  sc->temp3 = c_call(car(largs))(sc, sc->T2_1);
  largs = cadr(largs);
  car(sc->T2_1) = find_symbol_checked(sc, cadr(largs));
  car(sc->T2_2) = find_symbol_checked(sc, caddr(largs));
  car(sc->T2_2) = c_call(largs)(sc, sc->T2_1);
  car(sc->T2_1) = sc->temp3;
  return(c_call(arg)(sc, sc->T2_1));
}

static s7_pointer all_x_c_op_opssq_q_c(s7_scheme *sc, s7_pointer code)
{
  s7_pointer arg;
  arg = cadr(cadr(code));
  car(sc->T2_1) = find_symbol_checked(sc, cadr(arg));
  car(sc->T2_2) = find_symbol_checked(sc, caddr(arg));
  car(sc->T1_1) = c_call(arg)(sc, sc->T2_1);
  car(sc->T2_1) = c_call(cadr(code))(sc, sc->T1_1);
  car(sc->T2_2) = caddr(code);
  return(c_call(code)(sc, sc->T2_1));
}

static s7_pointer all_x_c_a(s7_scheme *sc, s7_pointer arg)
{
  car(sc->T1_1) = ((s7_function)fcdr(cdr(arg)))(sc, cadr(arg));
  return(c_call(arg)(sc, sc->T1_1));
}

static s7_pointer all_x_c_ssa(s7_scheme *sc, s7_pointer arg)
{
  sc->temp3 = ((s7_function)fcdr(cdddr(arg)))(sc, cadddr(arg));
  car(sc->T3_1) = find_symbol_checked(sc, cadr(arg));
  car(sc->T3_2) = find_symbol_checked(sc, caddr(arg));
  car(sc->T3_3) = sc->temp3;
  return(c_call(arg)(sc, sc->T3_1));
}

static s7_pointer all_x_c_sca(s7_scheme *sc, s7_pointer arg)
{
  sc->temp3 = ((s7_function)fcdr(cdddr(arg)))(sc, cadddr(arg));
  car(sc->T3_1) = find_symbol_checked(sc, cadr(arg));
  car(sc->T3_2) = caddr(arg);
  car(sc->T3_3) = sc->temp3;
  return(c_call(arg)(sc, sc->T3_1));
}

static s7_pointer all_x_c_csa(s7_scheme *sc, s7_pointer arg)
{
  sc->temp3 = ((s7_function)fcdr(cdddr(arg)))(sc, cadddr(arg));
  car(sc->T3_1) = cadr(arg);
  car(sc->T3_2) = find_symbol_checked(sc, caddr(arg));
  car(sc->T3_3) = sc->temp3;
  return(c_call(arg)(sc, sc->T3_1));
}

static s7_pointer all_x_c_cas(s7_scheme *sc, s7_pointer arg)
{
  sc->temp3 = ((s7_function)fcdr(cddr(arg)))(sc, caddr(arg));
  car(sc->T3_1) = cadr(arg);
  car(sc->T3_3) = find_symbol_checked(sc, cadddr(arg));
  car(sc->T3_2) = sc->temp3;
  return(c_call(arg)(sc, sc->T3_1));
}

static void all_x_function_init(void)
{
  int i;
  for (i = 0; i < OPT_MAX_DEFINED; i++)
    all_x_function[i] = NULL;

  all_x_function[HOP_SAFE_C_C] = all_x_c_c;
  all_x_function[HOP_SAFE_C_Q] = all_x_c_q;
  all_x_function[HOP_SAFE_C_A] = all_x_c_a;
  all_x_function[HOP_SAFE_C_S] = all_x_c_s;

  all_x_function[HOP_SAFE_C_opCq] = all_x_c_opcq;
  all_x_function[HOP_SAFE_C_opSq] = all_x_c_opsq;
  all_x_function[HOP_SAFE_C_opSSq] = all_x_c_opssq;
  all_x_function[HOP_SAFE_C_opSCq] = all_x_c_opscq;
  all_x_function[HOP_SAFE_C_opSQq] = all_x_c_opsqq;

  all_x_function[HOP_SAFE_C_SC] = all_x_c_sc;
  all_x_function[HOP_SAFE_C_CS] = all_x_c_cs;
  all_x_function[HOP_SAFE_C_SQ] = all_x_c_sq;
  all_x_function[HOP_SAFE_C_SS] = all_x_c_ss;

  all_x_function[HOP_SAFE_C_opSq_S] = all_x_c_opsq_s;
  all_x_function[HOP_SAFE_C_opSq_C] = all_x_c_opsq_c;
  all_x_function[HOP_SAFE_C_S_opSq] = all_x_c_s_opsq;
  all_x_function[HOP_SAFE_C_S_opCq] = all_x_c_s_opcq;
  all_x_function[HOP_SAFE_C_opCq_S] = all_x_c_opcq_s;
  all_x_function[HOP_SAFE_C_opCq_C] = all_x_c_opcq_c;
  all_x_function[HOP_SAFE_C_C_opSq] = all_x_c_c_opsq;
  all_x_function[HOP_SAFE_C_C_opCq] = all_x_c_c_opcq;
  all_x_function[HOP_SAFE_C_opSSq_C] = all_x_c_opssq_c;
  all_x_function[HOP_SAFE_C_opSSq_S] = all_x_c_opssq_s;
  all_x_function[HOP_SAFE_C_S_opSSq] = all_x_c_s_opssq;
  all_x_function[HOP_SAFE_C_opSq_opSq] = all_x_c_opsq_opsq;
  all_x_function[HOP_SAFE_C_opCq_opCq] = all_x_c_opcq_opcq;
  all_x_function[HOP_SAFE_C_opSSq_opSSq] = all_x_c_opssq_opssq;
  all_x_function[HOP_SAFE_C_op_opSSq_q_C] = all_x_c_op_opssq_q_c;

  all_x_function[HOP_SAFE_C_CSA] = all_x_c_csa;
  all_x_function[HOP_SAFE_C_CAS] = all_x_c_cas;
  all_x_function[HOP_SAFE_C_SCA] = all_x_c_sca;
  all_x_function[HOP_SAFE_C_SSA] = all_x_c_ssa;
  all_x_function[HOP_SAFE_C_SSC] = all_x_c_ssc;
  all_x_function[HOP_SAFE_C_SCC] = all_x_c_scc;
  all_x_function[HOP_SAFE_C_SSS] = all_x_c_sss;
  all_x_function[HOP_SAFE_C_SCS] = all_x_c_scs;
  all_x_function[HOP_SAFE_C_CSS] = all_x_c_css;
  all_x_function[HOP_SAFE_C_CSC] = all_x_c_csc;
}

static s7_function all_x_eval(s7_scheme *sc, s7_pointer arg)
{
  if (is_pair(arg))
    {
      if (is_optimized(arg))
	{
	  if (optimize_data(arg) == HOP_SAFE_C_S)
	    {
	      if (car(arg) == sc->CDR) return(all_x_cdr_s);
	      if (car(arg) == sc->CAR) return(all_x_car_s);
	      if (car(arg) == sc->IS_NULL) return(all_x_null_s);
	      return(all_x_c_s);
	    }
	  return(all_x_function[optimize_data(arg)]);
	}
      if (car(arg) == sc->QUOTE)
	return(all_x_q);
      return(NULL);
    }
  if (is_symbol(arg))
    {
      if (is_keyword(arg))
	return(all_x_k);
      return(all_x_s);
    }
  return(all_x_c);
}


static s7_function cond_all_x_eval(s7_scheme *sc, s7_pointer arg)
{
  if (arg == sc->ELSE)
    return(all_x_else);
  return(all_x_eval(sc, arg));
}


static void choose_c_function(s7_scheme *sc, s7_pointer expr, s7_pointer func, int args)
{
  set_c_function(expr, c_function_chooser(func)(sc, func, args, expr));
}


static bool optimize_thunk(s7_scheme *sc, s7_pointer car_x, s7_pointer func, int hop)
{
  if ((is_c_function(func)) &&
      ((is_safe_procedure(func)) ||
       (c_function_call(func) == g_list) ||          /* (list) is safe */
       (c_function_call(func) == g_values)))         /* (values) is safe */
    {
      set_optimized(car_x);
      set_optimize_data(car_x, hop + OP_SAFE_C_C);
      choose_c_function(sc, car_x, func, 0);
      return(true);
    }
  
  if ((is_closure(func)) &&
      (is_null(closure_args(func))))                 /* no rest arg funny business */
    {
      set_unsafely_optimized(car_x);
      set_optimize_data(car_x, hop + ((is_safe_closure(func)) ? OP_SAFE_THUNK : OP_THUNK));
      /* it rarely happens that the safe_thunk's body is one-line/allxable
       *   so checking for that case above costs more than it saves.
       */
      set_ecdr(car_x, func);
      return(false);                                /* false because currently the C_PP stuff assumes safe procedure calls */
    }

  if ((is_closure_star(func)) &&
      (is_proper_list(sc, closure_args(func))) &&
      (has_simple_args(closure_body(func))))
    {
      set_unsafely_optimized(car_x);
      set_optimize_data(car_x, hop + ((is_safe_closure(func)) ? OP_SAFE_CLOSURE_STAR : OP_CLOSURE_STAR));
      set_ecdr(car_x, func);
      return(false); 
    }

  return(false);
}


static int combine_ops(s7_scheme *sc, combine_op_t op1, s7_pointer e1, s7_pointer e2)
{
  int op2;
  op2 = op_no_hop(e2);

  /* e_c_pp case (1) is slightly different from the others: e2 is not a part of e1
   */
  switch (op1)
    {
    case E_C_P:
      switch (op2)
	{
	case OP_SAFE_C_C: 
	  return(OP_SAFE_C_opCq); /* this includes the multi-arg C_C cases */

	case OP_SAFE_C_S: 
	  return(OP_SAFE_C_opSq);

	case OP_SAFE_C_SS: 
	  return(OP_SAFE_C_opSSq);

	case OP_SAFE_C_SQ: 
	  return(OP_SAFE_C_opSQq);

	case OP_SAFE_C_SC: 
	  return(OP_SAFE_C_opSCq);

	case OP_SAFE_C_CS: 
	  return(OP_SAFE_C_opCSq);

	case OP_SAFE_C_opSq:
	  return(OP_SAFE_C_op_opSq_q);

	case OP_SAFE_C_S_opSq:
	  return(OP_SAFE_C_op_S_opSq_q);

	case OP_SAFE_C_A:
	  return(OP_SAFE_C_opAq);

	case OP_SAFE_C_AA:
	  return(OP_SAFE_C_opAAq);

	case OP_SAFE_C_AAA:
	  return(OP_SAFE_C_opAAAq);
	}
      
      /* fprintf(stderr, "z: %s %s\n", opt_names[op2], DISPLAY(e1)); */
      return(OP_SAFE_C_Z); /* this splits out to A in optimize_func_one_arg */
      break;
      
    case E_C_SP:
      switch (op2)
	{
	case OP_SAFE_C_S:
	  set_ecdr(cdr(e1), cadr(e2));
	  return(OP_SAFE_C_S_opSq);

	case OP_SAFE_C_C:
	  set_ecdr(cdr(e1), cdr(e2));
	  return(OP_SAFE_C_S_opCq);

	case OP_SAFE_C_SC:
	  set_ecdr(cdr(e1), cadr(e2));
	  set_fcdr(cdr(e1), caddr(e2));
	  return(OP_SAFE_C_S_opSCq);
	  
	case OP_SAFE_C_CS:
	  /* (* a (- 1 b)), e1 is the full expr, e2 is (- 1 b) */
	  set_ecdr(cdr(e1), cadr(e2));
	  set_fcdr(cdr(e1), caddr(e2));
	  return(OP_SAFE_C_S_opCSq);

	case OP_SAFE_C_SS:
	  /* (* a (- b c)) */
	  set_ecdr(cdr(e1), cadr(e2));
	  set_fcdr(cdr(e1), caddr(e2));
	  return(OP_SAFE_C_S_opSSq);

	case OP_SAFE_C_opSSq_S:
	  return(OP_SAFE_C_S_op_opSSq_Sq);

	case OP_SAFE_C_S_opSSq:
	  return(OP_SAFE_C_S_op_S_opSSqq);

	case OP_SAFE_C_opSSq_opSSq:
	  return(OP_SAFE_C_S_op_opSSq_opSSqq);

	case OP_SAFE_C_SZ:
	  return(OP_SAFE_C_S_opSZq);

	case OP_SAFE_C_AA:
	  return(OP_SAFE_C_S_opAAq);

	case OP_SAFE_C_CSA:
	case OP_SAFE_C_CAS:
	case OP_SAFE_C_SCA:
	case OP_SAFE_C_SSA:
	case OP_SAFE_C_AAA:
	  return(OP_SAFE_C_S_opAAAq);
	}

      /* fprintf(stderr, "sz: %s %s\n", opt_name(e2), DISPLAY(e1)); */
      return(OP_SAFE_C_SZ);
      break;

    case E_C_PS:
      switch (op2)
	{
	case OP_SAFE_C_C:
	  return(OP_SAFE_C_opCq_S);

	case OP_SAFE_C_S:
	  return(OP_SAFE_C_opSq_S);
	  
	case OP_SAFE_C_CS:
	  return(OP_SAFE_C_opCSq_S);

	case OP_SAFE_C_SC:
	  return(OP_SAFE_C_opSCq_S);

	case OP_SAFE_C_SS:
	  return(OP_SAFE_C_opSSq_S);
	}
      /* fprintf(stderr, "zs: %s %s\n", opt_name(e2), DISPLAY(e1)); */
      return(OP_SAFE_C_ZS);
      break;

    case E_C_PC:
      switch (op2)
	{
	case OP_SAFE_C_C:
	  return(OP_SAFE_C_opCq_C);

	case OP_SAFE_C_S:
	  return(OP_SAFE_C_opSq_C);

	case OP_SAFE_C_CS:
	  return(OP_SAFE_C_opCSq_C);

	case OP_SAFE_C_SS:
	  return(OP_SAFE_C_opSSq_C);

	case OP_SAFE_C_SC:
	  return(OP_SAFE_C_opSCq_C);

	case OP_SAFE_C_opSSq:
	  return(OP_SAFE_C_op_opSSq_q_C);
	}

      /* fprintf(stderr, "zc: %s %s\n", opt_name(e2), DISPLAY(e1)); */
      return(OP_SAFE_C_ZC);
      break;

    case E_C_CP:
      switch (op2)
	{
	case OP_SAFE_C_C:
	  set_ecdr(cdr(e1), cdr(e2));
	  return(OP_SAFE_C_C_opCq);

	case OP_SAFE_C_S:
	  set_ecdr(cdr(e1), cadr(e2));
	  return(OP_SAFE_C_C_opSq);

	case OP_SAFE_C_CS:
	  set_ecdr(cdr(e1), cadr(e2));
	  set_fcdr(cdr(e1), caddr(e2));
	  return(OP_SAFE_C_C_opCSq);

	case OP_SAFE_C_SC:
	  set_ecdr(cdr(e1), cadr(e2));
	  set_fcdr(cdr(e1), caddr(e2));
	  return(OP_SAFE_C_C_opSCq);
	  
	case OP_SAFE_C_SS:
	  set_ecdr(cdr(e1), cadr(e2));
	  set_fcdr(cdr(e1), caddr(e2));
	  return(OP_SAFE_C_C_opSSq);

	case OP_SAFE_C_S_opCq:
	  return(OP_SAFE_C_C_op_S_opCqq);

	}
      /* fprintf(stderr, "cz: %s %s\n", opt_name(e2), DISPLAY(e1)); */
      return(OP_SAFE_C_CZ);
      break;

    case E_C_PP:
      switch (op2)
	{
	case OP_SAFE_C_S:
	  if (optimize_data_match(e1, OP_SAFE_C_S))
	    return(OP_SAFE_C_opSq_opSq);
	  if (optimize_data_match(e1, OP_SAFE_C_SS))
	    return(OP_SAFE_C_opSSq_opSq);
	  break;

	case OP_SAFE_C_C:
	  if (optimize_data_match(e1, OP_SAFE_C_C))
	    return(OP_SAFE_C_opCq_opCq);
	  if (optimize_data_match(e1, OP_SAFE_C_SS))
	    return(OP_SAFE_C_opSSq_opCq);
	  break;

	case OP_SAFE_C_SC:
	  if (optimize_data_match(e1, OP_SAFE_C_SC))
	    return(OP_SAFE_C_opSCq_opSCq);
	  break;

	case OP_SAFE_C_SS:
	  if (optimize_data_match(e1, OP_SAFE_C_C))
	    return(OP_SAFE_C_opCq_opSSq);
	  if (optimize_data_match(e1, OP_SAFE_C_SS))
	    return(OP_SAFE_C_opSSq_opSSq);
	  if (optimize_data_match(e1, OP_SAFE_C_S))
	    return(OP_SAFE_C_opSq_opSSq);
	  if (optimize_data_match(e1, OP_SAFE_C_opSSq_S))
	    return(OP_SAFE_C_op_opSSq_Sq_opSSq);
	  break;

	case OP_SAFE_C_opSSq:
	  if (optimize_data_match(e1, OP_SAFE_C_SS))
	    return(OP_SAFE_C_opSSq_op_opSSq_q);
	  break;
	}
      /* fprintf(stderr, "zz: %s %s %s %s\n", opt_name(e1), opt_name(e2), DISPLAY(e1), DISPLAY(e2)); */
      return(OP_SAFE_C_ZZ);
      break;
      
      /* currently ignored:
       * OP_SAFE_C_QS OP_SAFE_C_QQ OP_SAFE_C_CQ OP_SAFE_C_QC op_c_opqq[not used much]
       * [OP_SAFE_C_SSS] [OP_SAFE_C_SCS] [OP_SAFE_C_SSC]
       * [OP_SAFE_C_CSS] 
       * OP_SAFE_C_ALL_S OP_SAFE_C_ALL_X OP_SAFE_C_A OP_SAFE_C_AAAA (OP_SAFE_C_AZ) (OP_SAFE_C_ZA)
       * OP_SAFE_C_SQS
       */

    default:
      break;
    }
  return(OP_NO_OP);
}


#define arglist_length(P) gcdr(cdr(P))
#define set_arglist_length(P, X) set_gcdr(cdr(P), X)

static void annotate_args(s7_scheme *sc, s7_pointer args)
{
  s7_pointer p;
  for (p = args; is_pair(p); p = cdr(p))
    set_fcdr(p, (s7_pointer)all_x_eval(sc, car(p)));
}

static void annotate_arg(s7_scheme *sc, s7_pointer arg)
{
  set_fcdr(arg, (s7_pointer)all_x_eval(sc, car(arg)));
}

static s7_pointer check_or(s7_scheme *sc);

static void opt_generator(s7_scheme *sc, s7_pointer func, s7_pointer car_x, int hop)
{
  /* this is an optimization aimed at generators.  So we might as well go all out... */
  s7_pointer body;
  body = closure_body(func);
  if ((s7_list_length(sc, body) == 2) &&
      (caar(body) == sc->LET_SET) &&
      (is_optimized(car(body))) &&
      (optimize_data(car(body)) == HOP_SAFE_C_SQS))
    {
#if (!DISABLE_DEPRECATED)
      if (((caadr(body) == sc->WITH_LET) || (caadr(body) == sc->WITH_ENVIRONMENT)) &&
#else
	  if ((caadr(body) == sc->WITH_LET) &&
#endif
	  (is_symbol(cadr(cadr(body)))) &&
	  (cadr(cadr(body)) == car(closure_args(func))) &&
	  (cadddr(car(body)) == caadr(closure_args(func))))
	{
	  if (is_global(car(car_x)))
	    hop = 1; /* it's my party... */
	  set_optimize_data(car_x, hop + OP_SAFE_CLOSURE_STAR_S0);
	}
    }
}


static bool optimize_func_one_arg(s7_scheme *sc, s7_pointer car_x, s7_pointer func, int hop, int pairs, int symbols, int quotes, int bad_pairs)
{
  bool func_is_safe, func_is_c_function, func_is_closure;
  s7_pointer cadar_x;
  /* very often, car_x is already optimized */
  /* fprintf(stderr, "func_one_arg: %s: hop: %d, pairs: %d %d\n", DISPLAY_80(car_x), hop, pairs, bad_pairs); */

  func_is_closure = is_closure(func);
  if ((func_is_closure) &&
      (closure_arity_to_int(sc, func) != 1))
    return(false);

  if ((is_closure_star(func)) &&
      ((!has_simple_args(closure_body(func))) || 
       (is_null(closure_args(func)))))
    return(false);

  func_is_safe = is_safe_procedure(func);
  func_is_c_function = ((is_c_function(func)) || 
			((is_c_function_star(func)) && 
			 (c_function_all_args(func) == 1))); /* surely no need to check key here? */
  cadar_x = cadr(car_x);

  if (pairs == 0)
    {
      if (func_is_c_function)
	{
	  if (func_is_safe)                  /* safe c function */
	    {     
	      set_optimized(car_x);
	      set_optimize_data(car_x, hop + ((symbols == 0) ? OP_SAFE_C_C : OP_SAFE_C_S));

	      /* we can't simply check is_global here to forego symbol value lookup later because we aren't
	       *    tracking local vars, so the global bit may be on right now, but won't be when
	       *    this code is evaluated.  But memq(sym, e) would catch such cases.
	       *    I think it has already been checked for func, so we only need to look for cadar_x.
	       *    But global symbols are rare, and I don't see a huge savings in the lookup time --
	       *    in callgrind it's about 7/lookup in both cases.
	       */

	      choose_c_function(sc, car_x, func, 1);
	      return(true);
	    }
	  else                               /* c function is not safe */
	    {
	      set_unsafely_optimized(car_x);
	      if (symbols == 0)
		{
		  set_optimize_data(car_x, hop + OP_C_A);
		  annotate_arg(sc, cdr(car_x));
		  set_arglist_length(car_x, small_int(1));
		}
	      else 
		{
		  if (c_function_call(func) == g_read)
		    set_optimize_data(car_x, hop + OP_READ_S);
		  else set_optimize_data(car_x, hop + OP_C_S);
		}
	      choose_c_function(sc, car_x, func, 1);
	      return(false); 
	    }
	}
      else                                   /* func is from scheme */
	{
	  if (func_is_closure)
	    {
	      if (is_symbol(cadar_x))
		{
		  bool safe_case;
		  safe_case = is_safe_closure(func);
		  if (safe_case)
		    {
		      s7_pointer body;
		      body = closure_body(func);
		      /* (let () (define (f1 x) (vector-ref x 0)) (define (f2 x) (f1 x)) (f2 #(0))) 
		       * someday it might be worth pursuing this further
		       */
		      if ((is_pair(body)) &&
			  (is_null(cdr(body))) &&
			  (is_optimized(car(body))) &&  /* snd-test: (power-env pe) is safe_closure but body is not optimized? and c_call is nil */
			  (car(closure_args(func)) == cadar(body)))
			{
			  if ((is_null(cdddr(car(body)))) &&
			      (c_call(car(body)) == g_vector_ref_ic) &&
			      (integer(caddr(car(body))) >= 0) && (integer(caddr(car(body))) < 4))
			    {
			      switch (integer(caddr(car(body))))
				{
				case 0: set_c_function(car_x, vector_ref_ic_0); break;
				case 1: set_c_function(car_x, vector_ref_ic_1); break;
				case 2: set_c_function(car_x, vector_ref_ic_2); break;
				case 3: set_c_function(car_x, vector_ref_ic_3); break;
				}
			      set_optimize_data(car_x, HOP_SAFE_C_C); /* not hop+OP because c_function_is_ok gets confused -- need to fix this! */
			      set_optimized(car_x);
			      return(true);
			    }
			  if (is_safe_c_s(car(body)))
			    {
			      /* direct safe_c_s call by moving body pointers out a level -- this currently never happens
			       *   (let () (define (f1 x) (abs x)) (define (f2 x) (f1 x)) (f2 -1))
			       */
			      set_optimize_data(car_x, optimize_data(car(body)));
			      set_optimized(car_x);
			      set_ecdr(car_x, ecdr(car(body)));
			      set_fcdr(car_x, fcdr(car(body)));
			      return(true);
			    }
			}
		      set_optimize_data(car_x, hop + ((is_global(car(car_x))) ? OP_SAFE_GLOSURE_S : OP_SAFE_CLOSURE_S));
		    }
		  else set_optimize_data(car_x, hop + ((is_global(car(car_x))) ? OP_GLOSURE_S : OP_CLOSURE_S));
		}
	      else set_optimize_data(car_x, hop + ((is_safe_closure(func)) ? OP_SAFE_CLOSURE_C : OP_CLOSURE_C));
	      set_ecdr(car_x, func);
	      set_fcdr(car_x, cadar_x);
	      set_unsafely_optimized(car_x);
	      return(false); 
	    }
	  else /* not closure with normal arglist */
	    {
	      if (s7_is_vector(func))
		{
		  set_optimized(car_x);
		  set_optimize_data(car_x, hop + ((symbols == 1) ? OP_VECTOR_S : OP_VECTOR_C));
		  set_ecdr(car_x, func);
		  return(true);
		}

	      if (is_let(func))
		{
		  set_optimized(car_x);
		  set_optimize_data(car_x, hop + ((symbols == 1) ? OP_ENVIRONMENT_S : OP_ENVIRONMENT_C));
		  set_ecdr(car_x, func);
		  return(true);
		}

	      /* pairs == 0 here */
	      if ((is_closure_star(func)) &&
		  (symbols == 1))
		{
		  set_unsafely_optimized(car_x);
		  if (is_safe_closure(func))
		    {
		      set_optimize_data(car_x, hop + OP_SAFE_CLOSURE_STAR_S);
		      if (closure_star_arity_to_int(sc, func) == 2)
			{
			  s7_pointer defarg2;
			  defarg2 = cadr(closure_args(func));
			  if ((is_pair(defarg2)) &&
			      (is_real(cadr(defarg2))) &&
			      (real(cadr(defarg2)) == 0.0))
			    opt_generator(sc, func, car_x, hop);
			}
		    }
		  else set_optimize_data(car_x, hop + OP_CLOSURE_STAR_S);
		  set_ecdr(car_x, func);
		  set_fcdr(car_x, cadar_x);
		  return(false); 
		}

	      if ((func_is_safe) &&
		  (symbols == 0) &&
		  (is_c_function_star(func)) &&
		  (c_function_simple_defaults(func)))
		{
		  set_optimized(car_x);
		  set_optimize_data(car_x, hop + OP_SAFE_C_STAR_CD);
		  set_c_function(car_x, func);
		  return(true);
		}
	    }
	}
    }
  
  if (pairs == 1)
    {
      if (bad_pairs == 0)
	{
	  /* we can optimize whatever the arg involves */
	  if (func_is_c_function)
	    {
	      if (func_is_safe)
		{
		  int op;
		  op = combine_ops(sc, E_C_P, car_x, cadar_x);
		  set_optimized(car_x);
		  set_optimize_data(car_x, hop + op);
		  /* fallback is Z */
		  if (!hop) 
		    {
		      clear_hop(cadar_x);
		    }
		  else
		    {
		      if ((op == OP_SAFE_C_Z) &&
			  (is_all_x_op(optimize_data(cadar_x))))
			{
			  set_optimize_data(car_x, hop + OP_SAFE_C_A);
			  annotate_arg(sc, cdr(car_x));
			}
		    }
		  choose_c_function(sc, car_x, func, 1);
		  return(true);
		}
	      if (is_all_x_op(optimize_data(cadar_x)))
		{
		  set_unsafely_optimized(car_x);
		  set_optimize_data(car_x, hop + OP_C_A);
		  annotate_arg(sc, cdr(car_x));
		  set_arglist_length(car_x, small_int(1));
		  choose_c_function(sc, car_x, func, 1);
		  return(false); 
		}
	    }
	  else      /* func is from scheme */
	    {
	      if (func_is_closure)
		{
		  if ((is_optimized(cadar_x)) &&
		      (is_all_x_op(optimize_data(cadar_x))))
		    {
		      set_unsafely_optimized(car_x);
		      annotate_arg(sc, cdr(car_x));
		      set_arglist_length(car_x, small_int(1));
		      if (is_safe_closure(func))
			set_optimize_data(car_x, hop + ((is_global(car(car_x))) ? OP_SAFE_GLOSURE_A : OP_SAFE_CLOSURE_A));
		      else set_optimize_data(car_x, hop + ((is_global(car(car_x))) ? OP_GLOSURE_A : OP_CLOSURE_A));
		      set_ecdr(car_x, func);
		      return(false);
		    }
		  if (is_global(car(car_x)))
		    {
		      set_unsafely_optimized(car_x);
		      set_optimize_data(car_x, hop + ((is_safe_closure(func)) ? OP_SAFE_GLOSURE_P : OP_GLOSURE_P));
		      set_ecdr(car_x, func);
		      return(false); 
		    }
		}
	    }
	}
      else      /* bad_pairs is 1 */
	{
	  if (quotes == 1)
	    {
	      if (func_is_c_function)
		{
		  if (func_is_safe)
		    {
		      set_optimized(car_x);
		      set_optimize_data(car_x, hop + OP_SAFE_C_Q);
		      choose_c_function(sc, car_x, func, 1);
		      return(true);
		    }
		  set_unsafely_optimized(car_x);
		  set_optimize_data(car_x, hop + OP_C_A);
		  annotate_arg(sc, cdr(car_x));
		  set_arglist_length(car_x, small_int(1));
		  choose_c_function(sc, car_x, func, 1);
		  return(false); 
		}
	      if (func_is_closure)
		{
		  set_unsafely_optimized(car_x);
		  set_optimize_data(car_x, hop + ((is_safe_closure(func)) ? OP_SAFE_CLOSURE_Q : OP_CLOSURE_Q));
		  set_ecdr(car_x, func);
		  return(false); 
		}
	    }
	  else    /* bad pair is not quoted */
	    {
	      if (func_is_c_function)
		{
		  if (!func_is_safe)
		    {
		      s7_pointer lambda_expr;
		      lambda_expr = cadar_x;
		      if ((is_pair(lambda_expr)) &&
			  (is_lambda(sc, car(lambda_expr))) && /* check for stuff like (define (f) (eval (lambda 2))) */
			  (is_pair(cdr(lambda_expr))) &&   
			  (is_pair(cddr(lambda_expr))))
			{
			  if ((c_function_call(func) == g_call_with_exit) &&
			      (is_pair(cadr(lambda_expr))) &&
			      (is_null(cdadr(lambda_expr))))
			    {
			      set_unsafely_optimized(car_x);
			      set_optimize_data(car_x, hop + OP_CALL_WITH_EXIT);
			      choose_c_function(sc, car_x, func, 1);
			      set_fcdr(car_x, cdr(lambda_expr));
			      return(false);
			    }
			}
		    }
		  set_unsafely_optimized(car_x);
		  set_optimize_data(car_x, hop + OP_C_P);
		  choose_c_function(sc, car_x, func, 1);
		  return(false);
		}

	      if ((func_is_closure) &&
		  (is_global(car(car_x))))
		{
		  set_unsafely_optimized(car_x);
		  set_optimize_data(car_x, hop + ((is_safe_closure(func)) ? OP_SAFE_GLOSURE_P : OP_GLOSURE_P));
		  set_ecdr(car_x, func);
		  return(false); 
		}
	    }
	}

      if ((quotes == 0) &&
	  (func_is_c_function))
	{
	  if (func_is_safe)
	    {
	      set_optimized(car_x);
	      if (bad_pairs == 0)
		{
		  if (is_all_x_safe(sc, cadar_x))
		    {
		      set_optimize_data(car_x, hop + OP_SAFE_C_A);
		      annotate_arg(sc, cdr(car_x));
		    }
		  else set_optimize_data(car_x, hop + OP_SAFE_C_Z); 
		  choose_c_function(sc, car_x, func, 1);
		  set_arglist_length(car_x, small_int(1));
		  return(true);
		}
	      set_unsafe(car_x);
	      set_optimize_data(car_x, hop + OP_SAFE_C_P); /* not c_z because there might be multiple values */
	      choose_c_function(sc, car_x, func, 1);
	      return(false);
	      /* didn't we do all this already? */
	    }
	  else
	    {
	      set_unsafely_optimized(car_x);
	      set_optimize_data(car_x, hop + OP_C_P);
	      choose_c_function(sc, car_x, func, 1);
	      return(false);
	    }
	}
    }

  if (pairs == (quotes + all_x_count(car_x)))
    {
      if (((func_is_closure) ||
	   (is_closure_star(func))) &&
	  (!arglist_has_rest(sc, closure_args(func))))
	{
	  set_unsafely_optimized(car_x);
	  if (func_is_closure)
	    set_optimize_data(car_x, hop + ((is_safe_closure(func) ? OP_SAFE_CLOSURE_A : OP_CLOSURE_A)));
	  else set_optimize_data(car_x, hop + ((is_safe_closure(func) ? OP_SAFE_CLOSURE_STAR_ALL_X : OP_CLOSURE_STAR_ALL_X))); 
	  annotate_arg(sc, cdr(car_x));
	  set_ecdr(car_x, func);
	  set_arglist_length(car_x, small_int(1));
	  return(false);
	}
    }
  return(is_optimized(car_x));  
}


static bool optimize_func_two_args(s7_scheme *sc, s7_pointer car_x, s7_pointer func, int hop, int pairs, int symbols, int quotes, int bad_pairs)
{
  s7_pointer cadar_x, caddar_x;
  bool func_is_safe, func_is_c_function, func_is_closure;
  /* fprintf(stderr, "opt func_2 %s %s, pairs: %d, bad pairs: %d, hop: %d\n", DISPLAY(func), DISPLAY_80(car_x), pairs, bad_pairs, hop); */

  func_is_closure = is_closure(func);
  if ((func_is_closure) &&
      (closure_arity_to_int(sc, func) != 2))
    return(false);

  if ((is_closure_star(func)) &&
      ((!has_simple_args(closure_body(func))) ||
       (closure_star_arity_to_int(sc, func) < 2) ||
       (arglist_has_keyword(cdr(car_x)))))
    return(false);

  cadar_x = cadr(car_x);
  caddar_x = caddr(car_x);
  func_is_safe = is_safe_procedure(func);
  func_is_c_function = ((is_c_function(func)) ||
			((is_c_function_star(func)) && 
			 (c_function_all_args(func) == 2) &&
			 (!is_keyword(cadar_x))));
  if (pairs == 0)
    {
      if (func_is_c_function)
	{
	  if ((func_is_safe) ||
	      (c_function_call(func) == g_member) || /* the unsafe case has a third arg */
	      (c_function_call(func) == g_assoc))
	    {
	      /* another case here: set-car! and set-cdr! are safe if symbols==1 and cadar_x is the symbol (i.e. caddar_x is a constant) */

	      if (symbols == 0)
		set_optimize_data(car_x, hop + OP_SAFE_C_C);
	      else
		{
		  if (symbols == 2)
		    set_optimize_data(car_x, hop + OP_SAFE_C_SS); /* these two symbols are almost never the same, (sqrt (+ (* x x) (* y y))) */
		  else set_optimize_data(car_x, hop + ((is_symbol(cadar_x)) ? OP_SAFE_C_SC : OP_SAFE_C_CS));
		}
	      set_optimized(car_x);
	      choose_c_function(sc, car_x, func, 2);
	      return(true);
	    }
	  set_unsafely_optimized(car_x);
	  if ((symbols == 2) &&
	      (c_function_call(func) == g_apply))
	    {
	      set_optimize_data(car_x, hop + OP_APPLY_SS);
	      set_ecdr(car_x, func);
	      set_fcdr(car_x, caddar_x);
	    }
	  else 
	    {
	      set_optimize_data(car_x, hop + OP_C_ALL_X);
	      annotate_args(sc, cdr(car_x));
	      set_arglist_length(car_x, small_int(2));
	      choose_c_function(sc, car_x, func, 2);
	      if (is_safe_procedure(ecdr(car_x)))
		{
		  clear_unsafe(car_x);
		  set_optimized(car_x);
		  /* symbols can be 0..2 here, no pairs */
		  if (symbols == 1)
		    {
		      if (is_symbol(car(car_x)))
			set_optimize_data(car_x, hop + OP_SAFE_C_SC);
		      else set_optimize_data(car_x, hop + OP_SAFE_C_CS);
		    }
		  else 
		    {
		      if (symbols == 2)
			set_optimize_data(car_x, hop + OP_SAFE_C_SS);
		      else set_optimize_data(car_x, hop + OP_SAFE_C_C);
		    }
		  return(true);
		}
	    }
	  return(false); 
	}
      else
	{
	  if ((func_is_closure) &&
	      (symbols >= 1))
	    {
	      set_unsafely_optimized(car_x);
	      if (symbols == 2)
		{
		  bool safe_case;
		  safe_case = is_safe_closure(func);
		  if (safe_case)
		    set_optimize_data(car_x, hop + OP_SAFE_CLOSURE_SS);
		  else
		    {
		      if (!arglist_has_rest(sc, closure_args(func)))
			set_optimize_data(car_x, hop + OP_CLOSURE_SSb);
		      else set_optimize_data(car_x, hop + OP_CLOSURE_SS);
		    }
		}
	      else
		{
		  if (is_symbol(cadar_x))
		    set_optimize_data(car_x, hop + ((is_safe_closure(func) ? OP_SAFE_CLOSURE_SC : OP_CLOSURE_SC)));
		  else set_optimize_data(car_x, hop + ((is_safe_closure(func) ? OP_SAFE_CLOSURE_CS : OP_CLOSURE_CS)));
		}
	      set_ecdr(car_x, func);
	      set_fcdr(car_x, caddar_x);
	      return(false);
	    }
	  if ((is_closure_star(func)) &&
	      (symbols >= 1) &&
	      (is_symbol(cadar_x)))
	    {
	      set_unsafely_optimized(car_x);
	      if (symbols == 2)
		set_optimize_data(car_x, hop + ((is_safe_closure(func)) ? OP_SAFE_CLOSURE_STAR_SS : OP_CLOSURE_STAR_SX));
	      else 
		{
		  if (is_safe_closure(func))
		    {
		      set_optimize_data(car_x, hop + OP_SAFE_CLOSURE_STAR_SC);
		      if (caddar_x == real_zero)
			opt_generator(sc, func, car_x, hop);
		    }
		  else set_optimize_data(car_x, hop + OP_CLOSURE_STAR_SX);
		}
	      set_ecdr(car_x, func);
	      set_fcdr(car_x, caddar_x);
	      return(false); 
	    }
	}
    }
  else /* pairs != 0 */
    {
      if (bad_pairs == 0)
	{
	  /* using safe_c_aa here whenever possible was much slower */
	  
	  if (pairs == 2)
	    {
	      if (func_is_c_function)
		{
		  if ((func_is_safe) ||
		      (c_function_call(func) == g_member) ||
		      (c_function_call(func) == g_assoc))
		    {
		      int op;
		      op = combine_ops(sc, E_C_PP, cadar_x, caddar_x);
		      set_optimized(car_x);
		      set_optimize_data(car_x, hop + op);
		      /* fallback here is ZZ */
		      if (!hop) 
			{
			  clear_hop(cadar_x);
			  clear_hop(caddar_x);
			}
		      else
			{
			  if (op == OP_SAFE_C_ZZ)
			    {
			      if (is_all_x_safe(sc, cadar_x))
				{
				  if (is_all_x_safe(sc, caddar_x))
				    {
				      set_optimize_data(car_x, hop + OP_SAFE_C_AA);
				      annotate_args(sc, cdr(car_x));
				      set_arglist_length(car_x, small_int(2));
				    }
				  else
				    {
				      if (optimize_data(cadar_x) == HOP_SAFE_C_C)
					set_optimize_data(car_x, hop + OP_SAFE_C_opCq_Z);
				      else 
					{
					  set_optimize_data(car_x, hop + OP_SAFE_C_AZ);
					  annotate_arg(sc, cdr(car_x));
					  set_arglist_length(car_x, small_int(2));
					}
				    }
				}
			      else
				{
				  if (is_all_x_safe(sc, caddar_x))
				    {
				      set_optimize_data(car_x, hop + OP_SAFE_C_ZA);
				      annotate_arg(sc, cddr(car_x));
				      set_arglist_length(car_x, small_int(2));
				    }
				}
			    }
			}
		      choose_c_function(sc, car_x, func, 2); /* this might change the op to safe_c_c, so it has to be last */
		      return(true);
		    }
		}
	      else
		{
		  if ((func_is_closure) &&
		      (is_safe_c_s(cadar_x)) &&
		      (is_safe_c_s(caddar_x)))
		    {
		      set_unsafely_optimized(car_x);
		      if (is_safe_closure(func))
			set_optimize_data(car_x, hop + OP_SAFE_CLOSURE_opSq_opSq);
		      else set_optimize_data(car_x, hop + OP_CLOSURE_opSq_opSq);
		      set_ecdr(car_x, func);
		      set_fcdr(car_x, cadr(caddar_x));
		      set_fcdr(cdr(car_x), cadr(cadar_x));
		      return(false);
		    }
		}
	    }
	  if (pairs == 1)
	    {
	      if (func_is_c_function)
		{
		  if ((func_is_safe) ||
		      (c_function_call(func) == g_member) ||
		      (c_function_call(func) == g_assoc))
		    {
		      combine_op_t orig_op;
		      int op;
		      
		      if (is_pair(cadar_x))
			{
			  if (is_symbol(caddar_x))
			    orig_op = E_C_PS;
			  else orig_op = E_C_PC;
			  op = combine_ops(sc, orig_op, car_x, cadar_x);
			  if (!hop) clear_hop(cadar_x);
			}
		      else 
			{
			  if (is_symbol(cadar_x))
			    orig_op = E_C_SP;
			  else orig_op = E_C_CP;
			  op = combine_ops(sc, orig_op, car_x, caddar_x);
			  if (!hop) clear_hop(caddar_x);
			}

		      set_optimized(car_x);
		      set_optimize_data(car_x, hop + op);
		      choose_c_function(sc, car_x, func, 2);
		      return(true);
		    }
		  if (symbols == 1)
		    {
		      if (is_symbol(cadar_x))
			{
			  if (is_safe_c_s(caddar_x))
			    {
			      set_unsafely_optimized(car_x);
			      set_optimize_data(car_x, hop + OP_C_S_opSq);
			      set_ecdr(cdr(car_x), cadr(caddar_x));
			      choose_c_function(sc, car_x, func, 2);
			      return(false);
			    }
			  if (optimize_data_match(caddar_x, OP_SAFE_C_C))
			    {
			      set_unsafely_optimized(car_x);
			      set_optimize_data(car_x, hop + OP_C_S_opCq);
			      set_ecdr(cdr(car_x), cdr(caddar_x));
			      choose_c_function(sc, car_x, func, 2);
			      return(false);
			    }
			}
		    }
		}
	      else
		{
		  if (func_is_closure)
		    {
		      if (is_symbol(cadar_x))
			{
			  if (is_safe_c_s(caddar_x))
			    {
			      set_unsafely_optimized(car_x);
			      set_optimize_data(car_x, hop + ((is_safe_closure(func) ? OP_SAFE_CLOSURE_S_opSq : OP_CLOSURE_S_opSq)));
			      set_ecdr(car_x, func);
			      return(false); 
			    }
			  if ((is_optimized(caddar_x)) &&
			      (op_no_hop(caddar_x) == OP_SAFE_C_SS))
			    {
			      set_unsafely_optimized(car_x);
			      set_optimize_data(car_x, hop + ((is_safe_closure(func) ? OP_SAFE_CLOSURE_S_opSSq : OP_CLOSURE_S_opSSq)));
			      set_ecdr(car_x, func);
			      return(false); 
			    }
			}
		      if (is_symbol(caddar_x))
			{
			  if (is_safe_c_s(cadar_x))
			    {
			      set_unsafely_optimized(car_x);
			      set_optimize_data(car_x, hop + ((is_safe_closure(func) ? OP_SAFE_CLOSURE_opSq_S : OP_CLOSURE_opSq_S)));
			      set_ecdr(car_x, func);
			      set_fcdr(car_x, caddar_x);
			      return(false); 
			    }
			  if ((is_optimized(cadar_x)) &&
			      (op_no_hop(cadar_x) == OP_SAFE_C_SS))
			    {
			      set_unsafely_optimized(car_x);
			      set_optimize_data(car_x, hop + ((is_safe_closure(func) ? OP_SAFE_CLOSURE_opSSq_S : OP_CLOSURE_opSSq_S)));
			      set_ecdr(car_x, func);
			      return(false); 
			    }
			}
		    }
		}
	    }
	}
      else /* pairs != 0 and bad_pairs != 0 */
	{
	  if ((bad_pairs == 1) && (quotes == 1))
	    {
	      if (func_is_c_function)
		{
		  if ((func_is_safe) ||
		      (c_function_call(func) == g_member) ||
		      (c_function_call(func) == g_assoc))
		    {
		      if  (symbols == 1)
			{
			  set_optimized(car_x);
			  if (is_symbol(cadar_x))
			    set_optimize_data(car_x, hop + OP_SAFE_C_SQ);
			  else set_optimize_data(car_x, hop + OP_SAFE_C_QS);
			  choose_c_function(sc, car_x, func, 2);
			  return(true);
			}
		      else
			{
			  if (pairs == 1)
			    {
			      /* Q must be 1, symbols = 0, pairs = 1 (the quote), so this must be CQ or QC?
			       */
			      set_optimized(car_x);
			      if (is_pair(cadar_x))
				set_optimize_data(car_x, hop + OP_SAFE_C_QC);
			      else set_optimize_data(car_x, hop + OP_SAFE_C_CQ);
			      choose_c_function(sc, car_x, func, 2);
			      return(true);
			    }
			}
		    }
		  else
		    {
		      if (pairs == 1)
			{
			  set_unsafely_optimized(car_x);
			  set_optimize_data(car_x, hop + OP_C_ALL_X);
			  annotate_args(sc, cdr(car_x));
			  set_arglist_length(car_x, small_int(2));
			  choose_c_function(sc, car_x, func, 2);
			  return(false); 
			}
		    }
		}
	    }
	  else
	    {
	      if (quotes == 2)
		{
		  if (func_is_c_function)
		    {
		      if ((func_is_safe) ||
			  (c_function_call(func) == g_member) ||
			  (c_function_call(func) == g_assoc))
			{
			  set_optimized(car_x);
			  set_optimize_data(car_x, hop + OP_SAFE_C_QQ);
 			  choose_c_function(sc, car_x, func, 2);
			  return(true);
			}
		      set_unsafely_optimized(car_x);
		      set_optimize_data(car_x, hop + OP_C_ALL_X);
		      annotate_args(sc, cdr(car_x));
		      set_arglist_length(car_x, small_int(2));
		      choose_c_function(sc, car_x, func, 2);
		      return(false); 
		    }
		}
	      else
		{
		  if ((bad_pairs == 1) &&
		      (is_pair(cadar_x)) &&
		      (func_is_c_function) &&
		      ((is_symbol(caddar_x)) ||
		       ((pairs == 2) &&
			(is_safe_c_s(caddar_x)))))
		    {
		      s7_pointer lambda_expr;
		      lambda_expr = cadar_x;
		      
		      if ((is_pair(lambda_expr)) &&
			  (is_lambda(sc, car(lambda_expr))) && /* check for stuff like (define (f) (for-each (lambda 2) ...)) */
			  (is_pair(cdr(lambda_expr))) &&   
			  (is_pair(cddr(lambda_expr))) &&
			  (s7_list_length(sc, cadr(lambda_expr)) == 1) &&
			  (is_symbol(caadr(lambda_expr))) &&
			  (!is_immutable(caadr(lambda_expr))))
			{
			  choose_c_function(sc, car_x, func, 2);
			  if ((c_call(car_x) == g_for_each_1) &&
			      (is_symbol(caddar_x)))
			    {
			      set_unsafely_optimized(car_x);
			      set_optimize_data(car_x, hop + OP_C_FOR_EACH_LS);
			      return(false);
			    }
			  if (c_call(car_x) == g_for_each_2)
			    {
			      /* args are safe, body is one-liner */
			      set_unsafely_optimized(car_x);
			      if (is_symbol(caddar_x))
				set_optimize_data(car_x, hop + OP_C_FOR_EACH_LS_2);
			      else set_optimize_data(car_x, hop + OP_C_FOR_EACH_L_opSq);
			      return(false);
			    }
			}
		    }
		}
	    }
	}
    }

  if ((pairs == 1) &&
      (quotes == 0) &&
      (func_is_c_function) &&
      ((func_is_safe) ||
       (c_function_call(func) == g_member) ||
       (c_function_call(func) == g_assoc)))
    {
      if (symbols == 1)
	{
	  set_optimized(car_x);
	  if (bad_pairs == 0)
	    {
	      set_optimize_data(car_x, hop + ((is_symbol(cadar_x)) ? OP_SAFE_C_SZ : OP_SAFE_C_ZS));
	      choose_c_function(sc, car_x, func, 2);
	      return(true);
	    }
	  /* unknowns get here: (* amp (amps 0))
	   * also list: (make-polywave pitch (list 1 0.93 2 0.07))
	   * and (* vol (granulate gen))
	   */
	  set_unsafe(car_x);
	  set_optimize_data(car_x, hop + ((is_symbol(cadar_x)) ? OP_SAFE_C_SP : OP_SAFE_C_PS));
	  choose_c_function(sc, car_x, func, 2);
	  
	  if (is_symbol(caddar_x))
	    {
	      if ((is_optimized(cadar_x)) &&
		  (is_symbol(car(cadar_x))) && /* this is *really* tricky but sc->QQ_List is the bad case (i.e. function itself can be car) */
		  (is_symbol(cadr(cadar_x))) &&
		  (optimize_data(cadar_x) == HOP_UNKNOWN_G))
		set_optimize_data(car_x, hop + OP_SAFE_C_opVSq_S);
	    }
	  else
	    {
	      if ((is_optimized(caddar_x)) &&
		  (is_symbol(car(caddar_x))) &&
		  (is_symbol(cadr(caddar_x))) &&
		  (optimize_data(caddar_x) == HOP_UNKNOWN_G))
		set_optimize_data(car_x, hop + OP_SAFE_C_S_opVSq);
	    }
	  return(false);
	}
      if (symbols == 0)
	{
	  set_optimized(car_x);
	  if (bad_pairs == 0)
	    {
	      set_optimize_data(car_x, hop + ((is_pair(cadar_x)) ? OP_SAFE_C_CZ : OP_SAFE_C_ZC)); /* was symbol?? */
	      /* fprintf(stderr, "%d %s: %s\n", __LINE__, opt_name(car_x), DISPLAY(car_x)); */
	      choose_c_function(sc, car_x, func, 2);
	      return(true);
	    }
	  else
	    {
	      set_unsafe(car_x);
	      set_optimize_data(car_x, hop + ((is_pair(cadar_x)) ? OP_SAFE_C_PC : OP_SAFE_C_CP));
	      choose_c_function(sc, car_x, func, 2);
	      return(false);
	    }
	}
    }
  
  if ((pairs == 2) &&
      (func_is_c_function) &&
      ((func_is_safe) ||
       (c_function_call(func) == g_member) ||
       (c_function_call(func) == g_assoc)))
    {
      if ((bad_pairs == 1) &&
	  (is_safe_c_s(cadar_x)))
	{
	  /* unsafe func here won't work unless we check that later and make the new arg list (for {list} etc)
	   *   (and it has to be the last pair else the unknown_g stuff can mess up)
	   */
	  set_unsafely_optimized(car_x);
	  set_optimize_data(car_x, hop + OP_SAFE_C_opSq_P);
	  choose_c_function(sc, car_x, func, 2);
	  return(false);
	}
      else
	{
	  if (quotes == 0) 
	    {
	      set_unsafely_optimized(car_x);
	      if (is_all_x_safe(sc, cadar_x))
		{
		  set_optimize_data(car_x, hop + OP_SAFE_C_AP);
		  annotate_arg(sc, cdr(car_x));
		}
	      else 
		{
		  set_optimize_data(car_x, hop + OP_SAFE_C_PP);
		  if ((is_optimized(cadar_x)) &&
		      (is_symbol(car(cadar_x))) &&
		      (is_symbol(cadr(cadar_x))) &&
		      (optimize_data(cadar_x) == HOP_UNKNOWN_G) &&
		      (is_optimized(caddar_x)) &&
		      (is_symbol(car(caddar_x))) &&
		      (is_symbol(cadr(caddar_x))) &&
		      (optimize_data(caddar_x) == HOP_UNKNOWN_G))
		    set_optimize_data(car_x, hop + OP_SAFE_C_opVSq_opVSq);
		}
	      choose_c_function(sc, car_x, func, 2);
	      return(false);
	    }
	  else
	    {
	      if (quotes == 1)
		{
		  if (car(cadar_x) == sc->QUOTE)
		    set_optimize_data(car_x, hop + OP_SAFE_C_QP);
		  else set_optimize_data(car_x, hop + OP_SAFE_C_PQ);
		  set_unsafely_optimized(car_x);
		  choose_c_function(sc, car_x, func, 2);
		  return(false);
		}
	    }
	}
    }

  if (pairs == (quotes + all_x_count(car_x)))
    {
      if ((func_is_c_function) &&
	  (func_is_safe))
	{
	  set_optimized(car_x);
	  set_optimize_data(car_x, hop + OP_SAFE_C_AA);
	  annotate_args(sc, cdr(car_x));
	  set_arglist_length(car_x, small_int(2));
	  choose_c_function(sc, car_x, func, 2);
	  return(true);
	}
      if (((func_is_closure) ||
	   (is_closure_star(func))) &&
	  (!arglist_has_rest(sc, closure_args(func))))
	{
	  set_unsafely_optimized(car_x);
	  if (func_is_closure)
	    {
	      if (is_safe_closure(func))
		{
		  if (is_symbol(cadar_x))
		    set_optimize_data(car_x, hop + OP_SAFE_CLOSURE_SA);
		  else set_optimize_data(car_x, hop + OP_SAFE_CLOSURE_AA);
		}
	      else set_optimize_data(car_x, hop + OP_CLOSURE_AA);
	    }
	  else 
	    {
	      if (is_safe_closure(func))
		{
		  if ((is_symbol(cadar_x)) &&
		      (closure_star_arity_to_int(sc, func) == 2))
		    set_optimize_data(car_x, hop + OP_SAFE_CLOSURE_STAR_SA);
		  else set_optimize_data(car_x, hop + OP_SAFE_CLOSURE_STAR_ALL_X);
		}
	      else set_optimize_data(car_x, hop + OP_CLOSURE_STAR_ALL_X);
	    }
	  annotate_args(sc, cdr(car_x));
	  set_ecdr(car_x, func);
	  set_arglist_length(car_x, small_int(2));
	  return(false);
	}
    }

  if ((pairs == 1) &&
      (symbols == 1) &&
      (quotes == 0) &&
      (func_is_c_function) &&
      (!func_is_safe) &&
      (is_symbol(cadar_x)))
    {
      set_unsafely_optimized(car_x);
      set_optimize_data(car_x, hop + OP_C_SP);
      choose_c_function(sc, car_x, func, 2);
      return(false);
    }
  return(is_optimized(car_x));  
}


static bool optimize_func_three_args(s7_scheme *sc, s7_pointer car_x, s7_pointer func, int hop, int pairs, int symbols, int quotes, int bad_pairs)
{
  s7_pointer cadar_x, caddar_x, cadddar_x;
  bool func_is_safe, func_is_c_function, func_is_closure;

  func_is_closure = is_closure(func);

  if ((func_is_closure) &&
      (closure_arity_to_int(sc, func) != 3))
    return(false);

  if ((is_closure_star(func)) &&
      ((!has_simple_args(closure_body(func))) ||
       (closure_star_arity_to_int(sc, func) < 3) ||
       (arglist_has_keyword(cdr(car_x)))))
    return(false);

  cadar_x = cadr(car_x);
  caddar_x = caddr(car_x);
  cadddar_x = cadddr(car_x);

  func_is_safe = is_safe_procedure(func);
  func_is_c_function = ((is_c_function(func)) ||
			((is_c_function_star(func)) && 
			 (c_function_all_args(func) == 3) &&
			 (!is_keyword(cadar_x)) &&
			 (!is_keyword(caddar_x))));

  /* here quotes in safe functions are somewhat rare: list-ref, format, etc */
  if (pairs == 0)
    {
      if (func_is_c_function)
	{
	  if (func_is_safe)
	    {
	      set_optimized(car_x);
	      if (symbols == 0)
		set_optimize_data(car_x, hop + OP_SAFE_C_C);
	      else 
		{
		  if (symbols == 3)
		    {
		      set_optimize_data(car_x, hop + OP_SAFE_C_SSS);
		      set_ecdr(cdr(car_x), caddar_x);
		      set_fcdr(cdr(car_x), cadddar_x);
		    }
		  else
		    {
		      if (symbols == 2)
			{
			  set_ecdr(cdr(car_x), caddar_x);
			  set_fcdr(cdr(car_x), cadddar_x);
			  if (!is_symbol(cadar_x))
			    set_optimize_data(car_x, hop + OP_SAFE_C_CSS);
			  else
			    {
			      if (!is_symbol(cadddar_x))
				{
				  if (is_keyword(caddar_x))
				    set_optimize_data(car_x, hop + OP_SAFE_C_SCC);
				  else set_optimize_data(car_x, hop + OP_SAFE_C_SSC);
				}
			      else set_optimize_data(car_x, hop + OP_SAFE_C_SCS);
			    }
			}
		      else 
			{
			  if (is_symbol(cadar_x))
			    {
			      set_ecdr(cdr(car_x), caddar_x);
			      set_fcdr(cdr(car_x), cadddar_x);
			      set_optimize_data(car_x, hop + OP_SAFE_C_SCC);
			    }
			  else
			    {
			      if (is_symbol(caddar_x))
				{
				  set_ecdr(cdr(car_x), caddar_x);
				  set_fcdr(cdr(car_x), cadddar_x);
				  set_optimize_data(car_x, hop + OP_SAFE_C_CSC);
				}
			      else
				{
				  set_optimize_data(car_x, hop + OP_SAFE_C_AAA); /* fallback on all_x_c and s here -- a kludge */
				  annotate_args(sc, cdr(car_x));
				  set_arglist_length(car_x, small_int(3));
				}
			    }
			}
		    }
		}
	      choose_c_function(sc, car_x, func, 3);
	      return(true);
	    }
	}
      else
	{
	  if ((func_is_closure) &&
	      (symbols == 3) &&
	      (!is_safe_closure(func)) &&
	      (!arglist_has_rest(sc, closure_args(func))))
	    {
	      set_unsafely_optimized(car_x);
	      set_ecdr(car_x, func);
	      set_arglist_length(car_x, small_int(3));
	      set_optimize_data(car_x, hop + OP_CLOSURE_ALL_S);
	      return(false);
	    }
	}
    }

  if (pairs == quotes + all_x_count(car_x))
    {
      if (func_is_c_function)
	{
	  set_optimized(car_x);
	  if (func_is_safe)
	    {
	      if ((symbols == 2) &&
		  (quotes == 1))
		{
		  if ((is_symbol(cadar_x)) &&
		      (is_symbol(cadddar_x)))
		    {
		      set_ecdr(cdr(car_x), cadr(caddar_x));
		      set_fcdr(cdr(car_x), cadddar_x);
		      set_optimize_data(car_x, hop + OP_SAFE_C_SQS);
		      choose_c_function(sc, car_x, func, 3);
		      return(true);
		    }
		}
	      annotate_args(sc, cdr(car_x));
	      set_arglist_length(car_x, small_int(3));
	      set_optimize_data(car_x, hop + OP_SAFE_C_AAA);

	      if ((pairs == 1) &&
		  (symbols == 1))
		{
		  if (is_pair(cadddar_x))
		    {
		      if (is_symbol(caddar_x))
			set_optimize_data(car_x, hop + OP_SAFE_C_CSA);
		      else set_optimize_data(car_x, hop + OP_SAFE_C_SCA);
		    }
		  else
		    {
		      if ((is_pair(caddar_x)) &&
			  (is_symbol(cadddar_x)))
			set_optimize_data(car_x, hop + OP_SAFE_C_CAS);
		    }
		}
	      else
		{
		  if ((is_symbol(cadar_x)) &&
		      (is_symbol(caddar_x)))
		    set_optimize_data(car_x, hop + OP_SAFE_C_SSA);
		  /* perhaps SAFE_C_SS_opSq or opCq */
		}
	      choose_c_function(sc, car_x, func, 3);
	    }
	  else 
	    {
	      annotate_args(sc, cdr(car_x));
	      set_arglist_length(car_x, small_int(3));
	      set_optimize_data(car_x, hop + OP_C_ALL_X);
	      choose_c_function(sc, car_x, func, 3);
	      if (optimize_data(car_x) != HOP_SAFE_C_C) /* did chooser fix it up? */
		{
		  set_unsafe(car_x);
		  return(false); 
		}
	    }
	  return(true);
	}
      else 
	{
	  if (((func_is_closure) ||
	       (is_closure_star(func))) &&
	      (!arglist_has_rest(sc, closure_args(func))))
	    {
	      set_unsafely_optimized(car_x);
	      if (func_is_closure)
		{
		  if (is_safe_closure(func))
		    {
		      if (is_symbol(cadar_x))
			set_optimize_data(car_x, hop + OP_SAFE_CLOSURE_SAA);
		      else set_optimize_data(car_x, hop + OP_SAFE_CLOSURE_ALL_X);
		    }
		  else set_optimize_data(car_x, hop + OP_CLOSURE_ALL_X);
		}
	      else set_optimize_data(car_x, hop + ((is_safe_closure(func) ? OP_SAFE_CLOSURE_STAR_ALL_X : OP_CLOSURE_STAR_ALL_X)));
	      annotate_args(sc, cdr(car_x));
	      set_ecdr(car_x, func);
	      set_arglist_length(car_x, small_int(3));
	      return(false);
	    }
	}
    }

  if ((bad_pairs == 0) &&
      (func_is_c_function) &&
      (func_is_safe) &&
      (pairs > 0))
    {
      if ((symbols == 2) &&
	  (is_symbol(cadar_x)) &&
	  (is_symbol(caddar_x)))
	{
	  set_optimize_data(car_x, hop + OP_SAFE_C_SSZ);
	}
      else
	{
	  /* use either X or Z in all 8 choices */
	  if ((!is_pair(cadar_x)) ||
	      (is_all_x_op(optimize_data(cadar_x))))
	    {
	      annotate_arg(sc, cdr(car_x));
	      if ((!is_pair(caddar_x)) ||
		  (is_all_x_op(optimize_data(caddar_x))))
		{
		  set_optimize_data(car_x, hop + OP_SAFE_C_AAZ); /* here last can't be A because we checked for that above */
		  annotate_arg(sc, cddr(car_x));
		}
	      else
		{
		  if ((!is_pair(cadddar_x)) ||
		      (is_all_x_op(optimize_data(cadddar_x))))
		    {
		      set_optimize_data(car_x, hop + OP_SAFE_C_AZA); 
		      annotate_arg(sc, cdddr(car_x));
		    }
		  else set_optimize_data(car_x, hop + OP_SAFE_C_AZZ); 
		}
	    }
	  else
	    {
	      if ((!is_pair(caddar_x)) ||
		  (is_all_x_op(optimize_data(caddar_x))))
		{
		  annotate_arg(sc, cddr(car_x));
		  if ((!is_pair(cadddar_x)) ||
		      (is_all_x_op(optimize_data(cadddar_x))))
		    {
		      set_optimize_data(car_x, hop + OP_SAFE_C_ZAA); 
		      annotate_arg(sc, cdddr(car_x));
		    }
		  else set_optimize_data(car_x, hop + OP_SAFE_C_ZAZ); 
		}
	      else
		{
		  if ((!is_pair(cadddar_x)) ||
		      (is_all_x_op(optimize_data(cadddar_x))))
		    {
		      set_optimize_data(car_x, hop + OP_SAFE_C_ZZA); 
		      annotate_arg(sc, cdddr(car_x));
		    }
		  else set_optimize_data(car_x, hop + OP_SAFE_C_ZZZ); 
		}
	    }
	}
      set_optimized(car_x);		  
      choose_c_function(sc, car_x, func, 3);
      set_arglist_length(car_x, small_int(3));
    }

  /* aap is not better than ssp, sap also saves very little */
  if ((pairs == 1) &&
      (bad_pairs == 1) &&
      (symbols == 2) &&
      (func_is_c_function) &&
      (func_is_safe) &&
      (is_pair(cadddar_x)))
    {
      set_optimized(car_x);
      set_unsafe(car_x);
      set_optimize_data(car_x, hop + OP_SAFE_C_SSP);
      choose_c_function(sc, car_x, func, 3);
      return(false);
    }

  /* (define (hi) (catch #t (lambda () 1) (lambda args 2))) 
   *   first arg list must be (), second a symbol
   */
  if ((func_is_c_function) &&
      (c_function_call(func) == g_catch))
    {
      if (bad_pairs == 2)
	{
	  s7_pointer body_lambda, error_lambda;
	  body_lambda = caddar_x;
	  error_lambda = cadddar_x;
	  
	  if ((is_pair(body_lambda)) &&
	      (is_lambda(sc, car(body_lambda))) &&
	      (is_pair(error_lambda)) &&
	      (is_lambda(sc, car(error_lambda))) &&
	      (is_null(cadr(body_lambda))) &&
	      (is_not_null(cddr(body_lambda))) &&
	      (is_symbol(cadr(error_lambda))) &&
	      (!is_immutable(cadr(error_lambda))) &&
	      (is_not_null(cddr(error_lambda))))
	    {
	      s7_pointer error_result;
	      error_result = caddr(error_lambda);
	      set_unsafely_optimized(car_x);
	      if ((cadar_x == sc->T) &&
		  (is_null(cdddr(error_lambda))) &&
		  (!is_symbol(error_result)) &&
		  ((!is_pair(error_result)) || (car(error_result) == sc->QUOTE)))
		{
		  set_optimize_data(car_x, hop + OP_C_CATCH_ALL);
		  set_c_function(car_x, func);
		  if (is_pair(error_result))
		    set_fcdr(car_x, cadr(error_result));
		  else set_fcdr(car_x, error_result);
		  set_ecdr(cdr(car_x), cddr(body_lambda));
		}
	      else 
		{
		  set_optimize_data(car_x, hop + OP_C_CATCH);
		  choose_c_function(sc, car_x, func, 3);
		}
	      return(false);
	    }
	}
      else
	{
	  if ((bad_pairs == 1) &&
	      (is_symbol(caddar_x)))
	    {
	      s7_pointer error_lambda;
	      error_lambda = cadddar_x;
	      
	      if ((is_pair(error_lambda)) &&
		  (is_lambda(sc, car(error_lambda))) &&
		  (is_symbol(cadr(error_lambda))) &&
		  (!is_immutable(cadr(error_lambda))) &&
		  (is_not_null(cddr(error_lambda))))
		{
		  /* hop = 0; */
		  set_unsafely_optimized(car_x);
		  set_optimize_data(car_x, hop + OP_C_CATCH);
		  choose_c_function(sc, car_x, func, 3);
		  return(false);
		}
	    }
	}
    }

  if (bad_pairs > quotes) return(false);
  return(is_optimized(car_x));
}


static bool optimize_func_many_args(s7_scheme *sc, s7_pointer car_x, s7_pointer func, int hop, int args, int pairs, int symbols, int quotes, int bad_pairs)
{
  bool func_is_safe, func_is_c_function, func_is_closure;

  func_is_closure = is_closure(func);

  if ((func_is_closure) &&
      (closure_arity_to_int(sc, func) != args))
    return(false);

  if ((is_closure_star(func)) &&
      ((!has_simple_args(closure_body(func))) ||
       (closure_star_arity_to_int(sc, func) < args) ||
       (arglist_has_keyword(cdr(car_x)))))
    return(false);

  if (bad_pairs > quotes) return(false);

  func_is_safe = is_safe_procedure(func);
  func_is_c_function = is_c_function(func);
  if (func_is_c_function)
    {
      if (func_is_safe)
	{
	  if (pairs == 0)
	    {
	      if (symbols == 0)
		{
		  set_optimized(car_x);
		  set_optimize_data(car_x, hop + OP_SAFE_C_C);
		  choose_c_function(sc, car_x, func, args);
		  return(true);
		}
	      if ((symbols == args) &&
		  (args < GC_TRIGGER_SIZE))
		{
		  set_optimized(car_x);
		  set_optimize_data(car_x, hop + OP_SAFE_C_ALL_S);
		  if (args < NUM_SMALL_INTS)
		    set_arglist_length(car_x, small_int(args));
		  else set_arglist_length(car_x, make_permanent_integer(args));
		  choose_c_function(sc, car_x, func, args);
		  return(true);
		}
	    }
	}
    }
  else
    {
      if ((func_is_closure) &&
	  (pairs == 0) &&
	  ((symbols == args) || (symbols == 0)) &&
	  (args < GC_TRIGGER_SIZE))
	{
	  bool safe_case;
	  safe_case = is_safe_closure(func);
	  set_unsafely_optimized(car_x);
	  set_optimize_data(car_x, hop + ((safe_case) ? OP_SAFE_CLOSURE_ALL_X : OP_CLOSURE_ALL_X));
	  annotate_args(sc, cdr(car_x));
	  if (args < NUM_SMALL_INTS)
	    set_arglist_length(car_x, small_int(args));
	  else set_arglist_length(car_x, make_permanent_integer(args));
	  set_ecdr(car_x, func);
	      
	  if ((!safe_case) &&
	      (symbols == args) &&
	      (!arglist_has_rest(sc, closure_args(func))))
	    set_optimize_data(car_x, hop + OP_CLOSURE_ALL_S);
	  return(false);
	}
    }
  
  if ((func_is_c_function) &&
      (!func_is_safe) &&
      (pairs == (quotes + all_x_count(car_x))) &&
      (args < GC_TRIGGER_SIZE))
    {
      set_unsafely_optimized(car_x);
      set_optimize_data(car_x, hop + OP_C_ALL_X);
      annotate_args(sc, cdr(car_x));
      if (args < NUM_SMALL_INTS)
	set_arglist_length(car_x, small_int(args));
      else set_arglist_length(car_x, make_permanent_integer(args));
      choose_c_function(sc, car_x, func, args);
      return(false); 
      /* else use all_x below */
    }
  
  if (args < GC_TRIGGER_SIZE)
    {
      if ((func_is_c_function) &&
	  (func_is_safe) &&
	  (pairs == (quotes + all_x_count(car_x))))
	{
	  set_optimized(car_x);
	  if (args == 4) 
	    set_optimize_data(car_x, hop + OP_SAFE_C_AAAA);
	  else set_optimize_data(car_x, hop + OP_SAFE_C_ALL_X);
	  annotate_args(sc, cdr(car_x));
	  if (args < NUM_SMALL_INTS)
	    set_arglist_length(car_x, small_int(args));
	  else set_arglist_length(car_x, make_permanent_integer(args));
	  choose_c_function(sc, car_x, func, args);
	  return(true);
	}
      if (((func_is_closure) ||
	   (is_closure_star(func))) && 
	  (!arglist_has_rest(sc, closure_args(func))) &&
	  (pairs == (quotes + all_x_count(car_x))))
	{
	  set_unsafely_optimized(car_x);
	  if (func_is_closure)
	    set_optimize_data(car_x, hop + ((is_safe_closure(func)) ? OP_SAFE_CLOSURE_ALL_X : OP_CLOSURE_ALL_X));
	  else set_optimize_data(car_x, hop + ((is_safe_closure(func)) ? OP_SAFE_CLOSURE_STAR_ALL_X : OP_CLOSURE_STAR_ALL_X));
	  annotate_args(sc, cdr(car_x));
	  if (args < NUM_SMALL_INTS)
	    set_arglist_length(car_x, small_int(args));
	  else set_arglist_length(car_x, make_permanent_integer(args));
	  set_ecdr(car_x, func);
	  return(false); 
	}
    }
  
  if ((bad_pairs == 0) &&
      (func_is_c_function) &&
      (func_is_safe))
    {
      if ((args == 4) && (pairs == 4))
	{
	  set_optimized(car_x);
	  set_optimize_data(car_x, hop + OP_SAFE_C_ZZZZ);
	}
      choose_c_function(sc, car_x, func, args); /* don't assume hop_safe_c_c here -- let chooser set that */
    }
  return(is_optimized(car_x));
}


static bool optimize_syntax(s7_scheme *sc, s7_pointer x, s7_pointer func, int hop, s7_pointer e)
{
  s7_pointer p;
  opcode_t op;

  p = car(x);
  /* fprintf(stderr, "opt syntax %s\n     hop: %d, e: %s\n", DISPLAY(p), hop, DISPLAY(e)); */

  if (!is_pair(cdr(p))) /* cddr(p) might be null if, for example, (begin (let ...)) */
    return(false);

  op = (opcode_t)syntax_opcode(func);
  sc->w = e;
  switch (op)
    {
    case OP_QUOTE:
      return(false);

    case OP_LET:
      if (is_symbol(cadr(p)))
	{
	  if (global_slot(cadr(p)) != sc->NIL)
	    sc->w = cons(sc, add_sym_to_list(sc, cadr(p)), sc->w);
	  sc->w = collect_collisions(sc, caddr(p), sc->w);
	}
      else sc->w = collect_collisions(sc, cadr(p), sc->w);
      break;
      
    case OP_LET_STAR:
    case OP_LETREC:
    case OP_LETREC_STAR:
      sc->w = collect_collisions(sc, cadr(p), sc->w);
      break;

    case OP_DEFINE_MACRO:
    case OP_DEFINE_MACRO_STAR:
    case OP_DEFINE_BACRO:
    case OP_DEFINE_BACRO_STAR:
    case OP_DEFINE:
    case OP_DEFINE_STAR:
      if ((is_pair(cadr(p))) &&
	  (is_symbol(caadr(p))))
	sc->w = cons(sc, add_sym_to_list(sc, caadr(p)), sc->w);
      /* fall through */

    case OP_LAMBDA:
    case OP_LAMBDA_STAR:
      if (is_symbol(cadr(p))) /* (lambda args ...) */
	{
	  if (global_slot(cadr(p)) != sc->NIL)
	    sc->w = cons(sc, add_sym_to_list(sc, cadr(p)), sc->w);
	}
      else sc->w = collect_collisions(sc, cadr(p), sc->w);
      break;

    case OP_SET:
      if ((is_symbol(cadr(p))) &&
	  (global_slot(cadr(p)) != sc->NIL))
	sc->w = cons(sc, add_sym_to_list(sc, cadr(p)), sc->w);
      break;

    case OP_DO:
      sc->w = collect_collisions(sc, cadr(p), sc->w);
      break;

    case OP_WITH_LET:
      if (sc->safety != 0)
	hop = 0;
      /* we can't trust anything here, so hop ought to be off.  For example,
       *
       *    (define (hi)
       *      (let ((e (sublet (curlet)
       *                 (cons 'abs (lambda (a) (- a 1))))))
       *        (with-let e (abs -1))))
       *
       * returns 1 if hop is 1, but -2 outside the function body.
       */
      break;

    case OP_CASE:
      if (is_pair(cddr(p)))
	{
	  s7_pointer kp;
	  for (kp = cddr(p); is_pair(kp); kp = cdr(kp))
	    if (is_pair(car(kp)))
	      {
		s7_pointer k;
		for (k = caar(kp); is_pair(k); k = cdr(k))
		  if ((is_symbol(car(k))) &&
		      (symbol_tag(car(k)) == 0))
		    symbol_tag(car(k)) = 1;
	      }
	}
      break;

    default:
      break;
    }
  for (p = cdar(x); is_pair(p); p = cdr(p))
    if ((is_pair(car(p))) &&
	(!is_checked(car(p))))
      optimize_expression(sc, p, hop, sc->w);
  
  if ((hop == 1) &&
      (symbol_id(caar(x)) == 0))
    {
      if ((op == OP_IF) || (op == OP_OR) || (op == OP_AND))
	{
	  bool happy = true;
	  for (p = cdar(x); (happy) && (is_pair(p)); p = cdr(p))
	    happy = is_all_x_safe(sc, car(p));
	  
	  if ((happy) &&
	      (is_null(p)))    /* catch the syntax error later: (or #f . 2) etc */
	    {
	      int args, symbols = 0, pairs = 0, rest = 0;
	      s7_pointer car_x, sym = NULL;
	      bool c_s_is_ok = true;
	      
	      car_x = car(x);
	      for (args = 0, p = cdar(x); is_pair(p); p = cdr(p), args++)
		{
		  if (is_symbol(car(p)))
		    symbols++;
		  else
		    {
		      if (!is_pair(car(p)))
			rest++;
		      else 
			{
			  pairs++;
			  if ((c_s_is_ok) &&
			      ((!is_h_safe_c_s(car(p))) ||
			       ((sym) && (sym != cadar(p)))))
			    c_s_is_ok = false;
			  else sym = cadar(p);
			}
		    }
		}
	      
	      if ((op == OP_IF) &&
		  ((args < 2) || (args > 3))) /* syntax error */
		return(false); 
	      
	      set_optimized(car_x);
	      set_optimize_data(car_x, hop + OP_SAFE_C_C);
	      
	      if (pairs == 0)
		{
		  if (op == OP_OR)
		    set_c_function(car_x, or_direct);
		  else
		    {
		      if (op == OP_AND)
			set_c_function(car_x, and_direct);
		      else set_c_function(car_x, if_direct);
		    }
		  return(true);
		}
	      
	      if ((pairs == args) && 
		  (c_s_is_ok)) 
		{
		  if (op == OP_OR)
		    set_c_function(car_x, or_s_direct);
		  else
		    {
		      if (op == OP_AND)
			set_c_function(car_x, and_s_direct);
		      else set_c_function(car_x, if_s_direct);
		    }
		  return(true);
		}

	      for (p = cdar(x); is_pair(p); p = cdr(p))
		set_fcdr(p, (s7_pointer)all_x_eval(sc, car(p)));
	      
	      if (op == OP_OR)
		set_c_function(car_x, or_all_x);
	      else
		{
		  if (op == OP_AND)
		    set_c_function(car_x, and_all_x);
		  else 
		    {
		      if ((fcdr(cddr(car_x)) == (s7_pointer)all_x_q) &&
			  (is_pair(cdddr(car_x))))
			{
			  if (fcdr(cdddr(car_x)) == (s7_pointer)all_x_q)
			    set_c_function(car_x, if_all_x_qq);
			  else set_c_function(car_x, if_all_x_qa);
			}
		      else 
			{
			  if (is_null(cdddr(car_x)))
			    set_c_function(car_x, if_all_x1);
			  else set_c_function(car_x, if_all_x2);
			}
		    }
		}
	      return(true);
	    }
	  /* else we could check other if cases here (test is often all_x_safe) 
	   */
	}
    }
  return(false);
}


static bool rdirect_memq(s7_scheme *sc, s7_pointer symbol, s7_pointer symbols)
{
  s7_pointer x;
  if (symbol_tag(symbol) != sc->syms_tag)
    return(false);
  for (x = symbols; is_pair(x); x = cdr(x))
    if (car(x) == symbol)
      return(true);
  return(false);
}

static s7_pointer find_uncomplicated_symbol(s7_scheme *sc, s7_pointer symbol, s7_pointer e)
{ 
  s7_pointer x;
  long long int id;

  if (rdirect_memq(sc, symbol, e))
    return(sc->NIL);

  if (is_global(symbol))
    return(global_slot(symbol));

  id = symbol_id(symbol);
  for (x = sc->envir; id < let_id(x); x = outlet(x));
  for (; is_let(x); x = outlet(x))
    {
      s7_pointer y;
      if (let_id(x) == id)
	return(local_slot(symbol));

      for (y = let_slots(x); is_slot(y); y = next_slot(y))
	if (slot_symbol(y) == symbol)
	  return(y);
    }
  
  return(global_slot(symbol)); /* it's no longer global perhaps (local definition now inaccessible) */
  /* return(sc->NIL); */
} 


static bool optimize_expression(s7_scheme *sc, s7_pointer x, int hop, s7_pointer e)
{
  s7_pointer car_x, caar_x;
  /* fprintf(stderr, "opt expr: %s, hop: %d\n", DISPLAY_80(x), hop); */

  car_x = car(x);
  set_checked(car_x);
  caar_x = car(car_x);

  if (is_symbol(caar_x))
    {
      if (is_syntactic(caar_x))
	return(optimize_syntax(sc, x, slot_value(global_slot(caar_x)), hop, e));

      if (caar_x == sc->QUOTE)
	return(false);

      {
	s7_pointer func;
	func = find_uncomplicated_symbol(sc, caar_x, e);
	if (is_slot(func))
	  {
	    func = slot_value(func);
	    if (is_syntactic(func))
	      return(optimize_syntax(sc, x, func, hop, e));

	    /* we miss implicit indexing here because at this time, the data are not set */
	    if ((is_procedure(func)) ||
		(is_c_function(func)) ||
		(is_safe_procedure(func))) /* built-in applicable objects like vectors */
	      {
		int pairs = 0, symbols = 0, args = 0, bad_pairs = 0, quotes = 0, orig_hop;
		s7_pointer p;
		
		orig_hop = hop;
		/* if (is_global(caar_x)) fprintf(stderr, "%s is global\n", DISPLAY(caar_x)); */

		if ((is_any_closure(func)) ||      /* can't depend on ecdr here because it might not be global, or might be redefined locally */
		    ((!is_global(caar_x)) &&
		     ((!is_slot(global_slot(caar_x))) ||
		      (slot_value(global_slot(caar_x)) != func))))
		  {
		    /* (let () (define (f2 a) (+ a 1)) (define (f1 a) (f2 a)) (define (f2 a) (- a)) (f1 12))
		     * (let () (define (f2 a) (+ a 1)) (define (f1 a) (f2 a)) (define (f2 a) (- a 1)) (f1 12))
		     * and similar define* cases
		     */

		    /* if (!is_any_closure(func)) fprintf(stderr, "%s in %s is not global\n", DISPLAY(caar_x), DISPLAY(car_x)); */

		    hop = 0;
		    /* this is very tricky!  See s7test for some cases.  Basically, we need to protect a recursive call
		     *   of the current function being optimized from being confused with some previous definition
		     *   of the same name.  But method lists have global names so the global bit is off even though the
		     *   thing is actually a safe global.  But no closure can be considered safe in the hop sense --
		     *   even a global function might be redefined at anuy time, and previous uses of it in other functions
		     *   need to reflect its new value.  
		     *   So, closures are always checked, but built-in functions are used as if never redefined until that redefinition.
		     *   costs: index 6/1380, t502: 2/12900, bench: 43/4134, snd-test: 22/37200
		     * Syntax handling is already impure in s7, so the special handling of built-in functions doesn't
		     *   offend me much.  Consider each a sort of reader macro until someone redefines it -- previous
		     *   uses may not be affected because they might have been optimized away -- the result depends on the
		     *   current optimizer.
		     * Another case (from K Matheussen): 
		     *   (define (call-func func arg1 arg2) (define (call) (func arg1 arg2)) (call)) (call-func + 1 2.5) (call-func - 5 2)
		     *   when we get here originally "func" is +, hop=1, but just checking for !is_global(caar_x) is
		     *   not good enough -- if we load mockery.scm, nothing is global!
		     */
		  }
		/* but if we make a recursive call on a func, we've obviously already looked up that function, and
		 *   if it has not been shadowed, then we don't need to check it -- so the hop bit should be on
		 *   for that one case.
		 */

		for (p = cdr(car_x); is_pair(p); p = cdr(p), args++) /* check the args (the calling expression) */
		  {
		    if (is_symbol(car(p)))
		      symbols++;
		    else
		      {
			if (is_pair(car(p)))
			  {
			    pairs++;
			    if (!is_checked(car(p)))
			      {
				if (!optimize_expression(sc, p, orig_hop, e))
				  {
				    bad_pairs++;
				    if ((caar(p) == sc->QUOTE) &&
					(is_pair(cdar(p))) &&
					(is_null(cddar(p))))
				      quotes++;
				  }
			      }
			    else 
			      {
				if ((!is_optimized(car(p))) ||
				    (is_unsafe(car(p))))
				  {
				    bad_pairs++;
				    if ((caar(p) == sc->QUOTE) &&
					(is_pair(cdar(p))) &&
					(is_null(cddar(p))))
				      quotes++;
				  }
			      }
			  }
		      }
		  }
		if ((is_null(p)) &&                    /* if not null, dotted list of args? */
		    (s7_is_aritable(sc, func, args)))  /* we have a legit call, at least syntactically */
		  {
		    switch (args)
		      {
		      case 0: 
			return(optimize_thunk(sc, car_x, func, hop));
			
		      case 1:
			return(optimize_func_one_arg(sc, car_x, func, hop, pairs, symbols, quotes, bad_pairs));
			
		      case 2:
			return(optimize_func_two_args(sc, car_x, func, hop, pairs, symbols, quotes, bad_pairs));
			
		      case 3:
			return(optimize_func_three_args(sc, car_x, func, hop, pairs, symbols, quotes, bad_pairs));
			
		      default:
			return(optimize_func_many_args(sc, car_x, func, hop, args, pairs, symbols, quotes, bad_pairs));
		      }
		  }
		return(false);
	      }
	  }
	else
	  {
	    if ((sc->undefined_identifier_warnings) &&
		(func == sc->UNDEFINED) &&         /* caar_x is not in e or global */
		(symbol_tag(caar_x) == 0))         /*    and we haven't looked it up earlier */
	      {
		s7_pointer p;
		p = sc->input_port;
		if ((is_input_port(p)) &&
		    (port_file(p) != stdin) &&
		    (!port_is_closed(p)) &&
		    (port_filename(p)))
		  s7_warn(sc, 1024, "%s might be undefined (%s %u)\n", DISPLAY(caar_x), port_filename(p), port_line_number(p));
		else s7_warn(sc, 1024, "; %s might be undefined\n", DISPLAY(caar_x));
		symbol_tag(caar_x) = 1;             /* one warning is enough */
	      }
	    /* we need local definitions and func args in e?  also check is_symbol case below
	     */
	  }
      }

      /* caar(x) is a symbol but it's not a known procedure or a "safe" case = vector etc */
      {
	/* else maybe it's something like a let variable binding: (sqrtfreq (sqrt frequency)) */
	s7_pointer p;
	int len = 0, pairs = 0, symbols = 0, quotes = 0;

	for (p = cdr(car_x); is_pair(p); p = cdr(p), len++)
	  {
	    s7_pointer car_p;
	    car_p = car(p);
	    if (is_pair(car_p))
	      {
		pairs++;
		if ((hop != 0) && (car(car_p) == sc->QUOTE))
		  quotes++;
		if (!is_checked(car_p))
		  optimize_expression(sc, p, hop, e);
	      }
	    else
	      {
		if (is_symbol(car_p))
		  symbols++;
	      }
	  }

	if ((is_null(p)) &&              /* (+ 1 . 2) */
	    (!is_optimized(car_x)))
	  {
	    /* len=0 case is almost entirely arglists */
	    set_ecdr(car_x, sc->GC_NIL);
	    if (pairs == 0)
	      {
		if (len == 0)
		  {
		    /* hoping to catch object application here, as in readers in Snd */
		    set_unsafely_optimized(car_x);
		    set_optimize_data(car_x, hop + OP_UNKNOWN);
		    return(false);
		  }

		if (len == 1)
		  {
		    if (caar_x != sc->QUOTE) /* !! quote can be redefined locally, unsetting the T_SYNTACTIC flag -- can this happen elsewhere? */
		      {
			set_unsafely_optimized(car_x);
			set_optimize_data(car_x, hop + OP_UNKNOWN_G);
			/* hooboy -- we get here in let bindings...
			 * to save access to the caller, we'd need to pass it as an arg to optimize_expression
			 */
		      }
		    return(false); 
		  }

		if (len == 2)
		  {
		    set_unsafely_optimized(car_x);
		    if (symbols == 2)
		      set_optimize_data(car_x, hop + OP_UNKNOWN_GG);
		    else
		      {
			if (symbols == 0)
			  set_optimize_data(car_x, hop + OP_UNKNOWN_GG);
			else
			  {
			    if (is_symbol(cadr(car_x)))
			      set_optimize_data(car_x, hop + OP_UNKNOWN_GG);
			    else set_optimize_data(car_x, hop + OP_UNKNOWN_GG);
			  }
		      }
		    return(false); 
		  }

		if ((len >= 3) &&
		    (len == symbols))
		  {
		    set_unsafely_optimized(car_x);
		    set_optimize_data(car_x, hop + OP_UNKNOWN_ALL_S);
		    if (len < NUM_SMALL_INTS)
		      set_arglist_length(car_x, small_int(len));
		    else set_arglist_length(car_x, make_permanent_integer(len));
		    return(false); 
		  }
	      }
	    else /* pairs != 0 */
	      {
		s7_pointer cadar_x;
		cadar_x = cadr(car_x);
		if (pairs == 1)
		  {
		    if (len == 1)
		      {
			if (quotes == 1)
			  {
			    set_unsafely_optimized(car_x);
			    set_optimize_data(car_x, hop + OP_UNKNOWN_A);
			    return(false); 
			  }

			if (is_all_x_safe(sc, cadar_x))
			  {
			    set_arglist_length(car_x, small_int(1));
			    set_unsafely_optimized(car_x);
			    set_optimize_data(car_x, hop + OP_UNKNOWN_A);
			    return(false); 
			  }
		      }
		    else
		      {
			if (len == 2)
			  {
			    if ((is_all_x_safe(sc, cadar_x)) &&
				(is_all_x_safe(sc, caddr(car_x))))
			      {
				set_arglist_length(car_x, small_int(2));
				set_unsafely_optimized(car_x);
				set_optimize_data(car_x, hop + OP_UNKNOWN_AA);
				return(false); 
			      }
			  }
		      }
		  }

		if ((len == 2) &&
		    (is_all_x_safe(sc, cadar_x)) &&
		    (is_all_x_safe(sc, caddr(car_x))))
		  {
		    set_arglist_length(car_x, small_int(2));
		    set_unsafely_optimized(car_x);
		    set_optimize_data(car_x, hop + OP_UNKNOWN_AA);
		    return(false); 
		  }

		if ((pairs == (quotes + all_x_count(car_x))) &&
		    (len < GC_TRIGGER_SIZE))
		  {
		    set_unsafely_optimized(car_x);
		    set_optimize_data(car_x, hop + ((len == 1) ? OP_UNKNOWN_A : OP_UNKNOWN_ALL_X));
		    if (len < NUM_SMALL_INTS)
		      set_arglist_length(car_x, small_int(len));
		    else set_arglist_length(car_x, make_permanent_integer(len));
		    return(false); 
		  }
	      }
	  }
      }
    }
  else
    {
      /* caar(x) is not a symbol, but there might be interesting stuff here */
      /* (define (hi a) (case 1 ((1) (if (> a 2) a 2)))) */
      s7_pointer p;
      for (p = car_x; is_pair(p); p = cdr(p))
	{
	  if (is_pair(car(p)))
	    {
	      if (!is_checked(car(p)))
		optimize_expression(sc, p, hop, e);
	    }
	}
    }
  return(false);
}


static bool optimize(s7_scheme *sc, s7_pointer code, int hop, s7_pointer e)
{
  s7_pointer x;
  bool happy = true;
  /* fprintf(stderr, "optimize %s %d %s\n", DISPLAY_80(code), hop, DISPLAY_80(e)); */

  for (x = code; is_pair(x) && (!is_checked(x)); x = cdr(x))
    {
      set_checked(x);
      if (is_pair(car(x)))
	{
	  if (!optimize_expression(sc, x, hop, e))
	    happy = false;
	}
    }
  if ((!is_null(x)) &&
      (!is_pair(x)))
    {
      eval_error(sc, "stray dot in function body: ~S", code);
      return(false);
    }
  return(happy);
}


#define indirect_c_function_is_ok(Sc, X) (((optimize_data(X) & 0x1) != 0) || (c_function_is_ok(Sc, X)))

static bool is_hop_safe_closure(s7_pointer p)
{
  int op;
  op = optimize_data(p);
  return(((op & 1) == 1) &&
	 (op >= HOP_SAFE_THUNK) &&
	 (op < OP_SAFE_CLOSURE_STAR_S));
}

static bool tree_match(s7_scheme *sc, s7_pointer tree)
{
  if (is_null(tree))
    return(false);
  if (is_symbol(tree))
    return(is_matched(tree));
  if (is_pair(tree))
    return((tree_match(sc, car(tree))) ||
	   (tree_match(sc, cdr(tree))));
  return(false);
}

static bool arg_match(s7_scheme *sc, s7_pointer expr, s7_pointer args)
{
  s7_pointer p;
  bool unhappy = false;

  for (p = args; is_pair(p); p = cdr(p))
    if (is_pair(car(p)))
      set_matched(caar(p));
    else set_matched(car(p));
  if (!is_null(p))
    set_matched(p);

  unhappy = tree_match(sc, expr);

  for (p = args; is_pair(p); p = cdr(p))
    if (is_pair(car(p)))
      clear_matched(caar(p));
    else clear_matched(car(p));
  if (!is_null(p))
    clear_matched(p);
  return(unhappy);
}


static bool form_is_safe(s7_scheme *sc, s7_pointer func, s7_pointer args, s7_pointer x, bool at_end, bool *bad_set)
{
  /* called only from body_is_safe and itself */
  s7_pointer car_x;

  sc->cycle_counter++;
  if ((!is_proper_list(sc, x)) ||
      (sc->cycle_counter > 5000))
    return(false);

  car_x = car(x);
  if (is_syntactic(car_x))
    {
      switch (symbol_syntax_op(car_x))
	{
	case OP_OR:
	case OP_AND:
	case OP_BEGIN:
	case OP_WITH_BAFFLE:
	  if (!body_is_safe(sc, func, args, cdr(x), at_end, bad_set))
	    return(false);
	  break;
	  
	case OP_QUOTE:
	  break;
	  
	  /* in the name binders, we first have to check that "func" actually is the same thing as the caller's func */
	case OP_LET:
	case OP_LET_STAR:
	  if (is_symbol(cadr(x)))
	    {
	      s7_pointer vars, p;
	      if ((!is_pair(cddr(x))) ||
		  (!is_pair(cdddr(x)))) /* (let asdf) or possibly (let asdf ()) */
		return(false);
	      for (vars = caddr(x); is_pair(vars); vars = cdr(vars))
		{
		  if ((caar(vars) == func) ||
		      (caar(vars) == cadr(x)))
		    return(false); /* it's shadowed -- all bets are off! */
		}
	      
	      for (p = cdddr(x); is_not_null(p); p = cdr(p)); /* body might be empty: (define (f x) (let asd ())) */
	      if (is_pair(car(p)))
		{
		  /* what is going on here!?!? */
		  form_is_safe(sc, cadr(x), args, car(p), true, bad_set);
		  if ((at_end) && (!*bad_set))
		    form_is_safe(sc, func, args, car(p), true, bad_set);
		}
	      return(false);
	    }
	  /* fall through */
	  
	case OP_LETREC:
	case OP_LETREC_STAR:
	  {
	    bool happy = true;
	    if (is_pair(cadr(x)))
	      {
		s7_pointer vars;
		for (vars = cadr(x); is_pair(vars); vars = cdr(vars))
		  {
		    s7_pointer car_vars;
		    car_vars = car(vars);
		    if ((!is_pair(car_vars)) ||
			(!is_pair(cdr(car_vars))))
		      return(false);
		    
		    if ((is_pair(cadr(car_vars))) &&
			(!form_is_safe(sc, func, args, cadr(car_vars), false, bad_set)))
		      happy = false;
		    
		    if (car(car_vars) == func)
		      return(false); /* it's shadowed */
		  }
	      }
	    if ((!happy) || (!body_is_safe(sc, func, args, cddr(x), at_end, bad_set)))
	      return(false);
	  }
	  break;
	  
	case OP_IF:
	  if (!is_pair(cdr(x))) return(false); /* (if) ! */
	  if (!((!is_pair(cadr(x))) || (form_is_safe(sc, func, args, cadr(x), false, bad_set)))) return(false);
	  if (!((!is_pair(caddr(x))) || (form_is_safe(sc, func, args, caddr(x), at_end, bad_set)))) return(false);
	  if (!((!is_pair(cdddr(x))) || (!is_pair(cadddr(x))) || (form_is_safe(sc, func, args, cadddr(x), at_end, bad_set)))) return(false);
	  break;

	case OP_WHEN:
	case OP_UNLESS:
	  if (!is_pair(cdr(x))) return(false); /* (when) */
	  if (!((!is_pair(cadr(x))) || (form_is_safe(sc, func, args, cadr(x), false, bad_set)))) return(false);
	  if (!body_is_safe(sc, func, args, cddr(x), at_end, bad_set)) return(false);
	  break;

	case OP_COND:
	  {
	    s7_pointer p;
	    bool happy = true;
	    for (p = cdr(x); is_pair(p); p = cdr(p))
	      {
		s7_pointer expr;
		expr = car(p);
		if (is_pair(expr)) /* ?? */
		  {
		    if ((is_pair(car(expr))) && (!form_is_safe(sc, func, args, car(expr), false, bad_set)))
		      happy = false;
		    if ((is_pair(cdr(expr))) && (!body_is_safe(sc, func, args, cdr(expr), at_end, bad_set)))
		      happy = false;
		  }
	      }
	    if ((!happy) || (is_not_null(p)))
	      return(false);
	  }
	  break;
	  
	case OP_CASE:
	  {
	    s7_pointer p;
	    bool happy = true;
	    if ((is_pair(cadr(x))) && (!form_is_safe(sc, func, args, cadr(x), false, bad_set))) return(false);
	    for (p = cddr(x); is_pair(p); p = cdr(p))
	      {
		if ((is_pair(car(p))) && (!body_is_safe(sc, func, args, cdar(p), at_end, bad_set)))
		  happy = false;
	      }
	    return(happy);
	  }
	  break;
	  
	case OP_DO:
	  /* (do (...) (...) ...) */
	  if (!is_pair(cddr(x)))
	    return(false);
	  if (!body_is_safe(sc, func, args, cdddr(x), false, bad_set))
	    return(false);
	  if (is_pair(cadr(x)))
	    {
	      s7_pointer vars;
	      for (vars = cadr(x); is_pair(vars); vars = cdr(vars))
		{
		  if (!is_pair(car(vars)))
		    return(false);
		  
		  if ((caar(vars) == func) || 
		      (!is_pair(cdar(vars))))   /* (do ((a . 1) (b . 2)) ...) */
		    return(false);
		  
		  if ((is_pair(cadar(vars))) &&
		      (!form_is_safe(sc, func, args, cadar(vars), false, bad_set)))
		    return(false);

		  if ((is_pair(cddar(vars))) &&
		      (is_pair(caddar(vars))) &&
		      (!form_is_safe(sc, func, args, caddar(vars), false, bad_set)))
		    return(false);
		}
	    }
	  if ((is_pair(caddr(x))) &&
	      (!body_is_safe(sc, func, args, caddr(x), at_end, bad_set)))
	    return(false);
	  break;
	  
	case OP_SET:
	  /* if we set func, we have to make sure we abandon the tail call scan:
	   * (let () (define (hi a) (let ((v (vector 1 2 3))) (set! hi v) (hi a))) (hi 1))
	   */
	  if (!is_pair(cdr(x))) return(false); /* (set!) ! */
	  if (cadr(x) == func)
	    (*bad_set) = true;
	  
	  /* car(x) is set!, cadr(x) is settee or obj, caddr(x) is val */
	  if (is_symbol(caddr(x)))
	    return(false);

	  if ((((s7_is_constant(caddr(x))) && 
		(!is_pair(caddr(x)))) ||
	       (form_is_safe(sc, func, args, caddr(x), false, bad_set))) &&
	      (((is_pair(cadr(x))) &&
		(form_is_safe(sc, func, args, cadr(x), false, bad_set))) ||
	       ((is_symbol(cadr(x))) &&
		(!arg_match(sc, x, args)))))
	    return(true);
	  return(false);


	case OP_WITH_LET:
	  if (is_pair(cadr(x)))
	    return(false);

	  /* here args and func are trouble only if (with-let e[ = (curlet)] ...)
	   *   or some such subterfuge -- are we protected by unknown op -> false?
	   *   OP_BEGIN above has if (!body_is_safe(sc, func, args, cdr(x), at_end, bad_set))
	   */
	  if (!body_is_safe(sc, sc->F, sc->NIL, cddr(x), at_end, bad_set))
	    return(false);
#if 0
	  if ((!direct_memq(cadr(x), args)) &&
	      (!body_is_safe(sc, func, args, cddr(x), at_end, bad_set)))
	    return(false);
#endif
	  break;


	  /* op_define and friends are not safe: (define (a) (define b 3)...) tries to put b in the current env,
	   *   but in a safe func, that's a constant.  See s7test L 1865 for an example.
	   */
	default:
	  /* try to catch weird cases like: 
	   * (let () (define (hi1 a) (define (hi1 b) (+ b 1)) (hi1 a)) (hi1 1))
	   * (let () (define (hi1 a) (define (ho1 b) b) (define (hi1 b) (+ b 1)) (hi1 a)) (hi1 1))
	   */
	  if ((is_pair(cdr(x))) &&
	      ((cadr(x) == func) ||
	       ((is_pair(cadr(x))) && (caadr(x) == func))))
	    (*bad_set) = true;
	  return(false);
	}
    }
  else /* car(x) is not syntactic ?? */
    {
      if ((!is_optimized(x)) || 
	  ((is_unsafe(x)) &&
	   (!is_hop_safe_closure(x))))
	{
	  if (car_x == func) /* try to catch tail call */
	    {
	      s7_pointer p;

	      for (p = cdr(x); is_pair(p); p = cdr(p))
		if ((is_pair(car(p))) &&
		    (((!is_optimized(car(p))) &&
		      (caar(p) != sc->QUOTE)) ||
		     (is_unsafe(car(p))) ||
		     (caar(p) == func)))  /* ?? not sure about this */
		  return(false);

	      if ((at_end) && (is_null(p) && (!*bad_set)))
		{
		  /* set_tail_call(x); */
		  return(true);
		}
	      return(false);
	    }
	  
	  if (is_symbol(car_x))
	    {
	      if (is_global(car_x))
		{
		  s7_pointer f;
		  f = find_symbol_checked(sc, car_x);
		  if (((is_c_function(f)) &&
		       ((is_safe_procedure(f)) ||
			((is_null(cdddr(x))) &&
			 ((c_function_call(f) == g_member) || (c_function_call(f) == g_assoc))))) ||
		      ((is_closure(f)) &&
		       (is_safe_closure(f))))
		    {
		      s7_pointer p;
		      for (p = cdr(x); is_pair(p); p = cdr(p))
			if ((is_pair(car(p))) &&
			    ((!is_optimized(car(p))) ||
			     ((is_unsafe(car(p))) &&
			      (!is_hop_safe_closure(car(p))))))
			  {
			    if ((caar(p) != func) ||
				(!is_null(cdr(p))))
			      return(false);
			  }
		      if (!is_null(p))
			return(false);
		    }
		}
	      else
		{
		  s7_pointer f;
		  f = find_symbol(sc, car_x);
		  if (is_slot(f))
		    {
		      if ((is_syntax(slot_value(f))) || (is_any_macro(slot_value(f))))
			(*bad_set) = true;
		      else
			{
			  if ((is_closure(slot_value(f))) &&
			      (is_safe_closure(slot_value(f))))
			    return(true);
			}
		    }
		}
	    }
	  return(false);
	}
    }
  return(true);
}


static bool body_is_safe(s7_scheme *sc, s7_pointer func, s7_pointer args, s7_pointer body, bool at_end, bool *bad_set)
{
  /* called in optimize_lambda */
  s7_pointer p;

  sc->cycle_counter++;
  if (sc->cycle_counter > 5000)
    return(false);
  for (p = body; is_pair(p); p = cdr(p))
    {
      if ((is_pair(car(p))) &&
	  (!form_is_safe(sc, func, args, car(p), (at_end) && (is_null(cdr(p))), bad_set)))
	{
	  if ((at_end) && (is_pair(cdr(p))) && (!*bad_set))
	    {
	      /* we checked in check_define that this is not a circular list, but dotted? */
	      for (p = cdr(p); is_pair(cdr(p)); p = cdr(p));
	      if ((is_pair(p)) && (is_pair(car(p))))
		form_is_safe(sc, func, args, car(p), at_end, bad_set);
	    }
	  return(false);
	}
    }
  return(is_null(p));
}



/* ---------------------------------------- error checks ---------------------------------------- */

static s7_pointer check_lambda_args(s7_scheme *sc, s7_pointer args, int *arity)
{
  s7_pointer x;
  int i;

  if ((!is_pair(args)) && (!is_null(args)))
    {
      if (s7_is_constant(args))                       /* (lambda :a ...) */
	return(eval_error(sc, "lambda parameter '~S is a constant", args)); /* not ~A here, (lambda #\null do) for example */
      
      /* we currently accept (lambda i i . i) (lambda quote i)  (lambda : : . #()) (lambda : 1 . "")
       *   at this level, but when the lambda form is evaluated, it will trigger an error.
       */
      if (is_symbol(args))
	set_local(args);

      if (arity) (*arity) = -1;
      return(sc->F);
    }

  for (i = 0, x = args; is_pair(x); i++, x = cdr(x))
    {
      s7_pointer car_x;
      car_x = car(x);
      if (s7_is_constant(car_x))                       /* (lambda (pi) pi), constant here means not a symbol */
	{
	  if (is_pair(car_x))                          /* (lambda ((:hi . "hi") . "hi") 1) */
	    return(eval_error(sc, "lambda parameter '~S is a pair (perhaps you want define* or lambda*?)", car_x));
	  return(eval_error(sc, "lambda parameter '~S is a constant", car_x));
	}
      if (symbol_is_in_arg_list(car_x, cdr(x)))       /* (lambda (a a) ...) or (lambda (a . a) ...) */
	return(eval_error(sc, "lambda parameter '~S is used twice in the parameter list", car_x));
      set_local(car_x);
    }
  if (is_not_null(x))
    {
      if (s7_is_constant(x))                             /* (lambda (a . 0.0) a) or (lambda (a . :b) a) */
	return(eval_error(sc, "lambda :rest parameter '~S is a constant", x));
      i = -i - 1;
    }

  if (arity) (*arity) = i;
  return(sc->F);
}


static s7_pointer check_lambda_star_args(s7_scheme *sc, s7_pointer args, int *arity)
{
  s7_pointer top, v, w;
  int i;

  if (!s7_is_list(sc, args))
    {
      if (s7_is_constant(args))                                  /* (lambda* :a ...) */
	return(eval_error(sc, "lambda* parameter '~S is a constant", args));
      if (is_symbol(args))
	set_local(args);
      if (arity) (*arity) = -1;
      return(args);
    }

  top = args;
  v = args;
  for (i = 0, w = args; is_pair(w); i++, v = w, w = cdr(w))
    {
      s7_pointer car_w;
      car_w = car(w);
      if (is_pair(car_w))
	{
	  if (s7_is_constant(car(car_w)))                            /* (lambda* ((:a 1)) ...) */
	    return(eval_error_with_string(sc, CONSTANT_ARG_ERROR, car(car_w)));
	  if (symbol_is_in_arg_list(caar(w), cdr(w)))             /* (lambda* ((a 1) a) ...) */
	    return(eval_error(sc, "lambda* parameter '~A is used twice in the argument list", car(car_w)));
	  
	  if (!is_pair(cdr(car_w)))                                  /* (lambda* ((a . 0.0)) a) */
	    {
	      if (is_null(cdr(car_w)))                               /* (lambda* ((a)) ...) */
		return(eval_error(sc, "lambda* parameter default value missing? '~A", car_w));
	      return(eval_error(sc, "lambda* parameter is a dotted pair? '~A", car_w));
	    }
	  else
	    {
	      if ((is_pair(cadr(car_w))) &&                          /* (lambda* ((a (quote . -1))) ...) */
		  (s7_list_length(sc, cadr(car_w)) < 0))
		return(eval_error(sc, "lambda* parameter default value is improper? ~A", car_w));
	    }
	  
	  if (is_not_null(cddr(car_w)))                              /* (lambda* ((a 0.0 'hi)) a) */
	    return(eval_error(sc, "lambda* parameter has multiple default values? '~A", car_w));
	  
	  set_local(car(car_w));
	}
      else 
	{
	  if (car_w != sc->KEY_REST)
	    {
	      if (s7_is_constant(car_w))
		{
		  if ((car_w == sc->KEY_KEY) || 
		      (car_w == sc->KEY_OPTIONAL))
		    {
		      if ((!is_pair(cdr(w))) ||                   /* (lambda* (:key) 1) or (lambda* (:key . b) 1) */
			  (is_immutable(cadr(w))))                /* (lambda* (:key :optional) 1) */
			return(eval_error(sc, "lambda* :key or :optional parameter must be a normal symbol: ~A", w));
		      if (w == top) 
			top = cdr(w);
		      else cdr(v) = cdr(w);
		    }
		  else
		    {
		      if (car_w == sc->KEY_ALLOW_OTHER_KEYS)
			{
			  if (is_not_null(cdr(w)))                /* (lambda* (:allow-other-keys x) x) */
			    eval_error(sc, ":allow-other-keys should be the last parameter: ~A", args);
			}
		      else                                        /* (lambda* (pi) ...) */
			return(eval_error_with_string(sc, CONSTANT_ARG_ERROR, car_w));
		    }
		}
	      if (symbol_is_in_arg_list(car_w, cdr(w)))          /* (lambda* (a a) ...) or (lambda* (a . a) ...) */
		return(eval_error(sc, "lambda* parameter '~A is used twice in the argument list", car_w));
	      
	      if (!is_keyword(car_w)) set_local(car_w);
	    }
	  else
	    {
	      if (!is_pair(cdr(w)))                               /* (lambda* (:rest) ...) */
		return(eval_error(sc, "lambda* :rest parameter missing? ~A", w));
	      if (!is_symbol(cadr(w)))                         /* (lambda* (:rest (a 1)) ...) */
		{
		  if (!is_pair(cadr(w)))                          /* (lambda* (:rest 1) ...) */
		    return(eval_error(sc, "lambda* :rest parameter is not a symbol? ~A", w));
		  return(eval_error(sc, "lambda* :rest parameter can't have a default value. ~A", w));
		}
	      else
		{
		  if (is_immutable(cadr(w)))
		    return(s7_error(sc, sc->WRONG_TYPE_ARG,
				    list_2(sc, make_string_wrapper(sc, "can't bind an immutable object: ~S"), w)));
		}
	      set_local(cadr(w));
	    }
	}
    }
  if (is_not_null(w))
    {
      if (s7_is_constant(w))                             /* (lambda* (a . 0.0) a) or (lambda* (a . :b) a) */
	return(eval_error(sc, "lambda* :rest parameter '~A is a constant", w));
      if (is_symbol(w))
	set_local(w);
      i = -1;
    }
  if (arity) (*arity) = i;
  return(top);
}


static s7_pointer check_when(s7_scheme *sc)
{
  if (!is_pair(sc->code))                                            /* (when) or (when . 1) */
    return(eval_error(sc, "when has no expression or body:  ~A", sc->code));
  if (!is_pair(cdr(sc->code)))                                       /* (when 1) or (when 1 . 1) */
    return(eval_error(sc, "when has no body?:  ~A", sc->code));

  if ((is_overlaid(sc->code)) &&
      (cdr(ecdr(sc->code)) == sc->code))
    {
      pair_set_syntax_symbol(sc->code, sc->WHEN_UNCHECKED);
      
      if (is_symbol(car(sc->code)))
	pair_set_syntax_symbol(sc->code, sc->WHEN_S);
    }
  return(sc->code);
}


static s7_pointer check_unless(s7_scheme *sc)
{
  if (!is_pair(sc->code))                                            /* (unless) or (unless . 1) */
    return(eval_error(sc, "unless has no expression or body:  ~A", sc->code));
  if (!is_pair(cdr(sc->code)))                                       /* (unless 1) or (unless 1 . 1) */
    return(eval_error(sc, "unless has no body?:  ~A", sc->code));

  if ((is_overlaid(sc->code)) &&
      (cdr(ecdr(sc->code)) == sc->code))
    {
      pair_set_syntax_symbol(sc->code, sc->UNLESS_UNCHECKED);

      if (is_symbol(car(sc->code)))
	pair_set_syntax_symbol(sc->code, sc->UNLESS_S);
    }
  return(sc->code);
}

/* (apply unless (list values :key #f)) */


static s7_pointer check_case(s7_scheme *sc)
{
  bool keys_simple = true, have_else = false, has_feed_to = false, keys_single = true, bodies_simple = true, bodies_simplest = true;
  s7_pointer x, y;

  /* (let () (define (hi a) (case a ((0) (+ a 1)) ((1 2) a))) (define (ho) (hi 1)) (ho))
   */

  if (!is_pair(sc->code))                                            /* (case) or (case . 1) */
    return(eval_error(sc, "case has no selector:  ~A", sc->code));
  if (!is_pair(cdr(sc->code)))                                       /* (case 1) or (case 1 . 1) */
    return(eval_error(sc, "case has no clauses?:  ~A", sc->code));
  if (!is_pair(cadr(sc->code)))                                      /* (case 1 1) */
    return(eval_error(sc, "case clause is not a list? ~A", sc->code));
  
  for (x = cdr(sc->code); is_not_null(x); x = cdr(x))
    {
      if ((!is_pair(x)) ||                                        /* (case 1 ((2) 1) . 1) */
	  (!is_pair(car(x))))
	return(eval_error(sc, "case clause ~A messed up", x));	 
      if (!is_pair(cdar(x)))                                      /* (case 1 ((1))) */
	return(eval_error(sc, "case clause result missing: ~A", car(x)));

      if ((bodies_simple) && (!is_null(cddar(x))))
	{
	  bodies_simple = false;
	  bodies_simplest = false;
	}
      if (bodies_simplest)
	{
	  if ((is_pair(cadar(x))) &&
	      (caadar(x) != sc->QUOTE))
	    {
	      if (is_pair(caar(x)))
		bodies_simplest = false;
	      else
		{
		  if ((caar(x) != sc->ELSE) &&
		      ((!is_symbol(caar(x))) ||
		       (s7_symbol_value(sc, caar(x)) != sc->ELSE)))
		    bodies_simplest = false;
		}
	    }
	}
      y = caar(x);
      if (!is_pair(y))
	{
	  if ((y != sc->ELSE) &&                                  /* (case 1 (2 1)) */
	      ((!is_symbol(y)) ||
	       (s7_symbol_value(sc, y) != sc->ELSE)))             /* "proper list" below because: (case 1 (() 2) ... */
	    return(eval_error(sc, "case clause key list ~A is not a proper list or 'else'", y));
	  if (is_not_null(cdr(x)))                                  /* (case 1 (else 1) ((2) 1)) */
	    return(eval_error(sc, "case 'else' clause, ~A, is not the last clause", x));
	  have_else = true;
	}
      else
	{
	  /* what about (case 1 ((1) #t) ((1) #f)) [this is ok by guile]
	   *            (case 1 ((1) #t) ())
	   *            (case 1 ((2 2 2) 1)): guile says #<unspecified>
	   */
	  
	  /* the selector (sc->value) is evaluated, but the search key is not
	   *    (case '2 ((2) 3) (else 1)) -> 3
	   *    (case '2 (('2) 3) (else 1)) -> 1
	   */
	  if (!is_simple(car(y)))
	    keys_simple = false;
	  if (!is_null(cdr(y)))
	    keys_single = false;

	  for (y = cdr(y); is_not_null(y); y = cdr(y)) 
	    {
	      if (!is_pair(y))                                        /* (case () ((1 . 2) . hi) . hi) */
		return(eval_error(sc, "case key list is improper? ~A", x));
	      if (!is_simple(car(y)))
		keys_simple = false;
	    }
	}
      y = car(x);
      if ((cadr(y) == sc->FEED_TO) &&
	  (s7_symbol_value(sc, sc->FEED_TO) == sc->UNDEFINED))
	{
	  has_feed_to = true;
	  if (!is_pair(cddr(y)))                                  /* (case 1 (else =>)) */
	    return(eval_error(sc, "case: '=>' target missing?  ~A", y));
	  if (is_pair(cdddr(y)))                                  /* (case 1 (else => + - *)) */
	    return(eval_error(sc, "case: '=>' has too many targets: ~A", y));
	}
    }

  if ((is_overlaid(sc->code)) &&
      (cdr(ecdr(sc->code)) == sc->code))
    {
      for (x = cdr(sc->code); is_not_null(x); x = cdr(x))
	{
	  set_fcdr(x, caar(x));
	  if (is_pair(fcdr(x))) set_ecdr(x, cadar(x));
	}
      pair_set_syntax_symbol(sc->code, sc->CASE_UNCHECKED);

      if ((!has_feed_to) &&
	  (keys_simple))
	{
	  if (have_else) /* don't combine ifs ! */
	    {
	      if (is_symbol(car(sc->code)))
		{
		  if (bodies_simple)
		    {
		      if (keys_single)
			{
			  if (bodies_simplest)
			    pair_set_syntax_symbol(sc->code, sc->CASE_SIMPLEST_ELSE_C);
			  else pair_set_syntax_symbol(sc->code, sc->CASE_SIMPLEST_ELSE);
			  for (x = cdr(sc->code); is_not_null(x); x = cdr(x))
			    {
			      if (is_pair(caar(x)))
				{
				  set_fcdr(x, caaar(x));
				  /* ecdr(x) = cadar(x); */
				}
			      else 
				{
				  set_fcdr(x, sc->ELSE);
				  set_gcdr(sc->code, cadar(x)); 
				}
			    }
			}
		      else pair_set_syntax_symbol(sc->code, sc->CASE_SIMPLE);
		    }
		  else pair_set_syntax_symbol(sc->code, sc->CASE_SIMPLE);
		}
	    }
	  else 
	    {
	      if (keys_single)
		{
		  if ((bodies_simple) &&
		      (is_symbol(car(sc->code))))
		    pair_set_syntax_symbol(sc->code, sc->CASE_SIMPLEST);
		  else 
		    {
		      if ((is_optimized(car(sc->code))) &&
			  (optimize_data(car(sc->code)) == HOP_SAFE_C_SS))
			pair_set_syntax_symbol(sc->code, sc->CASE_SIMPLEST_SS);
		    }
		  for (x = cdr(sc->code); is_not_null(x); x = cdr(x))
		    set_fcdr(x, caaar(x));
		}
	      else 
		{
		  if (bodies_simple)
		    {
		      if (is_symbol(car(sc->code)))
			pair_set_syntax_symbol(sc->code, sc->CASE_SIMPLER_1); 
		      else
			{
			  if ((is_optimized(car(sc->code))) &&
			      (optimize_data(car(sc->code)) == HOP_SAFE_C_SS))
			    pair_set_syntax_symbol(sc->code, sc->CASE_SIMPLER_SS);
			}
		    }
		  else 
		    {
		      if (is_symbol(car(sc->code)))
			pair_set_syntax_symbol(sc->code, sc->CASE_SIMPLER); 
		    }
		}
	    }
	}
    }
  return(sc->code);
}


static s7_pointer optimize_lambda(s7_scheme *sc, bool unstarred_lambda, s7_pointer x, s7_pointer args, s7_pointer body);

static s7_pointer check_let(s7_scheme *sc)
{
  s7_pointer x, start;
  bool named_let;
  int vars;

  if (!is_pair(sc->code))               /* (let . 1) */
    {
      if (is_null(sc->code))            /* (let) */
	return(eval_error(sc, "let has no variables or body: ~A", sc->code));
      return(eval_error(sc, "let form is an improper list? ~A", sc->code));
    }
  
  if (!is_pair(cdr(sc->code)))          /* (let () ) */
    return(eval_error(sc, "let has no body: ~A", sc->code));

  if ((!s7_is_list(sc, car(sc->code))) && /* (let 1 ...) */
      (!is_symbol(car(sc->code))))
    return(eval_error(sc, "let variable list is messed up or missing: ~A", sc->code));
  
  /* we accept these (other schemes complain, but I can't see why -- a no-op is the user's business!):
   *   (let () (define (hi) (+ 1 2)))
   *   (let () (begin (define x 3)))
   *   (let () 3 (begin (define x 3)))
   *   (let () (define x 3))
   *   (let () (if #t (define (x) 3)))
   *
   * similar cases:
   *   (case 0 ((0) (define (x) 3) (x)))
   *   (cond (0 (define (x) 3) (x)))
   *   (and (define (x) x) 1)
   *   (begin (define (x y) y) (x (define (x y) y)))
   *   (if (define (x) 1) 2 3)
   *   (do () ((define (x) 1) (define (y) 2)))
   *
   * but we can get some humorous results: 
   *   (let ((x (lambda () 3))) (if (define (x) 4) (x) 0)) -> 4
   */
  
  named_let = (is_symbol(car(sc->code)));

  if (named_let)
    {
      if (!s7_is_list(sc, cadr(sc->code)))      /* (let hi #t) */
	return(eval_error(sc, "let variable list is messed up: ~A", sc->code));
      if (is_null(cddr(sc->code)))              /* (let hi () ) */
	return(eval_error(sc, "named let has no body: ~A", sc->code));
      if (is_immutable(car(sc->code)))
	return(s7_error(sc, sc->WRONG_TYPE_ARG,
			list_2(sc, make_string_wrapper(sc, "can't bind an immutable object: ~S"), sc->code)));
      set_local(car(sc->code));
      start = cadr(sc->code);
    }
  else start = car(sc->code);
  
  clear_syms_in_list(sc);
  for (vars = 0, x = start; is_pair(x); vars++, x = cdr(x))
    {
      s7_pointer y, carx;
      
      carx = car(x);
      
      if ((!is_pair(carx)) || (is_null(cdr(carx))))  /* (let ((x)) ...) or (let ((x 1) . (y 2)) ...) */
	return(eval_error(sc, "let variable declaration, but no value?: ~A", x));
      
      if (!(is_pair(cdr(carx))))                     /* (let ((x . 1))...) */
	return(eval_error(sc, "let variable declaration is not a proper list?: ~A", x));
      
      if (is_not_null(cddr(carx)))                   /* (let ((x 1 2 3)) ...) */
	return(eval_error(sc, "let variable declaration has more than one value?: ~A", x));
      
      /* currently if the extra value involves a read error, we get a kind of panicky-looking message:
       *   (let ((x . 2 . 3)) x)
       *   ;let variable declaration has more than one value?: (x error error "stray dot?: ...  ((x . 2 . 3)) x) ..")
       */
      
      y = car(carx);
      if (!(is_symbol(y)))
	return(eval_error(sc, "bad variable ~S in let", carx));
      
      if (is_immutable(y))
	return(s7_error(sc, sc->WRONG_TYPE_ARG,
			list_2(sc, make_string_wrapper(sc, "can't bind an immutable object: ~S"), x)));

      /* check for name collisions -- not sure this is required by Scheme */
      if (symbol_tag(y) == sc->syms_tag)
	return(eval_error(sc, "duplicate identifier in let: ~A", y));
      add_sym_to_list(sc, y);
      set_local(y);
    }
  
  /* we accept (let ((:hi 1)) :hi)
   *           (let ('1) quote) [guile accepts this]
   */
  
  if (is_not_null(x))                  /* (let* ((a 1) . b) a) */
    return(eval_error(sc, "let var list improper?: ~A", sc->code));

  if ((is_overlaid(sc->code)) &&
      (cdr(ecdr(sc->code)) == sc->code))
    {
      if (named_let)
	{
	  s7_pointer x;

	  if (is_null(start))
	    pair_set_syntax_symbol(sc->code, sc->NAMED_LET_NO_VARS);
	  else pair_set_syntax_symbol(sc->code, sc->NAMED_LET);

	  /* this is (let name ...) so the initial values need to be removed from the closure arg list */
	  sc->args = sc->NIL; /* sc->args is set to nil in named_let below */
	  for (x = start; is_pair(x); x = cdr(x)) 
	    sc->args = cons(sc, caar(x), sc->args);
	  optimize_lambda(sc, true, car(sc->code), sc->args = safe_reverse_in_place(sc, sc->args), cddr(sc->code));

	  /* apparently these guys are almost never safe */
	  return(sc->code);
	}

      if (is_null(start))
	pair_set_syntax_symbol(sc->code, sc->LET_NO_VARS);
      else 
	{
	  pair_set_syntax_symbol(sc->code, sc->LET_UNCHECKED);
	  if (is_null(cdr(start)))                                    /* one binding */
	    {
	      s7_pointer binding;

	      pair_set_syntax_symbol(sc->code, sc->LET_ONE);
	      binding = car(start);

	      if (is_pair(cadr(binding)))
		{
		  if (car(cadr(binding)) == sc->QUOTE)                    /* (let ((x 'x)) ...) */
		    pair_set_syntax_symbol(sc->code, sc->LET_Q);
		  else
		    {
		      if ((is_null(cddr(sc->code))) &&
			  (is_optimized(cadr(binding))))
			{
			  set_fcdr(cdr(sc->code), car(binding));
			  set_fcdr(sc->code, cadr(binding));
			  pair_set_syntax_symbol(sc->code, sc->LET_Z);
			}

		      if (is_h_safe_c_s(cadr(binding)))
			{
			  if ((is_null(cddr(sc->code))) &&
			      (is_pair(cadr(sc->code))))
			    {
			      pair_set_syntax_symbol(sc->code, sc->LET_R_P);
			      if ((!is_optimized(cadr(sc->code))) &&
				  (is_syntactic(caadr(sc->code))))
				{
				  /* the is_optimized check here and in other parallel cases protects against cases like:
				   *   (define (hi) (let ((e #f)) (let ((val (not e))) (if (boolean? val) val e)))) (hi)
				   * where the "(if...)" part is optimized as safe_c_s before we get here.  If we simply
				   * set pair_syntax_op(cadr(sc->code)) as below, the optimization bit is on, but the
				   * apparent optimize_op is now safe_c_qq! So eval ejects it and it is handled by the
				   * explicit ("trailers") code.
				   */
				  pair_syntax_op(cadr(sc->code)) = symbol_syntax_op(caadr(sc->code));
				  if (c_call(cadr(binding)) == g_car)
				    {
				      set_gcdr(sc->code, caaar(sc->code));
				      pair_set_syntax_symbol(sc->code, sc->LET_CAR_P);
				    }
				}
			    }
			  else pair_set_syntax_symbol(sc->code, sc->LET_R);
			  set_fcdr(sc->code, cadr(cadr(binding)));
			}
		      else 
			{
			  set_fcdr(sc->code, cadr(binding));
			  if (is_all_x_safe(sc, cadr(binding)))
			    {
			      if (optimize_data(cadr(binding)) == HOP_SAFE_C_SS)
				pair_set_syntax_symbol(sc->code, sc->LET_opSSq);
			      else
				{
				  if (optimize_data(cadr(binding)) == HOP_SAFE_C_C)
				    {
				      set_gcdr(sc->code, car(binding));
				      pair_set_syntax_symbol(sc->code, sc->LET_opCq);
				    }
				  else pair_set_syntax_symbol(sc->code, sc->LET_ALL_X);
				}
			    }
			}
		    }
		}
	      else
		{
		  set_fcdr(sc->code, cadaar(sc->code));    /* sc->code is of the form '(((x y))...) */
		  set_gcdr(sc->code, caaar(sc->code));
		  /* should we make sure T_LINE_NUMBER is not set? -- it won't be cur_code in an error message */
		  if (is_symbol(fcdr(sc->code)))
		    pair_set_syntax_symbol(sc->code, sc->LET_S);
		  else 
		    {
		      if ((is_null(cddr(sc->code))) &&
			  (is_pair(cadr(sc->code))))
			{
			  if (is_h_optimized(cadr(sc->code)))
			    pair_set_syntax_symbol(sc->code, sc->LET_C_D);
			}
		      else pair_set_syntax_symbol(sc->code, sc->LET_C);
		    }
		}
	    }
	  else 
	    {
	      if (vars < GC_TRIGGER_SIZE)
		{
		  s7_pointer p, x, op;
		  /* pair_set_syntax_symbol(sc->code, sc->LET_UNCHECKED); */
		  op = sc->NIL;
		  for (p = start; is_pair(p); p = cdr(p))
		    {
		      x = car(p);
		      if (is_pair(cadr(x)))
			{
			  if (car(cadr(x)) == sc->QUOTE)
			    op = sc->LET_ALL_X;
			  else
			    {
			      if (is_h_safe_c_s(cadr(x)))
				{
				  if ((op == sc->NIL) || (op == sc->LET_ALL_R))
				    op = sc->LET_ALL_R;
				  else op = sc->LET_ALL_X;
				}
			      else 
				{
				  if (is_all_x_safe(sc, cadr(x)))
				    op = sc->LET_ALL_X;
				  else 
				    {
				      op = sc->LET_UNCHECKED;
				      break;
				    }
				}
			    }
			}
		      else
			{
			  if (is_symbol(cadr(x)))
			    {
			      if ((op == sc->NIL) || (op == sc->LET_ALL_S))
				op = sc->LET_ALL_S;
			      else op = sc->LET_ALL_X;
			    }
			  else
			    {
			      if ((op == sc->NIL) || (op == sc->LET_ALL_C))
				op = sc->LET_ALL_C;
			      else op = sc->LET_ALL_X;
			    }
			}
		    }
		  pair_set_syntax_symbol(sc->code, op);
		}
	      else pair_set_syntax_symbol(sc->code, sc->LET_UNCHECKED);
	    }
	}
      if (pair_syntax_symbol(sc->code) == sc->LET_ALL_X)
	{
	  s7_pointer p;
	  for (p = start; is_pair(p); p = cdr(p))
	    set_fcdr(cdar(p), (s7_pointer)all_x_eval(sc, cadar(p)));
	}
    }
  return(sc->code);
}


static s7_pointer check_let_star(s7_scheme *sc)
{
  s7_pointer y;
  bool named_let;

  if (!is_pair(sc->code))                    /* (let* . 1) */
    return(eval_error(sc, "let* variable list is messed up: ~A", sc->code));
  
  if (!is_pair(cdr(sc->code)))               /* (let*) */
    return(eval_error(sc, "let* variable list is messed up: ~A", sc->code));
  
  named_let = (is_symbol(car(sc->code)));

  if (named_let)
    {
      if (!s7_is_list(sc, cadr(sc->code)))      /* (let* hi #t) */
	return(eval_error(sc, "let* variable list is messed up: ~A", sc->code));
      if (is_null(cddr(sc->code)))              /* (let* hi () ) */
	return(eval_error(sc, "named let* has no body: ~A", sc->code));
      if (is_immutable(car(sc->code)))
	return(s7_error(sc, sc->WRONG_TYPE_ARG,
			list_2(sc, make_string_wrapper(sc, "can't bind an immutable object: ~S"), sc->code)));
      set_local(car(sc->code));
      if ((!is_null(cadr(sc->code))) &&
	  ((!is_pair(cadr(sc->code))) ||            /* (let* hi x ... ) */
	   (!is_pair(caadr(sc->code))) ||           /* (let* hi (x) ...) */
	   (!is_pair(cdaadr(sc->code)))))           /* (let* hi ((x . 1)) ...) */
	return(eval_error(sc, "named let* variable declaration value is missing: ~A", sc->code));
    }
  else
    {
      if ((!is_null(car(sc->code))) &&
	  ((!is_pair(car(sc->code))) ||            /* (let* x ... ) */
	   (!is_pair(caar(sc->code))) ||           /* (let* (x) ...) */
	   (!is_pair(cdaar(sc->code)))))            /* (let* ((x . 1)) ...) */
	return(eval_error(sc, "let* variable declaration value is missing: ~A", sc->code));
    }

  for (y = ((named_let) ? cadr(sc->code) : car(sc->code)); is_pair(y); y = cdr(y))
    {
      s7_pointer x, z;
      x = car(y);
      if (!(is_symbol(car(x))))     /* (let* ((3 1)) 1) */
	return(eval_error(sc, "bad variable ~S in let*", x));

      z = car(x);
      if (is_immutable(z))
	return(s7_error(sc, sc->WRONG_TYPE_ARG,
			list_2(sc, make_string_wrapper(sc, "can't bind an immutable object: ~S"), x)));

      if (!is_pair(x))                 /* (let* ((x)) ...) */
	return(eval_error(sc, "let* variable declaration, but no value?: ~A", x));
      
      if (!(is_pair(cdr(x))))          /* (let* ((x . 1))...) */
	return(eval_error(sc, "let* variable declaration is not a proper list?: ~A", x));
      
      if (is_not_null(cddr(x)))        /* (let* ((x 1 2 3)) ...) */
	return(eval_error(sc, "let* variable declaration has more than one value?: ~A", x));
      
      x = cdr(y);
      if (is_pair(x)) 
	{ 
	  if (!is_pair(car(x)))             /* (let* ((x -1) 2) 3) */
	    return(eval_error(sc, "let* variable/binding is ~S?", car(x)));
	  
	  if (!is_pair(cdar(x)))            /* (let* ((a 1) (b . 2)) ...) */
	    return(eval_error(sc, "let* variable list is messed up? ~A", x));
	}
      else
	{
	  if (is_not_null(x))               /* (let* ((a 1) . b) a) */
	    return(eval_error(sc, "let* var list improper?: ~A", x));
	}

      /* currently (let* ((a 1) (a (+ a 1))) a) is 2, not an error! */
      set_local(z);
    }

  if ((is_overlaid(sc->code)) &&
      (cdr(ecdr(sc->code)) == sc->code))
    {
      if (named_let)
	{
	  if (is_null(cadr(sc->code)))
	    pair_set_syntax_symbol(sc->code, sc->NAMED_LET_NO_VARS);
	  else 
	    {
	      pair_set_syntax_symbol(sc->code, sc->NAMED_LET_STAR);
	      set_fcdr(sc->code, cadr(car(cadr(sc->code))));
	    }
	  return(sc->code);
	}

      pair_set_syntax_symbol(sc->code, sc->LET_STAR_UNCHECKED);
      if (is_null(car(sc->code)))
	pair_set_syntax_symbol(sc->code, sc->LET_NO_VARS); /* (let* () ...) */
      else 
	{
	  if (is_null(cdar(sc->code)))
	    {
	      s7_pointer binding;

	      pair_set_syntax_symbol(sc->code, sc->LET_ONE);
	      binding = caar(sc->code);

	      if (is_pair(cadr(binding)))
		{
		  if (car(cadr(binding)) == sc->QUOTE)
		    pair_set_syntax_symbol(sc->code, sc->LET_Q);
		  else
		    {
		      if ((is_optimized(cadr(binding))) &&
			  (is_null(cddr(sc->code))))
			{
			  set_fcdr(cdr(sc->code), car(binding));
			  set_fcdr(sc->code, cadr(binding));
			  pair_set_syntax_symbol(sc->code, sc->LET_Z);
			}

		      if (is_h_safe_c_s(cadr(binding)))
			{
			  if ((is_null(cddr(sc->code))) &&
			      (is_pair(cadr(sc->code))))
			    {
			      pair_set_syntax_symbol(sc->code, sc->LET_R_P);
			      if ((!is_optimized(cadr(sc->code))) && /* see note above */
				  (is_syntactic(caadr(sc->code))))
				{
				  pair_syntax_op(cadr(sc->code)) = symbol_syntax_op(caadr(sc->code));
				  if (c_call(cadr(binding)) == g_car)
				    {
				      set_gcdr(sc->code, caaar(sc->code));
				      pair_set_syntax_symbol(sc->code, sc->LET_CAR_P);
				    }
				}
			    }
			  else pair_set_syntax_symbol(sc->code, sc->LET_R);
			  set_fcdr(sc->code, cadr(cadr(binding)));
			}
		      else 
			{
			  set_fcdr(sc->code, cadr(binding));
			  if (is_all_x_safe(sc, cadr(binding)))
			    pair_set_syntax_symbol(sc->code, sc->LET_ALL_X);
			}
		    }
		}
	      else
		{
		  set_fcdr(sc->code, cadaar(sc->code));
		  set_gcdr(sc->code, caaar(sc->code)); /* added 18-Jun-13 to parallel LET_C_D cases above */
		  if (is_symbol(cadaar(sc->code)))
		    {
		      if (!is_null(cddr(sc->code)))
			pair_set_syntax_symbol(sc->code, sc->LET_S);
		    }
		  else 
		    {
		      if ((is_null(cddr(sc->code))) &&
			  (is_pair(cadr(sc->code))))
			{
			  if (is_h_optimized(cadr(sc->code)))
			    pair_set_syntax_symbol(sc->code, sc->LET_C_D);
			}
		      else pair_set_syntax_symbol(sc->code, sc->LET_C);
		    }
		}
	    }
	  else  /* more than one entry */
	    {
	      s7_pointer p, x, op;
	      op = sc->LET_STAR_ALL_X;
	      set_fcdr(sc->code, cadaar(sc->code));
	      for (p = car(sc->code); is_pair(p); p = cdr(p))
		{
		  x = car(p);
		  if (is_pair(cadr(x)))
		    {
		      if ((!is_all_x_safe(sc, cadr(x))) &&
			  (car(cadr(x)) != sc->QUOTE))
			{
			  op = sc->LET_STAR2;
			  break;
			}
		    }
		}
	      pair_set_syntax_symbol(sc->code, op);
	    }
	}
      if ((pair_syntax_symbol(sc->code) == sc->LET_ALL_X) ||
	  (pair_syntax_symbol(sc->code) == sc->LET_STAR_ALL_X))
	{
	  s7_pointer p;
	  for (p = car(sc->code); is_pair(p); p = cdr(p))
	    set_fcdr(cdar(p), (s7_pointer)all_x_eval(sc, cadar(p)));
	}
    }
  return(sc->code);
}


static s7_pointer check_letrec(s7_scheme *sc, bool letrec)
{
  s7_pointer x;
  if ((!is_pair(sc->code)) ||                 /* (letrec . 1) */
      (!is_pair(cdr(sc->code))) ||            /* (letrec) */
      (!s7_is_list(sc, car(sc->code))))       /* (letrec 1 ...) */
    return(eval_error_with_name(sc, "~A: variable list is messed up: ~A", sc->code));
  
  clear_syms_in_list(sc);
  for (x = car(sc->code); is_not_null(x); x = cdr(x))
    {
      s7_pointer y, carx;
      if (!is_pair(x))                        /* (letrec ((a 1) . 2) ...) */
	return(eval_error_with_name(sc, "~A: improper list of variables? ~A", sc->code));
      
      carx = car(x);
      if ((!is_pair(carx)) ||                 /* (letrec (1 2) #t) */
	  (!(is_symbol(car(carx)))))
	return(eval_error_with_name(sc, "~A: bad variable ~S", carx));
      
      y = car(carx);
      if (is_immutable(y))
	return(s7_error(sc, sc->WRONG_TYPE_ARG,
			list_2(sc, make_string_wrapper(sc, "can't bind an immutable object: ~S"), x)));

      if (!is_pair(cdr(carx)))                /* (letrec ((x . 1))...) */
	{
	  if (is_null(cdr(carx)))             /* (letrec ((x)) x) -- perhaps this is legal? */
	    return(eval_error_with_name(sc, "~A: variable declaration has no value?: ~A", carx));
	  return(eval_error_with_name(sc, "~A: variable declaration is not a proper list?: ~A", carx));
	}
      if (is_not_null(cddr(carx)))            /* (letrec ((x 1 2 3)) ...) */
	return(eval_error_with_name(sc, "~A: variable declaration has more than one value?: ~A", carx));

      /* check for name collisions -- this is needed in letrec* else which of the two legit values
       *    does our "rec" refer to, so to speak.
       */
      if (symbol_tag(y) == sc->syms_tag)
	return(eval_error_with_name(sc, "~A: duplicate identifier: ~A", y));
      add_sym_to_list(sc, y);
      set_local(y);
    }

  if ((is_overlaid(sc->code)) &&
      (cdr(ecdr(sc->code)) == sc->code))
    pair_set_syntax_symbol(sc->code, (letrec) ? sc->LETREC_UNCHECKED : sc->LETREC_STAR_UNCHECKED);

  return(sc->code);
}


static s7_pointer check_quote(s7_scheme *sc)
{
  if (!is_pair(sc->code))                    /* (quote . -1) */
    {
      if (is_null(sc->code))
	return(eval_error(sc, "quote: not enough arguments: ~A", sc->code));
      return(eval_error(sc, "quote: stray dot?: ~A", sc->code));
    }
  if (is_not_null(cdr(sc->code)))             /* (quote . (1 2)) or (quote 1 1) */
    return(eval_error(sc, "quote: too many arguments ~A", sc->code));
#if 0
  if ((is_overlaid(sc->code)) &&
      (cdr(ecdr(sc->code)) == sc->code))
    {
      pair_set_syntax_symbol(sc->code, sc->QUOTE_UNCHECKED);
    }
#endif
  return(sc->code);
}


static s7_pointer check_and(s7_scheme *sc)
{
  s7_pointer p;
  bool all_pairs;

  if (is_null(sc->code))
    return(sc->code);

  all_pairs = is_pair(sc->code);
  for (p = sc->code; is_pair(p); p = cdr(p))
    {
      if (!is_pair(car(p)))
	all_pairs = false;
    }

  if (is_not_null(p))                                    /* (and . 1) (and #t . 1) */
    return(eval_error(sc, "and: stray dot?: ~A", sc->code));

  if ((is_overlaid(sc->code)) &&
      (cdr(ecdr(sc->code)) == sc->code))
    {
      if (all_pairs)
	{
	  for (p = sc->code; is_pair(p); p = cdr(p))
	    set_fcdr(p, (s7_pointer)all_x_eval(sc, car(p))); /* this can be nil! */
	  pair_set_syntax_symbol(sc->code, sc->AND_P);
	}
      else pair_set_syntax_symbol(sc->code, sc->AND_UNCHECKED);
    }
  return(sc->code);
}


static s7_pointer check_or(s7_scheme *sc)
{
  s7_pointer p;
  bool all_pairs;

  if (is_null(sc->code))
    return(sc->code);

  all_pairs = is_pair(sc->code);
  for (p = sc->code; is_pair(p); p = cdr(p))
    {
      if (!is_pair(car(p)))
	all_pairs = false;
    }

  if (is_not_null(p))
    return(eval_error(sc, "or: stray dot?: ~A", sc->code));
  
  if ((is_overlaid(sc->code)) &&
      (cdr(ecdr(sc->code)) == sc->code))
    {
      if (all_pairs)
	{
	  s7_pointer p;
	  for (p = sc->code; is_pair(p); p = cdr(p))
	    set_fcdr(p, (s7_pointer)all_x_eval(sc, car(p)));
	  pair_set_syntax_symbol(sc->code, sc->OR_P);
	}
      else pair_set_syntax_symbol(sc->code, sc->OR_UNCHECKED);
    }
  return(sc->code);
}


static s7_pointer check_if(s7_scheme *sc)
{
  s7_pointer cdr_code;

  if (!is_pair(sc->code))                               /* (if) or (if . 1) */
    return(eval_error(sc, "(if): if needs at least 2 expressions: ~A", sc->code));
  
  cdr_code = cdr(sc->code);
  if (!is_pair(cdr_code))                          /* (if 1) */
    return(eval_error(sc, "(if ~A): if needs another clause", car(sc->code)));
  
  if (is_pair(cdr(cdr_code)))
    {
      if (is_not_null(cddr(cdr_code)))                   /* (if 1 2 3 4) */
	return(eval_error(sc, "too many clauses for if: ~A", sc->code));
    }
  else
    {
      if (is_not_null(cdr(cdr_code)))                    /* (if 1 2 . 3) */
	return(eval_error(sc, "if: ~A has improper list?", sc->code));
    }

  if ((is_overlaid(sc->code)) &&
      (cdr(ecdr(sc->code)) == sc->code))
    {
      s7_pointer test, t, f;
      pair_set_syntax_symbol(sc->code, sc->IF_UNCHECKED);

      test = car(sc->code);
      t = car(cdr_code);
      if (is_pair(cdr(cdr_code)))
	f = cadr(cdr_code);
      else f = sc->UNSPECIFIED;

      if (is_pair(test))
	{
	  if (is_pair(t))
	    {
	      if (is_pair(f))
		{
 		  if (is_h_optimized(test))
		    {
		      if (optimize_data(test) == HOP_SAFE_C_C)
			pair_set_syntax_symbol(sc->code, sc->SAFE_IF_CC_P_P);
		      else 
			{
			  if (is_h_safe_c_s(test))
			    {
			      if ((car(t) == sc->QUOTE) &&
				  (car(test) == sc->IS_NULL) &&
				  (is_syntactic(car(f))))
				{
				  pair_syntax_op(f) = symbol_syntax_op(car(f));
				  pair_set_syntax_symbol(sc->code, sc->SAFE_IF_IS_NULL_Q_P);
				  set_fcdr(sc->code, cadr(test));
				}
			      else 
				{
				  if (car(test) == sc->IS_PAIR)
				    {
				      pair_set_syntax_symbol(sc->code, sc->SAFE_IF_IS_PAIR_P_P);
				      set_fcdr(sc->code, cadr(test));
				    }
				  else
				    {
				      if (car(test) == sc->IS_SYMBOL)
					pair_set_syntax_symbol(sc->code, sc->SAFE_IF_IS_SYMBOL_P_P);
				      else pair_set_syntax_symbol(sc->code, sc->SAFE_IF_CS_P_P);
				      set_fcdr(sc->code, cadr(test));
				    }
				}
			    }
			  else
			    {
			      if (optimize_data(test) == HOP_SAFE_C_SQ)
				{
				  pair_set_syntax_symbol(sc->code, sc->SAFE_IF_CSQ_P_P);
				  set_fcdr(sc->code, cadr(caddr(test)));
				}
			      else
				{
				  if (optimize_data(test) == HOP_SAFE_C_SS)
				    {
				      pair_set_syntax_symbol(sc->code, sc->SAFE_IF_CSS_P_P);
				      set_fcdr(sc->code, caddr(test));
				    }
				  else
				    {
				      if (optimize_data(test) == HOP_SAFE_C_SC)
					{
					  pair_set_syntax_symbol(sc->code, sc->SAFE_IF_CSC_P_P);
					  set_fcdr(sc->code, caddr(test));
					}
				      else 
					{
					  if (is_all_x_safe(sc, test))
					    {
					      pair_set_syntax_symbol(sc->code, sc->IF_A_P_P);
					      set_fcdr(sc->code, (s7_pointer)all_x_eval(sc, test));
					    }
					  else pair_set_syntax_symbol(sc->code, sc->IF_Z_P_P);
					}
				    }
				}
			    }
			}
		    }
		  else
		    {
		      if (is_syntactic(car(test)))
			{
			  opcode_t new_op;
			  pair_syntax_op(test) = symbol_syntax_op(car(test));

			  if (symbol_syntax_op(car(test)) == OP_AND)
			    {
			      s7_pointer old_code;
			      old_code = sc->code;
			      sc->code = cdr(test);
			      check_and(sc);
			      new_op = symbol_syntax_op(car(test));
			      sc->code = old_code;
			      if (new_op == OP_AND_P)
				pair_set_syntax_symbol(sc->code, sc->IF_ANDP_P_P);
			    }
			  else
			    {
			      if (symbol_syntax_op(car(test)) == OP_OR)
				{
				  s7_pointer old_code;
				  old_code = sc->code;
				  sc->code = cdr(test);
				  check_or(sc);
				  new_op = symbol_syntax_op(car(test));
				  sc->code = old_code;
				      
				  if (new_op == OP_OR_P)
				    pair_set_syntax_symbol(sc->code, sc->IF_ORP_P_P);
				}
			    }
			}
		      else pair_set_syntax_symbol(sc->code, sc->IF_P_P_P);
		    }
		}
	      else 
		{
		  if (is_null(cdr(cdr_code)))
		    {
		      if (is_h_optimized(test))
			{
			  if (optimize_data(test) == HOP_SAFE_C_C)
			    pair_set_syntax_symbol(sc->code, sc->SAFE_IF_CC_P);
			  else 
			    {
			      if (is_h_safe_c_s(test))
				{
				  if (car(test) == sc->IS_PAIR)
				    {
				      pair_set_syntax_symbol(sc->code, sc->SAFE_IF_IS_PAIR_P);
				      set_fcdr(sc->code, cadr(test));
				    }
				  else
				    {
				      if (car(test) == sc->IS_SYMBOL)
					pair_set_syntax_symbol(sc->code, sc->SAFE_IF_IS_SYMBOL_P);
				      else 
					{
					  if (car(test) == sc->NOT)
					    pair_set_syntax_symbol(sc->code, sc->SAFE_IF_NOT_S_P);
					  else pair_set_syntax_symbol(sc->code, sc->SAFE_IF_CS_P); 
					}
				      set_fcdr(sc->code, cadr(test));
				    }
				}
			      else
				{
				  if (optimize_data(test) == HOP_SAFE_C_SQ)
				    {
				      pair_set_syntax_symbol(sc->code, sc->SAFE_IF_CSQ_P);
				      set_fcdr(sc->code, cadr(caddr(test)));
				    }
				  else
				    {
				      if (optimize_data(test) == HOP_SAFE_C_SS)
					{
					  pair_set_syntax_symbol(sc->code, sc->SAFE_IF_CSS_P);
					  set_fcdr(sc->code, caddr(test));
					}
				      else
					{
					  if (optimize_data(test) == HOP_SAFE_C_SC)
					    {
					      pair_set_syntax_symbol(sc->code, sc->SAFE_IF_CSC_P);
					      set_fcdr(sc->code, caddr(test));
					    }
					  else				
					    {
					      if (optimize_data(test) == HOP_SAFE_C_opSSq)
						{
						  pair_set_syntax_symbol(sc->code, sc->SAFE_IF_C_SS_P);
						  set_fcdr(sc->code, cadar(sc->code));
						}
					      else
						{
						  /* test is h_optimized here
						   */
						  if (is_all_x_safe(sc, test))
						    {
						      set_fcdr(sc->code, (s7_pointer)all_x_eval(sc, test));
						      pair_set_syntax_symbol(sc->code, (is_h_optimized(t)) ? sc->SAFE_IF_A_Z : sc->IF_A_P);
						    }
						  else
						    {
						      set_fcdr(sc->code, cadr(sc->code));
						      pair_set_syntax_symbol(sc->code, (is_h_optimized(t)) ? sc->SAFE_IF_Z_Z : sc->IF_Z_P);
						    }
						}
					    }
					}
				    }
				}
			    }
			}
		      else 
			{
			  if (is_syntactic(car(test)))
			    {
			      opcode_t new_op;
			      pair_set_syntax_symbol(sc->code, sc->IF_B_P);
			      pair_syntax_op(test) = symbol_syntax_op(car(test));
			      if (symbol_syntax_op(car(test)) == OP_AND)
				{
				  s7_pointer old_code;
				  old_code = sc->code;
				  sc->code = cdr(test);
				  check_and(sc);
				  new_op = symbol_syntax_op(car(test));
				  sc->code = old_code;

				  if (new_op == OP_AND_P)
				    pair_set_syntax_symbol(sc->code, sc->IF_ANDP_P);
				}
			      else
				{
				  if (symbol_syntax_op(car(test)) == OP_OR)
				    {
				      s7_pointer old_code;
				      old_code = sc->code;
				      sc->code = cdr(test);
				      check_or(sc);
				      new_op = symbol_syntax_op(car(test));
				      sc->code = old_code;
				      
				      if (new_op == OP_OR_P)
					pair_set_syntax_symbol(sc->code, sc->IF_ORP_P);
				    }
				}
			    }
			  else pair_set_syntax_symbol(sc->code, sc->IF_P_P);
			}
		    }
		  else
		    {
		      if (is_h_safe_c_s(test))
			{
			  if (car(test) == sc->IS_PAIR)
			    {
			      pair_set_syntax_symbol(sc->code, sc->SAFE_IF_IS_PAIR_P_X);
			      set_fcdr(sc->code, cadr(test));
			    }
			  else
			    {
			      pair_set_syntax_symbol(sc->code, sc->SAFE_IF_CS_P_X);
			      set_fcdr(sc->code, cadr(test));
			    }
			}
		      else 
			{
			  if (is_all_x_safe(sc, test))
			    {
			      set_fcdr(sc->code, (s7_pointer)all_x_eval(sc, test));
			      pair_set_syntax_symbol(sc->code, sc->IF_A_P_X);
			    }
			  else pair_set_syntax_symbol(sc->code, sc->IF_P_P_X);
			}
		    }
		}
	    }
	  else 
	    {
	      if (is_pair(f))
		{
		  if (is_h_optimized(test))
		    {
		      if (optimize_data(test) == HOP_SAFE_C_C)
			pair_set_syntax_symbol(sc->code, sc->SAFE_IF_CC_X_P);
		      else
			{
			  if (is_h_safe_c_s(test))
			    {
			      pair_set_syntax_symbol(sc->code, sc->SAFE_IF_CS_X_P);
			      set_fcdr(sc->code, cadr(test));
			      if ((is_symbol(t)) &&
				  (car(test) == sc->IS_NULL) &&
				  (is_syntactic(car(f))))
				{
				  pair_syntax_op(f) = symbol_syntax_op(car(f));
				  pair_set_syntax_symbol(sc->code, sc->SAFE_IF_IS_NULL_S_P);
				}
			    }
			  else 
			    {
			      if (optimize_data(test) == HOP_SAFE_C_SS)
				{
				  pair_set_syntax_symbol(sc->code, sc->SAFE_IF_CSS_X_P);
				  set_fcdr(sc->code, caddr(test));
				}
			      else
				{
				  if (optimize_data(test) == HOP_SAFE_C_SC)
				    {
				      if ((t == cadr(test)) &&
					  (is_h_optimized(f)))
					pair_set_syntax_symbol(sc->code, sc->SAFE_IF_CSC_X_O_A);
				      else pair_set_syntax_symbol(sc->code, sc->SAFE_IF_CSC_X_P);
				      set_fcdr(sc->code, caddr(test));
				    }
				  else pair_set_syntax_symbol(sc->code, sc->IF_P_X_P);
				}
			    }
			}
		    }
		  else pair_set_syntax_symbol(sc->code, sc->IF_P_X_P);
		}
	    }
	}
      else /* test is symbol or constant, but constant here is nutty */
	{
	  if (is_symbol(test))
	    {
	      if (is_pair(t))
		{
		  if (is_pair(f))
		    pair_set_syntax_symbol(sc->code, sc->IF_S_P_P);
		  else 
		    {
		      if (is_null(cdr(cdr_code)))
			pair_set_syntax_symbol(sc->code, sc->IF_S_P);
		      else pair_set_syntax_symbol(sc->code, sc->IF_S_P_X);
		    }
		}
	      else 
		{
		  if (is_pair(f))
		    pair_set_syntax_symbol(sc->code, sc->IF_S_X_P);
		}
	    }
	}
    }
  return(sc->code);
}


static s7_pointer optimize_lambda(s7_scheme *sc, bool unstarred_lambda, s7_pointer x, s7_pointer args, s7_pointer body)
{
  int len;
  len = s7_list_length(sc, body);

  if (len < 0)                                                               /* (define (hi) 1 . 2) */
    return(eval_error_with_name(sc, "~A: function body messed up, ~A", sc->code));

  if (len > 0)  /* i.e. not circular */
    {
      bool bad_set = false;

      clear_syms_in_list(sc);
      optimize(sc, body, 1, collect_collisions(sc, args, list_1(sc, add_sym_to_list(sc, x))));
      /* if the body is safe, we can optimize the calling sequence */
      if ((is_proper_list(sc, args)) &&
	  (!arglist_has_rest(sc, args)))
	{
	  if (!unstarred_lambda)
	    {
	      s7_pointer p, arg;
	      bool happy = true;
	      /* check default vals -- if none is an expression or symbol, set simple args */
	      for (p = args; is_pair(p); p = cdr(p))
		{
		  arg = car(p);
		  if ((is_pair(arg)) &&                /* has default value */
		      ((is_symbol(cadr(arg))) ||       /*    if default value might involve eval in any way, it isn't simple */
		       ((is_pair(cadr(arg))) &&        /*    pair as default only ok if it is (quote ...) */
			(car(cadr(arg)) != sc->QUOTE))))
		    {
		      happy = false;
		      break;
		    }
		}
	      if (happy)
		set_simple_args(body);
	    }
	  sc->cycle_counter = 0;
	  /* it is expensive to check for cycles with collect_shared_info, but the
	   *   body_is_safe process can get hung if the cycle is hidden from the simple
	   *   checks above. 
	   *       (define (hi) (let ((ctr 0)) #1=(begin (format #t "~D " ctr) (set! ctr (+ ctr 1)) (if (< ctr 4) #1# (newline)))))
	   *   tickles this bug.  In all my current test cases, we never get over 350 (lint), so checking
	   *   for 5000 seems safe.
	   *
	   * (added later: this comment may be out-of-date -- cycles are easy to detect)
	   */
	  if (body_is_safe(sc, x, args, body, true, &bad_set))
	    {
	      /* (define (hi a) (+ a 1) (hi (- a 1))) */
	      /* there is one problem with closure* here -- we can't trust anything that
	       *   has fancy (non-constant) default argument values.
	       */
	      if (unstarred_lambda)
		set_safe_closure(body);
	      else
		{
		  if (has_simple_args(body))
		    set_safe_closure(body);
		}
	    }
	}
    }
  return(x);
}


static s7_pointer check_define(s7_scheme *sc)
{
  s7_pointer x;
  int arity = CLOSURE_ARITY_NOT_SET;

  if (!is_pair(sc->code))
    return(eval_error_with_name(sc, "~A: nothing to define? ~A", sc->code));   /* (define) */
  
  if (!is_pair(cdr(sc->code)))
    {
      if (is_null(cdr(sc->code)))
	return(eval_error_with_name(sc, "~A: no value? ~A", sc->code));        /* (define var) */
      return(eval_error_with_name(sc, "~A: bad form? ~A", sc->code));          /* (define var . 1) */
    }
  if (!is_pair(car(sc->code)))
    {
      if (is_not_null(cddr(sc->code)))                                           /* (define var 1 . 2) */
	return(eval_error_with_name(sc, "~A: more than 1 value? ~A", sc->code)); /* (define var 1 2) */
      if (sc->op == OP_DEFINE_STAR)
	return(eval_error(sc, "define* is restricted to functions: (define* ~{~S~^ ~})", sc->code));
      
      x = car(sc->code);
      if (!is_symbol(x))                                                        /* (define 3 a) */
	return(eval_error_with_name(sc, "~A: define a non-symbol? ~S", x));
      if (is_keyword(x))                                                        /* (define :hi 1) */
	return(eval_error_with_name(sc, "~A ~A: keywords are constants", x));
      if (is_syntactic(x))                                                      /* (define and a) */
	{
	  if (sc->safety > 0)
	    s7_warn(sc, 128, "%s: syntactic keywords tend to behave badly if redefined", DISPLAY(x));
	  set_local(x);
	}
      if ((is_pair(cadr(sc->code))) &&               /* look for (define sym (lambda ...)) and treat it like (define (sym ...)...) */
	  (((symbol_id(sc->LAMBDA) == 0) && (caadr(sc->code) == sc->LAMBDA)) ||
	   ((symbol_id(sc->LAMBDA_STAR) == 0) && (caadr(sc->code) == sc->LAMBDA_STAR))))
	/* not is_global here because that bit might not be set for initial symbols (why not? -- redef as method etc) */
	optimize_lambda(sc, caadr(sc->code) == sc->LAMBDA, x, cadr(cadr(sc->code)), cddr(cadr(sc->code)));
    }
  else
    {
      x = caar(sc->code);
      if (!is_symbol(x))                                                        /* (define (3 a) a) */
	return(eval_error_with_name(sc, "~A: define a non-symbol? ~S", x));
      if (is_syntactic(x))                                                      /* (define (and a) a) */
	{
	  if (sc->safety > 0)
	    s7_warn(sc, 128, "%s: syntactic keywords tend to behave badly if redefined", DISPLAY(x));
	  set_local(x);
	}
      if (sc->op == OP_DEFINE_STAR)
	cdar(sc->code) = check_lambda_star_args(sc, cdar(sc->code), &arity);
      else check_lambda_args(sc, cdar(sc->code), &arity);

      optimize_lambda(sc, sc->op == OP_DEFINE, x, cdar(sc->code), cdr(sc->code));
    }

  if ((is_overlaid(sc->code)) &&
      (cdr(ecdr(sc->code)) == sc->code))
    {
      if ((arity >= 0) &&
	  (arity < NUM_SMALL_INTS))
	set_fcdr(sc->code, small_int(arity));
      else set_fcdr(sc->code, (arity == CLOSURE_ARITY_NOT_SET) ? arity_not_set : make_permanent_integer(arity));
      
      if (sc->op == OP_DEFINE)
	{
	  if ((is_pair(car(sc->code))) &&
	      (!symbol_has_accessor(x)) &&
	      (!is_immutable(x)))
	    pair_set_syntax_symbol(sc->code, sc->DEFINE_FUNCHECKED);
	  else pair_set_syntax_symbol(sc->code, sc->DEFINE_UNCHECKED);
	}
      else pair_set_syntax_symbol(sc->code, sc->DEFINE_STAR_UNCHECKED);
    }
  else set_fcdr(sc->code, sc->F);
  return(sc->code);
}


static s7_pointer check_with_let(s7_scheme *sc)
{
  if (!is_pair(sc->code))                            /* (with-let . "hi") */
    return(eval_error(sc, "with-let takes an environment argument: ~A", sc->code));
  if (!is_pair(cdr(sc->code)))                       /* (with-let e) -> an error? */
    return(eval_error(sc, "with-let body is messed up: ~A", sc->code));
  if ((!is_pair(cddr(sc->code))) &&
      (!is_null(cddr(sc->code))))
    return(eval_error(sc, "with-let body has stray dot? ~A", sc->code));

  if ((is_overlaid(sc->code)) &&
      (cdr(ecdr(sc->code)) == sc->code))
    {
      pair_set_syntax_symbol(sc->code, sc->WITH_LET_UNCHECKED);
      if ((is_symbol(car(sc->code))) &&
	  (is_pair(cadr(sc->code))))
	pair_set_syntax_symbol(sc->code, sc->WITH_LET_S);
    }
  return(sc->code);
}


static s7_pointer check_set(s7_scheme *sc)
{
  if (!is_pair(sc->code))
    {
      if (is_null(sc->code))                                             /* (set!) */
	return(eval_error(sc, "set!: not enough arguments: ~A", sc->code));
      return(eval_error(sc, "set!: stray dot? ~A", sc->code));           /* (set! . 1) */
    }
  if (!is_pair(cdr(sc->code)))                                
    {
      if (is_null(cdr(sc->code)))                                         /* (set! var) */
	return(eval_error(sc, "set!: not enough arguments: ~A", sc->code));
      return(eval_error(sc, "set!: stray dot? ~A", sc->code));           /* (set! var . 1) */
    }
  if (is_not_null(cddr(sc->code)))                                       /* (set! var 1 2) */
    return(eval_error(sc, "~A: too many arguments to set!", sc->code));
  
  /* cadr (the value) has not yet been evaluated */
  
  if (is_immutable(car(sc->code)))                                       /* (set! pi 3) */
    return(eval_error(sc, "set!: can't alter immutable object: ~S", car(sc->code)));
  
  if (is_pair(car(sc->code)))
    {
      if (is_pair(caar(sc->code)))
	{
	  if (!s7_is_list(sc, cdar(sc->code)))                          /* (set! ('(1 2) . 0) 1) */
	    eval_error(sc, "improper list of args to set!: ~A", sc->code);
	}
      if (!is_proper_list(sc, car(sc->code)))                           /* (set! ("hi" . 1) #\a) or (set! (#(1 2) . 1) 0) */
	eval_error(sc, "set! target is an improper list: (set! ~A ...)", car(sc->code));
    }
  else
    {
      if (!is_symbol(car(sc->code)))                                 /* (set! 12345 1) */
	return(eval_error(sc, "set! can't change ~S", car(sc->code)));
    }

  if ((is_overlaid(sc->code)) &&
      (cdr(ecdr(sc->code)) == sc->code))
    {
      if (is_pair(car(sc->code)))
	{
	  /* here we have (set! (...) ...) */
	  s7_pointer inner, value;
	  inner = car(sc->code);
	  value = cadr(sc->code);

	  pair_set_syntax_symbol(sc->code, sc->SET_UNCHECKED);
	  if (is_symbol(car(inner)))
	    {
	      if ((is_null(cdr(inner))) &&
		  (!is_pair(value)) &&
		  (is_global(car(inner))) &&
		  (is_c_function(slot_value(global_slot(car(inner))))) &&
		  (c_function_required_args(slot_value(global_slot(car(inner)))) == 0))
		pair_set_syntax_symbol(sc->code, sc->SET_PWS);
	      else
		{
		  if ((is_pair(cdr(inner))) &&
		      (!is_pair(cddr(inner)))) /* we check cddr(sc->code) above */
		    {
		      if (!is_pair(cadr(inner)))
			{
			  /* (set! (f s) ...) */
			  if (!is_pair(value))
			    pair_set_syntax_symbol(sc->code, sc->SET_PAIR);
			  else 
			    {
			      pair_set_syntax_symbol(sc->code, sc->SET_PAIR_P);
			      /* splice_in_values protects us here from values */
			      if (is_h_optimized(value)) /* this excludes h_unknown_g etc */
				{
				  pair_set_syntax_symbol(sc->code, sc->SET_PAIR_Z);
				  if (is_all_x_safe(sc, value))
				    {
				      s7_pointer obj;
				      annotate_arg(sc, cdr(sc->code));
				      pair_set_syntax_symbol(sc->code, sc->SET_PAIR_ZA);
				      obj = find_symbol_checked(sc, car(inner));
				      if ((is_c_function(obj)) &&
					  (is_c_function(c_function_setter(obj))))
					{
					  pair_set_syntax_symbol(sc->code, sc->SET_PAIR_A);
					}
				    }
				}
			      
			      /* e.g. (set! (f s) ...) where f is unknown to optimizer */
			      if (is_optimized(value))
				{
				  if (optimize_data(value) == HOP_UNKNOWN_G)
				    pair_set_syntax_symbol(sc->code, sc->SET_PAIR_UNKNOWN_G);
				  else
				    {
				      if ((optimize_data(value) == HOP_SAFE_C_S_opVSq) &&
					  (car(value) == sc->MULTIPLY) &&
					  (car(inner) == caaddr(value)) &&
					  (cadr(inner) == cadr(caddr(value))))
					pair_set_syntax_symbol(sc->code, sc->SET_FV_SCALED);
				    }
				}
			      /* else if (is_optimized(value)) fprintf(stderr, "%s %s\n", opt_names[optimize_data(value)], DISPLAY(sc->code)); */
			      /* here we can get h_unknown_g      (set! (current-peak-freqs new-place) (peak-freqs k))
			       *                 h_safe_c_opvsq_s (set! (sines k) (* (sines k) one-over-two-pi))
			       *                 h_unknown_a      (set! (str loc) (digit->char (random radix)))
			       */
			    }
			}
		      else
			{
			  if ((car(cadr(inner)) == sc->QUOTE) &&
			      (is_symbol(car(inner))) &&
			      ((is_symbol(value)) || (is_all_x_safe(sc, value))))
			    {
			      if (is_symbol(value))
				pair_set_syntax_symbol(sc->code, sc->SET_ENV_S);
			      else 
				{
				  pair_set_syntax_symbol(sc->code, sc->SET_ENV_ALL_X);
				  set_fcdr(cdr(sc->code), (s7_pointer)all_x_eval(sc, value));
				}
			    }
			  else
			    {
			      if ((is_optimized(cadr(inner))) &&
				  (optimize_data(cadr(inner)) == HOP_SAFE_C_C))
				{
				  if (!is_pair(value))
				    pair_set_syntax_symbol(sc->code, sc->SET_PAIR_C);
				  else 
				    {
				      /* splice_in_values protects us here from values */
				      pair_set_syntax_symbol(sc->code, sc->SET_PAIR_C_P);
				    }
				}
			    }
			}
		    }
		}
	    }
	}
      else pair_set_syntax_symbol(sc->code, sc->SET_NORMAL);
      
      if (is_symbol(car(sc->code)))
	{
	  s7_pointer settee, value;
	  settee = car(sc->code);
	  value = cadr(sc->code);

	  if ((!symbol_has_accessor(settee)) &&
	      (!is_syntactic(settee)))
	    {
	      if (is_symbol(value))
		pair_set_syntax_symbol(sc->code, sc->SET_SYMBOL_S);
	      else
		{
		  if (!is_pair(value))
		    pair_set_syntax_symbol(sc->code, sc->SET_SYMBOL_C);
		  else
		    {
		      if (car(value) == sc->QUOTE)
			pair_set_syntax_symbol(sc->code, sc->SET_SYMBOL_Q);
		      else
			{
			  /* if cadr(cadr) == car, or cdr(cadr) not null and cadr(cadr) == car, and cddr(cadr) == null,
			   *   it's (set! <var> (<op> <var> val)) or (<op> val <var>) or (<op> <var>)
			   *   in the set code, we get the slot as usual, then in case 1 above,
			   *   car(sc->T2_1) = slot_value(slot), car(sc->T2_2) = increment, call <op>, set slot_value(slot)
			   *
			   * this can be done in all combined cases where a symbol is repeated (do in particular)
			   */
			  
			  /* (define (hi) (let ((x 1)) (set! x (+ x 1)))) 
			   *   but the value might be values:
			   *   (let () (define (hi) (let ((x 0)) (set! x (values 1 2)) x)) (catch #t hi (lambda a a)) (hi))
			   *   which is caught in splice_in_values
			   */
			  pair_set_syntax_symbol(sc->code, sc->SET_SYMBOL_P);
			  if (is_h_safe_c_s(value))
			    {
			      pair_set_syntax_symbol(sc->code, sc->SET_SYMBOL_SAFE_S);
			      set_fcdr(sc->code, cadr(value));
			    }
			  else
			    {
			      if (is_optimized(value))
				{
				  pair_set_syntax_symbol(sc->code, sc->SET_SYMBOL_Z);
				  if (optimize_data(value) == HOP_UNKNOWN_G)
				    pair_set_syntax_symbol(sc->code, sc->SET_SYMBOL_UNKNOWN_G);

				  if (optimize_data(value) == HOP_SAFE_C_C)
				    {
				      if ((settee == cadr(value)) &&
					  (car(value) == sc->ADD) &&
					  (is_null(cdddr(value))) &&
					  (is_optimized(caddr(value))) &&
					  (optimize_data(caddr(value)) == HOP_SAFE_C_C) && /* paranoia... */
					  (s7_function_returns_temp(sc, caddr(value))))
					{
					  pair_set_syntax_symbol(sc->code, sc->INCREMENT_C_TEMP);
					  set_fcdr(sc->code, caddr(value));
					}
				      else
					{
					  pair_set_syntax_symbol(sc->code, sc->SET_SYMBOL_SAFE_C);
					  /* ecdr here points back? */
					  set_fcdr(sc->code, cdr(value));
					}
				    }
				  else
				    {
				      /* most of these special cases probably don't matter */
				      if (optimize_data(value) == HOP_SAFE_C_SS)
					{
					  if (settee == cadr(value))
					    pair_set_syntax_symbol(sc->code, sc->INCREMENT_SS);
					  else pair_set_syntax_symbol(sc->code, sc->SET_SYMBOL_SAFE_SS);
					  set_fcdr(sc->code, cdr(value));
					}
				      else
					{
					  if (optimize_data(value) == HOP_SAFE_C_SSS)
					    {
					      if ((settee == cadr(value)) &&
						  (car(value) == sc->ADD))
						pair_set_syntax_symbol(sc->code, sc->INCREMENT_SSS);
					      else pair_set_syntax_symbol(sc->code, sc->SET_SYMBOL_SAFE_SSS);
					      set_fcdr(sc->code, cdr(value));
					    }
					  else
					    {
					      if (is_all_x_safe(sc, value)) /* value = cadr(sc->code) */
						{
						  pair_set_syntax_symbol(sc->code, sc->SET_SYMBOL_A);
						  annotate_arg(sc, cdr(sc->code)); 
						}
					      if ((is_callable_c_op(optimize_data(value))) &&
						  (is_h_optimized(value)))
						{
						  if ((settee == cadr(value)) &&
						      (!is_null(cddr(value))))
						    {
						      if (is_null(cdddr(value)))
							{
							  if (is_all_x_safe(sc, caddr(value)))
							    {
							      /* this appears to give a slight savings over the SZ case */
							      pair_set_syntax_symbol(sc->code, sc->INCREMENT_SA);
							      annotate_arg(sc, cddr(value)); /* this sets fcdr(arg) */
							      set_fcdr(sc->code, cddr(value));
							    }
							  else
							    {
							      if (is_optimized(caddr(value)))
								{
								  pair_set_syntax_symbol(sc->code, sc->INCREMENT_SZ);
								  set_fcdr(sc->code, caddr(value));
								}
							    }
							}
						      else
							{
							  if ((is_null(cddddr(value))) &&
							      (is_all_x_safe(sc, caddr(value))) &&
							      (is_all_x_safe(sc, cadddr(value))))
							    {
							      pair_set_syntax_symbol(sc->code, sc->INCREMENT_SAA);
							      annotate_arg(sc, cddr(value));
							      annotate_arg(sc, cdddr(value)); 
							      set_fcdr(sc->code, cddr(value));
							    }
							}
						    }
						}
					    }
					}
				    }
				}
			    }
			  
			  if ((is_h_optimized(value)) &&
			      (!is_unsafe(value)) &&
			      (is_not_null(cdr(value))))               /* (set! x (y)) */
			    {
			      if (is_not_null(cddr(value)))
				{
				  if ((caddr(value) == small_int(1)) &&
				      (cadr(value) == settee))
				    {
				      if ((ecdr(value) == add_s1) ||
					  (ecdr(value) == add_cs1))
					pair_set_syntax_symbol(sc->code, sc->INCREMENT_1);
				      else 
					{
					  if ((ecdr(value) == subtract_s1) ||
					      (ecdr(value) == subtract_cs1))
					    pair_set_syntax_symbol(sc->code, sc->DECREMENT_1);
					}
				    }
				  else
				    {
				      if ((cadr(value) == small_int(1)) &&
					  (caddr(value) == settee) &&
					  (ecdr(value) == add_1s))
					pair_set_syntax_symbol(sc->code, sc->INCREMENT_1);
				      else
					{
					  if ((settee == caddr(value)) &&
					      (is_symbol(cadr(value))) &&
					      (caadr(sc->code) == sc->CONS))
					    {
					      pair_set_syntax_symbol(sc->code, sc->SET_CONS);
					      set_fcdr(sc->code, cadr(value));
					    }
					}
				    }
				}
			    }
			}
		    }
		}
	    }
	}
    }
  return(sc->code);
}


static bool safe_stepper(s7_scheme *sc, s7_pointer expr, s7_pointer vars)
{
  /* for now, just look for stepper as last element of any list
   *    any embedded set is handled by do-is-safe, so we don't need to descend into the depths
   */
  s7_pointer p;
  if (direct_memq(cadr(expr), vars))
    return(false);

  for (p = cdr(expr); is_pair(cdr(p)); p = cdr(p));

  if (is_pair(p))
    {
      if ((is_optimized(p)) &&
	  ((optimize_data(p) & 1) != 0) &&
	  (is_safe_c_op(optimize_data(p))))
	return(true);

      if (direct_memq(car(p), vars))
	return(false);
    }
  else 
    {
      if (direct_memq(p, vars))
	return(false);
    }
  return(true);
}

/* (define (hi) (do ((i 0 (+ i 1))) ((= i 3)) (let ((val 1)) (set! val 2) )))
 * (define (hi) (do ((i 0 (+ i 1))) ((= i 3)) (let ((val 3)) (set! val i) )))
 * (define (hi) (do ((i 0 (+ i 1))) ((= i 3)) (display i)) (newline))
 * (define (hi) (do ((i 0 (+ i 1))) ((= i 10)) (set! i (+ i 1)) (display i)) (newline))
 *
 * (define (hi a) (do ((i 0 (+ i 1))) ((= i a)) (display i)))
 */


static bool do_is_safe(s7_scheme *sc, s7_pointer body, s7_pointer steppers, s7_pointer var_list, bool *has_set)
{
  /* here any (unsafe?) closure or jumping-op (call/cc) or shadowed variable is trouble
   */
  s7_pointer p;
  /* if (s7_list_length(sc, body) <= 0) return(false); */
  /* fprintf(stderr, "do is safe: %s %s %s\n", DISPLAY(body), DISPLAY(steppers), DISPLAY(var_list)); */

  for (p = body; is_pair(p); p = cdr(p))
    {
      s7_pointer expr;
      expr = car(p);
      if (is_pair(expr))
	{
	  s7_pointer x;
	  x = car(expr);
	  if (is_symbol(x))
	    {
	      if (is_syntactic(x))
		{
		  opcode_t op;
		  s7_pointer func, vars;
		  func = slot_value(global_slot(x));
		  op = (opcode_t)syntax_opcode(func);
		  switch (op)
		    {
		    case OP_QUOTE:
		      break;

		    case OP_LET:
		    case OP_LET_STAR:
		      if (is_symbol(cadr(expr)))
			return(false);

		    case OP_LETREC:
		    case OP_LETREC_STAR:
		    case OP_DO:
		      for (vars = cadr(expr); is_pair(vars); vars = cdr(vars))
			{
			  s7_pointer var;
			  var = caar(vars);
			  if ((direct_memq(var, var_list)) ||
			      (direct_memq(var, steppers)))
			    return(false);

			  var_list = cons(sc, var, var_list);
			  sc->x = var_list;
			  if ((is_pair(cdar(vars))) &&
			      (!do_is_safe(sc, cdar(vars), steppers, var_list, has_set)))
			    {
			      sc->x = sc->NIL;
			      return(false);
			    }
			  sc->x = sc->NIL;
			}

		      if (!do_is_safe(sc, (op == OP_DO) ? cdddr(expr) : cddr(expr), steppers, var_list, has_set))
			return(false);
		      break;

		    case OP_SET:
		      if (!direct_memq(cadr(expr), var_list)) /* is some non-local variable being set? */
			(*has_set) = true;
		      if (!do_is_safe(sc, cddr(expr), steppers, var_list, has_set))
			return(false);
		      if (!safe_stepper(sc, expr, steppers))  /* is step var's value used as the stored value by set!? */
			return(false);
		      break;
		      
		    case OP_IF:
		    case OP_WHEN:
		    case OP_UNLESS:
		    case OP_COND:
		    case OP_CASE:
		    case OP_AND:
		    case OP_OR:
		    case OP_BEGIN:
		      if (!do_is_safe(sc, cdr(expr), steppers, var_list, has_set))
			return(false);
		      break;

		    case OP_WITH_LET:
		      return(true);

		    default:
		      return(false);
		      break;
		    }
		}
	      else
		{
		  if ((!is_optimized(expr)) ||
		      (is_unsafe(expr)) ||
		      (!do_is_safe(sc, cdr(expr), steppers, var_list, has_set)))
		    /* this is unreasonably retrictive because optimize_expression returns "unsafe"
		     *   even when everything is safe -- it's merely saying it could not find a
		     *   special optimization case for the expression.  
		     */
		    return(false);
		  else
		    {
		      if (is_setter(car(expr))) /* "setter" includes stuff like cons and vector */
			{
			  if (!direct_memq(cadr(expr), var_list))         /* non-local is being changed */
			    {
			      if ((direct_memq(cadr(expr), steppers)) ||  /* stepper is being set? */
				  (!is_pair(cddr(expr))) ||
				  (!is_pair(cdddr(expr))) ||
				  (is_pair(cddddr(expr))) ||
				  (is_symbol(cadddr(expr))) || /* memq cadddr expr steppers? */
				  (is_pair(cadddr(expr))))
				(*has_set) = true;
			    }
			  if (!do_is_safe(sc, cddr(expr), steppers, var_list, has_set))
			    return(false);		      
			  if (!safe_stepper(sc, expr, steppers))
			    return(false);
			}
		    }
		}
	    }
	  else 
	    {
	      return(false); 
	      /* car(expr) ("x") is not a symbol: ((mus-data loc) chan) for example
	       *   but that's actually safe since it's just in effect vector-ref
	       *   there are several examples in dlocsig: ((group-speakers group) i) etc
	       */
	    }
	}
    }
  return(true);
}


static bool is_dox_safe(int op)
{
  return((op == HOP_SAFE_C_S) ||
	 (op == HOP_SAFE_C_C) ||
	 (op == HOP_SAFE_C_CS) ||
	 (op == HOP_SAFE_C_SC) ||
	 (op == HOP_SAFE_C_SS) ||
	 (op == HOP_SAFE_C_SSC) ||
	 (op == HOP_SAFE_C_QS) ||
	 (op == HOP_SAFE_C_S_opCq));
}

static bool is_init_dox_safe(s7_scheme *sc, s7_pointer p)
{
  if (!is_pair(p)) return(true);
  if (car(p) == sc->QUOTE) return(true);
  if (!is_optimized(p)) return(false);
  return(is_dox_safe(optimize_data(p)));
}

static bool is_step_dox_safe(s7_scheme *sc, s7_pointer p)
{
  if (!is_pair(p)) return(true);
  if (car(p) == sc->QUOTE) return(false);
  if (!is_optimized(p)) return(false);
  return(is_dox_safe(optimize_data(p)));
}


static bool is_end_dox_safe(s7_scheme *sc, s7_pointer p)
{
  int op;
  
  if (!is_pair(p)) return(false);
  if (car(p) == sc->QUOTE) return(false);
  if (!is_optimized(p)) return(false);

  op = optimize_data(p);
  return((op == HOP_SAFE_C_S) ||
	 (op == HOP_SAFE_C_C) ||
	 (op == HOP_SAFE_C_SS) ||
	 (op == HOP_SAFE_C_SC) ||
	 (op == HOP_SAFE_C_CS) ||
	 (op == HOP_SAFE_C_QS) ||
	 (op == HOP_SAFE_C_S_opCq) ||
	 (op == HOP_SAFE_C_opSq_S) ||
	 (op == HOP_SAFE_C_S_opSq) ||
	 (op == HOP_SAFE_C_opSq_opSq) ||
	 (op == HOP_SAFE_C_S_opSCq) ||
	 (op == HOP_SAFE_C_S_opSSq));
}


typedef s7_pointer (*dox_function)(s7_scheme *sc, s7_pointer args, s7_pointer slot);

static s7_pointer step_dox_c_c(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  return(c_call(code)(sc, cdr(code)));
}

static s7_pointer step_dox_c(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  return(code);
}

static s7_pointer step_dox_s(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  return(find_symbol_checked(sc, code));
}

static s7_pointer step_dox_c_c_s_indirect(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  car(sc->T1_1) = slot_value(slot_pending_value(slot));
  return(((s7_function)c_function_call(c_function_base(ecdr(code))))(sc, sc->T1_1));
}

static s7_pointer step_dox_c_s(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  car(sc->T1_1) = find_symbol_checked(sc, cadr(code));
  return(c_call(code)(sc, sc->T1_1));
}

static s7_pointer step_dox_c_s_indirect(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  car(sc->T1_1) = slot_value(slot_pending_value(slot));
  return(c_call(code)(sc, sc->T1_1));
}

static s7_pointer step_dox_c_s_direct(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  car(sc->T1_1) = slot_value(slot);
  return(c_call(code)(sc, sc->T1_1));
}

static s7_pointer step_dox_read_char_s(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  s7_pointer port;
  port = slot_value(slot_pending_value(slot));
  if (is_input_port(port))
    return(chars[port_read_character(port)(sc, port)]);

  car(sc->T1_1) = port;
  return(g_read_char_1(sc, sc->T1_1));
}

static s7_pointer step_dox_c_sc(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  s7_pointer args;
  args = cdr(code);
  car(sc->T2_1) = find_symbol_checked(sc, car(args));
  car(sc->T2_2) = cadr(args);
  return(c_call(code)(sc, sc->T2_1));
}

static s7_pointer step_dox_c_sc_direct(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  car(sc->T2_1) = slot_value(slot);
  car(sc->T2_2) = caddr(code);
  return(c_call(code)(sc, sc->T2_1));
}

static s7_pointer step_dox_c_cs(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  s7_pointer args;
  args = cdr(code);
  car(sc->T2_2) = find_symbol_checked(sc, cadr(args));
  car(sc->T2_1) = car(args);
  return(c_call(code)(sc, sc->T2_1));
}

static s7_pointer step_dox_c_cs_direct(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  car(sc->T2_2) = slot_value(slot);
  car(sc->T2_1) = cadr(code);
  return(c_call(code)(sc, sc->T2_1));
}

static s7_pointer step_dox_c_ss(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  s7_pointer args, val;
  args = cdr(code);
  val = find_symbol_checked(sc, car(args));
  car(sc->T2_2) = find_symbol_checked(sc, cadr(args));
  car(sc->T2_1) = val;
  return(c_call(code)(sc, sc->T2_1));
}

static s7_pointer step_dox_c_ss_direct(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  car(sc->T2_1) = slot_value(slot);
  car(sc->T2_2) = slot_value(slot_pending_value(slot));
  return(c_call(code)(sc, sc->T2_1));
}

static s7_pointer step_dox_c_add_ss(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  car(sc->T2_1) = slot_value(slot);
  car(sc->T2_2) = slot_value(slot_pending_value(slot));
  return(g_add_2(sc, sc->T2_1));
}

static s7_pointer step_dox_c_subtract_ss(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  car(sc->T2_1) = slot_value(slot);
  car(sc->T2_2) = slot_value(slot_pending_value(slot));
  return(g_subtract_2(sc, sc->T2_1));
}

#if (!WITH_GMP)
static s7_pointer step_dox_c_subtract_i(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  /* this is hard to optimize because macros can cause hidden changes 
   */
  if ((is_integer(slot_value(slot))) &&
      (is_integer(slot_value(slot_pending_value(slot)))))
    return(make_integer(sc, integer(slot_value(slot)) - integer(slot_value(slot_pending_value(slot)))));
  car(sc->T2_1) = slot_value(slot);
  car(sc->T2_2) = slot_value(slot_pending_value(slot));
  return(g_subtract_2(sc, sc->T2_1));
}

static s7_pointer step_dox_c_subtract_f(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  if ((type(slot_value(slot)) == T_REAL) &&
      (type(slot_value(slot_pending_value(slot))) == T_REAL))
    return(make_real(sc, real(slot_value(slot)) - real(slot_value(slot_pending_value(slot)))));
  car(sc->T2_1) = slot_value(slot);
  car(sc->T2_2) = slot_value(slot_pending_value(slot));
  return(g_subtract_2(sc, sc->T2_1));
}

static s7_pointer step_dox_c_mutable_subtract_f(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  real(slot_value(slot)) -= real(slot_value(slot_pending_value(slot)));
  return(slot_value(slot));
}

static s7_pointer step_dox_c_mutable_add_f(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  real(slot_value(slot)) += real(slot_value(slot_pending_value(slot)));
  return(slot_value(slot));
}

static s7_pointer step_dox_c_mutable_add1(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  integer(slot_value(slot))++;
  return(slot_value(slot));
}

static s7_pointer step_dox_c_add_i(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  if ((is_integer(slot_value(slot))) &&
      (is_integer(slot_value(slot_pending_value(slot)))))
    return(make_integer(sc, integer(slot_value(slot)) + integer(slot_value(slot_pending_value(slot)))));
  car(sc->T2_1) = slot_value(slot);
  car(sc->T2_2) = slot_value(slot_pending_value(slot));
  return(g_add_2(sc, sc->T2_1));
}

static s7_pointer step_dox_c_add_f(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  if ((type(slot_value(slot)) == T_REAL) &&
      (type(slot_value(slot_pending_value(slot))) == T_REAL))
    return(make_real(sc, real(slot_value(slot)) + real(slot_value(slot_pending_value(slot)))));
  car(sc->T2_1) = slot_value(slot);
  car(sc->T2_2) = slot_value(slot_pending_value(slot));
  return(g_add_2(sc, sc->T2_1));
}
#endif

static s7_pointer step_dox_c_ssc(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  s7_pointer args, val;
  args = cdr(code);
  val = find_symbol_checked(sc, car(args));
  car(sc->T3_2) = find_symbol_checked(sc, cadr(args));
  car(sc->T3_1) = val;
  car(sc->T3_3) = caddr(args);
  return(c_call(code)(sc, sc->T3_1));
}

static s7_pointer step_dox_c_ssc_direct(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  car(sc->T3_1) = slot_value(slot);
  car(sc->T3_2) = slot_value(slot_pending_value(slot));
  car(sc->T3_3) = cadddr(code);
  return(c_call(code)(sc, sc->T3_1));
}

static s7_pointer step_dox_c_ssi_direct(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  s7_pointer a, b;
  a = slot_value(slot);
  b = slot_value(slot_pending_value(slot));
  if ((is_integer(a)) &&
      (is_integer(b)))
    return(make_integer(sc, integer(a) + integer(b) + integer(cadddr(code))));

  car(sc->T3_1) = a;
  car(sc->T3_2) = b;
  car(sc->T3_3) = cadddr(code);
  return(c_call(code)(sc, sc->T3_1));
}

static s7_pointer step_dox_c_qs(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  s7_pointer args;
  args = cdr(code);
  car(sc->T2_2) = find_symbol_checked(sc, cadr(args));
  car(sc->T2_1) = cadr(car(args));
  return(c_call(code)(sc, sc->T2_1));
}

static s7_pointer step_dox_c_qs_direct(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  car(sc->T2_2) = slot_value(slot);
  car(sc->T2_1) = cadr(cadr(code));
  return(c_call(code)(sc, sc->T2_1));
}

static s7_pointer step_dox_c_s_opcq(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  s7_pointer args, val;
  args = cdr(code);
  val = find_symbol_checked(sc, car(args));
  car(sc->T2_2) = c_call(cadr(args))(sc, ecdr(args)); /* any number of constants here */
  car(sc->T2_1) = val;
  return(c_call(code)(sc, sc->T2_1));
}

static s7_pointer step_dox_c_s_opcq_direct(s7_scheme *sc, s7_pointer code, s7_pointer slot)
{
  s7_pointer args;
  args = cdr(code);
  car(sc->T2_2) = c_call(cadr(args))(sc, ecdr(args)); /* any number of constants here */
  car(sc->T2_1) = slot_value(slot);
  return(c_call(code)(sc, sc->T2_1));
}

static s7_pointer dox_add_t1(s7_scheme *sc, s7_pointer args, s7_pointer slot)
{
  s7_pointer x;
  x = slot_value(slot);
  if (is_integer(x))
    return(make_integer(sc, integer(x) + 1));
  if (type(x) == T_REAL)
    return(make_real(sc, real(x) + 1.0));

  car(sc->T2_1) = x;
  car(sc->T2_2) = small_int(1);
  return(g_add_2(sc, sc->T2_1));
}

static s7_pointer dox_sub_t1(s7_scheme *sc, s7_pointer args, s7_pointer slot)
{
  s7_pointer x;
  x = slot_value(slot);
  if (is_integer(x))
    return(make_integer(sc, integer(x) - 1));
  if (type(x) == T_REAL)
    return(make_real(sc, real(x) - 1.0));

  car(sc->T2_1) = x;
  car(sc->T2_2) = small_int(1);
  return(g_subtract_2(sc, sc->T2_1));
}

static s7_pointer dox_add_ti(s7_scheme *sc, s7_pointer args, s7_pointer slot)
{
  s7_pointer x, y;
  
  x = slot_value(slot);
  y = caddr(args);
  if (is_integer(x))
    return(make_integer(sc, integer(x) + integer(y)));

  car(sc->T2_1) = x;
  car(sc->T2_2) = y;
  return(g_add_2(sc, sc->T2_1));
}

static s7_pointer dox_cdr_s(s7_scheme *sc, s7_pointer arg, s7_pointer slot)
{
  return((is_pair(slot_value(slot))) ? cdr(slot_value(slot)) : g_cdr(sc, list_1(sc, slot_value(slot))));
}


static dox_function dox_mutate(s7_scheme *sc, s7_pointer slot)
{
  s7_pointer expr;
  expr = slot_expression(slot);
#if (!WITH_GMP) 
  if ((((dox_function)fcdr(expr)) == step_dox_c_subtract_f) ||
      (((dox_function)fcdr(expr)) == step_dox_c_add_f))
    {
      if ((type(slot_value(slot)) == T_REAL) &&
	  (type(slot_value(slot_pending_value(slot))) == T_REAL))
	{
	  slot_value(slot) = s7_make_mutable_real(sc, real(slot_value(slot)));
	  if (((dox_function)fcdr(expr)) == step_dox_c_subtract_f)
	    return(step_dox_c_mutable_subtract_f);
	  else return(step_dox_c_mutable_add_f);
	}
    }
  else
    {
      if (((dox_function)fcdr(expr)) == dox_add_t1)
	{
	  if (type(slot_value(slot)) == T_INTEGER)
	    {
	      slot_value(slot) = make_mutable_integer(sc, integer(slot_value(slot)));
	      return(step_dox_c_mutable_add1);
	    }
	}
    }
#endif
  return((dox_function)fcdr(expr));
}
		    

static dox_function step_dox_eval(s7_scheme *sc, s7_pointer code, s7_pointer var)
{
  /* called in check_do, var=NULL if this is an init (not a step) check, code is the expr for var */
  if (!is_pair(code))
    {
      if (is_symbol(code))
	return(step_dox_s);
      return(step_dox_c);
    }
  switch (optimize_data(code))
    {
    case HOP_SAFE_C_C: 
      if (var == cadr(code))
	{
	  if (fcdr(code) == (s7_pointer)g_add_cs1)
	    return(dox_add_t1);

	  if (fcdr(code) == (s7_pointer)g_subtract_cs1)
	    return(dox_sub_t1);

	  if (fcdr(code) == (s7_pointer)g_add_si)
	    return(dox_add_ti);

	  /* (+ i c) (- i c)?
	   * (+ 1 i) -> cs
	   * 
	   * if (fcdr(code) == (s7_pointer)g_add_sf) return(dox_add_tf);
	   */
	}
      if ((var) &&
	  (is_symbol(cadr(code))) &&
	  (is_null(cddr(code))))
	return(step_dox_c_c_s_indirect);
      return(step_dox_c_c);

    case HOP_SAFE_C_S:       
      if (var == cadr(code))
	{
	  if (fcdr(code) == (s7_pointer)g_cdr)
	    return(dox_cdr_s);
	  return(step_dox_c_s_direct);
	}
      if (var)
	{
	  if (fcdr(code) == (s7_pointer)g_read_char_1)
	    return(step_dox_read_char_s);
	  return(step_dox_c_s_indirect);
	}
      return(step_dox_c_s);

    case HOP_SAFE_C_SSC:     
      if (var == cadr(code))
	{
	  if ((is_integer(cadddr(code))) &&
	      (car(code) == sc->ADD))
	    return(step_dox_c_ssi_direct);
	  return(step_dox_c_ssc_direct);
	}
      return(step_dox_c_ssc);

    case HOP_SAFE_C_SS: 
      if (var == cadr(code))
	{
	  if (c_call(code) == g_add_2) return(step_dox_c_add_ss);
	  if (c_call(code) == g_subtract_2) return(step_dox_c_subtract_ss);
	  return(step_dox_c_ss_direct);
	}
      return(step_dox_c_ss);

    case HOP_SAFE_C_SC:      return((var == cadr(code)) ? step_dox_c_sc_direct : step_dox_c_sc);
    case HOP_SAFE_C_CS:      return((var == caddr(code)) ? step_dox_c_cs_direct : step_dox_c_cs);
    case HOP_SAFE_C_QS:      return((var == caddr(code)) ? step_dox_c_qs_direct : step_dox_c_qs);
    case HOP_SAFE_C_S_opCq:  return((var == cadr(code)) ? step_dox_c_s_opcq_direct : step_dox_c_s_opcq);
    }
  return(NULL);
}


static s7_pointer end_dox_c_c(s7_scheme *sc, s7_pointer code)
{
  return(c_call(code)(sc, cdr(code)));
}

static s7_pointer end_dox_c_s(s7_scheme *sc, s7_pointer code)
{
  car(sc->T1_1) = slot_value(dox_slot1(sc->envir));
  return(c_call(code)(sc, sc->T1_1));
}

static s7_pointer end_dox_c_sc(s7_scheme *sc, s7_pointer code)
{
  s7_pointer args;
  args = cdr(code);
  car(sc->T2_1) = slot_value(dox_slot1(sc->envir));
  car(sc->T2_2) = cadr(args);
  return(c_call(code)(sc, sc->T2_1));
}

static s7_pointer end_dox_c_cs(s7_scheme *sc, s7_pointer code)
{
  s7_pointer args;
  args = cdr(code);
  car(sc->T2_2) = slot_value(dox_slot1(sc->envir));
  car(sc->T2_1) = car(args);
  return(c_call(code)(sc, sc->T2_1));
}

static s7_pointer end_dox_c_ss(s7_scheme *sc, s7_pointer code)
{
  car(sc->T2_1) = slot_value(dox_slot1(sc->envir));
  car(sc->T2_2) = slot_value(dox_slot2(sc->envir));
  return(c_call(code)(sc, sc->T2_1));
}

static s7_pointer end_dox_equal_ss(s7_scheme *sc, s7_pointer code)
{
  s7_pointer x, y;
  x = slot_value(dox_slot1(sc->envir));
  y = slot_value(dox_slot2(sc->envir));
  if ((is_integer(x)) && (is_integer(y)))
    return(make_boolean(sc, integer(x) == integer(y)));
  car(sc->T2_1) = x;
  car(sc->T2_2) = y;
  return(g_equal_2(sc, sc->T2_1));
}

static s7_pointer end_dox_c_qs(s7_scheme *sc, s7_pointer code)
{
  s7_pointer args;
  args = cdr(code);
  car(sc->T2_2) = slot_value(dox_slot1(sc->envir));
  car(sc->T2_1) = cadr(car(args));
  return(c_call(code)(sc, sc->T2_1));
}

static s7_pointer end_dox_c_s_opcq(s7_scheme *sc, s7_pointer code)
{
  s7_pointer args;
  args = cdr(code);
  car(sc->T2_2) = c_call(cadr(args))(sc, ecdr(args)); /* any number of constants here */
  car(sc->T2_1) = slot_value(dox_slot1(sc->envir));
  return(c_call(code)(sc, sc->T2_1));
}

static s7_pointer end_dox_c_opsq_s(s7_scheme *sc, s7_pointer code)
{
  car(sc->T1_1) = slot_value(dox_slot1(sc->envir));
  car(sc->T2_1) = c_call(cadr(code))(sc, sc->T1_1);
  car(sc->T2_2) = slot_value(dox_slot2(sc->envir));
  return(c_call(code)(sc, sc->T2_1));
}

static s7_pointer end_dox_c_s_opsq(s7_scheme *sc, s7_pointer code)
{
  car(sc->T1_1) = slot_value(dox_slot2(sc->envir));
  car(sc->T2_2) = c_call(caddr(code))(sc, sc->T1_1);
  car(sc->T2_1) = slot_value(dox_slot1(sc->envir));
  return(c_call(code)(sc, sc->T2_1));
}

static s7_pointer end_dox_c_opsq_opsq(s7_scheme *sc, s7_pointer code)
{
  car(sc->T1_1) = slot_value(dox_slot1(sc->envir));
  car(sc->T2_1) = c_call(cadr(code))(sc, sc->T1_1);
  car(sc->T1_1) = slot_value(dox_slot2(sc->envir));
  car(sc->T2_2) = c_call(caddr(code))(sc, sc->T1_1);
  return(c_call(code)(sc, sc->T2_1));
}

static s7_pointer end_dox_c_s_opscq(s7_scheme *sc, s7_pointer code)
{
  car(sc->T2_1) = slot_value(dox_slot2(sc->envir));
  car(sc->T2_2) = caddr(caddr(code));
  car(sc->T2_2) = c_call(caddr(code))(sc, sc->T2_1);
  car(sc->T2_1) = slot_value(dox_slot1(sc->envir));
  return(c_call(code)(sc, sc->T2_1));
}

static s7_pointer end_dox_c_s_opssq(s7_scheme *sc, s7_pointer code)
{
  car(sc->T2_1) = slot_value(dox_slot2(sc->envir));
  car(sc->T2_2) = find_symbol_checked(sc, caddr(caddr(code)));
  car(sc->T2_2) = c_call(caddr(code))(sc, sc->T2_1);
  car(sc->T2_1) = slot_value(dox_slot1(sc->envir));
  return(c_call(code)(sc, sc->T2_1));
}

static s7_pointer end_dox_is_eof(s7_scheme *sc, s7_pointer code)
{
  return(make_boolean(sc, is_eof(slot_value(dox_slot1(sc->envir)))));
}

static s7_pointer end_dox_equal_s_ic(s7_scheme *sc, s7_pointer code)
{
  s7_pointer x;
  x = slot_value(dox_slot1(sc->envir));
  if (is_integer(x))
    return(make_boolean(sc, integer(x) == integer(caddr(code))));
  if (is_real(x))
    return(make_boolean(sc, real(x) == integer(caddr(code))));
  return(sc->F);
}

static s7_pointer end_dox_or_not_ic(s7_scheme *sc, s7_pointer code)
{
  s7_pointer x;

  x = slot_value(dox_slot1(sc->envir));
  if (is_false(sc, x))
    return(sc->T);

  x = slot_value(dox_slot2(sc->envir));
  if (is_integer(x))
    return(make_boolean(sc, integer(x) == integer(caddr(caddr(code)))));
  if (is_real(x))
    return(make_boolean(sc, real(x) == integer(caddr(caddr(code)))));
  return(sc->F);
}

static s7_pointer end_dox_or_not_ss(s7_scheme *sc, s7_pointer code)
{
  s7_pointer x;

  x = slot_value(dox_slot1(sc->envir));
  if (is_false(sc, x))
    return(sc->T);

  x = caddr(code);
  car(sc->T2_1) = slot_value(dox_slot2(sc->envir));
  car(sc->T2_2) = find_symbol_checked(sc, caddr(x));
  return(c_call(x)(sc, sc->T2_1));
}

static s7_function end_dox_eval(s7_scheme *sc, s7_pointer code)
{
  switch (optimize_data(code))
    {
    case HOP_SAFE_C_C: 
      if (fcdr(code) == (s7_pointer)g_equal_s_ic)
	{
	  set_optimize_data(code, HOP_SAFE_C_SC); /* so the dox init section will pass us the cadr slot */
	  return(end_dox_equal_s_ic);
	}					
      if (fcdr(code) == (s7_pointer)g_or_all_x)
	{
	  /* code is the full expr: (or (not happy) (= i 512)) 
	   */
	  if (s7_list_length(sc, code) == 3)
	    {
	      s7_pointer arg1, arg2;
	      arg1 = cadr(code);
	      arg2 = caddr(code);
	      if ((is_pair(arg1)) &&
		  (is_pair(arg2)) &&
		  (car(arg1) == sc->NOT) &&
		  (is_symbol(cadr(arg1))))
		{
		  if (fcdr(arg2) == (s7_pointer)g_equal_s_ic)
		    {
		      set_optimize_data(code, HOP_SAFE_C_opSq_opSq);
		      return(end_dox_or_not_ic);
		    }
		  if ((fcdr(arg2) == (s7_pointer)g_equal_2) &&
		      (is_symbol(cadr(arg2))) &&
		      (is_symbol(caddr(arg2))))
		    {
		      set_optimize_data(code, HOP_SAFE_C_opSq_opSq);
		      return(end_dox_or_not_ss);
		    }
		}
	    }
	}
      return(end_dox_c_c);

    case HOP_SAFE_C_S:       
      if (fcdr(code) == (s7_pointer)g_is_eof_object)
	return(end_dox_is_eof);
      return(end_dox_c_s);

    case HOP_SAFE_C_SS:        
      if (fcdr(code) == (s7_pointer)g_equal_2)
	return(end_dox_equal_ss);
      return(end_dox_c_ss);

    case HOP_SAFE_C_SC:        
      if (fcdr(code) == (s7_pointer)g_equal_s_ic) /* yow! memories from a previous life? */
	return(end_dox_equal_s_ic);
      return(end_dox_c_sc);

    case HOP_SAFE_C_CS:        return(end_dox_c_cs);
    case HOP_SAFE_C_QS:        return(end_dox_c_qs);
    case HOP_SAFE_C_S_opCq:    return(end_dox_c_s_opcq);
    case HOP_SAFE_C_opSq_S:    return(end_dox_c_opsq_s);
    case HOP_SAFE_C_opSq_opSq: return(end_dox_c_opsq_opsq);
    case HOP_SAFE_C_S_opSq:    return(end_dox_c_s_opsq);
    case HOP_SAFE_C_S_opSCq:   return(end_dox_c_s_opscq);
    case HOP_SAFE_C_S_opSSq:   return(end_dox_c_s_opssq);
    }
  return(NULL);
}


static s7_pointer g_mutable_add1(s7_scheme *sc, s7_pointer args)
{
  integer(car(args))++;
  return(car(args));
}


static s7_pointer g_mutable_subtract1(s7_scheme *sc, s7_pointer args)
{
  integer(car(args))--;
  return(car(args));
}

static s7_pointer check_do_all_x(s7_scheme *sc, s7_pointer code)
{
  s7_pointer vars, end, p; /* end-test needs to be all-x, but not returned stuff */

  end = cadr(code);
  if (!is_pair(end)) return(code);
  set_fcdr(end, (s7_pointer)all_x_eval(sc, car(end)));
  if (!fcdr(end)) return(code);

  vars = car(code);
  if (!is_pair(vars)) return(code);
  for (p = vars; is_pair(p); p = cdr(p))
    {
      s7_pointer var;
      var = car(p);
      set_fcdr(cdr(var), (s7_pointer)all_x_eval(sc, cadr(var)));
      if (!fcdr(cdr(var))) return(code);
      if (is_pair(cddr(var)))
	{
	  set_fcdr(cddr(var), (s7_pointer)all_x_eval(sc, caddr(var)));
	  if (!fcdr(cddr(var))) return(code);
	}
    }

  if (is_null(p))
    {
      pair_set_syntax_symbol(code, sc->DO_ALL_X);
      return(sc->NIL);
    }
  return(sc->code);
}

enum {DOX_STEP_DEFAULT, DOX_STEP_SS, DOX_STEP_S, DOX_STEP_ADD, DOX_STEP_SUBTRACT};

static s7_pointer check_do(s7_scheme *sc)
{
  s7_pointer x;
  
  if ((!is_pair(sc->code)) ||                             /* (do . 1) */
      ((!is_pair(car(sc->code))) &&                       /* (do 123) */
       (is_not_null(car(sc->code)))))                     /* (do () ...) is ok */
    return(eval_error(sc, "do: var list is not a list: ~S", sc->code));
  
  if (!is_pair(cdr(sc->code)))                            /* (do () . 1) */
    return(eval_error(sc, "do body is messed up: ~A", sc->code));
  
  if ((!is_pair(cadr(sc->code))) &&                       /* (do ((i 0)) 123) */
      (is_not_null(cadr(sc->code))))                      /* no end-test? */
    return(eval_error(sc, "do: end-test and end-value list is not a list: ~A", sc->code));
  
  if (is_pair(car(sc->code)))
    {
      for (x = car(sc->code); is_pair(x); x = cdr(x))
	{
	  if (!(is_pair(car(x))))                         /* (do (4) (= 3)) */
	    return(eval_error(sc, "do: variable name missing? ~A", sc->code));

	  if (!is_symbol(caar(x)))                     /* (do ((3 2)) ()) */
	    return(eval_error(sc, "do step variable: ~S is not a symbol?", x));

	  if (is_immutable(caar(x)))                     /* (do ((pi 3 (+ pi 1))) ((= pi 4)) pi) */
	    return(eval_error(sc, "do step variable: ~S is immutable", x));
	  
	  if (is_pair(cdar(x)))
	    {
	      if ((!is_pair(cddar(x))) &&
		  (is_not_null(cddar(x))))               /* (do ((i 0 . 1)) ...) */
		return(eval_error(sc, "do: step variable info is an improper list?: ~A", sc->code));
	      
	      if ((is_pair(cddar(x))) && 
		  (is_not_null(cdr(cddar(x)))))          /* (do ((i 0 1 (+ i 1))) ...) */
		return(eval_error(sc, "do: step variable info has extra stuff after the increment: ~A", sc->code));
	    }
	  else return(eval_error(sc, "do: step variable has no initial value: ~A", x));
	  set_local(caar(x));

	  /* (do ((i)) ...) */
	}
      if (is_not_null(x))                                /* (do ((i 0 i) . 1) ((= i 1))) */
	return(eval_error(sc, "do: list of variables is improper: ~A", sc->code));
    }

  if (is_pair(cadr(sc->code)))
    {
      for (x = cadr(sc->code); is_pair(x); x = cdr(x));
      if (is_not_null(x))
	return(eval_error(sc, "stray dot in do end section? ~A", sc->code));
    }

  for (x = cddr(sc->code); is_pair(x); x = cdr(x));
  if (is_not_null(x))
    return(eval_error(sc, "stray dot in do body? ~A", sc->code));

  if ((is_overlaid(sc->code)) &&
      (cdr(ecdr(sc->code)) == sc->code))
    {
      s7_pointer vars, end, body;
      bool one_line;
      
      vars = car(sc->code);
      end = cadr(sc->code);
      body = cddr(sc->code);
      
      one_line = ((safe_list_length(sc, body) == 1) && (is_pair(car(body))));
      pair_set_syntax_symbol(sc->code, sc->DO_UNCHECKED);
      
      /* (define (hi) (do ((i 0 (+ i 1))) ((= i 3)) (display i)) (newline)) */
      /* (define (hi) (do ((i 1.5 (+ i 1))) ((= i 2.5)) (display i) (newline)))
       *   in OP_SAFE_DOTIMES, for example, if init value is not an integer, it goes to OP_SIMPLE_DO
       * remaining optimizable cases: we can step by 1 and use = for end, and yet simple_do(_p) calls the functions
       * geq happens as often as =, and -1 as step
       * also cdr as step to is_null as end
       * also what about no do-var cases? (do () ...)
       *
       * also do body is optimized expr: vector_set_3 via hop_safe_c_sss for example or (vset v i (vref w i))
       */
      if ((is_pair(end)) && (is_pair(car(end))) &&
	  (is_pair(vars)) && (is_null(cdr(vars))) &&
	  (is_pair(body)))
	{
	  /* loop has one step variable, and normal-looking end test
	   */
	  vars = car(vars);
	  if ((safe_list_length(sc, vars) == 3) &&
	      ((!is_pair(cadr(vars))) ||
	       ((is_optimized(cadr(vars))) &&
		(optimize_data(cadr(vars)) == HOP_SAFE_C_C))))
	    {
	      s7_pointer step_expr;
	      step_expr = caddr(vars);
	      
	      if ((is_optimized(step_expr)) &&
		  (((optimize_data(step_expr) == HOP_SAFE_C_SC) && (car(vars) == cadr(step_expr))) ||
		   ((optimize_data(step_expr) == HOP_SAFE_C_C) && (car(vars) == cadr(step_expr)) && 
		    ((ecdr(step_expr) == add_cs1) || (ecdr(step_expr) == subtract_cs1))) ||
		   ((optimize_data(step_expr) == HOP_SAFE_C_CS) && (car(vars) == caddr(step_expr)))))
		{
		  /* step var is (var const|symbol (op var const)|(op const var)) 
		   */
		  end = car(end);
		  
		  if ((is_optimized(end)) &&
		      (car(vars) == cadr(end)) &&
		      (cadr(end) != caddr(end)) &&
		      ((ecdr(end) == equal_s_ic) ||
		       (optimize_data(end) == HOP_SAFE_C_SS) || 
		       (optimize_data(end) == HOP_SAFE_C_SC)))
		    {
		      /* end var is (op var const|symbol) using same var as step 
		       *   so at least we can use SIMPLE_DO
		       */
		      bool has_set = false;
		      
		      if (ecdr(step_expr) == add_cs1)
			{
			  set_c_function(step_expr, add_s1);
			  set_optimize_data(step_expr, HOP_SAFE_C_SC);
			}
		      if (ecdr(step_expr) == subtract_cs1)
			{
			  set_c_function(step_expr, subtract_s1);
			  set_optimize_data(step_expr, HOP_SAFE_C_SC);
			}
		      if (ecdr(end) == equal_s_ic)
			{
			  set_c_function(end, equal_2);
			  set_optimize_data(end, HOP_SAFE_C_SC);
			}
		      
		      if ((ecdr(step_expr) == add_s1) &&
			  (ecdr(end) == equal_2) &&
			  (s7_is_integer(caddr(step_expr))) &&
			  (s7_integer(caddr(step_expr)) == 1))
			pair_set_syntax_symbol(sc->code, sc->SIMPLE_DO_A);
		      else pair_set_syntax_symbol(sc->code, sc->SIMPLE_DO);
		      
		      if ((one_line) &&
			  ((!is_optimized(car(body))) || (op_no_hop(car(body)) != OP_SAFE_C_C)) &&
			  (is_syntactic(caar(body))))
			{
			  pair_syntax_op(car(body)) = symbol_syntax_op(caar(body));
			  pair_set_syntax_symbol(sc->code, sc->SIMPLE_DO_P);
			  set_fcdr(sc->code, caddr(caar(sc->code)));
#if (!WITH_GMP)
			  if ((s7_is_integer(caddr(step_expr))) &&
			      (s7_integer(caddr(step_expr)) == 1) &&
			      (c_function_class(ecdr(step_expr)) == sc->add_class) &&
			      /* we check above that (car(vars) == cadr(step_expr))
			       *    and that         (car(vars) == cadr(end))
			       */
			      ((c_function_class(ecdr(end)) == sc->equal_class) ||
			       (ecdr(end) == geq_2)))
			    pair_set_syntax_symbol(sc->code, sc->DOTIMES_P);
#endif
			}
		      
		      if (do_is_safe(sc, body, sc->w = list_1(sc, car(vars)), sc->NIL, &has_set))
			{
			  /* now look for the very common dotimes case
			   */
			  if ((((s7_is_integer(caddr(step_expr))) &&
				(s7_integer(caddr(step_expr)) == 1)) ||
			       ((s7_is_integer(cadr(step_expr))) &&
				(s7_integer(cadr(step_expr)) == 1))) &&
			      (c_function_class(ecdr(step_expr)) == sc->add_class) &&
#if WITH_GMP
			      (c_function_class(ecdr(end)) == sc->equal_class)
#else
			      ((c_function_class(ecdr(end)) == sc->equal_class) ||
			       (ecdr(end) == geq_2))
#endif
			      )
			    {
			      /* we're stepping by +1 and going to =
			       *   the final integer check has to wait until run time (symbol value dependent)
			       */
			      pair_set_syntax_symbol(sc->code, sc->SAFE_DO);
			      if ((!has_set) &&
				  (c_function_class(ecdr(end)) == sc->equal_class))
				{
				  pair_set_syntax_symbol(sc->code, sc->SAFE_DOTIMES);
				  /* what are the most common cases here?
				   */
				  if (one_line)
				    {
				      if ((is_optimized(car(body))) &&
					  ((!c_function_ext(ecdr(car(body)))) || 
					   (!c_function_looped(ecdr(car(body))))))
					{
					  if (optimize_data(car(body)) == HOP_SAFE_C_C)
					    pair_set_syntax_symbol(sc->code, sc->SAFE_DOTIMES_C_C);
					  else
					    {
					      if (is_all_x_op(optimize_data(car(body))))
						{
						  annotate_arg(sc, body);
						  pair_set_syntax_symbol(sc->code, sc->SAFE_DOTIMES_C_A);
						}
					    }
					}
				      else
					{
					  if ((is_syntactic(caar(body))) &&
					      (symbol_syntax_op(caar(body)) == OP_LET) &&
					      (!is_null(cadar(body))) && /* it has at least one let var */
					      (is_null(cdadar(body))))   /* but only one */
					    {
					      s7_pointer x;
					      x = cddar(body);
					      
					      if ((is_optimized(car(x))) &&
						  (is_null(cdr(x))) && /* one liner? */
						  (optimize_data(car(x)) == HOP_SAFE_C_C))
						{
						  x = cadar(cadar(body));
						  if ((is_optimized(x)) &&
						      (returns_temp(ecdr(x))) &&
						      (optimize_data(x) == HOP_SAFE_C_C)) /* all_x_safe doesn't happen much here */
						    pair_set_syntax_symbol(sc->code, sc->SIMPLE_SAFE_DOTIMES);
						}
					    }
					}
				    }
				  else /* else not one line -- might be 0! */
				    {
				      s7_pointer p;
				      bool happy = true;
				      for (p = body; is_pair(p); p = cdr(p))
					if ((is_pair(car(p))) &&
					    ((!is_optimized(car(p))) ||
					     (!is_all_x_op(optimize_data(car(p))))))
					  {
					    happy = false;
					    break;
					  }
				      if (happy)
					{
					  annotate_args(sc, body);
					  pair_set_syntax_symbol(sc->code, sc->SAFE_DOTIMES_C_A);
					}
				    }
				}
			    }
			}
		      return(sc->NIL); /* tell OP_DO that this is a special case */
		    }
		}
	    }
	}
      
      /* we get here if there is more than one local var or anything "non-simple" about the rest 
       */
      /* (define (hi) (do ((i 0 (+ i 1))) ((= i 3)) (display i)) (newline)) 
       * (define (hi) (do ((i 0 (+ i 1)) (j 1 (+ j 1))) ((= i 3)) (display j))(newline))
       */
      
      vars = car(sc->code);
      end = cadr(sc->code);
      body = cddr(sc->code);
      
      if ((is_null(vars)) &&            /* do () () one-line safe opt body */
	  (is_null(end)) &&
	  (one_line) &&
	  (is_all_x_safe(sc, car(body))))
	{
	  annotate_arg(sc, body);
	  pair_set_syntax_symbol(sc->code, sc->SIMPLE_DO_FOREVER);
	  return(sc->NIL);
	}
      
      /* check end expression first */
      if (!is_end_dox_safe(sc, car(end)))
	{
	  if ((is_pair(car(end))) &&
	      (is_syntactic(caar(end))) &&
	      (symbol_syntax_op(caar(end)) == OP_OR))
	    {
	      s7_pointer old_code;
	      old_code = sc->code;
	      sc->code = cdar(end);
	      check_or(sc);
	      sc->code = old_code;
	      return(check_do_all_x(sc, sc->code));
	    }
	  else return(check_do_all_x(sc, sc->code));
	}

      /* vars can be nil (no steppers) */
      if (is_pair(vars))
	{
	  s7_pointer p, rp;
	  for (p = vars; is_pair(p); p = cdr(p))
	    {
	      s7_pointer var;
	      var = car(p);

	      if (!is_init_dox_safe(sc, cadr(var)))
		return(check_do_all_x(sc, sc->code));
	      
	      if ((is_pair(cddr(var))) &&
		  (!is_step_dox_safe(sc, caddr(var))))
		return(check_do_all_x(sc, sc->code));

	      /* we want to use the pending_value slot for other purposes, so make sure
	       *   the current val is not referred to in any trailing step exprs.  The inits
	       *   are ok because at init-time, the new frame is not connected.
	       *
	       * another tricky case: current var might be used in previous step expr(!)
	       */
	      var = car(var);
	      for (rp = vars; is_pair(rp); rp = cdr(rp)) /* look both back and forward for cross-talk */
		if ((rp != p) && 
		    (s7_tree_memq(sc, var, cddar(rp))))
		  return(check_do_all_x(sc, sc->code));
	    }
	}

      /* end and steps look ok! */
      pair_set_syntax_symbol(sc->code, sc->DOX);
      set_fcdr(sc->code, car(end));      /* end expr */
      set_fcdr(cdr(sc->code), (s7_pointer)end_dox_eval(sc, fcdr(sc->code))); 

      /* each step expr is safe so not an explicit set!
       */
      if (is_pair(vars))
	{
	  s7_pointer p;
	  for (p = vars; is_pair(p); p = cdr(p))
	    {
	      s7_pointer var;
	      var = car(p);

	      if ((is_pair(cadr(var))) &&
		  (caadr(var) != sc->QUOTE))
		set_fcdr(cdr(var), (s7_pointer)step_dox_eval(sc, cadr(var), NULL));      /* init choice */

	      if (is_pair(cddr(var)))
		{
		  dox_function f;
		  f = step_dox_eval(sc, caddr(var), car(var));                           /* step choice */
		  set_fcdr(cddr(var), (s7_pointer)f);

		  if ((f == step_dox_c_ss_direct) ||
		      (f == step_dox_c_ssc_direct) ||
		      (f == step_dox_c_ssi_direct))
		    pair_syntax_op(cddr(var)) = DOX_STEP_SS;
		  else
		    {
		      if ((f == step_dox_c_s_indirect) ||
			  (f == step_dox_c_c_s_indirect) ||
			  (f == step_dox_read_char_s))
			pair_syntax_op(cddr(var)) = DOX_STEP_S;
#if (WITH_GMP)
		      else pair_syntax_op(cddr(var)) = DOX_STEP_DEFAULT;
#else
		      else
			{
			  if ((f == step_dox_c_add_ss) ||
			      (f == step_dox_c_add_f) ||
			      (f == step_dox_c_add_i))
			    pair_syntax_op(cddr(var)) = DOX_STEP_ADD;
			  else
			    {
			      if ((f == step_dox_c_subtract_ss) ||
				  (f == step_dox_c_subtract_i) ||
				  (f == step_dox_c_subtract_f))
				pair_syntax_op(cddr(var)) = DOX_STEP_SUBTRACT;
			      else pair_syntax_op(cddr(var)) = DOX_STEP_DEFAULT;
			    }
			}
#endif
		    }
		}
	    }
	}
      /* there are only a couple of cases in snd-test where a multi-statement do body is completely all-x-able
       */
      return(sc->NIL);
    }
  return(sc->code);
}


static s7_pointer check_define_macro(s7_scheme *sc)
{
  s7_pointer x, y;

  if (!is_pair(sc->code))                                               /* (define-macro . 1) */
    return(eval_error_with_name(sc, "~A name missing (stray dot?): ~A", sc->code));
  if (!is_pair(car(sc->code)))                                          /* (define-macro a ...) */
    return(s7_wrong_type_arg_error(sc, op_names[(int)(sc->op)], 1, car(sc->code), "a list (name ...)"));
  
  x = caar(sc->code);
  if (!is_symbol(x))
    return(eval_error_with_name(sc, "~A: ~S is not a symbol?", x));
  if (dont_eval_args(x))                                               /* (define-macro (quote a) quote) */
    {
      if (sc->safety > 0)
	s7_warn(sc, 128, "%s: syntactic keywords tend to behave badly if redefined", DISPLAY(x));
      set_local(x);
    }
  if (is_immutable(x))
    return(eval_error_with_name(sc, "~A: ~S is immutable", x));
  
  if (!is_pair(cdr(sc->code)))                /* (define-macro (...)) */
    return(eval_error_with_name(sc, "~A ~A, but no body?", x));
  
  y = cdar(sc->code);            /* the arglist */
  if ((!s7_is_list(sc, y)) &&
      (!is_symbol(y)))
    return(s7_error(sc, sc->SYNTAX_ERROR,                                      /* (define-macro (mac . 1) ...) */
		    list_3(sc, make_string_wrapper(sc, "define-macro ~A argument list is ~S?"), x, y)));
  
  for ( ; is_pair(y); y = cdr(y))
    if ((!is_symbol(car(y))) &&
	((sc->op == OP_DEFINE_MACRO) || (sc->op == OP_DEFINE_BACRO)))
      return(s7_error(sc, sc->SYNTAX_ERROR,                                    /* (define-macro (mac 1) ...) */
		      list_3(sc, make_string_wrapper(sc, "define-macro ~A argument name is not a symbol: ~S"), x, y)));

  if ((sc->op == OP_DEFINE_MACRO_STAR) || (sc->op == OP_DEFINE_BACRO_STAR))
    check_lambda_star_args(sc, cdar(sc->code), NULL);
  else check_lambda_args(sc, cdar(sc->code), NULL);

  /* optimize_lambda(sc, (sc->op == OP_DEFINE_MACRO) || (sc->op == OP_DEFINE_BACRO), x, cdar(sc->code), cdr(sc->code)); */

  return(sc->code);
}


static void fixup_macro_overlay(s7_scheme *sc, s7_pointer p)
{
  if ((is_pair(p)) &&
      (is_proper_list(sc, p)) &&
      (!is_overlaid(p))) /* this was sc->code?! -- that can't be right, and probably doesn't matter anyway */
    {
      if (is_pair(cdr(p)))
	{
	  if (is_symbol(car(p)))
	    {
	      set_ecdr(cdr(p), p);
	      set_overlay(cdr(p));
	    }
	  fixup_macro_overlay(sc, cdr(p));
	}
      fixup_macro_overlay(sc, car(p));       /* there's no check for circles through car */
    }
}


static s7_pointer check_cond(s7_scheme *sc)
{
  bool has_feed_to = false;
  s7_pointer x;
  if (!is_pair(sc->code))                                             /* (cond) or (cond . 1) */
    return(eval_error(sc, "cond, but no body: ~A", sc->code));

  for (x = sc->code; is_pair(x); x = cdr(x))
    {
      if (!is_pair(car(x)))                                           /* (cond 1) or (cond (#t 1) 3) */
	return(eval_error(sc, "every clause in cond must be a list: ~A", car(x)));
      else
	{
	  s7_pointer y;
	  y = car(x);
	  if ((!is_pair(cdr(y))) && (!is_null(cdr(y))))               /* (cond (1 . 2)) */
	    return(eval_error(sc, "cond: stray dot? ~A", sc->code));
	  if ((cadr(y) == sc->FEED_TO) &&
	      (s7_symbol_value(sc, sc->FEED_TO) == sc->UNDEFINED))
	    {
	      has_feed_to = true;
	      if (!is_pair(cddr(y)))                                  /* (cond (#t =>)) or (cond (#t => . 1)) */
		return(eval_error(sc, "cond: '=>' target missing?  ~A", x));
	      if (is_pair(cdddr(y)))                                  /* (cond (1 => + abs)) */
		return(eval_error(sc, "cond: '=>' has too many targets: ~A", x));
	    }
	  /* currently we accept:
	   *     (cond (1 2) (=> . =>)) and all variants thereof, e.g. (cond (1 2) (=> 1 . 2) (1 2)) or 
	   *     (cond (1) (=>)) but Guile accepts this?
	   *     (cond (1) (1 =>))
	   * amusing (correct) case: (cond (1 => "hi")) -> #\i
	   */
	}
    }
  if (is_not_null(x))                                             /* (cond ((1 2)) . 1) */
    return(eval_error(sc, "cond: stray dot? ~A", sc->code));

  if ((is_overlaid(sc->code)) &&
      (cdr(ecdr(sc->code)) == sc->code))
    {
      if (has_feed_to)
	{
	  pair_set_syntax_symbol(sc->code, sc->COND_UNCHECKED);
	  if (is_null(cdr(sc->code)))
	    {
	      s7_pointer expr, f, arg;
	      expr = car(sc->code);
	      f = caddr(expr);
	      arg = cadr(f);

	      if ((is_pair(f)) &&
		  (car(f) == sc->LAMBDA) &&
		  (is_pair(arg)) &&
		  (is_null(cdr(arg))) &&
		  (is_symbol(car(arg))) &&
		  (is_null(cdr(cddr(f)))))
		{
		  /* (define (hi) (cond (#t => (lambda (s) s)))) */
		  set_fcdr(sc->code, caddar(sc->code));
		  pair_set_syntax_symbol(sc->code, sc->IF_P_FEED);
		}
	    }
	}
      else 
	{
	  s7_pointer p, sym = NULL;
	  bool xopt = true, c_s_is_ok = true;
	  pair_set_syntax_symbol(sc->code, sc->COND_SIMPLE);
	  
	  for (p = sc->code; xopt && (is_pair(p)); p = cdr(p))
	    {
	      xopt = is_all_x_safe(sc, caar(p));
	      if ((c_s_is_ok) &&
		  (caar(p) != sc->T) &&
		  (caar(p) != sc->ELSE))
		{
		  if ((!is_pair(caar(p))) || 
		      (!is_h_safe_c_s(caar(p))) ||
		      ((sym) && (sym != cadaar(p))))
		    c_s_is_ok = false;
		  else sym = cadaar(p);
		}
	    }
	  if (c_s_is_ok)
	    pair_set_syntax_symbol(sc->code, sc->COND_S);
	  else
	    {
	      if (xopt)
		{
		  pair_set_syntax_symbol(sc->code, sc->COND_ALL_X);
		  for (p = sc->code; is_pair(p); p = cdr(p))
		    set_fcdr(car(p), (s7_pointer)cond_all_x_eval(sc, caar(p))); /* handle 'else' specially here */
		}
	    }
	}
    }
  return(sc->code);
}


#if (!WITH_GCC)
#define closure_is_ok(Sc, Code, Type, Args)          (find_symbol_unchecked(Sc, car(Code)) == ecdr(Code))
#define closure_star_is_ok(Sc, Code, Type, Args)     (find_symbol_unchecked(Sc, car(Code)) == ecdr(Code))
#else

/* it is almost never the case that we already have the value and can see it in the current environment directly,
 * but once found, the value usually matches the current (ecdr(code))
 *
 * (_val_) is needed below because car(code) might be undefined (with-let can cause this confusion),
 *   and find_symbol_unchecked returns NULL in that case.
 */
#if 1
/* unlike the c_function_is_ok case, the macro form here is faster?? callgrind and time agree on this.
 */
#define closure_is_ok(Sc, Code, Type, Args) \
  ({ s7_pointer _code_, _val_; _code_ = Code; _val_ = find_symbol_unchecked(Sc, car(_code_)); \
     ((_val_ == ecdr(_code_)) || \
      ((_val_) && (typesflag(_val_) == (unsigned short)Type) &&		\
       ((closure_arity(_val_) == Args) || (closure_arity_to_int(Sc, _val_) == Args)) && \
       (set_ecdr(_code_, _val_)))); })
#else
static bool closure_is_ok(s7_scheme *sc, s7_pointer code, unsigned short type, int args)
{
  s7_pointer f;
  f = find_symbol_unchecked(sc, car(code));
  return ((f == ecdr(code)) ||
	  ((f) && 
	   (typesflag(f) == type) &&
	   ((closure_arity(f) == args) || (closure_arity_to_int(sc, f) == args)) &&
	   (set_ecdr(code, f))));
}
#endif

#define closure_star_is_ok(Sc, Code, Type, Args) \
  ({ s7_pointer _val_; _val_ = find_symbol_unchecked(Sc, car(Code));			\
     ((_val_ == ecdr(Code)) || \
      ((_val_) && (typesflag(_val_) == (unsigned short)Type) &&		\
       ((closure_arity(_val_) >= Args) || (closure_star_arity_to_int(Sc, _val_) >= Args)) && \
       (set_ecdr(Code, _val_)))); })

#endif

#define MATCH_UNSAFE_CLOSURE      (T_CLOSURE |      T_PROCEDURE)
#define MATCH_SAFE_CLOSURE        (T_CLOSURE |      T_PROCEDURE | T_SAFE_CLOSURE)
#define MATCH_UNSAFE_CLOSURE_STAR (T_CLOSURE_STAR | T_PROCEDURE)
#define MATCH_SAFE_CLOSURE_STAR   (T_CLOSURE_STAR | T_PROCEDURE | T_SAFE_CLOSURE)

/* since T_HAS_METHODS is on if there might be methods, this can protect us from that case */


static void annotate_expansion(s7_scheme *sc, s7_pointer p)
{
  s7_pointer x;
  /* p might not be a pair:
   *   (define-expansion (whatever->zero . whatever) 0)
   *   (+ 1 (whatever->zero 2 3) 4)
   *   5
   */
  if (is_pair(p))
    {
      if ((is_symbol(car(p))) &&
	  (is_pair(cdr(p))))
	{
	  set_ecdr(cdr(p), p);
	  set_overlay(cdr(p));
	}
      else
	{
	  if (is_pair(car(p)))
	    annotate_expansion(sc, car(p));
	}
      for (x = cdr(p); is_pair(x); x = cdr(x))
	if (is_pair(car(x)))
	  annotate_expansion(sc, car(x));
    }
}


static s7_pointer implicit_index(s7_scheme *sc, s7_pointer obj, s7_pointer indices)
{
  /* (let ((lst '("12" "34"))) (lst 0 1)) -> #\2
   * (let ((lst (list #(1 2) #(3 4)))) (lst 0 1)) -> 2
   *
   * this can get tricky:
   *   ((list (lambda (a) (+ a 1)) (lambda (b) (* b 2))) 1 2) -> 4
   * but what if func takes rest/optional args, etc?
   *   ((list (lambda args (car args))) 0 "hi" 0)
   *   should this return #\h or "hi"??
   *   currently it is "hi" which is consistent with
   *  ((lambda args (car args)) "hi" 0)
   * but...
   *   ((lambda (arg) arg) "hi" 0)
   * is currently an error (too many arguments)
   * it should be (((lambda (arg) arg) "hi") 0) -> #\h
   */
  
  switch (type(obj))
    {
    case T_VECTOR:                       /* (#(#(1 2) #(3 4)) 1 1) -> 4 */
      return(vector_ref_1(sc, obj, indices)); 
      
    case T_STRING:                       /* (#("12" "34") 0 1) -> #\2 */
      if (is_null(cdr(indices)))
	return(string_ref_1(sc, obj, car(indices)));
      return(s7_error(sc, 
		      sc->WRONG_NUMBER_OF_ARGS, 
		      list_3(sc, sc->TOO_MANY_ARGUMENTS, obj, indices)));
      
    case T_PAIR:                         /* (#((1 2) (3 4)) 1 0) -> 3, (#((1 (2 3))) 0 1 0) -> 2 */
      obj = list_ref_1(sc, obj, car(indices));
      if (is_pair(cdr(indices)))
	return(implicit_index(sc, obj, cdr(indices)));
      return(obj);

    case T_HASH_TABLE:                   /* ((vector (hash-table '(a . 1) '(b . 2))) 0 'a) -> 1 */
      obj = s7_hash_table_ref(sc, obj, car(indices));
      if (is_pair(cdr(indices)))
	return(implicit_index(sc, obj, cdr(indices)));
      return(obj);

    case T_C_OBJECT:
      return((*(c_object_ref(obj)))(sc, obj, indices));

    case T_ENVIRONMENT:
      obj = s7_let_ref(sc, obj, car(indices));
      if (is_pair(cdr(indices)))
	return(implicit_index(sc, obj, cdr(indices)));
      return(obj);

    default:                             /* (#(a b c) 0 1) -> error, but ((list (lambda (x) x)) 0 "hi") -> "hi" */
      return(g_apply(sc, list_2(sc, obj, indices)));
    }
}


/* this is transitional(?) -- first step toward collapsing the unknown fixups */
#define set_opt_and_goto_opt_eval(Code, Func, Op)    \
  {						     \
    set_optimize_data(Code, Op);		     \
    set_ecdr(Code, Func);			     \
    goto OPT_EVAL;				     \
  }


static void clear_all_optimizations(s7_scheme *sc, s7_pointer p)
{
  /* I believe that we would not have been optimized to begin with if the tree were circular,
   *   and this tree is supposed to be a function call + args -- a circular list here is a bug.
   */
  if (is_pair(p))
    {
      if (is_optimized(p))
	{
	  clear_optimized(p);
	  clear_optimize_data(p);
	  set_ecdr(p, sc->NIL);
	  set_fcdr(p, sc->NIL);

	}
      clear_all_optimizations(sc, cdr(p));
      clear_all_optimizations(sc, car(p));
    }
}


static bool a_is_ok(s7_scheme *sc, s7_pointer p)
{
  /* "A" here need not be a function call or "p" a pair (all_x_c etc) */
  if (is_pair(p))
    {
      if ((is_optimized(p)) &&
	  (!c_function_is_ok(sc, p)))
	return(false);
      if (car(p) != sc->QUOTE)
	return((a_is_ok(sc, car(p))) &&
	       (a_is_ok(sc, cdr(p))));
    }
  return(true);
}

#define c_function_is_ok_cadr(Sc, P) ((c_function_is_ok(Sc, P)) && (c_function_is_ok(Sc, cadr(P))))
#define c_function_is_ok_caddr(Sc, P) ((c_function_is_ok(Sc, P)) && (c_function_is_ok(Sc, caddr(P))))
#define c_function_is_ok_cadr_caddr(Sc, P) ((c_function_is_ok(Sc, P)) && (c_function_is_ok(Sc, cadr(P))) && (c_function_is_ok(Sc, caddr(P))))

#define a_is_ok_cadr(Sc, P) ((c_function_is_ok(Sc, P)) && (a_is_ok(Sc, cadr(P))))
#define a_is_ok_caddr(Sc, P) ((c_function_is_ok(Sc, P)) && (a_is_ok(Sc, caddr(P))))
#define a_is_ok_cadddr(Sc, P) ((c_function_is_ok(Sc, P)) && (a_is_ok(Sc, cadddr(P))))


static s7_pointer free_let(s7_scheme *sc, s7_pointer e)
{
  s7_pointer p;
  for (p = let_slots(e); is_slot(p); p = next_slot(p))
    {
      clear_type(p);
      (*(sc->free_heap_top++)) = p;
    }
  clear_type(e);
  (*(sc->free_heap_top++)) = e;
  return(sc->NIL);
}



/* -------------------------------- eval -------------------------------- */

#if WITH_GCC
#undef NEW_CELL
#define NEW_CELL(Sc, Obj) \
  do {						\
    if (Sc->free_heap_top <= Sc->free_heap_trigger) {try_to_call_gc(Sc); if ((Sc->begin_hook) && (call_begin_hook(Sc))) return(Sc->F);} \
    Obj = (*(--(Sc->free_heap_top))); \
    } while (0)
#endif

static s7_pointer eval(s7_scheme *sc, opcode_t first_op) 
{
  sc->op = first_op;

  /* this procedure can be entered recursively (via s7_call for example), so it's no place for a setjmp
   *   I don't think the recursion can hurt our continuations because s7_call is coming from hooks and
   *   callbacks that are implicit in our stack.
   */
  
  goto START_WITHOUT_POP_STACK;
  /* this ugly two-step is faster than other ways of writing this code
   */

 START:
  pop_stack(sc);

  /* syntax_opcode can be optimize_data, the field can be set at read time, we could
   *   probably combine the optimized and normal case statements, jump here if eval (eval_pair, opt_eval),
   *   and thereby save the is_syntactic and is_pair check in op_eval, op_begin would explicitly jump back here, no op_eval, 
   *   current trailers would be outside? and where would eval args go?  Huge change, might save 1% if lucky.
   *   see end of file -- I think this is too pessimistic and given rearrangement of the s7_cell layout,
   *   can be done without an increase in size.
   *
   * about half the cases don't care about args or op, but it's not simple to distribute the sc->args
   *   setting throughout this switch statement.  Lots of branches fall through to the next and there
   *   are many internal goto's to branches, so the code becomes a mess.  sc->op is even worse because
   *   we use it in several cases for error information or choice of next op, etc.
   */

 START_WITHOUT_POP_STACK:
  switch (sc->op) 
    {
    case OP_READ_INTERNAL:
      /* if we're loading a file, and in the file we evaluate something like:
       *
       *    (let ()
       *      (set-current-input-port (open-input-file "tmp2.r5rs"))
       *      (close-input-port (current-input-port)))
       *    ... (with no reset of input port to its original value)
       *
       * the load process tries to read the loaded string, but the sc->input_port is now closed,
       * and the original is inaccessible!  So we get a segfault in token.  We don't want to put
       * a port_is_closed check there because token only rarely is in this danger.  I think this
       * is the only place where we can be about to call token, and someone has screwed up our port.
       *
       * We can't call read_error here because it assumes the input string is ok!
       */

      if (port_is_closed(sc->input_port))
	return(s7_error(sc, sc->READ_ERROR, 
			list_1(sc, make_string_wrapper(sc, "our input port got clobbered!"))));

      sc->tok = token(sc);

      switch (sc->tok)
	{
	case TOKEN_EOF:
	  goto START;

	case TOKEN_RIGHT_PAREN:
	  read_error(sc, "unexpected close paren");

	case TOKEN_COMMA:
	  read_error(sc, "unexpected comma");

	default:
	  sc->value = read_expression(sc);
	  sc->current_line = port_line_number(sc->input_port);  /* this info is used to track down missing close parens */
	  sc->current_file = port_filename(sc->input_port);
	  goto START;
	}

      
      /* (read p) from scheme
       *    "p" becomes current input port for eval's duration, then pops back before returning value into calling expr
       */
    case OP_READ_DONE:
      pop_input_port(sc);

      if (sc->tok == TOKEN_EOF)
	sc->value = sc->EOF_OBJECT;
      sc->current_file = NULL; /* this is for error handling */
      goto START;
      

      /* load("file"); from C (g_load) -- assume caller will clean up
       *   read and evaluate exprs until EOF that matches (stack reflects nesting)
       */
    case OP_LOAD_RETURN_IF_EOF:  /* loop here until eof (via push stack below) */
      if (sc->tok != TOKEN_EOF)
	{
	  push_stack(sc, OP_LOAD_RETURN_IF_EOF, sc->NIL, sc->NIL);
	  push_stack(sc, OP_READ_INTERNAL, sc->NIL, sc->NIL);
	  sc->code = sc->value;
	  goto EVAL;             /* we read an expression, now evaluate it, and return to read the next */
	}
      sc->current_file = NULL;
      return(sc->F);
      
      
      /* (load "file") in scheme 
       *    read and evaluate all exprs, then upon EOF, close current and pop input port stack
       */
    case OP_LOAD_CLOSE_AND_POP_IF_EOF:
      if (sc->tok != TOKEN_EOF) 
	{
	  push_stack(sc, OP_LOAD_CLOSE_AND_POP_IF_EOF, sc->NIL, sc->NIL); /* was push args, code */
	  if ((!is_string_port(sc->input_port)) ||
	      (port_position(sc->input_port) < port_data_size(sc->input_port)))
	    {
	      push_stack(sc, OP_READ_INTERNAL, sc->NIL, sc->NIL);
	    }
	  else sc->tok = TOKEN_EOF;
	  sc->code = sc->value;
	  goto EVAL;             /* we read an expression, now evaluate it, and return to read the next */
	}
      s7_close_input_port(sc, sc->input_port);
      pop_input_port(sc);
      sc->current_file = NULL;

      if (is_multiple_value(sc->value))                    /* (load "file") where "file" is (values 1 2 3) */
	sc->value = splice_in_values(sc, multiple_value(sc->value));
      goto START;
      

      /* read and evaluate string expression(s?)
       *    assume caller (C via g_eval_c_string) is dealing with the string port
       */
    case OP_EVAL_STRING:
      /* this is the C side s7_eval_c_string. 
       */

      /* (eval-string (string-append "(list 1 2 3)" (string #\newline) (string #\newline))) 
       *    needs to be sure to get rid of the trailing white space before checking for EOF
       *    else it tries to eval twice and gets "attempt to apply 1?, line 2"
       */
      if ((sc->tok != TOKEN_EOF) && 
	  (port_position(sc->input_port) < port_data_size(sc->input_port))) /* ran past end somehow? */
	{
	  unsigned char c;
	  while (white_space[c = port_data(sc->input_port)[port_position(sc->input_port)++]])
	    if (c == '\n')
	      port_line_number(sc->input_port)++;

	  if (c != 0)
	    {
	      backchar(c, sc->input_port);
	      push_stack(sc, OP_EVAL_STRING, sc->NIL, sc->value);
	      push_stack(sc, OP_READ_INTERNAL, sc->NIL, sc->NIL);
	    }
	  else push_stack(sc, OP_EVAL_DONE, sc->NIL, sc->value);
	}
      else push_stack(sc, OP_EVAL_DONE, sc->NIL, sc->value);
      sc->code = sc->value;
      goto EVAL;

      
    case OP_EVAL_STRING_2:
      s7_close_input_port(sc, sc->input_port);
      pop_input_port(sc);

      if (is_multiple_value(sc->value))
	sc->value = splice_in_values(sc, multiple_value(sc->value));
      goto START;

      
    case OP_EVAL_STRING_1:
      if ((sc->tok != TOKEN_EOF) && 
	  (port_position(sc->input_port) < port_data_size(sc->input_port))) /* ran past end somehow? */
	{
	  unsigned char c;
	  while (white_space[c = port_data(sc->input_port)[port_position(sc->input_port)++]])
	    if (c == '\n')
	      port_line_number(sc->input_port)++;

	  if (c != 0)
	    {
	      backchar(c, sc->input_port);
	      push_stack(sc, OP_EVAL_STRING_1, sc->NIL, sc->value);
	      push_stack(sc, OP_READ_INTERNAL, sc->NIL, sc->NIL);
	    }
	  else push_stack(sc, OP_EVAL_STRING_2, sc->NIL, sc->NIL);
	}
      else push_stack(sc, OP_EVAL_STRING_2, sc->NIL, sc->NIL);
      sc->code = sc->value;
      goto EVAL;


      /* -------------------- sort! (heapsort, done directly so that call/cc in the sort function will work correctly) -------------------- */

    #define SORT_N integer(vector_element(sc->code, 0))
    #define SORT_K integer(vector_element(sc->code, 1))
    #define SORT_J integer(vector_element(sc->code, 2))
    #define SORT_K1 integer(vector_element(sc->code, 3))
    #define SORT_CALLS integer(vector_element(sc->code, 4))
    #define SORT_STOP integer(vector_element(sc->code, 5))
    #define SORT_DATA(K) vector_element(car(sc->args), K)
    #define SORT_LESSP cadr(sc->args)

    HEAPSORT:
      {
	s7_Int n, j, k;
	s7_pointer lx;
	n = SORT_N;
	k = SORT_K1;

	if ((n == k) || (k > ((s7_Int)(n / 2)))) /* k == n == 0 is the first case */
	  goto START;

	if (sc->safety != 0)
	  {
	    SORT_CALLS++;
	    if (SORT_CALLS > SORT_STOP)
	      eval_range_error(sc, "sort! is caught in an infinite loop, comparison: ~S", SORT_LESSP);
	  }
	j = 2 * k;
	SORT_J = j;
	if (j < n)
	  {
	    push_stack(sc, OP_SORT1, sc->args, sc->code);
	    lx = SORT_LESSP; /* cadr of sc->args */
	    if (needs_copied_args(lx))
	      sc->args = list_2(sc, SORT_DATA(j), SORT_DATA(j + 1)); 
	    else 
	      {
		car(sc->T2_1) = SORT_DATA(j);
		car(sc->T2_2) = SORT_DATA(j + 1);
		sc->args = sc->T2_1;
	      }
	    sc->code = lx;
	    goto APPLY;
	  }
	else sc->value = sc->F;
      }

    case OP_SORT1:
      {
	s7_Int j, k;
	s7_pointer lx;
	k = SORT_K1;
	j = SORT_J;
	if (is_true(sc, sc->value))
	  {
	    j = j + 1;
	    SORT_J = j;
	  }
	push_stack(sc, OP_SORT2, sc->args, sc->code);
	lx = SORT_LESSP;
	if (needs_copied_args(lx))
	  sc->args = list_2(sc, SORT_DATA(k), SORT_DATA(j)); 
	else 
	  {
	    car(sc->T2_1) = SORT_DATA(k);
	    car(sc->T2_2) = SORT_DATA(j);
	    sc->args = sc->T2_1;
	  }
	sc->code = lx;
	goto APPLY;
      }

    case OP_SORT2:
      {
	s7_Int j, k;
	k = SORT_K1;
	j = SORT_J;
	if (is_true(sc, sc->value))
	  {
	    s7_pointer lx;
	    lx = SORT_DATA(j);
	    SORT_DATA(j) = SORT_DATA(k);
	    SORT_DATA(k) = lx;
	  }
	else goto START;
	SORT_K1 = SORT_J;
	goto HEAPSORT;
      }

    case OP_SORT:
      /* coming in sc->args is sort args (data less?), sc->code = '(n k 0)
       * here we call the inner loop until k <= 0 [the local k! -- this is tricky because scheme passes args by value]
       */
      {
	s7_Int k;
	k = SORT_K;
	if (k <= 0)
	  goto SORT3;

	SORT_K = k - 1;
	SORT_K1 = k - 1;

	push_stack(sc, OP_SORT, sc->args, sc->code);
	goto HEAPSORT;
      }

      SORT3:
      case OP_SORT3:
	{
	  s7_Int n;
	  s7_pointer lx;
	  n = SORT_N;
	  if (n <= 0)
	    {
	      sc->value = car(sc->args);
	      goto START;
	    }
	  lx = SORT_DATA(0);
	  SORT_DATA(0) = SORT_DATA(n);
	  SORT_DATA(n) = lx;
	  SORT_N = n - 1;
	  SORT_K1 = 0;
	  push_stack(sc, OP_SORT3, sc->args, sc->code);
	  goto HEAPSORT;
	}

    case OP_SORT_PAIR_END:
      /* sc->value is the sort vector which needs to be copied into the original list */
      {
	s7_pointer p;
	s7_pointer *elements;
	int i, len;
	
	elements = s7_vector_elements(sc->value);
	len = vector_length(sc->value);
	for (i = 0, p = car(sc->args); i < len; i++, p = cdr(p))
	  car(p) = elements[i];
	
	sc->value = car(sc->args);
	goto START;
      }

    case OP_SORT_VECTOR_END:
      /* sc->value is the sort vector which needs to be copied into the original list */
      {
	s7_pointer *elements;
	int i, len;
	s7_pointer original_vector;
	
	elements = s7_vector_elements(sc->value);
	len = vector_length(sc->value);
	original_vector = car(sc->args);

	if (is_float_vector(original_vector))
	  {
	    s7_Double *flts;
	    flts = float_vector_elements(original_vector);
	    for (i = 0; i < len; i++)
	      flts[i] = real(elements[i]);
	  }
	else
	  {
	    s7_Int *ints;
	    ints = int_vector_elements(original_vector);
	    for (i = 0; i < len; i++)
	      ints[i] = integer(elements[i]);
	  }
	sc->value = original_vector;
	goto START;
      }

    case OP_SORT_STRING_END:
      {
	s7_pointer *elements;
	int i, len;
	s7_pointer original_string;
	unsigned char *str;
	
	elements = s7_vector_elements(sc->value);
	len = vector_length(sc->value);
	original_string = car(sc->args);
	str = (unsigned char *)string_value(original_string);

	if (is_bytevector(original_string))
	  {
	    for (i = 0; i < len; i++)
	      str[i] = (unsigned char)integer(elements[i]);
	  }
	else
	  {
	    for (i = 0; i < len; i++)
	      str[i] = character(elements[i]);
	  }
	sc->value = original_string;
	goto START;
      }
      break;

      /* batcher networks:
       *    ((0 2) (0 1) (1 2))
       *    ((0 2) (1 3) (0 1) (2 3) (1 2))
       *    etc -- see batcher in s7test.scm (from Doug Hoyte)
       * but since it has to be done here by hand, it turns into too much code, 3 is:
       *    < l0 l2 ?
       *    no goto L1
       *    < l0 l1 ?
       *    no  return 1 0 2
       *    < l1 l2?
       *    yes return 0 1 2 (direct)
       *    no  return 0 2 1
       *  L1:
       *    < l0 l1 ?
       *    yes return 2 0 1
       *    < l1 l2 ?
       *    yes return 1 2 0
       *    no  return 2 1 0
       * since each "<" op above goes to OP_APPLY, we have ca 5 labels, and ca 25-50 lines
       */


    case OP_MAP_SIMPLE:
      /* func = sc->code, func takes one arg, args = '(nil arglist) at the start with symbol_id = len
       *   the func is prechecked, as is the list.
       */
      {
	s7_pointer args;
	args = sc->args;
	if (sc->value != sc->NO_VALUE)
	  {
  	    if (is_multiple_value(sc->value))
 	      counter_result(args) = revappend(sc, multiple_value(sc->value), counter_result(args));
 	    else counter_result(args) = cons(sc, sc->value, counter_result(args));
  	  }
 	if (is_pair(counter_list(args)))
  	  {
 	    if ((--counter_count(args)) >= 0) /* protect against circular arg lists created by the map function! */
  	      {
  		s7_pointer code;
  		code = sc->code;
 		sc->x = car(counter_list(args));
 		counter_list(args) = cdr(counter_list(args));  /* move down list of args */
  		push_stack(sc, OP_MAP_SIMPLE, args, code);
		
		/* now call the function directly -- we know it is a T_CLOSURE
		 *   and that it can be called with one argument, so there's no need to go to
		 *   apply.
		 */
		if (counter_length(sc->args) != sc->capture_env_counter)
		  {
		    NEW_FRAME_WITH_SLOT(sc, closure_let(code), sc->envir, car(closure_args(code)), sc->x);
		    counter_let(args) = sc->envir;
		    counter_length(args) = sc->capture_env_counter;
		  }
		else sc->envir = old_frame_with_slot(sc, counter_let(args), sc->x);
		sc->x = sc->NIL;
		sc->code = closure_body(code);
		goto BEGIN1;
	      }
	  }
	sc->value = safe_reverse_in_place(sc, counter_result(args));
	goto START;
      }


    case OP_MAP:
      if (sc->value != sc->NO_VALUE)                   /* (map (lambda (x) (values)) (list 1)) */
	{
	  if (is_multiple_value(sc->value))            /* (map (lambda (x) (if (odd? x) (values x (* x 20)) (values))) (list 1 2 3 4)) */
	    counter_result(sc->args) = revappend(sc, multiple_value(sc->value), counter_result(sc->args));
	  /* not append_in_place here because sc->value has the multiple-values bit set */
	  else counter_result(sc->args) = cons(sc, sc->value, counter_result(sc->args));
	}

      if (counter_count(sc->args) < counter_length(sc->args))
	{
	  if (next_map(sc))
	    {
	      if (needs_copied_args(sc->code))
		sc->args = copy_list(sc, sc->args);
	      goto APPLY;
	    }
	}
      sc->value = safe_reverse_in_place(sc, counter_result(sc->args));
      /* here and below it is not safe to pre-release sc->args (the counter) */
      goto START;

      
      /* -------------------------------- FOR-EACH -------------------------------- */

      /* for-each et al remake the local frame, but that's only needed if the local env is exported,
       *   and that can only happen through make-closure in various guises and curlet.
       *   owlet captures, but it would require a deliberate error to use it in this context.
       *   c_objects call object_set_let but that requires a prior curlet or sublet.  So we have
       *   sc->capture_env_counter that is incremented every time an environment is captured, then
       *   here we save that ctr, call body, on rerun check ctr, if it has not changed we are safe and can reuse frame.
       */

    FOR_EACH_SIMPLE:
    case OP_FOR_EACH_SIMPLE:
      /* func = sc->code, func takes one arg, args = arglist 
       */
      {
	s7_pointer args;
	args = counter_list(sc->args);

	if (is_pair(args))
	  {
	    /* now call the function directly as in map above. 
	     *    counter_let does not need mark because it is sc->envir here
  	     */
	    s7_pointer code;
	    code = sc->code;
	    
	    if (counter_length(sc->args) != sc->capture_env_counter)
	      {
		NEW_FRAME_WITH_SLOT(sc, closure_let(code), sc->envir, car(closure_args(code)), car(args));  /* set function arg value */
		counter_let(sc->args) = sc->envir;
		counter_length(sc->args) = sc->capture_env_counter;
	      }
	    else sc->envir = old_frame_with_slot(sc, counter_let(sc->args), car(args));
	    counter_list(sc->args) = cdr(args);
	    push_stack(sc, OP_FOR_EACH_SIMPLE, sc->args, code);
	    sc->code = closure_body(code);
	    goto BEGIN1;
	  }
	sc->value = sc->UNSPECIFIED;
	goto START;
      }


    FOR_EACH_SIMPLER:
    case OP_FOR_EACH_SIMPLER:
      {
	s7_pointer args, counter;
	counter = sc->args;
	args = counter_list(counter);

	if (is_pair(args))
	  {
	    s7_pointer code;
	    code = sc->code;
	    if (counter_length(counter) != sc->capture_env_counter)
	      {
		NEW_FRAME_WITH_SLOT(sc, closure_let(code), sc->envir, car(closure_args(code)), car(args));  /* set function arg value */
		counter_let(counter) = sc->envir;
		counter_length(counter) = sc->capture_env_counter;
	      }
	    else sc->envir = old_frame_with_slot(sc, counter_let(counter), car(args));
	    counter_list(counter) = cdr(args);
	    push_stack(sc, OP_FOR_EACH_SIMPLER, counter, code);
	    sc->code = car(closure_body(code));
	    goto EVAL; 
	  }
	sc->value = sc->UNSPECIFIED;
	goto START;
      }


    FOR_EACH_SIMPLEST:
      NEW_FRAME(sc, counter_let(sc->args), counter_result(sc->args));
      /* counter_result is GC protected.  This is the catch thunk's frame */

    case OP_FOR_EACH_SIMPLEST:
      {
	s7_pointer args, counter;
	counter = sc->args;
	args = counter_list(counter);
	if (is_pair(args))
	  {
	    s7_pointer code, p;
	    code = sc->code;
	    if (counter_length(counter) != sc->capture_env_counter)
	      {
		NEW_FRAME_WITH_SLOT(sc, closure_let(code), sc->envir, car(closure_args(code)), car(args));  /* set function arg value */
		counter_let(counter) = sc->envir;
		counter_length(counter) = sc->capture_env_counter;
	      }
	    else sc->envir = old_frame_with_slot(sc, counter_let(counter), car(args));
	    counter_list(counter) = cdr(args);
	    push_stack(sc, OP_FOR_EACH_SIMPLEST, counter, code);
	    code = car(closure_body(code));

	    if (counter_count(counter) == 0)
	      {
		counter_count(counter) = 1;
		p = old_frame_in_env(sc, counter_result(counter), sc->envir);
		catch_all_goto_loc(p) = s7_stack_top(sc);
		catch_all_op_loc(p) = (int)(sc->op_stack_now - sc->op_stack);
		catch_all_result(p) = fcdr(code);
	      }
	    else
	      {
		p = counter_result(counter);
		let_slots(p) = sc->NIL;                  
		let_id(p) = ++sc->let_number; 
		outlet(p) = sc->envir;
	      }
	    sc->envir = p;
	    push_stack_no_args(sc, OP_CATCH_ALL, code);
	    sc->code = ecdr(cdr(code));                   /* the body of the first lambda */
	    goto BEGIN1;
	  }
	sc->value = sc->UNSPECIFIED;
	goto START;
      }


    case OP_FOR_EACH:
      if (counter_count(sc->args) < counter_length(sc->args))
	{
	  if (next_for_each(sc)) 
	    {
	      if (needs_copied_args(sc->code))
		sc->args = copy_list(sc, sc->args);
	      goto APPLY;
	    }
	}
      sc->value = sc->UNSPECIFIED;
      goto START;


      /* -------------------------------- MEMBER -------------------------------- */
    case OP_MEMBER_IF:
      /* code=func, args = (list original args) with ecdr->position in cadr (the list), value = result of comparison
       */
      if (sc->value != sc->F)            /* previous comparison was not #f -- return list */
	{
	  sc->value = ecdr(sc->args);
	  goto START;
	}

      if (!is_pair(cdr(ecdr(sc->args))))     /* no more args -- return #f */
	{
	  sc->value = sc->F;
	  goto START;
	}
      set_ecdr(sc->args, cdr(ecdr(sc->args)));  /* cdr down arg list */
      push_stack(sc, OP_MEMBER_IF1, sc->args, sc->code);
      sc->args = list_2(sc, caar(sc->args), car(ecdr(sc->args)));
      goto APPLY;


    case OP_MEMBER_IF1:
      if (sc->value != sc->F)            /* previous comparison was not #f -- return list */
	{
	  sc->value = ecdr(sc->args);
	  goto START;
	}

      if (!is_pair(cdr(ecdr(sc->args))))     /* no more args -- return #f */
	{
	  sc->value = sc->F;
	  goto START;
	}
      set_ecdr(sc->args, cdr(ecdr(sc->args)));  /* cdr down arg list */

      /* circular list check */
      if (ecdr(sc->args) == fcdr(sc->args))
	{
	  sc->value = sc->F;
	  goto START;
	}
      set_fcdr(sc->args, cdr(fcdr(sc->args)));  /* cdr down the slow list (check for circular list) */

      push_stack(sc, OP_MEMBER_IF, sc->args, sc->code);
      sc->args = list_2(sc, caar(sc->args), car(ecdr(sc->args)));
      goto APPLY;


      /* -------------------------------- ASSOC -------------------------------- */
    case OP_ASSOC_IF1:
    case OP_ASSOC_IF:
      /* code=func, args=(list args) with f/ecdr=list, value=result of comparison
       *   (assoc 3 '((1 . a) (2 . b) (3 . c) (4 . d)) =)
       */
      if (sc->value != sc->F)            /* previous comparison was not #f -- return (car list) */
	{
	  sc->value = car(ecdr(sc->args));
	  goto START;
	}

      if (!is_pair(cdr(ecdr(sc->args))))     /* (assoc 3 '((1 . 2) . 3) =) or nil */
	{
	  sc->value = sc->F;
	  goto START;
	}
      set_ecdr(sc->args, cdr(ecdr(sc->args)));  /* cdr down arg list */

      if (sc->op == OP_ASSOC_IF1)
	{
	  /* circular list check */
	  if (ecdr(sc->args) == fcdr(sc->args))
	    {
	      sc->value = sc->F;
	      goto START;
	    }
	  set_fcdr(sc->args, cdr(fcdr(sc->args)));  /* cdr down the slow list */
	  push_stack(sc, OP_ASSOC_IF, sc->args, sc->code);
	}
      else push_stack(sc, OP_ASSOC_IF1, sc->args, sc->code);

      if (!is_pair(car(ecdr(sc->args))))     /* (assoc 1 '((2 . 2) 3) =) -- we access caaadr below */
	eval_type_error(sc, "assoc: second arg is not an alist: ~S", sc->args);
      /* not sure about this -- we could simply skip the entry both here and in g_assoc
       *   (assoc 1 '((2 . 2) 3)) -> #f
       *   (assoc 1 '((2 . 2) 3) =) -> error currently
       */

      sc->args = list_2(sc, caar(sc->args), caar(ecdr(sc->args)));
      goto APPLY;


      /* ---------------- OP_APPLY ---------------- */
    case OP_APPLY:      /* apply 'code' to 'args' */
      if (needs_copied_args(sc->code))
	sc->args = copy_list(sc, sc->args);
      goto APPLY;
      /* (let ((lst '((1 2)))) (define (identity x) x) (cons (apply identity lst) lst))
       */


      /* -------------------------------- DO -------------------------------- */

      /* opt order should probably be looped, ex_fallback, direct */

      /* this is the fm-violin optimization */
    SIMPLE_SAFE_DOTIMES:
    case OP_SIMPLE_SAFE_DOTIMES:
      /* set up as in SAFE_DOTIMES, pull in all the let vars
       *   run body without any jumps: update and check step,
       *   SAFE_C_C of each let var, SAFE_C_C of body
       */
      {
	s7_pointer vars, init_val, func, body, stepper, lets;

	vars = car(sc->code);
	init_val = cadr(car(vars));
	if (is_symbol(init_val))
	  init_val = find_symbol_checked(sc, init_val);
	if (s7_is_integer(init_val))
	  {
	    s7_pointer end_expr, end_val, let_var, p;
	    end_expr = caadr(sc->code);
	    end_val = caddr(end_expr);
	    if (is_symbol(end_val))
	      end_val = find_symbol_checked(sc, end_val);
	    if (s7_is_integer(end_val))
	      {
		sc->envir = new_frame_in_env(sc, sc->envir); 
		sc->args = add_slot(sc, caar(vars), make_mutable_integer(sc, s7_integer(init_val)));
		if (s7_integer(init_val) == s7_integer(end_val))
		  {
		    sc->code = cdr(cadr(sc->code));
		    goto SAFE_DO_END_CLAUSES;
		  }
		denominator(slot_value(sc->args)) = s7_integer(end_val);

		/* add the let vars but not initialized yet 
		 */
		for (let_var = car(cdaddr(sc->code)); is_pair(let_var); let_var = cdr(let_var))
		  add_slot(sc, caar(let_var), s7_make_mutable_real(sc, 1.5));
		
		stepper = slot_value(sc->args);
		func = caddr(caddr(sc->code));
		
		if ((c_function_ext(ecdr(func))) &&
		    (c_function_let_looped(ecdr(func))))
		  {
		    s7_function f;
		    s7_pointer result;
			
		    f = (s7_function)c_function_call(c_function_let_looped(ecdr(func)));
		    car(sc->T3_1) = stepper;
		    car(sc->T3_2) = caddr(sc->code);
		    car(sc->T3_3) = outlet(sc->envir);
		    result = f(sc, sc->T3_1);
			
		    if (result)
		      {
			sc->code = cdr(cadr(sc->code));
			goto SAFE_DO_END_CLAUSES;
		      }
		    /* else fall into the ordinary loop */
		  }

		body = cdr(caddr(caddr(sc->code)));
		lets = car(cdaddr(sc->code));

		while (true)
		  {
		    /* eval let + body  (cddr(sc->code)) */
		    for (let_var = lets; is_pair(let_var); let_var = cdr(let_var))
		      {
			p = find_local_symbol(sc, caar(let_var), sc->envir);
			slot_set_value(p, c_call(cadar(let_var))(sc, cdadar(let_var)));
		      }
		    c_call(func)(sc, body);
		    numerator(stepper)++;
		    if (numerator(stepper) == denominator(stepper))
		      {
			sc->code = cdr(cadr(sc->code));
			goto SAFE_DO_END_CLAUSES;
		      }
		  }
	      }
	  }
	pair_set_syntax_symbol(sc->code, sc->SIMPLE_DO);
	goto SIMPLE_DO;
      }


    SAFE_DOTIMES_C_C:
    case OP_SAFE_DOTIMES_C_C:
      {
	s7_pointer vars, init_val, func, body, stepper;

	vars = car(sc->code);
	init_val = cadr(car(vars));
	if (is_symbol(init_val))
	  init_val = find_symbol_checked(sc, init_val);
	else
	  {
	    if (is_pair(init_val))
	      init_val = c_call(init_val)(sc, cdr(init_val));
	  }
	if (s7_is_integer(init_val))
	  {
	    s7_pointer end_expr, end_val;
	    end_expr = caadr(sc->code);
	    end_val = caddr(end_expr);
	    if (is_symbol(end_val))
	      end_val = find_symbol_checked(sc, end_val);
	    if (s7_is_integer(end_val))
	      {
		/* (let () (define (hi a) (do ((i a (+ i 1))) ((= i a) i) (+ a 1))) (hi 1)) */
		sc->envir = new_frame_in_env(sc, sc->envir); 
		sc->args = add_slot(sc, caar(vars), make_mutable_integer(sc, s7_integer(init_val)));

		if (s7_integer(init_val) == s7_integer(end_val))
		  {
		    sc->code = cdr(cadr(sc->code));
		    goto SAFE_DO_END_CLAUSES;
		  }

		denominator(slot_value(sc->args)) = s7_integer(end_val);

		func = caddr(sc->code);
		body = cdr(caddr(sc->code));
		stepper = slot_value(sc->args);

		if ((c_function_ext(ecdr(func))) && (c_function_looped(ecdr(func))))
		  {
		    s7_function f;
		    s7_pointer result;
		    f = (s7_function)c_function_call(c_function_looped(ecdr(func)));
		    result = f(sc, sc->z = cons(sc, stepper, body));
		    sc->z = sc->NIL;
		    if (result)
		      {
			sc->code = cdr(cadr(sc->code));
			goto SAFE_DO_END_CLAUSES;
		      }
		    /* else fall into the ordinary loop */
		  }

		if (sc->ex_fallback)
		  {
		    s7_ex *e = NULL;
		    e = sc->ex_fallback(sc, func, sc->envir);
		    if (e)
		      {
			while (true)
			  {
			    e->f(e);
			    numerator(stepper)++;
			    if (numerator(stepper) == denominator(stepper))
			      {
				e->free(e);
				sc->code = cdr(cadr(sc->code));
				goto SAFE_DO_END_CLAUSES;
			      }
			  }
		      }
		  }

		while (true)
		  {
		    /* eval body (cddr(sc->code)) */
		    c_call(func)(sc, body);
		    numerator(stepper)++;
		    if (numerator(stepper) == denominator(stepper))
		      {
			sc->code = cdr(cadr(sc->code));
			goto SAFE_DO_END_CLAUSES;
		      }
		  }
	      }
	  }
	pair_set_syntax_symbol(sc->code, sc->SIMPLE_DO);
	goto SIMPLE_DO;
      }


    SAFE_DOTIMES_C_A:
    case OP_SAFE_DOTIMES_C_A:
      {
	s7_pointer vars, init_val, body, stepper;
	s7_function func;

	vars = car(sc->code);
	init_val = cadr(car(vars));
	if (is_symbol(init_val))
	  init_val = find_symbol_checked(sc, init_val);
	else
	  {
	    if (is_pair(init_val))
	      init_val = c_call(init_val)(sc, cdr(init_val));
	  }
	if (s7_is_integer(init_val))
	  {
	    s7_pointer end_expr, end_val;
	    end_expr = caadr(sc->code);
	    end_val = caddr(end_expr);
	    if (is_symbol(end_val))
	      end_val = find_symbol_checked(sc, end_val);
	    if (s7_is_integer(end_val))
	      {
		/* (let () (define (hi) (do ((i 10 (+ i 1))) ((= i 10) i) (display i))) (hi))
		 */
		sc->envir = new_frame_in_env(sc, sc->envir); 
		sc->args = add_slot(sc, caar(vars), make_mutable_integer(sc, s7_integer(init_val)));

		if (s7_integer(init_val) == s7_integer(end_val))
		  {
		    sc->code = cdr(cadr(sc->code));
		    goto SAFE_DO_END_CLAUSES;
		  }

		denominator(slot_value(sc->args)) = s7_integer(end_val);
		stepper = slot_value(sc->args);
		body = cddr(sc->code);

		if ((!is_pair(body)) ||
		    ((!is_pair(car(body))) &&
		     (is_null(cdr(body)))))
		  {
		    numerator(stepper) = denominator(stepper);
		    sc->code = cdr(cadr(sc->code));
		    goto SAFE_DO_END_CLAUSES;
		  }

		if (is_pair(cdr(body)))
		  {
		    while (true)
		      {
			s7_pointer p;
			for (p = body; is_pair(p); p = cdr(p))
			  ((s7_function)fcdr(p))(sc, car(p));

			numerator(stepper)++;
			if (numerator(stepper) == denominator(stepper))
			  {
			    sc->code = cdr(cadr(sc->code));
			    goto SAFE_DO_END_CLAUSES;
			  }
		      }
		  }
		else
		  {
		    func = (s7_function)(fcdr(body));
		    body = car(body);

		    if (sc->ex_fallback)
		      {
			s7_ex *e = NULL;
			e = sc->ex_fallback(sc, body, sc->envir);
			if (e)
			  {
			    while (true)
			      {
				e->f(e);
				numerator(stepper)++;
				if (numerator(stepper) == denominator(stepper))
				  {
				    e->free(e);
				    sc->code = cdr(cadr(sc->code));
				    goto SAFE_DO_END_CLAUSES;
				  }
			      }
			  }
		      }
#if 0
		    if (func == all_x_c_s)
		      {
			dox_slot1(sc->envir) = find_symbol(sc, cadr(caddr(sc->code)));
			func = end_dox_c_s;
		      }
#endif
		    while (true)
		      {
			func(sc, body);
			numerator(stepper)++;
			if (numerator(stepper) == denominator(stepper))
			  {
			    sc->code = cdr(cadr(sc->code));
			    goto SAFE_DO_END_CLAUSES;
			  }
		      }
		  }
	      }
	  }
	pair_set_syntax_symbol(sc->code, sc->SIMPLE_DO);
	goto SIMPLE_DO;
      }


    SAFE_DOTIMES:
    case OP_SAFE_DOTIMES:
      {
	s7_pointer vars, init_val;
	vars = car(sc->code);
	init_val = cadr(car(vars));
	if (is_symbol(init_val))
	  init_val = find_symbol_checked(sc, init_val);
	else
	  {
	    if (is_pair(init_val))
	      init_val = c_call(init_val)(sc, cdr(init_val));
	  }

	if (s7_is_integer(init_val))
	  {
	    s7_pointer end_expr, end_val, code;
	    code = sc->code;
	    end_expr = caadr(code);
	    end_val = caddr(end_expr);
	    if (is_symbol(end_val))
	      end_val = find_symbol_checked(sc, end_val);
	    if (s7_is_integer(end_val))
	      {
		sc->code = cddr(code);
		sc->envir = new_frame_in_env(sc, sc->envir); 
		sc->args = add_slot(sc, caar(vars), make_mutable_integer(sc, s7_integer(init_val)));

		denominator(slot_value(sc->args)) = s7_integer(end_val);
		/* (define (hi) (do ((i 1 (+ 1 i))) ((= i 1) i))) -- we need the frame even if the loop is not evaluated
		 */
		if ((is_null(sc->code)) ||
		    ((!is_pair(car(sc->code))) &&
		     (is_null(cdr(sc->code)))))
		  {
		    numerator(slot_value(sc->args)) = s7_integer(end_val);
		    sc->code = cdr(cadr(code));
		    goto SAFE_DO_END_CLAUSES;
		  }

		if (s7_integer(init_val) == s7_integer(end_val))
		  {
		    sc->code = cdr(cadr(code));
		    goto SAFE_DO_END_CLAUSES;
		  }

		if ((is_null(cdr(sc->code))) &&
		    (is_pair(car(sc->code))))
		  {
		    s7_ex *e = NULL;

		    sc->code = car(sc->code);
		    set_fcdr(code, sc->code);

		    if ((typesflag(sc->code) == SYNTACTIC_PAIR) || 
			(typesflag(car(sc->code)) == SYNTACTIC_TYPE))
		      {
			/* (with-sound () (mosquito 0 5 560 .2))
			 */
			/* if we don't handle it here, check_let mangles it, and a subsequent return here sees OP_LET_ALL_X or whatever
			 */

			if (((symbol_syntax_op(car(sc->code)) == OP_LET) ||
			     (symbol_syntax_op(car(sc->code)) == OP_LET_STAR)) && 
			    (is_null(cdddr(sc->code))))
			  {
			    s7_pointer flp;
			    /* it's a let of some sort, and one-line body */

			    /* try let-looped */
			    flp = caddr(sc->code);
			    if ((is_optimized(flp)) &&
				(c_function_ext(ecdr(flp))) &&
				(c_function_let_looped(ecdr(flp))))
			      {
				bool happy = true;
				s7_pointer p;
				for (p = cadr(sc->code); is_pair(p); p = cdr(p))
				  if (!is_optimized(cadar(p)))
				    {
				      happy = false;
				      break;
				    }

				if (happy)
				  {
				    s7_function f;
				    s7_pointer result, old_e;
				    old_e = sc->envir;
				    sc->envir = new_frame_in_env(sc, sc->envir); 
				    for (p = cadr(sc->code); is_pair(p); p = cdr(p))
				      add_slot(sc, caar(p), s7_make_mutable_real(sc, 1.5));

				    f = (s7_function)c_function_call(c_function_let_looped(ecdr(flp)));
				    car(sc->T3_1) = slot_value(sc->args);
				    car(sc->T3_2) = sc->code;
				    car(sc->T3_3) = outlet(old_e);
				    /* let_looped is let*_looped in fact -- should we pass old_e itself here if OP_LET?
				     *   or add another arg telling c_function_let_looped where to get its locals?
				     */
				    result = f(sc, sc->T3_1);
				    free_let(sc, sc->envir);
				    sc->envir = old_e;

				    if (result)
				      {
					sc->code = cdr(cadr(code));
					goto SAFE_DO_END_CLAUSES;
				      }
				    /* else fall into the ordinary loop */
				  }
			      }
			  }
			  
			push_stack(sc, OP_SAFE_DOTIMES_STEP_P, sc->args, code);
			if (typesflag(sc->code) == SYNTACTIC_PAIR)
			  sc->op = (opcode_t)pair_syntax_op(sc->code);
			else
			  {
			    sc->op = (opcode_t)symbol_syntax_op(car(sc->code));
			    pair_syntax_op(sc->code) = sc->op;
			    set_type(sc->code, SYNTACTIC_PAIR);
			  }
			sc->code = cdr(sc->code);
			goto START_WITHOUT_POP_STACK;
		      }
		    else
		      {
			/* try direct looper */
			if ((is_optimized(sc->code)) &&
			    (c_function_ext(ecdr(sc->code))) &&
			    (c_function_looped(ecdr(sc->code))))
			  {
			    s7_pointer body, stepper, result;
			    s7_function f;

			    stepper = slot_value(sc->args);
			    body = cdr(sc->code);
			    f = (s7_function)c_function_call(c_function_looped(ecdr(sc->code)));
			    result = f(sc, sc->z = cons(sc, stepper, body));
			    sc->z = sc->NIL;
			    if (result)
			      {
				sc->code = cdr(cadr(code));
				goto SAFE_DO_END_CLAUSES;
			      }
			  }

			/* try ex_fallback (gf*) */ 
			if ((sc->ex_fallback) &&
			    (!is_unsafe_do(sc->code)))
			  { 
			    e = sc->ex_fallback(sc, sc->code, sc->envir);
			    if (e)
			      {
				s7_pointer stepper;
				stepper = slot_value(sc->args);
				while (true)
				  {
				    e->f(e);
				    numerator(stepper)++;
				    if (numerator(stepper) == denominator(stepper))
				      {
					e->free(e);
					sc->code = cdr(cadr(code));
					goto SAFE_DO_END_CLAUSES;
				      }
				  }
			      }
			  }
			
			if (is_optimized(sc->code))
			  {
			    set_unsafe_do(sc->code);
			    push_stack(sc, OP_SAFE_DOTIMES_STEP_O, sc->args, code);
			    goto OPT_EVAL;
			  }
		      }
		  }

		/* here we know the body is not a one-liner, or something ridiculous */
		set_fcdr(code, sc->code);
		push_stack(sc, OP_SAFE_DOTIMES_STEP, sc->args, code);		  
		goto BEGIN1;
	      }
	  }
	pair_set_syntax_symbol(sc->code, sc->SIMPLE_DO);
	goto SIMPLE_DO;
      }


    case OP_SAFE_DOTIMES_STEP_P:
      {
	s7_pointer arg;
	arg = slot_value(sc->args);
	numerator(arg)++;
	if (numerator(arg) == denominator(arg))
	  {
	    sc->code = cdr(cadr(sc->code));
	    goto DO_END_CLAUSES;
	  }
	push_stack(sc, OP_SAFE_DOTIMES_STEP_P, sc->args, sc->code);
	sc->code = fcdr(sc->code);
	sc->op = (opcode_t)pair_syntax_op(sc->code);
	sc->code = cdr(sc->code);
	goto START_WITHOUT_POP_STACK;
      }


    case OP_SAFE_DOTIMES_STEP_O:
      {
	s7_pointer arg;
	arg = slot_value(sc->args);
	numerator(arg)++;
	if (numerator(arg) == denominator(arg))
	  {
	    sc->code = cdr(cadr(sc->code));
	    goto DO_END_CLAUSES;
	  }
	push_stack(sc, OP_SAFE_DOTIMES_STEP_O, sc->args, sc->code);
	sc->code = fcdr(sc->code);
	goto OPT_EVAL; 
      }


    case OP_SAFE_DOTIMES_STEP_A:
      {
	s7_pointer arg;
	/* no calls?? */
	arg = slot_value(sc->args);
	car(sc->T2_1) = arg;
	car(sc->T2_2) = sc->value;
	c_call(fcdr(sc->code))(sc, sc->T2_1);

	numerator(arg)++;
	if (numerator(arg) == denominator(arg))
	  {
	    sc->code = cdr(cadr(sc->code));
	    goto DO_END_CLAUSES;
	  }

	push_stack(sc, OP_SAFE_DOTIMES_STEP_A, sc->args, sc->code);
	sc->code = caddr(fcdr(sc->code));
	goto OPT_EVAL;
      }


    case OP_SAFE_DOTIMES_STEP:
      {
	s7_pointer arg;
	arg = slot_value(sc->args);
	numerator(arg)++;
	if (numerator(arg) == denominator(arg))
	  {
	    sc->code = cdr(cadr(sc->code));
	    goto DO_END_CLAUSES;
	  }
	push_stack(sc, OP_SAFE_DOTIMES_STEP, sc->args, sc->code);

	arg = fcdr(sc->code);
	/* here we know the body has more than one form */
	push_stack_no_args(sc, OP_BEGIN1, cdr(arg));
	sc->code = car(arg);
	goto EVAL;
      }


    SAFE_DO:
    case OP_SAFE_DO:
      {
	/* body is safe, step = +1, end is =, but stepper and end might be set (or at least indirectly exported) in the body:
	 *    (let ((lst ())) (do ((i 0 (+ i 1))) ((= i 10)) (let ((j (min i 100))) (set! lst (cons j lst)))) lst)
	 *  however, we're very restrictive about this in check_do and do_is_safe; even this is considered trouble:
	 *    (let ((x 0)) (do ((i i (+ i 1))) ((= i 7)) (set! x (+ x i))) x)
	 */
	s7_pointer end, init_val, end_val, code;

	code = sc->code;
	init_val = cadaar(code);
	if (is_symbol(init_val))
	  init_val = find_symbol_checked(sc, init_val);
	else
	  {
	    if (is_pair(init_val))
	      init_val = c_call(init_val)(sc, cdr(init_val));
	  }
	
	end = caddr(car(cadr(code)));
	if (is_symbol(end))
	  end_val = find_symbol_checked(sc, end);
	else end_val = end;
	
	/* (let ((sum 0)) (define (hi) (do ((i 10 (+ i 1))) ((= i 10) i) (set! sum (+ sum i)))) (hi))
	 */
	sc->envir = new_frame_in_env(sc, sc->envir);
	dox_slot1(sc->envir) = add_slot(sc, caaar(code), init_val);

#if WITH_GMP
	if (s7_integer(init_val) == s7_integer(end_val))
#else
	if ((s7_integer(init_val) == s7_integer(end_val)) ||
	    ((s7_integer(init_val) > s7_integer(end_val)) &&
	     (ecdr(car(cadr(code))) == geq_2)))
#endif
	  {
	    sc->code = cdr(cadr(code));
	    goto SAFE_DO_END_CLAUSES;
	  }
	if (is_symbol(end))
	  sc->args = find_symbol(sc, end);
	else sc->args = make_slot(sc, end, end); /* here and elsewhere sc->args is used for GC protection */
	dox_slot2(sc->envir) = sc->args;

	sc->code = cddr(code);
	if (is_unsafe_do(sc->code))              /* we've seen this loop before and it's not optimizable */
	  {
	    set_fcdr(code, sc->code);
	    push_stack(sc, OP_SAFE_DO_STEP, sc->args, code);
	    goto BEGIN1;
	  }

	if ((is_null(cdr(sc->code))) &&
	    (is_pair(car(sc->code))))
	  {
	    s7_ex *e = NULL;
	    s7_pointer obj, body;
	    body = car(sc->code);
	    /* very few calls */
	    if ((sc->ex_fallback) &&
		(!is_unsafe_do(sc->code)) &&
		(car(body) == sc->SET) &&
		(is_pair(cadr(body))) && 
		(is_symbol(caadr(body))) &&
		(is_null(cdddr(body))) &&
		(cadr(cadr(body)) == caaar(code)) &&
		(is_null(cddr(cadr(body)))) &&
		(is_float_vector(obj = find_symbol_unchecked(sc, caadr(body)))))
	      {
		e = sc->ex_fallback(sc, caddr(body), sc->envir);
		if (e)
		  {
		    s7_Int i, start, lim;
		    lim = s7_integer(end_val);
		    start = s7_integer(init_val);
		    for (i = start; i < lim; i++)
		      float_vector_element(obj, i) = e->f(e);
		    e->free(e);
		    
		    slot_value(dox_slot1(sc->envir)) = slot_value(dox_slot2(sc->envir));
		    sc->code = cdr(cadr(code));
		    goto SAFE_DO_END_CLAUSES;
		  }
	      }

	    set_unsafe_do(sc->code);
	    set_fcdr(code, sc->code);
	    push_stack(sc, OP_SAFE_DO_STEP_1, sc->args, code);
	    sc->code = body;
	    goto EVAL;
	  }
	else
	  {
	    set_unsafe_do(sc->code);
	    set_fcdr(code, sc->code);
	    push_stack(sc, OP_SAFE_DO_STEP, sc->args, code);
	    goto BEGIN1;
	  }
      }

    case OP_SAFE_DO_STEP_1:
      {
	s7_pointer body;
	body = car(fcdr(sc->code));
	if (typesflag(body) == SYNTACTIC_PAIR)
	  {
	    if ((pair_syntax_op(body) == OP_INCREMENT_SA) || 
		(pair_syntax_op(body) == OP_INCREMENT_1))
	      {
		s7_pointer sym_slot, arg, code, step_slot, end_slot;
		s7_function incr, increment;
		s7_Int step, end;
		bool use_geq = false;

		sym_slot = find_symbol(sc, cadr(body));
		arg = cddr(caddr(body));
		code = cdr(sc->code);
		step_slot = dox_slot1(sc->envir);
		end_slot = dox_slot2(sc->envir);

#if (!WITH_GMP)
		use_geq = ((is_optimized(caadr(code))) && (ecdr(caadr(code)) == geq_2));
#endif
		if (pair_syntax_op(body) == OP_INCREMENT_1)
		  {
		    increment = all_x_c;
		    incr = g_add_s1;
		  }
		else 
		  {
		    incr = (s7_function)(fcdr(caddr(body)));
		    increment = (s7_function)(fcdr(arg));
		  }
		arg = car(arg);

		while (true)
		  {
		    step = s7_integer(slot_value(step_slot)) + 1;
		    slot_set_value(step_slot, make_integer(sc, step));
		    end = s7_integer(slot_value(end_slot));

		    if ((step == end) || 
			((use_geq) && (step > end)))
		      {
			sc->code = cdar(code);
			goto DO_END_CLAUSES;
		      }
		    car(sc->T2_2) = increment(sc, arg);
		    car(sc->T2_1) = slot_value(sym_slot);
		    slot_set_value(sym_slot, incr(sc, sc->T2_1));
		  }
	      }
	  }
	/* here we know we can goto EVAL */
      }


    case OP_SAFE_DO_STEP:
      {
	s7_Int step, end;
	s7_pointer args, code, slot;

	args = sc->envir;
	code = sc->code;
	slot = dox_slot1(args);

	step = s7_integer(slot_value(slot)) + 1;
	slot_set_value(slot, make_integer(sc, step));
	end = s7_integer(slot_value(dox_slot2(args)));
#if WITH_GMP
	if (step == end)
#else
        if ((step == end) ||
	    ((step > end) &&
	     (ecdr(caadr(code)) == geq_2)))
#endif	      
	  {
	    sc->code = cdadr(code);
	    goto DO_END_CLAUSES;
	  }
	push_stack(sc, OP_SAFE_DO_STEP, sc->args, code);
	sc->code = fcdr(code);
	goto BEGIN1;
      }


    SIMPLE_DO_A:
    case OP_SIMPLE_DO_A:
      sc->op = OP_SIMPLE_DO_A;

    SIMPLE_DO:
    case OP_SIMPLE_DO:
      {
	/* body might not be safe in this case, but the step and end exprs are easy
	 *   "not safe" merely means we hit something that the optimizer can't specialize like (+ (* (abs (- ...))))
	 */
	s7_pointer init, end, code;

	code = sc->code;
	sc->envir = new_frame_in_env(sc, sc->envir);
	init = cadaar(code);
	if (is_symbol(init))
	  sc->value = find_symbol_checked(sc, init);
	else 
	  {
	    if (is_pair(init))
	      sc->value = c_call(init)(sc, cdr(init));
	    else sc->value = init;
	  }
	dox_slot1(sc->envir) = add_slot(sc, caaar(code), sc->value);
	end = caddr(caadr(code));
	if (is_symbol(end))
	  sc->args = find_symbol(sc, end);
	else 
	  {
	    s7_pointer slot;
	    NEW_CELL_NO_CHECK(sc, slot);
	    slot_symbol(slot) = end;
	    slot_set_value(slot, end);
	    set_type(slot, T_SLOT | T_IMMUTABLE);
	    sc->args = slot;
	  }
	dox_slot2(sc->envir) = sc->args;

	car(sc->T2_1) = slot_value(dox_slot1(sc->envir));
	car(sc->T2_2) = slot_value(dox_slot2(sc->envir));
	if (is_true(sc, c_call(caadr(code))(sc, sc->T2_1)))
	  {
	    sc->code = cdadr(code);
	    goto DO_END_CLAUSES;
	  }

	set_fcdr(code, cddr(code));
	if ((is_null(cdr(fcdr(code)))) &&
	    (is_pair(car(fcdr(code)))) &&
	    /* (is_optimized(car(fcdr(code)))) && */
	    /* (is_all_x_op(optimize_data(car(fcdr(code))))) && */
	    (is_symbol(cadr(caddr(caar(code)))))) /* caar=(i 0 (+ i 1)), caddr=(+ i 1), so this is apparently checking that the stepf is reasonable? */
	  {
	    s7_pointer step_var, ctr, end, body;
	    s7_function func = NULL, endf, stepf;
	    s7_ex *exd = NULL;

	    body = car(fcdr(code));
	    ctr = dox_slot1(sc->envir);
	    end = dox_slot2(sc->envir);
	    endf = c_call(caadr(code));
	    stepf = c_call(caddr(caar(code)));
	    step_var = caddr(caddr(caar(code))); 

	    if (is_symbol(car(body)))
	      {
		s7_pointer f;
		f = s7_symbol_value(sc, car(body));

		if ((is_c_function(f)) &&
		    (c_function_ext(f)) &&
		    (c_function_ex_parser(f)))
		  {
		    exd = c_function_ex_parser(f)(sc, body);
		    if (exd)
		      {
			bool mut;
			mut = is_step_safe(f);

			if ((mut) &&
			    (type(slot_value(ctr)) == T_INTEGER) &&
			    (((s7_function)stepf == g_subtract_s1) ||
			     ((s7_function)stepf == g_add_s1)))
			  {
			    slot_value(ctr) = make_mutable_integer(sc, integer(slot_value(ctr)));
			    if ((s7_function)stepf == g_subtract_s1)
			      stepf = g_mutable_subtract1;
			    else stepf = g_mutable_add1;
			  }
			else mut = false;

			while (true)
			  {
			    exd->func(exd);
		    
			    car(sc->T2_1) = slot_value(ctr);
			    car(sc->T2_2) = step_var;
			    slot_set_value(ctr, stepf(sc, sc->T2_1));
		    
			    car(sc->T2_1) = slot_value(ctr);
			    car(sc->T2_2) = slot_value(end);
			    if (is_true(sc, endf(sc, sc->T2_1)))
			      {
				exd->free(exd);
				sc->code = cdr(cadr(code));
				if (!is_null(sc->code))
				  {
				    if (mut)
				      clear_mutable(slot_value(ctr));
				    goto BEGIN1;
				  }
				sc->value = sc->NIL;
				goto START;	
			      }
			  }
		      }
		  }
	      }

	    if ((!func) &&
		(is_optimized(car(fcdr(code)))) && 
		(is_all_x_op(optimize_data(car(fcdr(code))))))
	      func = all_x_eval(sc, car(fcdr(code)));
	    
	    if (func)
	      {
		s7_pointer a1 = NULL, a2 = NULL, a3 = NULL;
		s7_function f1 = NULL;
		if (func == all_x_c_sss)
		  {
		    a1 = find_symbol(sc, cadr(body));
		    a2 = find_symbol(sc, caddr(body));
		    a3 = find_symbol(sc, cadddr(body));
		    f1 = c_call(body);
		  }

		while (true)
		  {
		    if (a1)
		      {
			car(sc->A3_1) = slot_value(a1);
			car(sc->A3_2) = slot_value(a2);
			car(sc->A3_3) = slot_value(a3);
			f1(sc, sc->A3_1);
		      }
		    else func(sc, body);
		    
		    car(sc->T2_1) = slot_value(ctr);
		    car(sc->T2_2) = step_var;
		    slot_set_value(ctr, stepf(sc, sc->T2_1));
		    
		    car(sc->T2_1) = slot_value(ctr);
		    car(sc->T2_2) = slot_value(end);
		    if (is_true(sc, endf(sc, sc->T2_1)))
		      {
			sc->code = cdr(cadr(code));
			goto DO_END_CLAUSES;
		      }
		  }
	      }
	  }
	if (sc->op == OP_SIMPLE_DO_A)
	  push_stack(sc, OP_SIMPLE_DO_STEP_A, sc->args, code);
	else push_stack(sc, OP_SIMPLE_DO_STEP, sc->args, code);

	sc->code = fcdr(code);
	goto BEGIN1;
      }


    case OP_SIMPLE_DO_STEP:
      {
	s7_pointer step, ctr, end, code;

	ctr = dox_slot1(sc->envir);
	end = dox_slot2(sc->envir);
	code = sc->code;

	step = caddr(caar(code)); 
	if (is_symbol(cadr(step))) /* bench 2006888 2006888 */
	  {
	    car(sc->T2_1) = slot_value(ctr);
	    car(sc->T2_2) = caddr(step);
	  }
	else
	  {
	    car(sc->T2_2) = slot_value(ctr);
	    car(sc->T2_1) = cadr(step);
	  }
	slot_set_value(ctr, c_call(step)(sc, sc->T2_1));

	car(sc->T2_1) = slot_value(ctr);
	car(sc->T2_2) = slot_value(end);
	if (is_true(sc, c_call(caadr(code))(sc, sc->T2_1)))
	  {
	    sc->code = cdr(cadr(code));
	    goto DO_END_CLAUSES;
	  }
	push_stack(sc, OP_SIMPLE_DO_STEP, sc->args, code);
	sc->code = fcdr(code);
	goto BEGIN1;
      }


    case OP_SIMPLE_DO_STEP_A:
      {
	/* (((i 0 (+ i 1))) ((= i 1000)) (set! mx (max mx (abs (f1 signal)))) (set! signal 0.0))
	 * (((i 0 (+ i 1))) ((= i 20)) (outa i (sine-env e)))
	 */
	s7_pointer val, ctr, end, code;

	code = sc->code;
	ctr = dox_slot1(sc->envir);
	val = slot_value(ctr);
	end = slot_value(dox_slot2(sc->envir));

	if (is_integer(val))
	  {
	    slot_set_value(ctr, make_integer(sc, integer(val) + 1));
	    val = slot_value(ctr);
	    if (is_integer(end))
	      {
		if (integer(val) == integer(end))
		  {
		    sc->code = cdr(cadr(code));
		    goto DO_END_CLAUSES;
		  }
	      }
	    else
	      {
		car(sc->T2_1) = val;
		car(sc->T2_2) = end;
		if (is_true(sc, g_equal_2(sc, sc->T2_1)))
		  {
		    sc->code = cdr(cadr(code));
		    goto DO_END_CLAUSES;
		  }
	      }
	  }
	else
	  {
	    car(sc->T1_1) = val; /* add_s1 ignores cadr(args) */
	    slot_set_value(ctr, g_add_s1(sc, sc->T1_1));
	    val = slot_value(ctr);

	    car(sc->T2_1) = val;
	    car(sc->T2_2) = end;
	    if (is_true(sc, g_equal_2(sc, sc->T2_1)))
	      {
		sc->code = cdr(cadr(code));
		goto DO_END_CLAUSES;
	      }
	  }
	push_stack(sc, OP_SIMPLE_DO_STEP_A, sc->args, code);
	sc->code = fcdr(code);
	goto BEGIN1;
      }


    SIMPLE_DO_FOREVER:
    case OP_SIMPLE_DO_FOREVER:
      {
	s7_pointer body;
	s7_function func;

	body = cddr(sc->code);
	if ((c_function_ext(ecdr(car(body)))) && (c_function_looped(ecdr(car(body)))))
	  {
	    func = (s7_function)c_function_call(c_function_looped(ecdr(car(body))));
	    if (func(sc, sc->z = cons(sc, small_int(0), cdar(body)))) /* make it compatible with the other looped calls */
	      goto START; /* won't happen? */
	    sc->z = sc->NIL;
	    /* else fall into the ordinary loop */
	  }
	
	/* this was mistakenly evaluating fcdr(body) but that means any inner it is unevaluated! */
	func = (s7_function)fcdr(body);
	while (true) {func(sc, car(body));}
      }


    SIMPLE_DO_P:
    case OP_SIMPLE_DO_P:
      {
	/* body is one line, syntactic */
	s7_pointer init, end, code;
	code = sc->code;
	sc->envir = new_frame_in_env(sc, sc->envir);

	init = cadaar(code);
	if (is_symbol(init))
	  sc->value = find_symbol_checked(sc, init);
	else 
	  {
	    if (is_pair(init))
	      sc->value = c_call(init)(sc, cdr(init));
	    else sc->value = init;
	  }
	dox_slot1(sc->envir) = add_slot(sc, caaar(code), sc->value);
	
	end = caddr(caadr(code));
	if (is_symbol(end))
	  sc->args = find_symbol(sc, end);
	else 
	  {
	    s7_pointer slot;
	    NEW_CELL_NO_CHECK(sc, slot);
	    slot_symbol(slot) = end;
	    slot_set_value(slot, end);
	    set_type(slot, T_SLOT | T_IMMUTABLE);
	    sc->args = slot;
	  }
	dox_slot2(sc->envir) = sc->args;

	car(sc->T2_1) = slot_value(dox_slot1(sc->envir));
	car(sc->T2_2) = slot_value(dox_slot2(sc->envir));
	if (is_true(sc, c_call(caadr(code))(sc, sc->T2_1)))
	  {
	    sc->code = cdadr(code);
	    goto DO_END_CLAUSES;
	  }

	/* (((i 0 (+ 1 i))) ((= i len)) (set! (v i) (read-mix-sample reader)))
	 */
	push_stack(sc, OP_SIMPLE_DO_STEP_P, sc->args, code);
	sc->code = caddr(code);
	goto EVAL;
      }


    case OP_SIMPLE_DO_STEP_P:
      {
	s7_pointer step, ctr, code;

	code = sc->code;
	ctr = dox_slot1(sc->envir);

	step = fcdr(code);                   /* caddr(caar(sc->code)) */
	if (is_symbol(cadr(step)))
	  {
	    car(sc->T2_1) = slot_value(ctr);
	    car(sc->T2_2) = caddr(step);
	  }
	else
	  {
	    car(sc->T2_2) = slot_value(ctr);
	    car(sc->T2_1) = cadr(step);
	  }
	slot_set_value(ctr, c_call(step)(sc, sc->T2_1));

	car(sc->T2_1) = slot_value(dox_slot1(sc->envir));
	car(sc->T2_2) = slot_value(dox_slot2(sc->envir));
	if (is_true(sc, c_call(caadr(code))(sc, sc->T2_1)))
	  {
	    sc->code = cdadr(code);
	    goto DO_END_CLAUSES;
	  }
	push_stack(sc, OP_SIMPLE_DO_STEP_P, sc->args, code);
	code = caddr(code);
	sc->cur_code = code;
	sc->op = (opcode_t)pair_syntax_op(code);
	sc->code = cdr(code);
	goto START_WITHOUT_POP_STACK;
      }


    DOTIMES_P:
    case OP_DOTIMES_P:
      {
	s7_pointer init, end, code;
	code = sc->code;
	sc->envir = new_frame_in_env(sc, sc->envir);

	init = cadaar(code);
	if (is_symbol(init))
	  sc->value = find_symbol_checked(sc, init);
	else 
	  {
	    if (is_pair(init))
	      sc->value = c_call(init)(sc, cdr(init));
	    else sc->value = init;
	  }
	dox_slot1(sc->envir) = add_slot(sc, caaar(code), sc->value);
	
	set_fcdr(code, caadr(code));
	end = caddr(fcdr(code));
	if (is_symbol(end))
	  sc->args = find_symbol(sc, end);
	else 
	  {
	    s7_pointer slot;
	    NEW_CELL_NO_CHECK(sc, slot);
	    slot_symbol(slot) = end;
	    slot_set_value(slot, end);
	    set_type(slot, T_SLOT | T_IMMUTABLE);
	    sc->args = slot;
	  }
	dox_slot2(sc->envir) = sc->args;

	car(sc->T2_1) = slot_value(dox_slot1(sc->envir));
	car(sc->T2_2) = slot_value(dox_slot2(sc->envir));
	if (is_true(sc, c_call(caadr(code))(sc, sc->T2_1)))
	  {
	    sc->code = cdadr(code);
	    goto DO_END_CLAUSES;
	  }
	push_stack(sc, OP_DOTIMES_STEP_P, sc->args, code);
	sc->code = caddr(code);
	goto EVAL;
      }


    case OP_DOTIMES_STEP_P:
      {
	s7_pointer ctr, now, end, code, end_test;

	code = sc->code;
	ctr = dox_slot1(sc->envir);
	now = slot_value(ctr);
	end = slot_value(dox_slot2(sc->envir));
	end_test = fcdr(code);

	if (is_integer(now))
	  {
	    slot_set_value(ctr, make_integer(sc, integer(now) + 1));
	    now = slot_value(ctr);
	    if (is_integer(end))
	      {
#if (!WITH_GMP)
		if ((integer(now) == integer(end)) ||
		    ((integer(now) > integer(end)) && 
		     (ecdr(end_test) == geq_2)))
#else
	        if (integer(now) == integer(end))
#endif
		  { 
		    sc->code = cdadr(code);
		    goto DO_END_CLAUSES;
		  }
	      }
	    else
	      {
		car(sc->T2_1) = now;
		car(sc->T2_2) = end;
		if (is_true(sc, c_call(end_test)(sc, sc->T2_1)))
		  {
		    sc->code = cdadr(code);
		    goto DO_END_CLAUSES;
		  }
	      }
	  }
	else
	  {
	    car(sc->T1_1) = now;
	    slot_set_value(ctr, g_add_s1(sc, sc->T1_1)); 
	    /* (define (hi) (let ((x 0.0) (y 1.0)) (do ((i y (+ i 1))) ((= i 6)) (do ((i i (+ i 1))) ((>= i 7)) (set! x (+ x i)))) x))
	     */
	    car(sc->T2_1) = slot_value(ctr);
	    car(sc->T2_2) = end;
	    if (is_true(sc, c_call(end_test)(sc, sc->T2_1)))
	      {
		sc->code = cdadr(code);
		goto DO_END_CLAUSES;
	      }
	  }
	push_stack(sc, OP_DOTIMES_STEP_P, sc->args, code);
	code = caddr(code);
	sc->cur_code = code;
	sc->op = (opcode_t)pair_syntax_op(code);
	sc->code = cdr(code);
	goto START_WITHOUT_POP_STACK;
      }


      /* DOX cases */
    DOX:
    case OP_DOX:
      /* any number of steppers using dox exprs, end also dox, body and end result arbitrary.
       *    since all these exprs are local, we don't need to jump until the body
       */
      {
	long long int id;
	s7_pointer frame, vars, slot;

	NEW_FRAME(sc, sc->envir, frame); /* new frame is not tied into the symbol lookup process yet */
	for (vars = car(sc->code); is_pair(vars); vars = cdr(vars))
	  {
	    s7_pointer expr;
	    NEW_CELL_NO_CHECK(sc, slot);	
	    slot_symbol(slot) = caar(vars);
	    
	    expr = cadar(vars);
	    if (is_pair(expr))
	      {
		if (car(expr) == sc->QUOTE)
		  slot_set_value(slot, cadr(expr));
		else slot_set_value(slot, ((dox_function)fcdr(cdar(vars)))(sc, expr, NULL));
	      }
	    else
	      {
		if (is_symbol(expr))
		  slot_set_value(slot, find_symbol_checked(sc, expr));
		else slot_set_value(slot, expr);
	      }
	    slot_expression(slot) = cddar(vars);
	    set_type(slot, T_SLOT | T_IMMUTABLE);
	    next_slot(slot) = let_slots(frame);
	    let_slots(frame) = slot;
	  }

	sc->envir = frame;
	id = let_id(frame);
	for (slot = let_slots(frame); is_slot(slot); slot = next_slot(slot))
	  symbol_set_local(slot_symbol(slot), id, slot);

	for (slot = let_slots(frame); is_slot(slot); slot = next_slot(slot))
	  if (is_pair(slot_expression(slot)))
	    switch (pair_syntax_op(slot_expression(slot)))
	      {
	      case DOX_STEP_SS: 
		slot_pending_value(slot) = find_symbol(sc, caddar(slot_expression(slot))); 
		break;

	      case DOX_STEP_S: 
		slot_pending_value(slot) = find_symbol(sc, cadar(slot_expression(slot)));  
		break;

	      case DOX_STEP_ADD:
		slot_pending_value(slot) = find_symbol(sc, caddar(slot_expression(slot))); 
		set_fcdr(slot_expression(slot), (s7_pointer)step_dox_c_add_ss);
#if (!WITH_GMP)
		if (type(slot_value(slot_pending_value(slot))) == type(slot_value(slot)))
		  {
		    if (is_integer(slot_value(slot)))
		      set_fcdr(slot_expression(slot), (s7_pointer)step_dox_c_add_i);
		    else
		      {
			if (type(slot_value(slot)) == T_REAL)
			  set_fcdr(slot_expression(slot), (s7_pointer)step_dox_c_add_f);
		      }
		  }
#endif
		break;

	      case DOX_STEP_SUBTRACT:
		slot_pending_value(slot) = find_symbol(sc, caddar(slot_expression(slot))); 
		set_fcdr(slot_expression(slot), (s7_pointer)step_dox_c_subtract_ss);
#if (!WITH_GMP)
		if (type(slot_value(slot_pending_value(slot))) == type(slot_value(slot)))
		  {
		    if (is_integer(slot_value(slot)))
		      set_fcdr(slot_expression(slot), (s7_pointer)step_dox_c_subtract_i);
		    else
		      {
			if (type(slot_value(slot)) == T_REAL)
			  set_fcdr(slot_expression(slot), (s7_pointer)step_dox_c_subtract_f);
		      }
		  }
#endif
		break;

	      case DOX_STEP_DEFAULT:
		break;
	      }

	initialize_dox_end_vars(sc, fcdr(sc->code));
	
	if (is_true(sc, ((s7_function)fcdr(cdr(sc->code)))(sc, fcdr(sc->code))))
	  {
	    /* if no end result exprs, we return nil, but others probably #<unspecified>
	     *    (let ((x (do ((i 0 (+ i 1))) (#t)))) x) -> ()
	     */
	    sc->code = cdadr(sc->code);
	    goto DO_END_CLAUSES;
	  }

	{
	  s7_pointer code;
	  code = cddr(sc->code);

	  if (is_null(code))
	    {
	      s7_function endf;
	      s7_pointer endp, slots;
	      endf = (s7_function)fcdr(cdr(sc->code));
	      endp = fcdr(sc->code);
	      slots = let_slots(sc->envir);
	      while (true)
		{
		  s7_pointer slot;
		  for (slot = slots; is_slot(slot); slot = next_slot(slot))
		    if (is_pair(slot_expression(slot)))
		      slot_set_value(slot, ((dox_function)fcdr(slot_expression(slot)))(sc, car(slot_expression(slot)), slot));
		  if (is_true(sc, endf(sc, endp)))
		    {
		      sc->code = cdadr(sc->code);
		      goto DO_END_CLAUSES;
		    }
		}
	    }

	  if ((is_null(cdr(code))) &&
	      (is_pair(car(code))))
	    {
	      code = car(code);

	      if ((typesflag(code) == SYNTACTIC_PAIR) ||
		  (typesflag(car(code)) == SYNTACTIC_TYPE))
		{
		  push_stack_no_args(sc, OP_DOX_STEP_P, sc->code);

		  if (typesflag(code) == SYNTACTIC_PAIR)
		    sc->op = (opcode_t)pair_syntax_op(code);
		  else
		    {
		      sc->op = (opcode_t)symbol_syntax_op(car(code));
		      pair_syntax_op(code) = sc->op;
		      set_type(code, SYNTACTIC_PAIR);
		    }
		  sc->code = cdr(code);
		  goto START_WITHOUT_POP_STACK;
		}

	      if (is_symbol(car(code)))
		{
		  s7_pointer f;
		  f = s7_symbol_value(sc, car(code));
		  
		  if ((is_c_function(f)) &&
		      (c_function_ext(f)) &&
		      (c_function_ex_parser(f)))
		    {
		      s7_ex *exd = NULL;
		      exd = c_function_ex_parser(f)(sc, code);
		      if (exd)
			{
			  s7_function endf;
			  s7_pointer endp, slots, slot;
			  int arg;
			  s7_pointer p, slot1 = NULL, slot2 = NULL, slot3 = NULL, expr1 = NULL, expr2 = NULL, expr3 = NULL;
			  dox_function d1 = NULL, d2 = NULL, d3 = NULL;
			  bool mut; /* C++ claims "mutable" */
			  
			  mut = is_step_safe(f);
			  
			  for (arg = 0, p = let_slots(sc->envir); is_slot(p); p = next_slot(p))
			    if (is_pair(slot_expression(p)))
			      {
				arg++;
				if (!slot1) 
				  {
				    slot1 = p; 
				    expr1 = slot_expression(slot1);
				    if (mut)
				      d1 = dox_mutate(sc, p);
				    else d1 = (dox_function)fcdr(expr1);
				    expr1 = car(expr1);
				  }
				else 
				  {
				    if (!slot2)
				      {
					slot2 = p;
					expr2 = slot_expression(slot2);
					if (mut)
					  d2 = dox_mutate(sc, p);
					else d2 = (dox_function)fcdr(expr2);
					expr2 = car(expr2);
				      }
				    else
				      {
					if (!slot3)
					  {
					    slot3 = p;
					    expr3 = slot_expression(slot3);
					    if (mut)
					      d3 = dox_mutate(sc, p);
					    else d3 = (dox_function)fcdr(expr3);
					    expr3 = car(expr3);
					  }
				      }
				  }
			      }
			  
			  endf = (s7_function)fcdr(cdr(sc->code));
			  endp = fcdr(sc->code);
			  slots = let_slots(sc->envir);
			  
			  if (arg == 1)
			    {
			      while (true)
				{
				  exd->func(exd);
				  slot_set_value(slot1, d1(sc, expr1, slot1));
				  if (is_true(sc, endf(sc, endp)))
				    {
				      exd->free(exd);
				      sc->code = cdadr(sc->code);
				      if (!is_null(sc->code))
					{
					  if (mut)
					    clear_mutable(slot_value(slot1));
					  goto BEGIN1;
					}
				      sc->value = sc->NIL;
				      goto START;	
				    }
				}
			    }
			  
			  if (arg < 4)
			    {
			      while (true)
				{
				  exd->func(exd);
				  slot_set_value(slot1, d1(sc, expr1, slot1));
				  slot_set_value(slot2, d2(sc, expr2, slot2));
				  if (slot3) slot_set_value(slot3, d3(sc, expr3, slot3));
				  if (is_true(sc, endf(sc, endp)))
				    {
				      exd->free(exd);
				      sc->code = cdadr(sc->code);
				      if (!is_null(sc->code))
					{
					  if (mut)
					    {
					      clear_mutable(slot_value(slot1));
					      clear_mutable(slot_value(slot2));
					      if (slot3) clear_mutable(slot_value(slot3));
					    }
					  goto BEGIN1;
					}
				      sc->value = sc->NIL;
				      goto START;	
				    }
				}
			    }
			  
			  while (true)
			    {
			      exd->func(exd);
			      
			      for (slot = slots; is_slot(slot); slot = next_slot(slot))
				if (is_pair(slot_expression(slot)))
				  slot_set_value(slot, ((dox_function)fcdr(slot_expression(slot)))(sc, car(slot_expression(slot)), slot));
			      
			      if (is_true(sc, endf(sc, endp)))
				{
				  exd->free(exd);
				  sc->code = cdadr(sc->code);
				  if (!is_null(sc->code))
				    {
				      if (mut)
					{
					  if (slot1) clear_mutable(slot_value(slot1));
					  if (slot2) clear_mutable(slot_value(slot2));
					}
				      goto BEGIN1;
				    }
				  sc->value = sc->NIL;
				  goto START;	
				}
			    }
			}
		    }
		}

	      if (is_all_x_safe(sc, code))
		{
		  s7_function body, endf;
		  s7_pointer endp, slots;

		  body = all_x_eval(sc, code);
		  endf = (s7_function)fcdr(cdr(sc->code));
		  endp = fcdr(sc->code);
		  slots = let_slots(sc->envir);

		  while (true)
		    {
		      s7_pointer slot;
		      body(sc, code);
		      for (slot = slots; is_slot(slot); slot = next_slot(slot))
			if (is_pair(slot_expression(slot)))
			  slot_set_value(slot, ((dox_function)fcdr(slot_expression(slot)))(sc, car(slot_expression(slot)), slot));
		      if (is_true(sc, endf(sc, endp)))
			{
			  sc->code = cdadr(sc->code);
			  goto DO_END_CLAUSES;
			}
		    }
		}
	    }
	}
	push_stack_no_args(sc, OP_DOX_STEP, sc->code);
	sc->code = cddr(sc->code);
	goto BEGIN1;
      }
	

      case OP_DOX_STEP:
	{
	  s7_pointer slot;

	  for (slot = let_slots(sc->envir); is_slot(slot); slot = next_slot(slot))
	    if (is_pair(slot_expression(slot)))
	      slot_set_value(slot, ((dox_function)fcdr(slot_expression(slot)))(sc, car(slot_expression(slot)), slot));

	  if (is_true(sc, ((s7_function)fcdr(cdr(sc->code)))(sc, fcdr(sc->code))))
	    {
	      sc->code = cdadr(sc->code);
	      goto DO_END_CLAUSES;
	    }
	  push_stack_no_args(sc, OP_DOX_STEP, sc->code);
	  sc->code = cddr(sc->code);
	  goto BEGIN1;
	}

      case OP_DOX_STEP_P:
	{
	  s7_pointer slot;

	  for (slot = let_slots(sc->envir); is_slot(slot); slot = next_slot(slot))
	    if (is_pair(slot_expression(slot)))
	      slot_set_value(slot, ((dox_function)fcdr(slot_expression(slot)))(sc, car(slot_expression(slot)), slot));

	  if (is_true(sc, ((s7_function)fcdr(cdr(sc->code)))(sc, fcdr(sc->code))))
	    {
	      sc->code = cdadr(sc->code);
	      goto DO_END_CLAUSES;
	    }
	  push_stack_no_args(sc, OP_DOX_STEP_P, sc->code);
	  sc->code = caddr(sc->code);
          sc->op = (opcode_t)pair_syntax_op(sc->code);
	  sc->code = cdr(sc->code);
	  goto START_WITHOUT_POP_STACK;
	}

    DO_ALL_X:
    case OP_DO_ALL_X:
      {
	long long int id;
	s7_pointer frame, vars, end, slot;

	NEW_FRAME(sc, sc->envir, frame); /* new frame is not tied into the symbol lookup process yet */
	for (vars = car(sc->code); is_pair(vars); vars = cdr(vars))
	  {
	    s7_pointer var;
	    NEW_CELL_NO_CHECK(sc, slot);
	    var = car(vars);
	    slot_symbol(slot) = car(var);
	    slot_set_value(slot, ((s7_function)fcdr(cdr(var)))(sc, cadr(var)));
	    slot_expression(slot) = cddr(var);
	    set_type(slot, T_SLOT | T_IMMUTABLE);
	    next_slot(slot) = let_slots(frame);
	    let_slots(frame) = slot;
	  }
	let_slots(frame) = reverse_slots(sc, let_slots(frame));
	sc->envir = frame;
	id = let_id(frame);
	for (slot = let_slots(frame); is_slot(slot); slot = next_slot(slot))
	  symbol_set_local(slot_symbol(slot), id, slot);
      
	end = cadr(sc->code);
	if (is_true(sc, ((s7_function)fcdr(end))(sc, car(end))))
	  {
	    sc->code = cdadr(sc->code);
	    goto DO_END_CLAUSES;
	  }
	push_stack_no_args(sc, OP_DO_ALL_X_STEP, sc->code);
	sc->code = cddr(sc->code);
	goto BEGIN1;
      }

    case OP_DO_ALL_X_STEP:
      {
	s7_pointer slot;
	/* not like DOX case because we can't assume vars are independent */

	for (slot = let_slots(sc->envir); is_slot(slot); slot = next_slot(slot))
	  if (is_pair(slot_expression(slot)))
	    slot_pending_value(slot) = ((s7_function)fcdr(slot_expression(slot)))(sc, car(slot_expression(slot)));

	for (slot = let_slots(sc->envir); is_slot(slot); slot = next_slot(slot))
	  if (is_pair(slot_expression(slot)))
	    slot_set_value(slot, slot_pending_value(slot));

	if (is_true(sc, ((s7_function)fcdr(cadr(sc->code)))(sc, caadr(sc->code))))
	  {
	    sc->code = cdadr(sc->code);
	    goto DO_END_CLAUSES;
	  }
	push_stack_no_args(sc, OP_DO_ALL_X_STEP, sc->code);
	sc->code = cddr(sc->code);
	goto BEGIN1;
      }
      

	/* we could use slot_pending_value, slot_expression, not this extra list, but the list seems simpler.
	 */
    #define DO_VAR_SLOT(P) ecdr(P)
    #define DO_VAR_NEW_VALUE(P) cdr(P)
    #define DO_VAR_STEP_EXPR(P) car(P)

    DO_STEP:
    case OP_DO_STEP:
      /* increment all vars, return to endtest 
       *   these are also updated in parallel at the end, so we gather all the incremented values first
       *
       * here we know car(sc->args) is not null, args is the list of steppable vars,
       *   any unstepped vars in the do var section are not in this list, so
       *   (do ((i 0 (+ i 1)) (j 2)) ...)
       *   arrives here with sc->args:
       *   '(((+ i 1) . 0))
       */
      push_stack(sc, OP_DO_END, sc->args, sc->code);
      sc->args = car(sc->args);                /* the var data lists */
      sc->code = sc->args;                     /* save the top of the list */

    DO_STEP1:
      /* on each iteration, each arg incr expr is evaluated and the value placed in caddr while we cdr down args
       * finally args is nil...
       */
      if (is_null(sc->args))
	{
	  s7_pointer x;

	  for (x = sc->code; is_not_null(x); x = cdr(x))
	    slot_set_value(DO_VAR_SLOT(car(x)), DO_VAR_NEW_VALUE(car(x)));

	  /* some schemes rebind here, rather than reset, but that is expensive,
	   *    and only matters once in a blue moon (closure over enclosed lambda referring to a do var)
	   *    and the caller can easily mimic the correct behavior in that case by adding a let or using a named let,
	   *    making the rebinding explicit.
	   *
	   * Hmmm... I'll leave this alone, but there are other less cut-and-dried cases:
	   *
	   *   (let ((j (lambda () 0))
	   *         (k 0))
	   *     (do ((i (j) (j))
	   *          (j (lambda () 1) (lambda () (+ i 1)))) ; bind here hits different "i" than set!
	   *         ((= i 3) k)
	   *       (set! k (+ k i))))
	   *
	   *   is it 6 or 3?
	   *
	   * if we had a way to tell that there were no lambdas in the do expression, would that
	   *   guarantee that set was ok?  Here's a bad case:
	   *
	   *   (let ((f #f))
	   *     (do ((i 0 (+ i 1)))
	   *         ((= i 3))
	   *       (let () ; so that the define is ok
	   *         (define (x) i)
	   *         (if (= i 1) (set! f x))))
	   *    (f))
	   *
	   * s7 says 3, guile says 1.
	   *
	   * I wonder if what they're actually talking about is a kind of shared value problem.  If we
	   *   set the value directly (not the cdr(binding) but, for example, integer(cdr(binding))), then
	   *   every previous reference gets changed as a side-effect.  In the current code, we're "binding" 
	   *   the value in the sense that on each step, a new value is assigned to the step variable.
	   *   In the "direct" case, (let ((v #(0 0 0))) (do ((i 0 (+ i 1))) ((= i 3) v) (set! (v i) i)) 
	   *   would return #(3 3 3).
	   *
	   * if sc->capture_env_counter changes, would it be sufficient to simply make a new slot?
	   *   I think not; the closure retains the current env chain, not the slots, so we need a new env.
	   */
	  
	  sc->value = sc->NIL;
	  pop_stack_no_op(sc); 
	  goto DO_END;
	}
      push_stack(sc, OP_DO_STEP2, sc->args, sc->code);
      
      /* here sc->args is a list like (((i . 0) (+ i 1) 0) ...)
       *   so sc->code becomes (+ i 1) in this case 
       */
      sc->code = DO_VAR_STEP_EXPR(car(sc->args));
      goto EVAL;
      

    case OP_DO_STEP2:
      DO_VAR_NEW_VALUE(car(sc->args)) = sc->value;            /* save current value */
      sc->args = cdr(sc->args);                               /* go to next step var */
      goto DO_STEP1;


    case OP_DO: 
      /* sc->code is the stuff after "do" */
      if (is_null(check_do(sc)))
	{
	  if (car(ecdr(sc->code)) == sc->SIMPLE_DO)
	    goto SIMPLE_DO;
	  if (car(ecdr(sc->code)) == sc->SIMPLE_DO_P)
	    goto SIMPLE_DO_P;
	  if (car(ecdr(sc->code)) == sc->SIMPLE_DO_A)
	    goto SIMPLE_DO_A;
	  if (car(ecdr(sc->code)) == sc->DOTIMES_P)
	    goto DOTIMES_P;
	  if (car(ecdr(sc->code)) == sc->SAFE_DOTIMES_C_A)
	    goto SAFE_DOTIMES_C_A;
	  if (car(ecdr(sc->code)) == sc->SAFE_DOTIMES_C_C)
	    goto SAFE_DOTIMES_C_C;
	  if (car(ecdr(sc->code)) == sc->SIMPLE_SAFE_DOTIMES)
	    goto SIMPLE_SAFE_DOTIMES;
	  if (car(ecdr(sc->code)) == sc->SAFE_DO)
	    goto SAFE_DO;
	  if (car(ecdr(sc->code)) == sc->DOX)
	    goto DOX;
	  if (car(ecdr(sc->code)) == sc->DO_ALL_X)
	    goto DO_ALL_X;
	  if (car(ecdr(sc->code)) == sc->SIMPLE_DO_FOREVER)
	    goto SIMPLE_DO_FOREVER;
	  goto SAFE_DOTIMES;
	}

    case OP_DO_UNCHECKED:
      if (is_null(car(sc->code)))                           /* (do () ...) -- (let ((i 0)) (do () ((= i 1)) (set! i 1))) */
	{
	  sc->envir = new_frame_in_env(sc, sc->envir); 
	  sc->args = cons_unchecked(sc, sc->NIL, cadr(sc->code));
	  sc->code = cddr(sc->code);
	  goto DO_END;
	}
      /* eval each init value, then set up the new frame (like let, not let*) */
      sc->args = sc->NIL;                             /* the evaluated var-data */
      sc->value = sc->code;                           /* protect it */
      sc->code = car(sc->code);                       /* the vars */
      

    DO_INIT:
    case OP_DO_INIT:
      sc->args = cons(sc, sc->value, sc->args);    /* code will be last element (first after reverse) */
      if (is_pair(sc->code))
	{
	  /* here sc->code is a list like: ((i 0 (+ i 1)) ...)
	   *   so cadar gets the init value.
	   *
	   * we accept:
	   *       (do ((i 1) (i 2)) (#t i)) -> 2
	   *       (do () (1) . "hi") -- this is like (do () (#t #t) (asdf))
	   */
	  s7_pointer init;
	  init = cadar(sc->code);
	  if (is_pair(init))
	    {
	      push_stack(sc, OP_DO_INIT, sc->args, cdr(sc->code));
	      sc->code = init;
	      goto EVAL; 
	    }
	  if (is_symbol(init))
	    sc->value = find_symbol_checked(sc, init);
	  else sc->value = init;
	  sc->code = cdr(sc->code);
	  goto DO_INIT;
	}

      /* all the initial values are now in the args list */
      {
	s7_pointer x, y, z;
	sc->args = safe_reverse_in_place(sc, sc->args);
	sc->code = car(sc->args);                       /* saved at the start */
	z = sc->args;
	sc->args = cdr(sc->args);                       /* init values */

	/* sc->envir = new_frame_in_env(sc, sc->envir); */
	/* sc->args was cons'd above, so it should be safe to reuse it as the new frame */
	sc->envir = old_frame_in_env(sc, z, sc->envir);
	
	/* run through sc->code and sc->args adding '( caar(car(code)) . car(args) ) to sc->envir,
	 *    also reuse the value cells as the new frame slots.
	 */
	sc->value = sc->NIL;
	y = sc->args;
	for (x = car(sc->code); is_not_null(y); x = cdr(x)) 
	  {
	    s7_pointer sym, args, val;
	    sym = caar(x);
	    val = car(y);
	    args = cdr(y);
	    
	    slot_symbol(y) = sym;
	    slot_set_value(y, val);
	    set_type(y, T_SLOT | T_IMMUTABLE);
	    next_slot(y) = let_slots(sc->envir);
	    let_slots(sc->envir) = y;
	    symbol_set_local(sym, let_id(sc->envir), y);

	    if (is_not_null(cddar(x)))                /* else no incr expr, so ignore it henceforth */
	      {
		s7_pointer p;
		p = cons(sc, caddar(x), val);
		set_ecdr(p, y);
					              /* val is just a place-holder -- this is where we store the new value */
		sc->value = cons_unchecked(sc, p, sc->value);
	      }
	    y = args;
	  }
	sc->args = cons(sc, sc->value = safe_reverse_in_place(sc, sc->value), cadr(sc->code));
	sc->code = cddr(sc->code);
	
	/* here args is a list of 2 or 3 lists, first is (list (list (var . binding) incr-expr init-value) ...), second is end-expr, third can be result expr
	 *   so for (do ((i 0 (+ i 1))) ((= i 3) (+ i 1)) ...) args is ((((i . 0) (+ i 1) 0 #f)) (= i 3) (+ i 1))
	 */
      }


    DO_END:
    case OP_DO_END:
      /* here vars have been init'd or incr'd
       *    args = (list var-data end-expr return-expr-if-any)
       *      if (do ((i 0 (+ i 1))) ((= i 3) 10)),            args: (vars (= i 3) 10)
       *      if (do ((i 0 (+ i 1))) ((= i 3))),               args: (vars (= i 3)) and result expr is () == (begin)
       *      if (do ((i 0 (+ i 1))) (#t 10 12)),              args: (vars #t 10 12), result: ([begin] 10 12) -> 12 
       *      if (call-with-exit (lambda (r) (do () () (r)))), args: '(())
       *    code = body
       */

      if (is_not_null(cdr(sc->args)))
	{
	  push_stack(sc, OP_DO_END1, sc->args, sc->code);
	  sc->code = cadr(sc->args);               /* evaluate the end expr */
	  goto EVAL;
	}
      else 
	{
	  /* (do ((...)) () ...) -- no endtest */
	  if (is_pair(sc->code))
	    {
	      if (is_null(car(sc->args)))
		push_stack(sc, OP_DO_END, sc->args, sc->code);
	      else push_stack(sc, OP_DO_STEP, sc->args, sc->code);
	      goto BEGIN1;
	    }
	  else
	    {
	      /* no body? */
	      if (is_null(car(sc->args)))
		goto DO_END;
	      goto DO_STEP;
	    }
	}


    case OP_DO_END1:
      /* sc->value is the result of end-test evaluation */
      if (is_true(sc, sc->value))
	{
	  /* we're done -- deal with result exprs 
	   *   if there isn't an end test, there also isn't a result (they're in the same list)
	   */
	  sc->code = cddr(sc->args);                /* result expr (a list -- implicit begin) */
	  
	  clear_type(sc->args);
	  (*(sc->free_heap_top++)) = sc->args;
	  sc->args = sc->NIL;

	  if (is_null(sc->code))
	    {
	      sc->value = sc->NIL;
	      goto START;
	    }
	}
      else
	{
	  /* evaluate the body and step vars, etc */
	  if (is_null(car(sc->args)))
	    push_stack(sc, OP_DO_END, sc->args, sc->code);
	  else push_stack(sc, OP_DO_STEP, sc->args, sc->code);
	  /* sc->code is ready to go */
	}
      goto BEGIN1;

      
    SAFE_DO_END_CLAUSES:
      if (is_null(sc->code)) 
	{
	  /* sc->args = sc->NIL; */
	  sc->envir = free_let(sc, sc->envir);
	  sc->value = sc->NIL;
	  goto START;
	}
      goto DO_END_CODE;
      
    DO_END_CLAUSES:
      if (is_null(sc->code)) 
	{
	  sc->value = sc->NIL;
	  goto START;
	}
    DO_END_CODE:
      if (is_pair(cdr(sc->code)))
	{
	  push_stack_no_args(sc, OP_BEGIN1, cdr(sc->code));
	  sc->code = car(sc->code);
	  goto EVAL;
	}
      sc->code = car(sc->code);
      if (is_pair(sc->code))
	goto EVAL;
      if (is_symbol(sc->code))
	sc->value = find_symbol_checked(sc, sc->code);
      else sc->value = sc->code;
      goto START;
      

      /* -------------------------------- BEGIN -------------------------------- */
    POP_BEGIN:
      {
	s7_pointer code;
	sc->envir = main_stack_let(sc);
	code = main_stack_code(sc); 
	sc->code =  car(code); 
	if (is_null(cdr(code)))  /* about 1/2 of these are symbols/#f but checking here costs more than it saves */
	  pop_main_stack(sc);                 
	else main_stack_code(sc) = cdr(code); 
	goto EVAL;
      }


   case OP_BEGIN:
      if (!is_proper_list(sc, sc->code)) /* proper list includes nil, I think */
	eval_error_with_name(sc, "~A: unexpected dot? ~A", sc->code);

      if ((!is_null(cdr(sc->code))) &&
	  (is_overlaid(sc->code)) &&
	  (cdr(ecdr(sc->code)) == sc->code))
	pair_set_syntax_symbol(sc->code, sc->BEGIN_UNCHECKED);

    case OP_BEGIN_UNCHECKED:
      if ((sc->begin_hook) && (call_begin_hook(sc))) return(sc->F);
      if (is_null(sc->code))                   /* (begin) -> () */
	{
	  sc->value = sc->NIL;
	  goto START;
	}

    BEGIN1:
    case OP_BEGIN1:
      if (is_pair(cdr(sc->code)))
	push_stack_no_args(sc, OP_BEGIN1, cdr(sc->code));
      sc->code = car(sc->code);
      /* goto EVAL; */


    EVAL:
    case OP_EVAL: 
      /* main part of evaluation 
       *   at this point, it's sc->code we care about; sc->args is not relevant.
       */
      /* fprintf(stderr, "    eval: %s\n", DISPLAY_80(sc->code)); */
      if (typesflag(sc->code) == SYNTACTIC_PAIR)  /* xor is not faster here */
	{
	  sc->cur_code = sc->code;                /* in case an error occurs, this helps tell us where we are */
	  sc->op = (opcode_t)pair_syntax_op(sc->code);
	  sc->code = cdr(sc->code);
	  goto START_WITHOUT_POP_STACK;
	  /* it is only slightly faster to use labels as values (computed gotos) here
	   */
	}

      if (is_optimized(sc->code)) 
	{
	  s7_pointer code;

	OPT_EVAL:
	  code = sc->code;
	  sc->cur_code = code;
	  /* fprintf(stderr, "opt eval: %s %s %s\n", opt_names[optimize_op(code)], DISPLAY_80(code), describe_type_bits(sc, car(code))); */

	  switch (optimize_op(code))
	    {
	      /* -------------------------------------------------------------------------------- */
	    case OP_SAFE_C_C: 
	      if (!c_function_is_ok(sc, code)) break;

	    case HOP_SAFE_C_C:
	      sc->value = c_call(code)(sc, cdr(code)); /* this includes all safe calls where all args are constants */
	      goto START;

	      
	    case OP_SAFE_C_Q:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_Q:
	      car(sc->T1_1) = cadr(cadr(code));
	      sc->value = c_call(code)(sc, sc->T1_1);
	      goto START;
	      
	      
	    case OP_SAFE_C_S:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_S:
	      car(sc->T1_1) = find_symbol_checked(sc, cadr(code));
	      sc->value = c_call(code)(sc, sc->T1_1);
	      goto START;
	      
	      
	    case OP_SAFE_C_SS:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_SS:
	      {
		s7_pointer val, args;
		args = cdr(code);
		val = find_symbol_checked(sc, car(args));
		car(sc->T2_2) = find_symbol_checked(sc, cadr(args));
		car(sc->T2_1) = val;
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START; 
	      }
	      

	    case OP_SAFE_C_ALL_S:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_ALL_S:
	      {
		int num_args;
		s7_pointer args, p;

		num_args = integer(arglist_length(code));
		if ((num_args != 0) &&
		    (num_args < NUM_SAFE_LISTS) &&
		    (!list_is_in_use(sc->safe_lists[num_args])))
		  {
		    sc->args = sc->safe_lists[num_args];
		    set_list_in_use(sc->args);
		  }
		else sc->args = make_list(sc, num_args, sc->NIL);
		
		for (args = cdr(code), p = sc->args; is_pair(args); args = cdr(args), p = cdr(p))
		  car(p) = find_symbol_checked(sc, car(args));
		clear_list_in_use(sc->args);
		sc->value = c_call(code)(sc, sc->args);
		goto START;
	      }

	      
	    case OP_SAFE_C_SC:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    SAFE_C_SC:
	    case HOP_SAFE_C_SC:
	      {
		s7_pointer args;
		args = cdr(code);
		car(sc->T2_1) = find_symbol_checked(sc, car(args));
		car(sc->T2_2) = cadr(args);
		sc->value = c_call(code)(sc, sc->T2_1);
		/* IF_BEGIN_POP_STACK(sc); */
		if (main_stack_op(sc) == OP_BEGIN1)
		  {
		    sc->envir = main_stack_let(sc);
		    code = main_stack_code(sc); 
		    sc->code = car(code); 
		    if (is_null(cdr(code))) 
		      pop_main_stack(sc);                 
		    else main_stack_code(sc) = cdr(code); 
		    if (is_optimized(sc->code))
		      {
			if (optimize_op(sc->code) == HOP_SAFE_C_SC)
			  {
			    code = sc->code;
			    goto SAFE_C_SC;
			  }
			goto OPT_EVAL;
		      }
		    goto EVAL;
		  }
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_CS:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_CS:
	      {
		s7_pointer args;
		args = cdr(code);
		car(sc->T2_2) = find_symbol_checked(sc, cadr(args));
		car(sc->T2_1) = car(args);
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_SQ:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_SQ:
	      {
		s7_pointer args;
		args = cdr(code);
		car(sc->T2_1) = find_symbol_checked(sc, car(args));
		car(sc->T2_2) = cadr(cadr(args));
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_QS:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_QS:
	      {
		s7_pointer args;
		args = cdr(code);
		car(sc->T2_2) = find_symbol_checked(sc, cadr(args));
		car(sc->T2_1) = cadr(car(args));
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_QQ:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_QQ:
	      {
		s7_pointer args;
		args = cdr(code);
		car(sc->T2_1) = cadr(car(args));
		car(sc->T2_2) = cadr(cadr(args));
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_CQ:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_CQ:
	      {
		s7_pointer args;
		args = cdr(code);
		car(sc->T2_1) = car(args);
		car(sc->T2_2) = cadr(cadr(args));
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_QC:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_QC:
	      {
		s7_pointer args;
		args = cdr(code);
		car(sc->T2_1) = cadr(car(args));
		car(sc->T2_2) = cadr(args);
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }


	    case OP_SAFE_C_Z:
	      if (!c_function_is_ok(sc, code)) break;
	      /* I think a_is_ok of cadr here and below is redundant -- they'll be checked when Z is 
	       *    because we cleared the hop bit after combine_ops.
	       */
	      
	    case HOP_SAFE_C_Z:
	      push_stack(sc, OP_SAFE_C_P_1, sc->NIL, code);
	      sc->code = cadr(code);
	      goto OPT_EVAL;
	      
	      
	    case OP_SAFE_C_CZ:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_CZ:
	      push_stack(sc, OP_SAFE_C_SZ_1, cadr(code), code);
	      sc->code = caddr(code);
	      goto OPT_EVAL;
	      
	      
	    case OP_SAFE_C_ZC:
	      if (!c_function_is_ok(sc, code)) break;

	    case HOP_SAFE_C_ZC:
	      push_stack(sc, OP_SAFE_C_ZS_1, caddr(code), code);
	      sc->code = cadr(code);
	      goto OPT_EVAL;
	      
	      
	    case OP_SAFE_C_SZ:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_SZ:
	      push_stack(sc, OP_SAFE_C_SZ_1, find_symbol_checked(sc, cadr(code)), code);
	      sc->code = caddr(code);
	      /* splitting out the all_x cases here and elsewhere saves nothing */
	      goto OPT_EVAL;
	      
	      
	    case OP_SAFE_C_ZS:
	      if (!a_is_ok_cadr(sc, code)) break;
	      
	    case HOP_SAFE_C_ZS:
	      push_stack(sc, OP_SAFE_C_ZS_1, find_symbol_checked(sc, caddr(code)), code);
	      sc->code = cadr(code);
	      goto OPT_EVAL;
	      /* b: h_safe_c_css: 166564, s: h_safe_c_opcq_s: 186404 h_safe_c_opssq: 138728
	       */

	      
	    case OP_SAFE_C_opAq:
	      if (!a_is_ok_cadr(sc, code)) break;
	      
	    case HOP_SAFE_C_opAq:
	      {
		s7_pointer arg;
		arg = cadr(code);
		car(sc->A1_1) = ((s7_function)fcdr(cdr(arg)))(sc, cadr(arg));
		car(sc->T1_1) = c_call(arg)(sc, sc->A1_1);
		sc->value = c_call(code)(sc, sc->T1_1);
		goto START;
	      }


	    case OP_SAFE_C_opAAq:
	      if (!a_is_ok_cadr(sc, code)) break;
	      
	    case HOP_SAFE_C_opAAq:
	      {
		s7_pointer arg;
		arg = cadr(code);
		car(sc->A2_1) = ((s7_function)fcdr(cdr(arg)))(sc, cadr(arg));
		car(sc->A2_2) = ((s7_function)fcdr(cddr(arg)))(sc, caddr(arg));
		car(sc->T1_1) = c_call(arg)(sc, sc->A2_1);
		sc->value = c_call(code)(sc, sc->T1_1);
		goto START;
	      }


	    case OP_SAFE_C_opAAAq:
	      if (!a_is_ok_cadr(sc, code)) break;
	      
	    case HOP_SAFE_C_opAAAq:
	      {
		s7_pointer arg;
		arg = cadr(code);
		car(sc->A3_1) = ((s7_function)fcdr(cdr(arg)))(sc, cadr(arg));
		car(sc->A3_2) = ((s7_function)fcdr(cddr(arg)))(sc, caddr(arg));
		car(sc->A3_3) = ((s7_function)fcdr(cdddr(arg)))(sc, cadddr(arg));
		car(sc->T1_1) = c_call(arg)(sc, sc->A3_1);
		sc->value = c_call(code)(sc, sc->T1_1);
		goto START;
	      }


	    case OP_SAFE_C_S_opAAq:
	      if (!a_is_ok_caddr(sc, code)) break;
	      
	    case HOP_SAFE_C_S_opAAq:
	      {
		s7_pointer arg;
		arg = caddr(code);
		car(sc->A2_1) = ((s7_function)fcdr(cdr(arg)))(sc, cadr(arg));
		car(sc->A2_2) = ((s7_function)fcdr(cddr(arg)))(sc, caddr(arg));
		car(sc->T2_2) = c_call(arg)(sc, sc->A2_1);
		car(sc->T2_1) = find_symbol_checked(sc, cadr(code));
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }


	    case OP_SAFE_C_S_opAAAq:
	      if (!a_is_ok_caddr(sc, code)) break;
	      
	    case HOP_SAFE_C_S_opAAAq:
	      {
		s7_pointer arg, p;
		p = caddr(code);
		arg = cdr(p);
		car(sc->A3_1) = ((s7_function)fcdr(arg))(sc, car(arg));
		arg = cdr(arg);
		car(sc->A3_2) = ((s7_function)fcdr(arg))(sc, car(arg));
		arg = cdr(arg);
		car(sc->A3_3) = ((s7_function)fcdr(arg))(sc, car(arg));
		car(sc->T2_2) = c_call(p)(sc, sc->A3_1);
		car(sc->T2_1) = find_symbol_checked(sc, cadr(code));
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      

	    case OP_SAFE_C_S_opSZq:
	      if (!a_is_ok_caddr(sc, code)) break;
	      
	    case HOP_SAFE_C_S_opSZq:
	      push_stack(sc, OP_SAFE_C_SZ_SZ, find_symbol_checked(sc, cadr(caddr(code))), code);
	      sc->code = caddr(caddr(code));
	      goto OPT_EVAL;
	      
	      
	    case OP_SAFE_C_AZ:
	      if (!a_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_AZ:
	      push_stack(sc, OP_SAFE_C_SZ_1, ((s7_function)fcdr(cdr(code)))(sc, cadr(code)), code);
	      sc->code = caddr(code);
	      goto OPT_EVAL;
	      /* s: h_safe_c_s_op_s_opssqq: 204308 */
	      
	      
	    case OP_SAFE_C_ZA:
	      if (!a_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_ZA:
	      /* here we can't use ZS order because we sometimes assume left->right arg evaluation (binary-io.scm for example) */
	      push_stack(sc, OP_SAFE_C_ZA_1, sc->NIL, code);
	      sc->code = cadr(code);
	      goto OPT_EVAL;
	      /* b: h_safe_c_c_op_s_opcqq: 400000 */

	      
	    case OP_SAFE_C_ZZ:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_ZZ:
	      /* most of the component Z's here are very complex:
	       *    264600: (+ (* even-amp (oscil (vector-ref evens k) (+ even-freq val))) (* odd-amp...
	       */
	      push_stack(sc, OP_SAFE_C_ZZ_1, sc->NIL, code);
	      sc->code = cadr(code);
	      goto OPT_EVAL;
	      
	      
	    case OP_SAFE_C_opCq_Z:
	      if (!a_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_opCq_Z:
	      push_stack(sc, OP_SAFE_C_ZZ_2, c_call(cadr(code))(sc, cdr(cadr(code))), code);
	      sc->code = caddr(code);
	      goto OPT_EVAL;

	      
	    case OP_SAFE_C_ZAA:
	      if (!a_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_ZAA:
	      push_stack(sc, OP_SAFE_C_ZAA_1, sc->NIL, code);
	      sc->code = cadr(code);
	      goto OPT_EVAL;
	      
	      
	    case OP_SAFE_C_AZA:
	      if (!a_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_AZA:
	      push_stack(sc, OP_SAFE_C_AZA_1, ((s7_function)fcdr(cdr(code)))(sc, cadr(code)), code);
	      sc->code = caddr(code);
	      goto OPT_EVAL;
	      
	      
	    case OP_SAFE_C_SSZ:
	      if (!a_is_ok_cadddr(sc, code)) break;
	      
	    case HOP_SAFE_C_SSZ:
	      push_stack(sc, OP_SAFE_C_SSZ_1, find_symbol_checked(sc, cadr(code)), code);
	      sc->code = cadddr(code);
	      goto OPT_EVAL;
	      
	      
	    case OP_SAFE_C_AAZ:
	      if (!a_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_AAZ:
	      push_op_stack(sc, ((s7_function)fcdr(cdr(code)))(sc, cadr(code)));
	      push_stack(sc, OP_SAFE_C_AAZ_1, ((s7_function)fcdr(cddr(code)))(sc, caddr(code)), code);
	      sc->code = cadddr(code);
	      goto OPT_EVAL;
	      
	      
	    case OP_SAFE_C_ZZA:
	      if (!a_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_ZZA:
	      push_stack(sc, OP_SAFE_C_ZZA_1, sc->NIL, code);
	      sc->code = cadr(code);
	      goto OPT_EVAL;
	      
	      
	    case OP_SAFE_C_ZAZ:
	      if (!a_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_ZAZ:
	      push_stack(sc, OP_SAFE_C_ZAZ_1, sc->NIL, code);
	      sc->code = cadr(code);
	      goto OPT_EVAL;
	      
	      
	    case OP_SAFE_C_AZZ:
	      if (!a_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_AZZ:
	      push_stack(sc, OP_SAFE_C_AZZ_1, ((s7_function)fcdr(cdr(code)))(sc, cadr(code)), code);
	      sc->code = caddr(code);
	      goto OPT_EVAL;
	      
	      
	    case OP_SAFE_C_ZZZ:
	      if (!a_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_ZZZ:
	      push_stack(sc, OP_SAFE_C_ZZZ_1, sc->NIL, code);
	      sc->code = cadr(code);
	      goto OPT_EVAL;


	    case OP_SAFE_C_ZZZZ:
	      if (!a_is_ok(sc, code)) break;

	    case HOP_SAFE_C_ZZZZ:
	      push_stack(sc, OP_SAFE_C_ZZZZ_1, sc->NIL, code);
	      sc->code = cadr(code);
	      goto OPT_EVAL;

	      
	    case OP_SAFE_C_A:
	      if (!a_is_ok_cadr(sc, code)) break;

	    case HOP_SAFE_C_A:
	      car(sc->A1_1) = ((s7_function)fcdr(cdr(code)))(sc, cadr(code));
	      sc->value = c_call(code)(sc, sc->A1_1);
	      goto START;


	    case OP_SAFE_C_AA:
	      if (!a_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_AA:
	      car(sc->A2_1) = ((s7_function)fcdr(cdr(code)))(sc, cadr(code));
	      car(sc->A2_2) = ((s7_function)fcdr(cddr(code)))(sc, caddr(code));
	      sc->value = c_call(code)(sc, sc->A2_1);
	      goto START;


	    case OP_SAFE_C_AAA:
	      if (!a_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_AAA:
	      {
		s7_pointer arg;
		arg = cdr(code);
		car(sc->A3_1) = ((s7_function)fcdr(arg))(sc, car(arg));
		arg = cdr(arg);
		car(sc->A3_2) = ((s7_function)fcdr(arg))(sc, car(arg));
		arg = cdr(arg);
		car(sc->A3_3) = ((s7_function)fcdr(arg))(sc, car(arg));
		sc->value = c_call(code)(sc, sc->A3_1);
		goto START;
	      }


	    case OP_SAFE_C_SSA:
	      if (!a_is_ok_cadddr(sc, code)) break;
	      
	    case HOP_SAFE_C_SSA:
	      {
		s7_pointer arg;
		arg = cdr(code);
		car(sc->A3_1) = find_symbol_checked(sc, car(arg));
		arg = cdr(arg);
		car(sc->A3_2) = find_symbol_checked(sc, car(arg));
		arg = cdr(arg);
		car(sc->A3_3) = ((s7_function)fcdr(arg))(sc, car(arg));
		sc->value = c_call(code)(sc, sc->A3_1);
		goto START;
	      }


	    case OP_SAFE_C_CSA:
	      if (!a_is_ok_cadddr(sc, code)) break;
	      
	    case HOP_SAFE_C_CSA:
	      {
		s7_pointer arg;
		arg = cdr(code);
		car(sc->A3_1) = car(arg);
		arg = cdr(arg);
		car(sc->A3_2) = find_symbol_checked(sc, car(arg));
		arg = cdr(arg);
		car(sc->A3_3) = ((s7_function)fcdr(arg))(sc, car(arg));
		sc->value = c_call(code)(sc, sc->A3_1);
		goto START;
	      }


	    case OP_SAFE_C_SCA:
	      if (!a_is_ok_cadddr(sc, code)) break;
	      
	    case HOP_SAFE_C_SCA:
	      {
		s7_pointer arg;
		arg = cdr(code);
		car(sc->A3_1) = find_symbol_checked(sc, car(arg));
		arg = cdr(arg);
		car(sc->A3_2) = car(arg);
		arg = cdr(arg);
		car(sc->A3_3) = ((s7_function)fcdr(arg))(sc, car(arg));
		sc->value = c_call(code)(sc, sc->A3_1);
		goto START;
	      }


	    case OP_SAFE_C_CAS:
	      if (!a_is_ok_caddr(sc, code)) break;
	      
	    case HOP_SAFE_C_CAS:
	      {
		s7_pointer arg;
		arg = cdr(code);
		car(sc->A3_1) = car(arg);
		arg = cdr(arg);
		car(sc->A3_2) = ((s7_function)fcdr(arg))(sc, car(arg));
		car(sc->A3_3) = find_symbol_checked(sc, cadr(arg));
		sc->value = c_call(code)(sc, sc->A3_1);
		goto START;
	      }


	    case OP_SAFE_C_AAAA:
	      if (!a_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_AAAA:
	      {
		s7_pointer arg;
		arg = cdr(code);
		car(sc->A4_1) = ((s7_function)fcdr(arg))(sc, car(arg));
		arg = cdr(arg);
		car(sc->A4_2) = ((s7_function)fcdr(arg))(sc, car(arg));
		arg = cdr(arg);
		car(sc->A4_3) = ((s7_function)fcdr(arg))(sc, car(arg));
		arg = cdr(arg);
		car(sc->A4_4) = ((s7_function)fcdr(arg))(sc, car(arg));
		sc->value = c_call(code)(sc, sc->A4_1);
		goto START;
	      }


	    case OP_SAFE_C_ALL_X:
	      if (!a_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_ALL_X:
	      {
		int num_args;
		s7_pointer args, p;

		num_args = integer(arglist_length(code));
		if ((num_args != 0) &&
		    (num_args < NUM_SAFE_LISTS) &&
		    (!list_is_in_use(sc->safe_lists[num_args])))
		  {
		    sc->args = sc->safe_lists[num_args];
		    set_list_in_use(sc->args);
		  }
		else sc->args = make_list(sc, num_args, sc->NIL);

		for (args = cdr(code), p = sc->args; is_pair(args); args = cdr(args), p = cdr(p))
		  car(p) = ((s7_function)fcdr(args))(sc, car(args));
		clear_list_in_use(sc->args);
		
		sc->value = c_call(code)(sc, sc->args);
		/* we can't release a temp here:
		 *   (define (hi) (vector 14800 14020 (oscil os) (* 1/3 14800) 14800 (* 1/2 14800))) (hi) where os returns non-zero:
		 *   #(14800 14020 <output-string-port> 14800/3 14800 7400)
		 */
		goto START;
	      }

	      
	    case OP_SAFE_C_SQS:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_SQS:
	      {
		/* (let-set! gen 'fm fm);  many of these are handled in safe_closure_star_s0 */
		s7_pointer val1, args;
		args = cdr(code);
		val1 = find_symbol_checked(sc, car(args));
		car(sc->T3_3) = find_symbol_checked(sc, fcdr(args));
		car(sc->T3_2) = ecdr(args);
		car(sc->T3_1) = val1;
		sc->value = c_call(code)(sc, sc->T3_1);
		IF_BEGIN_POP_STACK(sc);
	      }
	      

	    case OP_SAFE_C_SCS:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_SCS:
	      {
		/* (define (hi) (let ((x 32) (lst '(0 1))) (list-set! lst 0 x) x)) */
		s7_pointer val1, args;
		args = cdr(code);
		
		val1 = find_symbol_checked(sc, car(args));
		car(sc->T3_3) = find_symbol_checked(sc, fcdr(args));
		car(sc->T3_2) = ecdr(args);
		car(sc->T3_1) = val1;
		sc->value = c_call(code)(sc, sc->T3_1);
		/* goto START; */
		IF_BEGIN_POP_STACK(sc);
	      }
	      

	    case OP_SAFE_C_SSC:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_SSC:
	      {
		/* (define (hi) (let ((v #(0 1 2)) (i 0)) (vector-set! v i 1) v)) */
		s7_pointer val1, args;
		args = cdr(code);
		
		val1 = find_symbol_checked(sc, car(args));
		car(sc->T3_2) = find_symbol_checked(sc, ecdr(args));
		car(sc->T3_3) = fcdr(args);
		car(sc->T3_1) = val1;
		sc->value = c_call(code)(sc, sc->T3_1);
		IF_BEGIN_POP_STACK(sc);
	      }
	      

	    case OP_SAFE_C_SCC:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_SCC:
	      {
		/* (make-env E :length 100) */
		s7_pointer args;
		args = cdr(code);
		
		car(sc->T3_1) = find_symbol_checked(sc, car(args));
		car(sc->T3_2) = ecdr(args);
		car(sc->T3_3) = fcdr(args);
		sc->value = c_call(code)(sc, sc->T3_1);
		goto START;
	      }
	      

	    case OP_SAFE_C_CSC:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_CSC:
	      {
		s7_pointer args;
		args = cdr(code);
		
		car(sc->T3_2) = find_symbol_checked(sc, ecdr(args));
		car(sc->T3_1) = car(args);
		car(sc->T3_3) = fcdr(args);
		sc->value = c_call(code)(sc, sc->T3_1);
		goto START;
	      }
	      

	    case OP_SAFE_C_CSS:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_CSS:
	      {
		s7_pointer val1, args;
		args = cdr(code);
		
		val1 = find_symbol_checked(sc, fcdr(args));
		car(sc->T3_2) = find_symbol_checked(sc, ecdr(args));
		car(sc->T3_3) = val1;
		car(sc->T3_1) = car(args);
		sc->value = c_call(code)(sc, sc->T3_1);
		goto START;
	      }

	    case OP_SAFE_C_SSS:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_SSS:
	      {
		s7_pointer val1, val2, args;
		args = cdr(code);
		
		val1 = find_symbol_checked(sc, car(args));
		val2 = find_symbol_checked(sc, ecdr(args));
		car(sc->T3_3) = find_symbol_checked(sc, fcdr(args));
		car(sc->T3_1) = val1;
		car(sc->T3_2) = val2;
		sc->value = c_call(code)(sc, sc->T3_1);
		goto START;
	      }
	      

	    case OP_SAFE_C_opCq:
	      if (!c_function_is_ok_cadr(sc, code)) break;
	      
	    case HOP_SAFE_C_opCq:
	      car(sc->T1_1) = c_call(car(cdr(code)))(sc, cdar(cdr(code))); /* OP_SAFE_C_C can involve any number of ops */
	      sc->value = c_call(code)(sc, sc->T1_1);
	      goto START;
	      

	    case OP_SAFE_C_opSq:
	      if (!c_function_is_ok_cadr(sc, code)) break;
	      
	    case HOP_SAFE_C_opSq:
	      {
		s7_pointer args;
		args = cadr(code);
		car(sc->T1_1) = find_symbol_checked(sc, cadr(args));
		car(sc->T1_1) = c_call(args)(sc, sc->T1_1);
		sc->value = c_call(code)(sc, sc->T1_1);
		goto START;
	      }
	      
	    case OP_SAFE_C_op_opSq_q:
	      if ((!c_function_is_ok(sc, code)) || (!c_function_is_ok(sc, cadr(code))) || (!c_function_is_ok(sc, cadr(cadr(code))))) break;
	      
	    case HOP_SAFE_C_op_opSq_q:
	      {
		s7_pointer outer, args;
		outer = cadr(code);
		args = cadr(outer);
		car(sc->T1_1) = find_symbol_checked(sc, cadr(args));
		car(sc->T1_1) = c_call(args)(sc, sc->T1_1);
		car(sc->T1_1) = c_call(outer)(sc, sc->T1_1);
		sc->value = c_call(code)(sc, sc->T1_1);
		goto START;
	      }
	      
	    case OP_SAFE_C_op_S_opSq_q:
	      if ((!c_function_is_ok(sc, code)) || (!c_function_is_ok(sc, cadr(code))) || (!c_function_is_ok(sc, caddr(cadr(code))))) break;
	      
	    case HOP_SAFE_C_op_S_opSq_q:
	      {
		/* (exp (* r (cos x))) */
		s7_pointer outer, args;
		outer = cadr(code);
		args = caddr(outer);
		car(sc->T1_1) = find_symbol_checked(sc, cadr(args));
		car(sc->T2_2) = c_call(args)(sc, sc->T1_1);
		car(sc->T2_1) = find_symbol_checked(sc, cadr(outer));
		car(sc->T1_1) = c_call(outer)(sc, sc->T2_1);
		sc->value = c_call(code)(sc, sc->T1_1);
		goto START;
	      }
	      
	    case OP_SAFE_C_P:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_P:
	      push_stack(sc, OP_EVAL_ARGS_P_1, sc->NIL, code); /* catch values etc */
	      sc->code = cadr(code);
	      goto EVAL;  
	      
	      
	    case OP_SAFE_C_PS:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_PS:
	      push_stack(sc, OP_EVAL_ARGS_P_3, sc->NIL, code); /* gotta wait in this case */
	      sc->code = cadr(code);
	      goto EVAL;  
	      
	      
	    case OP_SAFE_C_PC:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_PC:
	      push_stack(sc, OP_EVAL_ARGS_P_4, caddr(code), code);
	      sc->code = cadr(code);
	      goto EVAL;  
	      
	      
	    case OP_SAFE_C_PQ:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_PQ:
	      push_stack(sc, OP_EVAL_ARGS_P_4, cadr(caddr(code)), code); /* was P_5, but that's the same as P_4 */
	      sc->code = cadr(code);
	      goto EVAL;  
	      
	      
	    case OP_SAFE_C_SP:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_SP:
	      push_stack(sc, OP_EVAL_ARGS_P_2, find_symbol_checked(sc, cadr(code)), code);
	      sc->code = caddr(code);
	      goto EVAL;  
	      
	      
	    case OP_SAFE_C_AP:
	      if ((!c_function_is_ok(sc, code)) || (!a_is_ok(sc, cadr(code)))) break;
	      
	    case HOP_SAFE_C_AP:
	      push_stack(sc, OP_EVAL_ARGS_P_2, ((s7_function)fcdr(cdr(code)))(sc, cadr(code)), code);
	      sc->code = caddr(code);
	      goto EVAL;  
	      
	      
	    case OP_SAFE_C_CP:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_CP:
	      push_stack(sc, OP_EVAL_ARGS_P_2, cadr(code), code);
	      sc->code = caddr(code);
	      goto EVAL; 
	      
	      
	    case OP_SAFE_C_QP:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_QP:
	      push_stack(sc, OP_EVAL_ARGS_P_2, cadr(cadr(code)), code);
	      sc->code = caddr(code);
	      goto EVAL; 
	      
	      
	    case OP_SAFE_C_PP:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_PP:
	      push_stack(sc, OP_SAFE_C_PP_1, sc->NIL, code);
	      sc->code = cadr(code);
	      goto EVAL; 
	      
	      
	    case OP_SAFE_C_SSP:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_SSP:
	      push_stack(sc, OP_EVAL_ARGS_SSP_1, sc->NIL, code);
	      sc->code = cadddr(code);
	      goto EVAL;  
	      

	    case OP_SAFE_C_opSSq:
	      if (!c_function_is_ok_cadr(sc, code)) break;
	      
	    case HOP_SAFE_C_opSSq:
	      {
		s7_pointer args, val1;
		args = cadr(code);
		val1 = find_symbol_checked(sc, cadr(args));
		car(sc->T2_2) = find_symbol_checked(sc, caddr(args));
		car(sc->T2_1) = val1;
		car(sc->T1_1) = c_call(args)(sc, sc->T2_1);
		sc->value = c_call(code)(sc, sc->T1_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_opSCq:
	      if (!c_function_is_ok_cadr(sc, code)) break;
	      
	    case HOP_SAFE_C_opSCq:
	      {
		s7_pointer args;
		args = cadr(code);
		car(sc->T2_1) = find_symbol_checked(sc, cadr(args));
		car(sc->T2_2) = caddr(args);
		car(sc->T1_1) = c_call(args)(sc, sc->T2_1);
		sc->value = c_call(code)(sc, sc->T1_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_opCSq:
	      if (!c_function_is_ok_cadr(sc, code)) break;
	      
	    case HOP_SAFE_C_opCSq:
	      {
		s7_pointer args;
		args = cadr(code);
		car(sc->T2_2) = find_symbol_checked(sc, caddr(args));
		car(sc->T2_1) = cadr(args);
		car(sc->T1_1) = c_call(args)(sc, sc->T2_1);
		sc->value = c_call(code)(sc, sc->T1_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_opSQq:
	      if (!c_function_is_ok_cadr(sc, code)) break;
	      
	    case HOP_SAFE_C_opSQq:
	      {
		s7_pointer args;
		args = cadr(code);
		car(sc->T2_1) = find_symbol_checked(sc, cadr(args));
		car(sc->T2_2) = cadr(caddr(args)); 
		car(sc->T1_1) = c_call(args)(sc, sc->T2_1);
		sc->value = c_call(code)(sc, sc->T1_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_S_opSq:
	      if (!c_function_is_ok_caddr(sc, code)) break;
	      
	    case HOP_SAFE_C_S_opSq:
	      {
		s7_pointer args, val;
		args = cdr(code);
		val = find_symbol_checked(sc, car(args));
		car(sc->T1_1) = find_symbol_checked(sc, ecdr(args));
		car(sc->T2_2) = c_call(cadr(args))(sc, sc->T1_1);
		car(sc->T2_1) = val;
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	    case OP_SAFE_C_S_opCq: 
	      if (!c_function_is_ok_caddr(sc, code))break;
	      
	    case HOP_SAFE_C_S_opCq: 
	      {
		s7_pointer args, val;
		args = cdr(code);
		val = find_symbol_checked(sc, car(args));
		car(sc->T2_2) = c_call(cadr(args))(sc, ecdr(args)); /* any number of constants here */
		car(sc->T2_1) = val;
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_C_opSq:
	      if (!c_function_is_ok_caddr(sc, code)) break;
	      
	    case HOP_SAFE_C_C_opSq:
	      {
		s7_pointer args;
		args = cdr(code);
		car(sc->T1_1) = find_symbol_checked(sc, ecdr(args));
		car(sc->T2_2) = c_call(cadr(args))(sc, sc->T1_1);
		car(sc->T2_1) = car(args);
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_C_opCq:
	      if (!c_function_is_ok_caddr(sc, code)) break;
	      
	    case HOP_SAFE_C_C_opCq:
	      {
		s7_pointer args;
		args = cdr(code);
		car(sc->T2_2) = c_call(cadr(args))(sc, ecdr(args)); /* any # of args */
		car(sc->T2_1) = car(args);
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_C_opCSq:
	      if (!c_function_is_ok_caddr(sc, code)) break;
	      
	    case HOP_SAFE_C_C_opCSq:
	      {
		s7_pointer args;
		args = cdr(code);
		car(sc->T2_2) = find_symbol_checked(sc, fcdr(args));
		car(sc->T2_1) = ecdr(args);
		car(sc->T2_2) = c_call(cadr(args))(sc, sc->T2_1);
		car(sc->T2_1) = car(args);
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_C_opSSq:
	      if (!c_function_is_ok_caddr(sc, code)) break;
	      
	    case HOP_SAFE_C_C_opSSq:
	      {
		s7_pointer args, val;
		args = cdr(code);
		val = find_symbol_checked(sc, ecdr(args));
		car(sc->T2_2) = find_symbol_checked(sc, fcdr(args));
		car(sc->T2_1) = val;
		car(sc->T2_2) = c_call(cadr(args))(sc, sc->T2_1);
		car(sc->T2_1) = car(args);
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_opCSq_C:
	      if (!c_function_is_ok_cadr(sc, code)) break;
	      
	    case HOP_SAFE_C_opCSq_C:
	      {
		s7_pointer args;
		args = cdr(code);
		car(sc->T2_2) = find_symbol_checked(sc, caddr(car(args)));
		car(sc->T2_1) = cadr(car(args));
		car(sc->T2_1) = c_call(car(args))(sc, sc->T2_1);
		car(sc->T2_2) = cadr(args);
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_opSSq_C:
	      if (!c_function_is_ok_cadr(sc, code)) break;
	      
	    case HOP_SAFE_C_opSSq_C:
	      {
		s7_pointer args, val;
		args = cdr(code);
		val = find_symbol_checked(sc, cadr(car(args)));
		car(sc->T2_2) = find_symbol_checked(sc, caddr(car(args)));
		car(sc->T2_1) = val;
		car(sc->T2_1) = c_call(car(args))(sc, sc->T2_1);
		car(sc->T2_2) = cadr(args);
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      

	    case OP_SAFE_C_opSSq_S:
	      if (!c_function_is_ok_cadr(sc, code)) break;
	      
	    case HOP_SAFE_C_opSSq_S:
	      {
		s7_pointer args, val, val1;
		args = cdr(code);
		val = find_symbol_checked(sc, cadr(car(args)));
		val1 = find_symbol_checked(sc, cadr(args));
		car(sc->T2_2) = find_symbol_checked(sc, caddr(car(args)));
		car(sc->T2_1) = val;
		car(sc->T2_1) = c_call(car(args))(sc, sc->T2_1);
		car(sc->T2_2) = val1;
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }


	    case OP_SAFE_C_op_opSSq_q_C:
	      if ((!c_function_is_ok(sc, code)) || (!c_function_is_ok(sc, cadr(code))) || (!c_function_is_ok(sc, cadr(cadr(code))))) break;

	    case HOP_SAFE_C_op_opSSq_q_C:
	      {
		/* code: (> (magnitude (- old new)) 0.001
		 */
		s7_pointer arg;
		arg = cadr(cadr(code));
		car(sc->T2_1) = find_symbol_checked(sc, cadr(arg));
		car(sc->T2_2) = find_symbol_checked(sc, caddr(arg));
		car(sc->T1_1) = c_call(arg)(sc, sc->T2_1);
		car(sc->T2_1) = c_call(cadr(code))(sc, sc->T1_1);
		car(sc->T2_2) = caddr(code);
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_S_op_opSSq_Sq:
	      if ((!c_function_is_ok(sc, code)) || (!c_function_is_ok(sc, caddr(code))) || (!c_function_is_ok(sc, cadr(caddr(code))))) break;
	      
	    case HOP_SAFE_C_S_op_opSSq_Sq:
	      {
		/* (let () (define (hi a b c d) (+ a (* (- b c) d))) (define (ho) (hi 1 2 3 4)) (ho)) 
		 *   or actually... (oscil fmosc1 (+ (* fm1-rat vib) fuzz))
		 *      and that is then packaged as opCq...: (* (env indf1) (oscil fmosc1 (+ (* fm1-rat vib) fuzz)))
		 *      and that is then (+ ...)	
		 *   but now this is handled in clm2xen.c
		 */
		s7_pointer args, val, val1;
		args = caddr(code);                            /* (* (- b c) d) */
		val1 = cadr(args);
		val = find_symbol_checked(sc, cadr(val1));                  /* b */
		car(sc->T2_2) = find_symbol_checked(sc, caddr(val1));       /* c */
		car(sc->T2_1) = val;
		val = find_symbol_checked(sc, caddr(args));                 /* d */
		car(sc->T2_1) = c_call(val1)(sc, sc->T2_1);    /* (- b c) */
		car(sc->T2_2) = val;
		car(sc->T2_2) = c_call(args)(sc, sc->T2_1);    /* (* ...) */
		car(sc->T2_1) = find_symbol_checked(sc, cadr(code));        /* a */
		sc->value = c_call(code)(sc, sc->T2_1);        /* (+ ...) */
		goto START;
	      }

	      
	    case OP_SAFE_C_S_op_S_opSSqq:
	      if ((!c_function_is_ok(sc, code)) || (!c_function_is_ok(sc, caddr(code))) || (!c_function_is_ok(sc, caddr(caddr(code))))) break;
	      
	    case HOP_SAFE_C_S_op_S_opSSqq:
	      {
		/* (let () (define (hi a b c d) (+ a (* d (- b c)))) (define (ho) (hi 1 2 3 4)) (ho)) */
		s7_pointer args, val, val1;
		args = caddr(code);                            /* (* d (- b c)) */
		val1 = caddr(args);
		val = find_symbol_checked(sc, cadr(val1));                  /* b */
		car(sc->T2_2) = find_symbol_checked(sc, caddr(val1));       /* c */
		car(sc->T2_1) = val;
		val = find_symbol_checked(sc, cadr(args));                  /* d */
		car(sc->T2_2) = c_call(val1)(sc, sc->T2_1);    /* (- b c) */
		car(sc->T2_1) = val;
		car(sc->T2_2) = c_call(args)(sc, sc->T2_1);    /* (* ...) */
		car(sc->T2_1) = find_symbol_checked(sc, cadr(code));        /* a */
		sc->value = c_call(code)(sc, sc->T2_1);        /* (+ ...) */
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_S_op_opSSq_opSSqq:
	      if (!a_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_S_op_opSSq_opSSqq:
	      {
		/* (* s (f3 (f1 a b) (f2 c d))) */
		s7_pointer args, f1, op1, op2;

		args = caddr(code);                            
		op1 = cadr(args);
		op2 = caddr(args);

		car(sc->T2_1) = find_symbol_checked(sc, cadr(op1));
		car(sc->T2_2) = find_symbol_checked(sc, caddr(op1));		   
		f1 = c_call(op1)(sc, sc->T2_1);

		car(sc->T2_1) = find_symbol_checked(sc, cadr(op2));
		car(sc->T2_2) = find_symbol_checked(sc, caddr(op2));			   
		car(sc->T2_2) = c_call(op2)(sc, sc->T2_1);

		car(sc->T2_1) = f1;
		car(sc->T2_2) = c_call(args)(sc, sc->T2_1);
		car(sc->T2_1) = find_symbol_checked(sc, cadr(code));        
		sc->value = c_call(code)(sc, sc->T2_1);        
		goto START;
	      }


	    case OP_SAFE_C_op_opSSq_Sq_opSSq:
	      if (!a_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_op_opSSq_Sq_opSSq:
	      {
		/* (op4 (op1 (op2 s1 s2) s3) (op3 s4 s5)) 
		 * (define (hi a b c d e) (+ (+ (+ a b) c) (+ d e)))
		 */
		s7_pointer op1, op2, op3, val1;

		op1 = cadr(code);    
		op3 = caddr(code);   
		op2 = cadr(op1);

		car(sc->T2_1) = find_symbol_checked(sc, cadr(op3));
		car(sc->T2_2) = find_symbol_checked(sc, caddr(op3));
		val1 = c_call(op3)(sc, sc->T2_1);

		car(sc->T2_1) = find_symbol_checked(sc, cadr(op2));
		car(sc->T2_2) = find_symbol_checked(sc, caddr(op2));
		car(sc->T2_1) = c_call(op2)(sc, sc->T2_1);
		car(sc->T2_2) = find_symbol_checked(sc, caddr(op1));

		car(sc->T2_1) = c_call(op1)(sc, sc->T2_1);
		car(sc->T2_2) = val1;
		sc->value = c_call(code)(sc, sc->T2_1);        
		goto START;
	      }


	    case OP_SAFE_C_opSSq_op_opSSq_q:
	      if (!a_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_opSSq_op_opSSq_q:
	      {
		/* (op (f1 a b) (f2 (f3 c d))) */
		s7_pointer op1, op2, op3, val1;
		op1 = cadr(code);
		op2 = caddr(code);
		op3 = cadr(op2);
		
		car(sc->T2_1) = find_symbol_checked(sc, cadr(op1));
		car(sc->T2_2) = find_symbol_checked(sc, caddr(op1));
		val1 = c_call(op1)(sc, sc->T2_1);
		
		car(sc->T2_1) = find_symbol_checked(sc, cadr(op3));
		car(sc->T2_2) = find_symbol_checked(sc, caddr(op3));			    /* perhaps: this is normally already ok (b == d) */
		car(sc->T1_1) = c_call(op3)(sc, sc->T2_1);
		
		car(sc->T2_2) = c_call(op2)(sc, sc->T1_1);
		car(sc->T2_1) = val1;
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }


	    case OP_SAFE_C_opSCq_S:
	      if (!c_function_is_ok_cadr(sc, code)) break;
	      
	    case HOP_SAFE_C_opSCq_S:
	      {
		s7_pointer args, val1;
		args = cdr(code);
		val1 = find_symbol_checked(sc, cadr(args));
		car(sc->T2_1) = find_symbol_checked(sc, cadr(car(args)));
		car(sc->T2_2) = caddr(car(args));
		car(sc->T2_1) = c_call(car(args))(sc, sc->T2_1);
		car(sc->T2_2) = val1;
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_opSCq_C:
	      if (!c_function_is_ok_cadr(sc, code)) break;
	      
	    case HOP_SAFE_C_opSCq_C:
	      {
		s7_pointer args;
		args = cdr(code);
		car(sc->T2_1) = find_symbol_checked(sc, cadr(car(args)));
		car(sc->T2_2) = caddr(car(args));
		car(sc->T2_1) = c_call(car(args))(sc, sc->T2_1);
		car(sc->T2_2) = cadr(args);
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_opCSq_S:
	      if (!c_function_is_ok_cadr(sc, code)) break;
	      
	    case HOP_SAFE_C_opCSq_S:
	      {
		s7_pointer args, val1;
		args = cdr(code);
		val1 = find_symbol_checked(sc, cadr(args));
		car(sc->T2_2) = find_symbol_checked(sc, caddr(car(args)));
		car(sc->T2_1) = cadr(car(args));
		car(sc->T2_1) = c_call(car(args))(sc, sc->T2_1);
		car(sc->T2_2) = val1;
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_S_opSCq:
	      if (!c_function_is_ok_caddr(sc, code)) break;
	      
	    case HOP_SAFE_C_S_opSCq:
	      {
		s7_pointer val1, args;
		args = cdr(code);
		val1 = find_symbol_checked(sc, car(args));
		car(sc->T2_1) = find_symbol_checked(sc, ecdr(args));
		car(sc->T2_2) = fcdr(args);
		car(sc->T2_2) = c_call(cadr(args))(sc, sc->T2_1);
		car(sc->T2_1) = val1;
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_C_opSCq:
	      if (!c_function_is_ok_caddr(sc, code)) break;
	      
	    case HOP_SAFE_C_C_opSCq:
	      {
		s7_pointer args;
		args = cdr(code);
		car(sc->T2_1) = find_symbol_checked(sc, ecdr(args));
		car(sc->T2_2) = fcdr(args);
		car(sc->T2_2) = c_call(cadr(args))(sc, sc->T2_1);
		car(sc->T2_1) = car(args);
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_S_opSSq:
	      if (!c_function_is_ok_caddr(sc, code)) break;
	      
	    case HOP_SAFE_C_S_opSSq:
	      {
		/* (* a (- b c)) */
		s7_pointer val1, val2, args;
		args = cdr(code);
		val1 = find_symbol_checked(sc, car(args));
		val2 = find_symbol_checked(sc, ecdr(args));
		car(sc->T2_2) = find_symbol_checked(sc, fcdr(args));
		car(sc->T2_1) = val2;
		car(sc->T2_2) = c_call(cadr(args))(sc, sc->T2_1);
		car(sc->T2_1) = val1;
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_S_opCSq:
	      if (!c_function_is_ok_caddr(sc, code)) break;
	      
	    case HOP_SAFE_C_S_opCSq:
	      {
		/* (* a (- 1 b)) or (logand a (ash 1 b)) */
		s7_pointer val1, args;
		args = cdr(code);
		val1 = find_symbol_checked(sc, car(args));                     /* a */
		car(sc->T2_2) = find_symbol_checked(sc, fcdr(args));           /* b */
		car(sc->T2_1) = ecdr(args);                       /* 1 */
		car(sc->T2_2) = c_call(cadr(args))(sc, sc->T2_1); /* (- 1 b) */
		car(sc->T2_1) = val1;
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_opSq_S:
	      if (!c_function_is_ok_cadr(sc, code)) break;
	      
	    case HOP_SAFE_C_opSq_S:
	      {
		s7_pointer args;
		args = cdr(code);
		car(sc->T1_1) = find_symbol_checked(sc, cadr(car(args)));
		sc->temp3 = c_call(car(args))(sc, sc->T1_1);
		car(sc->T2_2) = find_symbol_checked(sc, cadr(args));
		car(sc->T2_1) = sc->temp3;
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_opSq_P:
	      if (!c_function_is_ok_cadr(sc, code)) break;
	      
	    case HOP_SAFE_C_opSq_P:
	      {
		s7_pointer args;
		args = cadr(code);
		car(sc->T1_1) = find_symbol_checked(sc, cadr(args));
		push_stack(sc, OP_SAFE_C_opSq_P_1, c_call(args)(sc, sc->T1_1), sc->code);
		sc->code = caddr(code);
		goto EVAL; 
	      }

	      
	    case OP_SAFE_C_opCq_S:
	      if (!c_function_is_ok_cadr(sc, code)) break;
	      
	    case HOP_SAFE_C_opCq_S:
	      {
		s7_pointer args, val;
		args = cdr(code);
		val = find_symbol_checked(sc, cadr(args));
		car(sc->T2_1) = c_call(car(args))(sc, cdr(car(args)));
		car(sc->T2_2) = val;
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_opCq_C:
	      if (!c_function_is_ok_cadr(sc, code)) break;
	      
	    case HOP_SAFE_C_opCq_C:
	      {
		s7_pointer args;
		args = cdr(code);
		car(sc->T2_1) = c_call(car(args))(sc, cdr(car(args)));
		car(sc->T2_2) = cadr(args); /* the second C stands for 1 arg? */
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_opSq_C:
	      if (!c_function_is_ok_cadr(sc, code)) break;
	      
	    case HOP_SAFE_C_opSq_C:
	      {
		s7_pointer args;
		args = cdr(code);
		car(sc->T1_1) = find_symbol_checked(sc, cadr(car(args)));
		car(sc->T2_1) = c_call(car(args))(sc, sc->T1_1);
		car(sc->T2_2) = cadr(args);
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_C_op_S_opCqq:
	      if (!a_is_ok(sc, code)) break;
	      
	    case HOP_SAFE_C_C_op_S_opCqq:
	      {
		/* (define (hi a) (< 1.0 (+ a (* a 2)))) */
		s7_pointer args, arg1, arg2;
		args = cdr(code);     /* C_op_S_opCqq */
		arg1 = cadr(args);    /* op_S_opCqq */
		arg2 = caddr(arg1);   /* opCq */
		car(sc->T2_2) = c_call(arg2)(sc, cdr(arg2));
		car(sc->T2_1) = find_symbol_checked(sc, cadr(arg1));
		car(sc->T2_2) = c_call(arg1)(sc, sc->T2_1);
		car(sc->T2_1) = car(args);   
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_opSq_opSq:
	      if (!c_function_is_ok_cadr_caddr(sc, code)) break;
	      
	    case HOP_SAFE_C_opSq_opSq:
	      {
		s7_pointer args;
		args = cdr(code);
		car(sc->T1_1) = find_symbol_checked(sc, cadr(car(args)));
		sc->temp3 = c_call(car(args))(sc, sc->T1_1);
		args = cadr(args);
		car(sc->T1_1) = find_symbol_checked(sc, cadr(args));
		car(sc->T2_2) = c_call(args)(sc, sc->T1_1);
		car(sc->T2_1) = sc->temp3;
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_opCq_opCq:
	      if (!c_function_is_ok_cadr_caddr(sc, code)) break;
	      
	    case HOP_SAFE_C_opCq_opCq:
	      {
		s7_pointer args;
		args = cdr(code);
		car(sc->T2_1) = c_call(car(args))(sc, cdr(car(args)));
		car(sc->T2_2) = c_call(cadr(args))(sc, cdr(cadr(args)));
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_opCq_opSSq:
	      if (!c_function_is_ok_cadr_caddr(sc, code)) break;
	      
	    case HOP_SAFE_C_opCq_opSSq:
	      {
		s7_pointer args, val;
		/* code: (/ (+ bn 1) (+ bn an)) */
		args = cdr(code);
		val = c_call(car(args))(sc, cdr(car(args)));
		args = cdr(args);
		car(sc->T2_1) = find_symbol_checked(sc, cadar(args));
		car(sc->T2_2) = find_symbol_checked(sc, caddar(args));
		car(sc->T2_2) = c_call(car(args))(sc, sc->T2_1);
		car(sc->T2_1) = val;
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_opSCq_opSCq:
	      if (!c_function_is_ok_cadr_caddr(sc, code)) break;
	      
	    case HOP_SAFE_C_opSCq_opSCq:
	      {
		s7_pointer args, val2;
		args = cdr(code);
		val2 = find_symbol_checked(sc, cadr(cadr(args)));
		car(sc->T2_1) = find_symbol_checked(sc, cadr(car(args)));
		car(sc->T2_2) = caddr(car(args));
		sc->temp3 = c_call(car(args))(sc, sc->T2_1);
		car(sc->T2_1) = val2;
		car(sc->T2_2) = caddr(cadr(args));
		car(sc->T2_2) = c_call(cadr(args))(sc, sc->T2_1);
		car(sc->T2_1) = sc->temp3;
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_opSSq_opSSq:
	      if (!c_function_is_ok_cadr_caddr(sc, code)) break;
	      
	    case HOP_SAFE_C_opSSq_opSSq:
	      {
		s7_pointer args, val3, val4;
		args = cdr(code);
		val3 = find_symbol_checked(sc, caddr(car(args)));
		val4 = find_symbol_checked(sc, caddr(cadr(args)));
		
		car(sc->T2_1) = find_symbol_checked(sc, cadr(car(args)));
		car(sc->T2_2) = val3;
		sc->temp3 = c_call(car(args))(sc, sc->T2_1);
		car(sc->T2_1) = find_symbol_checked(sc, cadr(cadr(args)));
		car(sc->T2_2) = val4;
		car(sc->T2_2) = c_call(cadr(args))(sc, sc->T2_1);
		car(sc->T2_1) = sc->temp3;
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_opSSq_opSq:
	      if (!c_function_is_ok_cadr_caddr(sc, code)) break;
	      
	    case HOP_SAFE_C_opSSq_opSq:
	      {
		s7_pointer args, val3;
		args = cdr(code);
		val3 = find_symbol_checked(sc, caddr(car(args)));
		car(sc->T2_1) = find_symbol_checked(sc, cadr(car(args)));
		car(sc->T2_2) = val3;
		val3 = c_call(car(args))(sc, sc->T2_1);
		car(sc->T1_1) = find_symbol_checked(sc, cadr(cadr(args)));
		car(sc->T2_2) = c_call(cadr(args))(sc, sc->T1_1);
		car(sc->T2_1) = val3;
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_opSq_opSSq:
	      if (!c_function_is_ok_cadr_caddr(sc, code)) break;
	      
	    case HOP_SAFE_C_opSq_opSSq:
	      {
		s7_pointer args, val3;
		args = cdr(code);
		val3 = find_symbol_checked(sc, cadr(cadr(args)));
		car(sc->T2_2) = find_symbol_checked(sc, caddr(cadr(args)));
		car(sc->T2_1) = val3;
		val3 = c_call(cadr(args))(sc, sc->T2_1);
		car(sc->T1_1) = find_symbol_checked(sc, cadr(car(args)));
		car(sc->T2_1) = c_call(car(args))(sc, sc->T1_1);
		car(sc->T2_2) = val3;
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }
	      
	      
	    case OP_SAFE_C_opSSq_opCq:
	      if (!c_function_is_ok_cadr_caddr(sc, code)) break;
	      
	    case HOP_SAFE_C_opSSq_opCq:
	      {
		s7_pointer arg1, arg2, val3;
		arg1 = cadr(code);
		arg2 = caddr(code);
		val3 = find_symbol_checked(sc, caddr(arg1));
		car(sc->T2_1) = find_symbol_checked(sc, cadr(arg1));
		car(sc->T2_2) = val3;
		car(sc->T2_1) = c_call(arg1)(sc, sc->T2_1);
		car(sc->T2_2) = c_call(arg2)(sc, cdr(arg2));
		sc->value = c_call(code)(sc, sc->T2_1);
		goto START;
	      }


	      /* c_function_star */
	    case OP_SAFE_C_STAR_CD:
	      if (!c_function_is_ok(sc, code)) break;

	    case HOP_SAFE_C_STAR_CD:
	      {
		int i;
		s7_pointer par, call_args, func;
		s7_pointer *df;
		func = ecdr(code);
		call_args = c_function_call_args(func);
		df = c_function_arg_defaults(func);  
		for (i = 1, par = cdr(call_args); is_pair(par); i++, par = cdr(par))
		  car(par) = df[i];
		car(call_args) = cadr(code);
		sc->value = c_call(code)(sc, call_args);
		goto START;
	      }

	      
	      /* -------------------------------------------------------------------------------- */
	    case OP_C_S_opSq:
	      if ((!c_function_is_ok(sc, code)) || (!indirect_c_function_is_ok(sc, caddr(code)))) break;
	      
	    case HOP_C_S_opSq:
	      {
		s7_pointer args, val;
		args = cdr(code);
		val = find_symbol_checked(sc, car(args));
		car(sc->T1_1) = find_symbol_checked(sc, ecdr(args));
		sc->args = list_2(sc, val, c_call(cadr(args))(sc, sc->T1_1));
		sc->value = c_call(code)(sc, sc->args);
		goto START;
	      }

	      
	    case OP_C_S_opCq:
	      if ((!c_function_is_ok(sc, code)) || (!indirect_c_function_is_ok(sc, caddr(code)))) break;
	      
	    case HOP_C_S_opCq:
	      {
		s7_pointer args, val;
		args = cdr(code);
		sc->temp3 = find_symbol_checked(sc, car(args));
		val = c_call(cadr(args))(sc, ecdr(args));
		sc->args = list_2(sc, sc->temp3, val);
		sc->value = c_call(code)(sc, sc->args);
		goto START;
	      }


	    case OP_C_S:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_C_S:
	      sc->args = list_1(sc, find_symbol_checked(sc, cadr(code)));
	      sc->value = c_call(code)(sc, sc->args);
	      goto START;
	      
	      
	    case OP_READ_S:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_READ_S:
	      {
		/* another lint opt */
		s7_pointer port;
		port = find_symbol_checked(sc, cadr(code));
		
		if (!is_input_port(port)) /* was also not stdin */
		  {
		    sc->value = g_read(sc, list_1(sc, port));
		    goto START;
		  }
		/* I guess this port_is_closed check is needed because we're going down a level below */
		if (port_is_closed(port))
		  simple_wrong_type_argument_with_type(sc, sc->READ, port, AN_OPEN_PORT);
		
		if (is_function_port(port))
		  sc->value = (*(port_input_function(port)))(sc, S7_READ, port);
		else
		  {
		    if ((is_string_port(port)) &&
			(port_data_size(port) <= port_position(port)))
		      sc->value = sc->EOF_OBJECT;
		    else
		      {
			push_input_port(sc, port);
			push_stack(sc, OP_READ_DONE, sc->NIL, sc->NIL); /* this stops the internal read process so we only get one form */
			sc->tok = token(sc);
			switch (sc->tok)
			  {
			  case TOKEN_EOF:
			    goto START;
			    
			  case TOKEN_RIGHT_PAREN:
			    read_error(sc, "unexpected close paren");
			    
			  case TOKEN_COMMA:
			    read_error(sc, "unexpected comma");
			    
			  default:
			    sc->value = read_expression(sc);
			    sc->current_line = port_line_number(sc->input_port);  /* this info is used to track down missing close parens */
			    sc->current_file = port_filename(sc->input_port);
			  }
		      }
		  }
		goto START;
	      }
	      
	      
	    case OP_C_A:
	      if (!a_is_ok_cadr(sc, code)) break;
	      
	    case HOP_C_A:
	      sc->args = list_1(sc, ((s7_function)fcdr(cdr(code)))(sc, cadr(code)));
	      sc->value = c_call(code)(sc, sc->args);
	      goto START;
	      
	      
	    case OP_C_P:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_C_P:
	      push_stack(sc, OP_C_P_1, sc->NIL, code);
	      sc->code = cadr(code);
	      if (is_optimized(sc->code))
		goto OPT_EVAL;
	      goto EVAL; 
	      
	      
	    case OP_C_SP:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_C_SP:
	      push_stack(sc, OP_C_SP_1, find_symbol_checked(sc, cadr(code)), code);
	      sc->code = caddr(code);
	      if (is_optimized(sc->code))
		goto OPT_EVAL;
	      goto EVAL; 
	      
	      
	    case OP_APPLY_SS:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_APPLY_SS:
	      sc->code = find_symbol_checked(sc, cadr(code)); /* global search here was slower */
	      sc->args = find_symbol_checked(sc, fcdr(code));
	      if (!is_proper_list(sc, sc->args))        /* (apply + #f) etc */
		return(apply_list_error(sc, sc->args));
	      if (needs_copied_args(sc->code))
		{
		  sc->args = copy_list(sc, sc->args);
		  if (is_any_macro(sc->code))                   /* (apply mac '(3)) -> (apply mac '((mac 3))) */
		    {                                           /* these always copy args (except c_macros, sigh) */
		      push_stack(sc, OP_EVAL_MACRO, sc->NIL, sc->args);
		      sc->args = list_1(sc, cons(sc, sc->code, sc->args));
		    }
		}
	      goto APPLY;


	    case OP_C_ALL_X:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_C_ALL_X:
	      {
		s7_pointer args, p;
		sc->args = make_list(sc, integer(arglist_length(code)), sc->NIL);
		for (args = cdr(code), p = sc->args; is_pair(args); args = cdr(args), p = cdr(p))
		  car(p) = ((s7_function)fcdr(args))(sc, car(args));
		sc->value = c_call(code)(sc, sc->args);
		IF_BEGIN_POP_STACK(sc);
	      }
	      
	      
	    case OP_CALL_WITH_EXIT:
	      if (!c_function_is_ok(sc, code)) break;
	      check_lambda_args(sc, cadr(cadr(code)), NULL); 
	      
	    case HOP_CALL_WITH_EXIT:
	      {
		s7_pointer go, args;
		go = make_goto(sc);
		args = fcdr(code);
		push_stack(sc, OP_DEACTIVATE_GOTO, go, code); /* code arg is ignored, but perhaps this is safer in GC? */
		NEW_FRAME_WITH_SLOT(sc, sc->envir, sc->envir, caar(args), go);
		sc->code = cdr(args);
		goto BEGIN1;
	      } 
	      
	      
	    case OP_C_FOR_EACH_LS:
	      if (!c_function_is_ok(sc, code)) break;
	      check_lambda_args(sc, cadr(cadr(code)), NULL);
	      
	    case HOP_C_FOR_EACH_LS:
	      {
		/* for_each_1: (let () (define (f b) b) (define (hi) (let ((lst '(1 2 3))) (for-each (lambda (a) 123 (f a)) lst))) (hi)) 
		 */
		s7_pointer x, y, z;
		
		y = cdadr(code);
		make_closure_without_capture(sc, x, car(y), cdr(y), sc->envir);
		
		z = find_symbol_checked(sc, caddr(code));
		if ((!is_pair(z)) ||
		    (s7_list_length(sc, z) == 0))
		  {
		    sc->value = g_for_each(sc, list_2(sc, x, z));
		    goto START;
		  }
		sc->code = x;
		sc->args = make_simple_counter(sc, z);
		
		goto FOR_EACH_SIMPLE;
	      }
	      
	      
	    case OP_C_FOR_EACH_LS_2:
	      if (!c_function_is_ok(sc, code)) break;
	      check_lambda_args(sc, cadr(cadr(code)), NULL);
	      
	    case HOP_C_FOR_EACH_LS_2:
	      {
		/* for_each_2: (let () (define (f b) b) (define (hi) (let ((lst '(1 2 3))) (for-each (lambda (a) (f a)) lst))) (hi))
		 */
		s7_pointer x, y, z;
		
		z = find_symbol_checked(sc, caddr(code));
		if (is_null(z))
		  {
		    sc->value = sc->UNSPECIFIED;
		    goto START;
		  }

		y = cdadr(code);
		make_closure_without_capture(sc, x, car(y), cdr(y), sc->envir); /* car=args, cdr=code */

		if ((!is_pair(z)) ||
		    (s7_list_length(sc, z) == 0))
		  {
		    sc->value = g_for_each(sc, list_2(sc, x, z));
		    goto START;
		  }
		sc->code = x;
		sc->args = make_simple_counter(sc, z);
		if ((is_optimized(car(closure_body(x)))) &&
		    (optimize_data(car(closure_body(x))) == HOP_C_CATCH_ALL))
		  goto FOR_EACH_SIMPLEST;
		goto FOR_EACH_SIMPLER;
	      }
	      
	      
	    case OP_C_FOR_EACH_L_opSq:
	      if ((!c_function_is_ok(sc, code)) || (!indirect_c_function_is_ok(sc, caddr(code)))) break;
	      check_lambda_args(sc, cadr(cadr(code)), NULL);
	      
	    case HOP_C_FOR_EACH_L_opSq:
	      {
		s7_pointer x, y, z;
		
		y = cdadr(code);
		make_closure_without_capture(sc, x, car(y), cdr(y), sc->envir);
		
		y = caddr(code);
		car(sc->T1_1) = find_symbol_checked(sc, cadr(y));
		z = c_call(y)(sc, sc->T1_1);
		
		if ((!is_pair(z)) ||
		    (s7_list_length(sc, z) == 0))
		  {
		    sc->value = g_for_each(sc, list_2(sc, x, z));
		    goto START;
		  }
		sc->code = x;
		sc->args = make_simple_counter(sc, z);
		if ((is_optimized(car(closure_body(x)))) &&
		    (optimize_data(car(closure_body(x))) == HOP_C_CATCH_ALL))
		  goto FOR_EACH_SIMPLEST;
		goto FOR_EACH_SIMPLER;
	      }
	      
	      
	    case OP_C_CATCH:
	      if (!c_function_is_ok(sc, code)) break;
	      check_lambda_args(sc, cadr(cadddr(code)), NULL);
	      
	    case HOP_C_CATCH:
	      {
		/* (catch #t (lambda () (set! ("hi") #\a)) (lambda args args)) 
		 *    code is (catch #t (lambda () ....) (lambda args ....))
		 */
		s7_pointer p, e = NULL, f, body, args;
		
		args = cddr(code);
		body = car(args);
		if (is_symbol(body))
		  {
		    body = find_symbol_checked(sc, body);
		    /* now if body is not a lambda form, we have to cancel the optimization */
		    if (!is_closure(body))
		      break;
		    if (!is_null(closure_args(body)))
		      return(wrong_type_argument_with_type(sc, sc->CATCH, small_int(2), body, A_THUNK));
		    e = closure_let(body);
		    body = closure_body(body);
		  }
		else
		  {
		    if (!is_null(cadr(body)))
		      return(wrong_type_argument_with_type(sc, sc->CATCH, small_int(2), body, A_THUNK));
		    body = cddr(body);
		    e = sc->envir;
		  }
		
		/* defer making the error lambda */
		/* z = cdadr(args); make_closure_with_let(sc, y, car(z), cdr(z), sc->envir); */ 
		
		NEW_CELL(sc, p);                     /* the catch object sitting on the stack */
		
		/* check target */
		f = cadr(code);
		if (!is_pair(f))                     /* (catch #t ...) or (catch sym ...) */
		  {
		    if (is_symbol(f))
		      catch_tag(p) = find_symbol_checked(sc, f);
		    else catch_tag(p) = f;
		  }
		else catch_tag(p) = cadr(f);         /* (catch 'sym ...) */
		
		catch_goto_loc(p) = s7_stack_top(sc);
		catch_op_loc(p) = (int)(sc->op_stack_now - sc->op_stack);
		catch_handler(p) = cdadr(args);      /* not yet a closure... */
		set_type(p, T_CATCH);
		
		push_stack(sc, OP_CATCH_1, code, p); /* code ignored here, except by GC */
		NEW_FRAME(sc, e, sc->envir); 
		sc->code = body;
		goto BEGIN1;
	      }


	    case OP_C_CATCH_ALL:
	      if (!c_function_is_ok(sc, code)) break;
	      
	    case HOP_C_CATCH_ALL:
	      {
		/* (catch #t (lambda () ...) (lambda args #f)
		 */
		s7_pointer p;
		NEW_FRAME(sc, sc->envir, sc->envir); 
		/* catch_all needs 3 pieces of info: the goto/op locs and the result
		 *   the locs are unsigned ints, so this fits in the new frame's dox1/2 fields.
		 */
		p = sc->envir;
		catch_all_goto_loc(p) = s7_stack_top(sc);
		catch_all_op_loc(p) = (int)(sc->op_stack_now - sc->op_stack);
		catch_all_result(p) = fcdr(code);
		push_stack_no_args(sc, OP_CATCH_ALL, code);
		sc->code = ecdr(cdr(code));                   /* the body of the first lambda */
		goto BEGIN1;                                  /* removed one_liner check here -- rare */
	      }


	      /* -------------------------------------------------------------------------------- */
	    case OP_THUNK:
	      if (!closure_is_ok(sc, code, MATCH_UNSAFE_CLOSURE, 0)) {set_optimize_data(code, OP_UNKNOWN); goto OPT_EVAL;}
	      
	    case HOP_THUNK:
	      CHECK_STACK_SIZE(sc); 
	      /* this recursion check is consistent with the other unsafe closure calls, but we're probably in big trouble:
	       *   (letrec ((a (lambda () (cons 1 (b)))) (b (lambda () (a)))) (b))
	       * unfortunately the alternative is a segfault when we wander off the end of the stack.
	       */
	      NEW_FRAME(sc, closure_let(ecdr(code)), sc->envir);
	      sc->code = closure_body(ecdr(code));
	      goto BEGIN1;
	      
	      
	    case OP_SAFE_THUNK:
	      if (!closure_is_ok(sc, code, MATCH_SAFE_CLOSURE, 0)) {set_optimize_data(code, OP_UNKNOWN); goto OPT_EVAL;}
	      
	    case HOP_SAFE_THUNK:
	      /* (let ((x 1)) (let () (define (f) x) (let ((x 0)) (define (g) (set! x 32) (f)) (g)))) */
	      /* NEW_FRAME(sc, closure_let(ecdr(code)), sc->envir); */

	      sc->envir = closure_let(ecdr(code));
	      sc->code = closure_body(ecdr(code));
	      goto BEGIN1;


	    case OP_SAFE_CLOSURE_S:
	      if (!closure_is_ok(sc, code, MATCH_SAFE_CLOSURE, 1)) {set_optimize_data(code, OP_UNKNOWN_G); goto OPT_EVAL;}
	      
	    case HOP_SAFE_CLOSURE_S:
	      /* since a tail call is safe, we can't change the current env's let_id until
	       *   after we do the lookup -- it might be the current func's arg, and we're
	       *   about to call the same func.
	       */
	      sc->envir = old_frame_with_slot(sc, closure_let(ecdr(code)), find_symbol_checked(sc, fcdr(code)));
	      sc->code = closure_body(ecdr(code));
	      goto BEGIN1;
	      
	      
	    case OP_SAFE_GLOSURE_S:
	      if ((symbol_id(car(code)) != 0) ||
		  (ecdr(code) != slot_value(global_slot(car(code)))))
		{set_optimize_data(code, OP_UNKNOWN_G); goto OPT_EVAL;}
	      
	    case HOP_SAFE_GLOSURE_S:
	      sc->envir = old_frame_with_slot(sc, closure_let(ecdr(code)), find_symbol_checked(sc, fcdr(code)));
	      sc->code = closure_body(ecdr(code));
	      goto BEGIN1;
	      
	      
	    case OP_SAFE_CLOSURE_C:
	      if (!closure_is_ok(sc, code, MATCH_SAFE_CLOSURE, 1)) {set_optimize_data(code, OP_UNKNOWN_G); goto OPT_EVAL;}
	      
	    case HOP_SAFE_CLOSURE_C:
	      sc->envir = old_frame_with_slot(sc, closure_let(ecdr(code)), cadr(code));
	      sc->code = closure_body(ecdr(code));
	      goto BEGIN1;

	      
	    case OP_SAFE_CLOSURE_Q:
	      if (!closure_is_ok(sc, code, MATCH_SAFE_CLOSURE, 1)) {set_optimize_data(code, OP_UNKNOWN_A); goto OPT_EVAL;}
	      
	    case HOP_SAFE_CLOSURE_Q:
	      sc->envir = old_frame_with_slot(sc, closure_let(ecdr(code)), cadr(cadr(code)));
	      sc->code = closure_body(ecdr(code));
	      goto BEGIN1;
	      
	      
	    case OP_SAFE_GLOSURE_P:
	      if ((symbol_id(car(code)) != 0) || (ecdr(code) != slot_value(global_slot(car(code))))) break;

	    case HOP_SAFE_GLOSURE_P:
	      push_stack(sc, OP_SAFE_CLOSURE_P_1, sc->NIL, code);
	      sc->code = cadr(code);
	      goto EVAL;


	    case OP_SAFE_CLOSURE_A:
	      if (!closure_is_ok(sc, code, MATCH_SAFE_CLOSURE, 1)) {set_optimize_data(code, OP_UNKNOWN_A); goto OPT_EVAL;}
	      if (!indirect_c_function_is_ok(sc, cadr(code))) break;
	      
	    case HOP_SAFE_CLOSURE_A:
	      sc->envir = old_frame_with_slot(sc, closure_let(ecdr(code)), ((s7_function)fcdr(cdr(code)))(sc, cadr(code)));
	      sc->code = closure_body(ecdr(code));
	      goto BEGIN1;
	      
	      
	    case OP_SAFE_GLOSURE_A:
	      if ((symbol_id(car(code)) != 0) ||
		  (ecdr(code) != slot_value(global_slot(car(code)))))
		{set_optimize_data(code, OP_UNKNOWN_A); goto OPT_EVAL;}
	      if (!indirect_c_function_is_ok(sc, cadr(code))) break;
	      
	    case HOP_SAFE_GLOSURE_A:
	      sc->envir = old_frame_with_slot(sc, closure_let(ecdr(code)), ((s7_function)fcdr(cdr(code)))(sc, cadr(code)));
	      sc->code = closure_body(ecdr(code));
	      goto BEGIN1;
	      
	      
	    case OP_SAFE_CLOSURE_SS:
	      if (!closure_is_ok(sc, code, MATCH_SAFE_CLOSURE, 2)) {set_optimize_data(code, OP_UNKNOWN_GG); goto OPT_EVAL;}
	      
	    case HOP_SAFE_CLOSURE_SS:
	      sc->envir = old_frame_with_two_slots(sc, closure_let(ecdr(code)), find_symbol_checked(sc, cadr(code)), find_symbol_checked(sc, fcdr(code)));
	      sc->code = closure_body(ecdr(code));
	      goto BEGIN1;


	    case OP_SAFE_CLOSURE_SC:
	      if (!closure_is_ok(sc, code, MATCH_SAFE_CLOSURE, 2)) {set_optimize_data(code, OP_UNKNOWN_GG); goto OPT_EVAL;}
	      
	    case HOP_SAFE_CLOSURE_SC:
	      sc->envir = old_frame_with_two_slots(sc, closure_let(ecdr(code)), find_symbol_checked(sc, cadr(code)), fcdr(code));
	      sc->code = closure_body(ecdr(code));
	      goto BEGIN1;
	      
	      
	    case OP_SAFE_CLOSURE_CS:
	      if (!closure_is_ok(sc, code, MATCH_SAFE_CLOSURE, 2)) {set_optimize_data(code, OP_UNKNOWN_GG); goto OPT_EVAL;}
	      
	    case HOP_SAFE_CLOSURE_CS:
	      sc->envir = old_frame_with_two_slots(sc, closure_let(ecdr(code)), cadr(code), find_symbol_checked(sc, fcdr(code)));
	      sc->code = closure_body(ecdr(code));
	      goto BEGIN1;
	      
	      
	    case OP_SAFE_CLOSURE_SA:
	      if (!closure_is_ok(sc, code, MATCH_SAFE_CLOSURE, 2)) {set_optimize_data(code, OP_UNKNOWN_AA); goto OPT_EVAL;}
	      
	    case HOP_SAFE_CLOSURE_SA:
	      {
		s7_pointer args;
		args = cddr(code);
		args = ((s7_function)fcdr(args))(sc, car(args));
		sc->envir = old_frame_with_two_slots(sc, closure_let(ecdr(code)), find_symbol_checked(sc, cadr(code)), args);
		sc->code = closure_body(ecdr(code));
		goto BEGIN1;
	      }
	      

	    case OP_SAFE_CLOSURE_AA:
	      if (!closure_is_ok(sc, code, MATCH_SAFE_CLOSURE, 2)) {set_optimize_data(code, OP_UNKNOWN_AA); goto OPT_EVAL;}

	    case HOP_SAFE_CLOSURE_AA:
	      {
		s7_pointer args, y, z;
		args = cdr(code);
		y = ((s7_function)fcdr(args))(sc, car(args));
		args = cdr(args);
		z = ((s7_function)fcdr(args))(sc, car(args));
		sc->envir = old_frame_with_two_slots(sc, closure_let(ecdr(code)), y, z);
		sc->code = closure_body(ecdr(code));
		goto BEGIN1;
	      }
	      

	    case OP_SAFE_CLOSURE_opSq_opSq:
	      if (!closure_is_ok(sc, code, MATCH_SAFE_CLOSURE, 2)) {set_optimize_data(code, OP_UNKNOWN_AA); goto OPT_EVAL;}
	      if ((!indirect_c_function_is_ok(sc, cadr(code))) || (!indirect_c_function_is_ok(sc, caddr(code)))) break;
	      
	    case HOP_SAFE_CLOSURE_opSq_opSq:
	      {
		s7_pointer args;
		args = cdr(code);
		
		car(sc->T1_1) = find_symbol_checked(sc, fcdr(args));
		sc->temp3 = c_call(car(args))(sc, sc->T1_1);
		car(sc->T1_1) = find_symbol_checked(sc, fcdr(code));

		sc->envir = old_frame_with_two_slots(sc, closure_let(ecdr(code)), sc->temp3, c_call(cadr(args))(sc, sc->T1_1));
		sc->code = closure_body(ecdr(code));
		goto BEGIN1;
	      }


	    case OP_SAFE_CLOSURE_opSq_S:
	      if (!closure_is_ok(sc, code, MATCH_SAFE_CLOSURE, 2)) {set_optimize_data(code, OP_UNKNOWN_AA); goto OPT_EVAL;}
	      if (!indirect_c_function_is_ok(sc, cadr(code))) break;
	      
	    case HOP_SAFE_CLOSURE_opSq_S:
	      {
		s7_pointer val1;
		val1 = find_symbol_checked(sc, fcdr(code));
		car(sc->T1_1) = find_symbol_checked(sc, cadr(cadr(code)));
		sc->envir = old_frame_with_two_slots(sc, closure_let(ecdr(code)), c_call(cadr(code))(sc, sc->T1_1), val1);
		sc->code = closure_body(ecdr(code));
		goto BEGIN1;
	      }
	      
	      
	    case OP_SAFE_CLOSURE_S_opSq:
	      if (!closure_is_ok(sc, code, MATCH_SAFE_CLOSURE, 2)) {set_optimize_data(code, OP_UNKNOWN_AA); goto OPT_EVAL;}
	      if (!indirect_c_function_is_ok(sc, caddr(code))) break;
	      
	    case HOP_SAFE_CLOSURE_S_opSq:
	      {
		s7_pointer val;
		val = find_symbol_checked(sc, cadr(code)); /* the first S */
		car(sc->T1_1) = find_symbol_checked(sc, cadr(caddr(code)));
		sc->envir = old_frame_with_two_slots(sc, closure_let(ecdr(code)), val, c_call(caddr(code))(sc, sc->T1_1));
		sc->code = closure_body(ecdr(code));
		goto BEGIN1;
	      }

	      
	    case OP_SAFE_CLOSURE_S_opSSq:
	      if (!closure_is_ok(sc, code, MATCH_SAFE_CLOSURE, 2)) {set_optimize_data(code, OP_UNKNOWN_AA); goto OPT_EVAL;}
	      if (!indirect_c_function_is_ok(sc, caddr(code))) break;
	      
	    case HOP_SAFE_CLOSURE_S_opSSq:
	      {
		s7_pointer val, args;
		val = find_symbol_checked(sc, cadr(code));
		args = caddr(code);
		car(sc->T2_1) = find_symbol_checked(sc, cadr(args));
		car(sc->T2_2) = find_symbol_checked(sc, caddr(args));
		sc->envir = old_frame_with_two_slots(sc, closure_let(ecdr(code)), val, c_call(args)(sc, sc->T2_1));
		sc->code = closure_body(ecdr(code));
		goto BEGIN1;
	      }

	      
	    case OP_SAFE_CLOSURE_opSSq_S:
	      if (!closure_is_ok(sc, code, MATCH_SAFE_CLOSURE, 2)) {set_optimize_data(code, OP_UNKNOWN_AA); goto OPT_EVAL;}
	      if (!indirect_c_function_is_ok(sc, cadr(code))) break;
	      
	    case HOP_SAFE_CLOSURE_opSSq_S:
	      {
		/* (let () (define (hi a b) (+ a b)) (define (ho a b c) (hi (log a b) c)) (ho 8 2 3)) -> 6 */
		s7_pointer val, args;

		val = find_symbol_checked(sc, caddr(code));
		args = cadr(code);
		car(sc->T2_1) = find_symbol_checked(sc, cadr(args));
		car(sc->T2_2) = find_symbol_checked(sc, caddr(args));
		sc->envir = old_frame_with_two_slots(sc, closure_let(ecdr(code)), c_call(args)(sc, sc->T2_1), val);
		sc->code = closure_body(ecdr(code));
		goto BEGIN1;
	      }


	    case OP_SAFE_CLOSURE_SAA:
	      if (!closure_is_ok(sc, code, MATCH_SAFE_CLOSURE, 3)) break;

	    case HOP_SAFE_CLOSURE_SAA:
	      {
		s7_pointer args, y, z;
		args = cddr(code);
		y = ((s7_function)fcdr(args))(sc, car(args));
		args = cdr(args);
		z = ((s7_function)fcdr(args))(sc, car(args));
		sc->envir = old_frame_with_three_slots(sc, closure_let(ecdr(code)), find_symbol_checked(sc, cadr(code)), y, z);
		sc->code = closure_body(ecdr(code));
		goto BEGIN1;
	      }

	      
	    case OP_SAFE_CLOSURE_ALL_X:
	      if (!closure_is_ok(sc, code, MATCH_SAFE_CLOSURE, integer(arglist_length(code)))) break;

	    case HOP_SAFE_CLOSURE_ALL_X:
	      {
		s7_pointer args, p, env, x, z;
		int num_args;
		unsigned long long int id;

		num_args = integer(arglist_length(code));
		if ((num_args != 0) &&
		    (num_args < NUM_SAFE_LISTS) &&
		    (!list_is_in_use(sc->safe_lists[num_args])))
		  {
		    sc->args = sc->safe_lists[num_args];
		    set_list_in_use(sc->args);
		  }
		else sc->args = make_list(sc, num_args, sc->NIL); 

		for (args = cdr(code), p = sc->args; is_pair(args); args = cdr(args), p = cdr(p))
		  car(p) = ((s7_function)fcdr(args))(sc, car(args));
		clear_list_in_use(sc->args);
		sc->code = ecdr(code);

		id = ++sc->let_number;
		env = closure_let(sc->code);
		let_id(env) = id;
		
		for (x = let_slots(env), z = sc->args; is_slot(x); x = next_slot(x), z = cdr(z))
		  {
		    slot_set_value(x, car(z));
		    symbol_set_local(slot_symbol(x), id, x);
		  }
		sc->envir = env;
		sc->code = closure_body(sc->code);
		
		if (is_pair(cdr(sc->code)))
		  {
		    push_stack_no_args(sc, OP_BEGIN1, cdr(sc->code));
		    sc->code = car(sc->code);
		  }
		else
		  {
		    sc->code = car(sc->code);
		    if (is_optimized(sc->code))
		      goto OPT_EVAL;
		  }
		goto EVAL;
	      }
	      
	      
	      /* -------------------------------------------------------------------------------- */

	    case OP_SAFE_CLOSURE_STAR_SS:
	      if (!closure_star_is_ok(sc, code, MATCH_SAFE_CLOSURE_STAR, 2)) {set_optimize_data(code, OP_UNKNOWN_GG); goto OPT_EVAL;}
	      
	    case HOP_SAFE_CLOSURE_STAR_SS:
	      {
		s7_pointer x, val1, val2;
		/* the finders have to operate in the current environment, so we can't change sc->envir until later */
		val1 = find_symbol_checked(sc, cadr(code));
		val2 = find_symbol_checked(sc, fcdr(code)); /* caddr */
		sc->envir = old_frame_with_slot(sc, closure_let(ecdr(code)), val1);

		x = next_slot(let_slots(closure_let(ecdr(code))));
		slot_set_value(x, val2); 
		symbol_set_local(slot_symbol(x), let_id(sc->envir), x);

		car(sc->T2_1) = next_slot(x);
		car(sc->T2_2) = cddr(closure_args(ecdr(code)));
		goto FILL_SAFE_CLOSURE_STAR;
	      }
	      
	      
	    case OP_SAFE_CLOSURE_STAR_SC:
	      if (!closure_star_is_ok(sc, code, MATCH_SAFE_CLOSURE_STAR, 2)) {set_optimize_data(code, OP_UNKNOWN_GG); goto OPT_EVAL;}
	      
	    case HOP_SAFE_CLOSURE_STAR_SC:
	      {
		s7_pointer x;
		sc->envir = old_frame_with_slot(sc, closure_let(ecdr(code)), find_symbol_checked(sc, cadr(code)));

		x = next_slot(let_slots(closure_let(ecdr(code))));
		slot_set_value(x, caddr(code));
		symbol_set_local(slot_symbol(x), let_id(sc->envir), x);

		car(sc->T2_1) = next_slot(x);
		car(sc->T2_2) = cddr(closure_args(ecdr(code)));
		goto FILL_SAFE_CLOSURE_STAR;
	      }
	      
	      
	    case OP_SAFE_CLOSURE_STAR_SA:
	      if (!closure_star_is_ok(sc, code, MATCH_SAFE_CLOSURE_STAR, 2)) break;
	      
	    case HOP_SAFE_CLOSURE_STAR_SA:
	      {
		s7_pointer arg;
		/* the second arg needs to be evaluated before we set sc->envir.
		 *   we checked at optimize time that this closure takes only 2 args.
		 */
		arg = cddr(code);
		arg = ((s7_function)fcdr(arg))(sc, car(arg)); 
		sc->envir = old_frame_with_two_slots(sc, closure_let(ecdr(code)), find_symbol_checked(sc, cadr(code)), arg);
		
		sc->code = closure_body(ecdr(code));
		goto BEGIN1;
	      }
	      
	      
	    case OP_SAFE_CLOSURE_STAR_ALL_X:
	      if (!closure_star_is_ok(sc, code, MATCH_SAFE_CLOSURE_STAR, integer(arglist_length(code)))) break;

	    case HOP_SAFE_CLOSURE_STAR_ALL_X:
	      {
		s7_pointer args, p, orig_args, e;
		/* (let () (define* (hi (a 1)) (+ a 1)) (define (ho) (hi (* 2 3))) (ho)) 
		 * (do ((i 0 (+ i 1))) ((= i 11)) (envelope-interp (/ i 21) '(0 0 100 1)))
		 */

		e = closure_let(ecdr(code));
		for (args = cdr(code), p = let_slots(e), orig_args = closure_args(ecdr(code)); 
		     is_pair(args); 
		     args = cdr(args), orig_args = cdr(orig_args), p = next_slot(p))
		  slot_pending_value(p) = ((s7_function)fcdr(args))(sc, car(args));
		
		/* we're out of caller's args, so fill rest of environment slots from the defaults */
		for (; is_slot(p); p = next_slot(p), orig_args = cdr(orig_args))
		  {
		    s7_pointer defval;
		    if (is_pair(car(orig_args)))
		      {
			defval = cadar(orig_args);
			if (is_pair(defval))
			  slot_pending_value(p) = cadr(defval);
			else slot_pending_value(p) = defval;
		      }
		    else slot_pending_value(p) = sc->F;
		  }

		/* we have to put off the actual environment update in case this is a tail recursive call */
		let_id(e) = ++sc->let_number;
		for (p = let_slots(e); is_slot(p); p = next_slot(p))
		  {
		    slot_set_value(p, slot_pending_value(p));
		    symbol_set_local(slot_symbol(p), let_id(e), p);
		  }
		
		sc->envir = e;
		sc->code = closure_body(ecdr(code));
		goto BEGIN1;
	      }


	    case OP_SAFE_CLOSURE_STAR:
	      if (!closure_star_is_ok(sc, code, MATCH_SAFE_CLOSURE_STAR, 0)) {set_optimize_data(code, OP_UNKNOWN); goto OPT_EVAL;}
	      
	    case HOP_SAFE_CLOSURE_STAR:
	      /* (let () (define* (hi (a 100)) (random a)) (define (ho) (hi)) (ho)) */
	      sc->envir = closure_let(ecdr(code));
	      let_id(sc->envir) = ++sc->let_number;
	      car(sc->T2_1) = let_slots(closure_let(ecdr(code)));
	      car(sc->T2_2) = closure_args(ecdr(code));
	      goto FILL_SAFE_CLOSURE_STAR;


	    case OP_SAFE_CLOSURE_STAR_S0:
	      if (!closure_star_is_ok(sc, code, MATCH_SAFE_CLOSURE_STAR, 1)) {set_optimize_data(code, OP_UNKNOWN_G); goto OPT_EVAL;}
	      
	    case HOP_SAFE_CLOSURE_STAR_S0:
	      /* here we know we have (let-set! arg1 'name arg2) (with-env arg1 ...) as the safe closure body.
	       *   since no errors can come from the 1st, there's no need for the procedure env.
	       *   so do the set and with-env by hand, leaving with the env body.
	       */
	      {
		s7_pointer e, arg2, body;

		e = find_symbol_checked(sc, cadr(code));         /* S of S0 above */
		if (e == sc->rootlet)
		  sc->envir = sc->NIL;
		else 
		  {
		    if (!is_let(e))
		      eval_type_error(sc, "with-let takes an environment argument: ~A", e);
		    sc->envir = e;
		  }

		body = closure_body(ecdr(code));
		arg2 = cadr(caddr(car(body)));                   /* car(body): (let-set! gen 'fm fm) for example */
		
		if (e != sc->rootlet)
		  {
		    s7_pointer p;
		    let_id(e) = ++sc->let_number;
		    for (p = let_slots(e); is_slot(p); p = next_slot(p))
		      {
			s7_pointer sym;
			sym = slot_symbol(p);
			symbol_set_local(sym, sc->let_number, p);
			if (sym == arg2)
			  slot_set_value(p, real_zero);          /* if this doesn't happen should we raise an error? (currently let-set! does not) */
		      }
		  }
		sc->code = cddr(cadr(body));
		goto BEGIN1;
	      }
	      
	      
	    case OP_SAFE_CLOSURE_STAR_S:
	      if (!closure_star_is_ok(sc, code, MATCH_SAFE_CLOSURE_STAR, 1)) {set_optimize_data(code, OP_UNKNOWN_G); goto OPT_EVAL;}
	      
	    case HOP_SAFE_CLOSURE_STAR_S:
	      sc->envir = old_frame_with_slot(sc, closure_let(ecdr(code)), find_symbol_checked(sc, fcdr(code)));
	      /* that sets the first arg to the passed symbol value; now set default values, if any */
	      car(sc->T2_1) = next_slot(let_slots(closure_let(ecdr(code))));
	      car(sc->T2_2) = cdr(closure_args(ecdr(code)));
	      /* fall through */

	      FILL_SAFE_CLOSURE_STAR:
		{
		  s7_pointer p, x;
		  x = car(sc->T2_1);
		  p = car(sc->T2_2);
		  for (; is_pair(p); p = cdr(p), x = next_slot(x))
		    {
		      s7_pointer defval;
		      if (is_pair(car(p)))
			{
			  defval = cadar(p);
			  if (is_pair(defval))
			    slot_set_value(x, cadr(defval));
			  else slot_set_value(x, defval);
			}
		      else slot_set_value(x, sc->F);
		      symbol_set_local(slot_symbol(x), let_id(sc->envir), x);
		    }
		  }
		sc->code = closure_body(ecdr(code));
		goto BEGIN1;
	      
	      
	      /* -------------------------------------------------------------------------------- */

	    case OP_GOTO:
	      set_ecdr(code, find_symbol_checked(sc, car(code)));
	      if (!is_goto(ecdr(code)))	{set_optimize_data(code, OP_UNKNOWN); goto OPT_EVAL;}

	    case HOP_GOTO:
	      sc->args = sc->NIL;
	      sc->code = ecdr(code);
	      call_with_exit(sc);
	      goto START;
	      

	    case OP_GOTO_C:
	      /* call-with-exit repeat use internally is very rare, so let's just look it up 
	       */
	      set_ecdr(code, find_symbol_checked(sc, car(code)));
	      if (!is_goto(ecdr(code)))
		{
		  set_optimize_data(code, OP_UNKNOWN_G);
		  goto OPT_EVAL;
		}

	    case HOP_GOTO_C:
	      /* (return #t) -- recognized via OP_UNKNOWN_G, ecdr(code) is the function [parallels OP_CLOSURE_C]
	       */
	      sc->args = cdr(code);
	      sc->code = ecdr(code);
	      call_with_exit(sc);
	      goto START;
	      

	    case OP_GOTO_S:
	      set_ecdr(code, find_symbol_checked(sc, car(code)));
	      if (!is_goto(ecdr(code))) {set_optimize_data(code, OP_UNKNOWN_G); goto OPT_EVAL;}

	    case HOP_GOTO_S:
	      sc->args = list_1(sc, find_symbol_checked(sc, cadr(code)));
	      /* I think this needs listification because call_with_exit might call dynamic unwinders etc. */
	      sc->code = ecdr(code);
	      call_with_exit(sc);
	      goto START;
	      

	    case OP_GOTO_A:
	      set_ecdr(code, find_symbol_checked(sc, car(code)));
	      if (!is_goto(ecdr(code))) {set_optimize_data(code, OP_UNKNOWN_A); goto OPT_EVAL;}

	    case HOP_GOTO_A:
	      sc->args = list_1(sc, ((s7_function)fcdr(cdr(code)))(sc, cadr(code)));
	      sc->code = ecdr(code);
	      call_with_exit(sc);
	      goto START;

	      /* for T_CONTINUATION, set sc->args to list_1(sc, ...) as in goto (and code?), the call_with_current_continuation */
	      

	    case OP_CLOSURE_C:
	      if (!closure_is_ok(sc, code, MATCH_UNSAFE_CLOSURE, 1)) {set_optimize_data(code, OP_UNKNOWN_G); goto OPT_EVAL;}
	      
	    case HOP_CLOSURE_C:
	      sc->value = cadr(code);
	      goto UNSAFE_CLOSURE_ONE;
	      
	      
	    case OP_CLOSURE_Q:
	      if (!closure_is_ok(sc, code, MATCH_UNSAFE_CLOSURE, 1)) {set_optimize_data(code, OP_UNKNOWN_A); goto OPT_EVAL;}
	      
	    case HOP_CLOSURE_Q:
	      sc->value = cadr(cadr(code));
	      goto UNSAFE_CLOSURE_ONE;
	      
	      
	    case OP_CLOSURE_A:
	      if (!closure_is_ok(sc, code, MATCH_UNSAFE_CLOSURE, 1)) {set_optimize_data(code, OP_UNKNOWN_A); goto OPT_EVAL;}
	      if (!indirect_c_function_is_ok(sc, cadr(code))) break;

	    case HOP_CLOSURE_A:
	      sc->value = ((s7_function)fcdr(cdr(code)))(sc, cadr(code));
	      goto UNSAFE_CLOSURE_ONE;
	      
	      
	    case OP_GLOSURE_A:
#if 0
	      if (((symbol_id(car(code)) == 0) && (!is_global(car(code)))) ||
		  ((symbol_id(car(code)) != 0) && (is_global(car(code)))))
		fprintf(stderr, "%s %d %lld\n", DISPLAY(code), is_global(car(code)), symbol_id(car(code)));
#endif
	      if ((symbol_id(car(code)) != 0) ||
		  (ecdr(code) != slot_value(global_slot(car(code)))))
		{set_optimize_data(code, OP_UNKNOWN_A); goto OPT_EVAL;}
	      if (!indirect_c_function_is_ok(sc, cadr(code))) break;

	    case HOP_GLOSURE_A:
	      sc->value = ((s7_function)fcdr(cdr(code)))(sc, cadr(code));
	      goto UNSAFE_CLOSURE_ONE;


	    case OP_GLOSURE_P:
	      if ((symbol_id(car(code)) != 0) || (ecdr(code) != slot_value(global_slot(car(code))))) break;

	    case HOP_GLOSURE_P:
	      push_stack(sc, OP_CLOSURE_P_1, sc->NIL, code);
	      sc->code = cadr(code);
	      goto EVAL;


	    case OP_GLOSURE_S:
	      if ((symbol_id(car(code)) != 0) ||
		  (ecdr(code) != slot_value(global_slot(car(code)))))
		{set_optimize_data(code, OP_UNKNOWN_G); goto OPT_EVAL;}
	      
	    case HOP_GLOSURE_S:
	      sc->value = find_symbol_checked(sc, fcdr(code));
	      goto UNSAFE_CLOSURE_ONE;


	    case OP_CLOSURE_S:
	      if (!closure_is_ok(sc, code, MATCH_UNSAFE_CLOSURE, 1)) {set_optimize_data(code, OP_UNKNOWN_G); goto OPT_EVAL;}
	      
	    case HOP_CLOSURE_S:
	      sc->value = find_symbol_checked(sc, fcdr(code));
	      /* goto UNSAFE_CLOSURE_ONE; */
	      
	    UNSAFE_CLOSURE_ONE:
	      CHECK_STACK_SIZE(sc);
	      code = ecdr(code);
	      NEW_FRAME_WITH_CHECKED_SLOT(sc, closure_let(code), sc->envir, car(closure_args(code)), sc->value);
	      sc->code = closure_body(code);
	      goto BEGIN1;
	      

	    case OP_CLOSURE_SSb:
	      if (!closure_is_ok(sc, code, MATCH_UNSAFE_CLOSURE, 2)) {set_optimize_data(code, OP_UNKNOWN_GG); goto OPT_EVAL;}
	      
	    case HOP_CLOSURE_SSb:
	      {
		s7_pointer p, lx;
		CHECK_STACK_SIZE(sc);
		sc->value = find_symbol_checked(sc, cadr(code));
		lx = find_symbol_checked(sc, fcdr(code));
		code = ecdr(code);
		p = closure_args(code);
		NEW_FRAME_WITH_TWO_SLOTS(sc, closure_let(code), sc->envir, car(p), sc->value, cadr(p), lx);
		sc->code = closure_body(code);
		goto BEGIN1;
	      }


	    case OP_CLOSURE_SS:
	      if (!closure_is_ok(sc, code, MATCH_UNSAFE_CLOSURE, 2)) {set_optimize_data(code, OP_UNKNOWN_GG); goto OPT_EVAL;}
	      
	    case HOP_CLOSURE_SS:
	      /* only called if one of these symbols has an accessor */
	      car(sc->T2_1) = find_symbol_checked(sc, cadr(code));
	      car(sc->T2_2) = find_symbol_checked(sc, fcdr(code));
	      goto UNSAFE_CLOSURE_TWO;
	      
	      
	    case OP_CLOSURE_SC:
	      if (!closure_is_ok(sc, code, MATCH_UNSAFE_CLOSURE, 2)) {set_optimize_data(code, OP_UNKNOWN_GG); goto OPT_EVAL;}
	      
	    case HOP_CLOSURE_SC:
	      car(sc->T2_1) = find_symbol_checked(sc, cadr(code));
	      car(sc->T2_2) = fcdr(code);
	      goto UNSAFE_CLOSURE_TWO;
	      
	      
	    case OP_CLOSURE_CS:
	      if (!closure_is_ok(sc, code, MATCH_UNSAFE_CLOSURE, 2)) {set_optimize_data(code, OP_UNKNOWN_GG); goto OPT_EVAL;}
	      
	    case HOP_CLOSURE_CS:
	      car(sc->T2_1) = cadr(code);
	      car(sc->T2_2) = find_symbol_checked(sc, fcdr(code));
	      goto UNSAFE_CLOSURE_TWO;
	      
	      
	    case OP_CLOSURE_opSq_opSq:
	      if (!closure_is_ok(sc, code, MATCH_UNSAFE_CLOSURE, 2)) {set_optimize_data(code, OP_UNKNOWN_AA); goto OPT_EVAL;}
	      if ((!indirect_c_function_is_ok(sc, cadr(code))) || (!indirect_c_function_is_ok(sc, caddr(code)))) break;
	      
	    case HOP_CLOSURE_opSq_opSq:
	      {
		s7_pointer args;
		args = cdr(code);
		car(sc->T1_1) = find_symbol_checked(sc, fcdr(args));
		car(sc->T2_1) = c_call(car(args))(sc, sc->T1_1);
		car(sc->T1_1) = find_symbol_checked(sc, fcdr(code));
		car(sc->T2_2) = c_call(cadr(args))(sc, sc->T1_1);
		goto UNSAFE_CLOSURE_TWO;
	      }


	    case OP_CLOSURE_AA:
	      if (!closure_is_ok(sc, code, MATCH_UNSAFE_CLOSURE, 2)) {set_optimize_data(code, OP_UNKNOWN_AA); goto OPT_EVAL;}
	      if ((is_optimized(cadr(code))) && (!indirect_c_function_is_ok(sc, cadr(code)))) break;
	      if ((is_optimized(caddr(code))) && (!indirect_c_function_is_ok(sc, caddr(code)))) break;
	      
	    case HOP_CLOSURE_AA:
	      {
		s7_pointer args;
		args = cdr(code);
		sc->temp2 = ((s7_function)fcdr(args))(sc, car(args));
		args = cdr(args);
		car(sc->T2_2) = ((s7_function)fcdr(args))(sc, car(args));
		car(sc->T2_1) = sc->temp2; /* the all_x functions use temp3 */
		goto UNSAFE_CLOSURE_TWO;
	      }
	      

	    case OP_CLOSURE_ALL_S:
	      if (!closure_is_ok(sc, code, MATCH_UNSAFE_CLOSURE, integer(arglist_length(code))))
		{
		  set_optimize_data(code, OP_UNKNOWN_ALL_S);
		  goto OPT_EVAL;
		}

	    case HOP_CLOSURE_ALL_S:
	      {
		s7_pointer args, p, func, e;
		/* in this case, we have just lambda (not lambda*), and no dotted arglist,
		 *   and no accessed symbols in the arglist, and we know the arglist matches the parameter list.
		 */
		CHECK_STACK_SIZE(sc);

		func = ecdr(code);
		/* we need to get the slot names from the current function, but the values from the calling environment
		 */
		NEW_FRAME(sc, closure_let(func), e);
		sc->z = e;
		for (p = closure_args(func), args = cdr(code); is_pair(p); p = cdr(p), args = cdr(args))
		  ADD_SLOT(e, car(p), find_symbol_checked(sc, car(args)));
		sc->envir = e;
		sc->z = sc->NIL;

		sc->code = closure_body(func);
		goto BEGIN1;
	      }

	      
	    case OP_CLOSURE_ALL_X:
	      if (!closure_is_ok(sc, code, MATCH_UNSAFE_CLOSURE, integer(arglist_length(code)))) 
		{
		  set_optimize_data(code, OP_UNKNOWN_ALL_X);
		  goto OPT_EVAL;
		}

	    case HOP_CLOSURE_ALL_X:
	      {
		/* here also, all the args are simple, major case is 3 args but would closure_aaa be faster?
		 */
		s7_pointer args, p, func, e;
		CHECK_STACK_SIZE(sc);

		func = ecdr(code);
		NEW_FRAME(sc, closure_let(func), e);
		sc->z = e;

		for (p = closure_args(func), args = cdr(code); is_pair(p); p = cdr(p), args = cdr(args))
		  {
		    s7_pointer val;
		    val = ((s7_function)fcdr(args))(sc, car(args));
		    /* can't use ADD_SLOT here -- not sure I understand the problem */
		    ADD_SLOT_CHECKED(e, car(p), val);
		  }
		sc->envir = e;
		sc->z = sc->NIL;

		sc->code = closure_body(func);
		goto BEGIN1;
	      }
	      
	      
	    case OP_CLOSURE_opSq_S:
	      if (!closure_is_ok(sc, code, MATCH_UNSAFE_CLOSURE, 2)) {set_optimize_data(code, OP_UNKNOWN_AA); goto OPT_EVAL;}
	      if (!indirect_c_function_is_ok(sc, cadr(code))) break;
	      
	    case HOP_CLOSURE_opSq_S:
	      {
		s7_pointer args;
		car(sc->T2_2) = find_symbol_checked(sc, fcdr(code));
		args = cadr(code);
		car(sc->T1_1) = find_symbol_checked(sc, cadr(args));
		car(sc->T2_1) = c_call(args)(sc, sc->T1_1);
		goto UNSAFE_CLOSURE_TWO;
	      }
	      
	      
	    case OP_CLOSURE_S_opSq:
	      if (!closure_is_ok(sc, code, MATCH_UNSAFE_CLOSURE, 2)) {set_optimize_data(code, OP_UNKNOWN_AA); goto OPT_EVAL;}
	      if (!indirect_c_function_is_ok(sc, caddr(code))) break;
	      
	    case HOP_CLOSURE_S_opSq:
	      {
		s7_pointer args;
		car(sc->T2_1) = find_symbol_checked(sc, cadr(code));
		args = caddr(code);
		car(sc->T1_1) = find_symbol_checked(sc, cadr(args));
		car(sc->T2_2) = c_call(args)(sc, sc->T1_1);
		/* goto UNSAFE_CLOSURE_TWO; */
	      }
	      

	    UNSAFE_CLOSURE_TWO:
	      {
		s7_pointer args;
		CHECK_STACK_SIZE(sc);
		code = ecdr(sc->code);
		args = closure_args(code);

		NEW_FRAME_WITH_CHECKED_SLOT(sc, closure_let(code), sc->envir, car(args), car(sc->T2_1));
		ADD_SLOT(sc->envir, cadr(args), car(sc->T2_2));

		sc->code = closure_body(code);
		goto BEGIN1;
	      }
	      
	      
	    case OP_CLOSURE_S_opSSq:
	      if (!closure_is_ok(sc, code, MATCH_UNSAFE_CLOSURE, 2)) {set_optimize_data(code, OP_UNKNOWN_AA); goto OPT_EVAL;}
	      if (!indirect_c_function_is_ok(sc, caddr(code))) break;
	      
	    case HOP_CLOSURE_S_opSSq:
	      {
		s7_pointer args;
		car(sc->T1_1) = find_symbol_checked(sc, cadr(code));
		args = caddr(code);
		car(sc->T2_1) = find_symbol_checked(sc, cadr(args));
		car(sc->T2_2) = find_symbol_checked(sc, caddr(args));
		car(sc->T2_2) = c_call(args)(sc, sc->T2_1);
		car(sc->T2_1) = car(sc->T1_1);
		goto UNSAFE_CLOSURE_TWO;
	      }
	      
	      
	    case OP_CLOSURE_opSSq_S:
	      if (!closure_is_ok(sc, code, MATCH_UNSAFE_CLOSURE, 2)) {set_optimize_data(code, OP_UNKNOWN_AA); goto OPT_EVAL;}
	      if (!indirect_c_function_is_ok(sc, cadr(code))) break;
	      
	    case HOP_CLOSURE_opSSq_S:
	      {
		/* (let () (define (hi a b) (set! a (+ a 1)) (+ a b)) (define (ho a b c) (hi (log a b) c)) (ho 8 2 3)) -> 7 */
		s7_pointer args;

		car(sc->T1_1) = find_symbol_checked(sc, caddr(code));
		args = cadr(code);
		car(sc->T2_1) = find_symbol_checked(sc, cadr(args));
		car(sc->T2_2) = find_symbol_checked(sc, caddr(args));
		car(sc->T2_1) = c_call(args)(sc, sc->T2_1);
		car(sc->T2_2) = car(sc->T1_1);
		goto UNSAFE_CLOSURE_TWO;
	      }
	      

	      /* -------------------------------------------------------------------------------- */

	    case OP_CLOSURE_STAR_ALL_X:
	      if (!closure_star_is_ok(sc, code, MATCH_UNSAFE_CLOSURE_STAR, integer(arglist_length(code))))
		{
		  set_optimize_data(code, OP_UNKNOWN_ALL_X);
		  goto OPT_EVAL;
		}

	    case HOP_CLOSURE_STAR_ALL_X:
	      {
		/* here also, all the args are simple */
		/* (let () (define* (hi (a 1)) (list a)) (define (ho) (hi (* 2 3))) (ho)) 
		 */
		s7_pointer args, p, func, new_args;

		func = ecdr(code);
		sc->args = make_list(sc, closure_star_arity_to_int(sc, func), sc->NIL);
		new_args = sc->args;

		for (p = closure_args(func), args = cdr(code); is_pair(args); p = cdr(p), args = cdr(args), new_args = cdr(new_args))
		  car(new_args) = ((s7_function)fcdr(args))(sc, car(args));

		for (; is_pair(p); p = cdr(p), new_args = cdr(new_args))
		  {
		    s7_pointer defval;
		    if (is_pair(car(p)))
		      {
			defval = cadar(p);
			if (is_pair(defval))
			  car(new_args) = cadr(defval);
			else car(new_args) = defval;
		      }
		    else car(new_args) = sc->F;
		  }
		sc->code = ecdr(code);
		goto UNSAFE_CLOSURE_STAR;
	      }


	    UNSAFE_CLOSURE_STAR_TWO:
	      {
		s7_pointer args;
		code = ecdr(sc->code);
		args = closure_args(code);

		NEW_FRAME_WITH_CHECKED_SLOT(sc, closure_let(code), sc->envir, (is_pair(car(args))) ? caar(args) : car(args), car(sc->T2_1));
		ADD_SLOT(sc->envir, (is_pair(cadr(args))) ? caadr(args) : cadr(args), car(sc->T2_2));

		sc->code = closure_body(code);
		goto BEGIN1;
	      }
	      

	    case OP_CLOSURE_STAR_SX:
	      if (!closure_star_is_ok(sc, code, MATCH_UNSAFE_CLOSURE_STAR, 2)) {set_optimize_data(code, OP_UNKNOWN_GG); goto OPT_EVAL;}
	      
	    case HOP_CLOSURE_STAR_SX:
	      {
		s7_pointer val1, val2, args;
		args = cddr(closure_args(ecdr(code)));
		val1 = find_symbol_checked(sc, cadr(code));
		val2 = caddr(code);
		if (is_symbol(val2))
		  val2 = find_symbol_checked(sc, val2);
		if (is_null(args))
		  {
		    car(sc->T2_1) = val1;
		    car(sc->T2_2) = val2;
		    goto UNSAFE_CLOSURE_STAR_TWO;
		  }

		sc->args = list_2(sc, val2, val1);
		sc->value = args;
		goto FILL_CLOSURE_STAR;
	      }


	    case OP_CLOSURE_STAR:
	      if (!closure_star_is_ok(sc, code, MATCH_UNSAFE_CLOSURE_STAR, 0)) {set_optimize_data(code, OP_UNKNOWN); goto OPT_EVAL;}
	      
	    case HOP_CLOSURE_STAR:
	      /* (let () (define* (hi (a 1)) (list a)) (define (ho) (hi)) (ho)) */
	      sc->value = closure_args(ecdr(code));
	      sc->args = sc->NIL;
	      goto FILL_CLOSURE_STAR;


	    case OP_CLOSURE_STAR_S:
	      if (!closure_star_is_ok(sc, code, MATCH_UNSAFE_CLOSURE_STAR, 1)) {set_optimize_data(code, OP_UNKNOWN_G); goto OPT_EVAL;}
	      
	    case HOP_CLOSURE_STAR_S:
	      sc->value = cdr(closure_args(ecdr(code)));
	      sc->args = list_1(sc, find_symbol_checked(sc, fcdr(code)));
	      /* fall through */


	    FILL_CLOSURE_STAR:
	      {
		s7_pointer p;
		p = sc->value;
		for (; is_pair(p); p = cdr(p))
		  {
		    s7_pointer defval;
		    if (is_pair(car(p)))
		      {
			defval = cadar(p);
			if (is_pair(defval))
			  sc->args = cons(sc, cadr(defval), sc->args);
			else sc->args = cons(sc, defval, sc->args);
		      }
		    else sc->args = cons(sc, sc->F, sc->args);
		  }
		sc->args = safe_reverse_in_place(sc, sc->args);
		sc->code = ecdr(code);
		/* fall through */
	      }

	    UNSAFE_CLOSURE_STAR:
	      
	      NEW_FRAME(sc, closure_let(sc->code), sc->envir);
	      {
		s7_pointer x, z, e;
		unsigned long long int id;
		
		e = sc->envir;
		id = let_id(e);
		
		for (x = closure_args(sc->code), z = sc->args; is_pair(x); x = cdr(x))
		  {
		    s7_pointer sym, args, val;
		    if (is_pair(car(x)))
		      sym = caar(x);
		    else sym = car(x);
		    val = car(z);
		    args = cdr(z);

		    slot_symbol(z) = sym;
		    symbol_set_local(sym, id, z);
		    slot_set_value(z, val);
		    set_type(z, T_SLOT | T_IMMUTABLE);
		    next_slot(z) = let_slots(e);
		    let_slots(e) = z;
		    z = args;
		  }
		
		sc->code = closure_body(sc->code);
		goto BEGIN1;
	      }

	      
	      /* -------------------------------------------------------------------------------- */

	      #define UNPRINT 0
	      #define UNGAD 0
	    case OP_UNKNOWN:
	    case HOP_UNKNOWN:
	      {
		s7_pointer f;
		f = find_symbol_checked(sc, car(code));
		if (UNGAD) fprintf(stderr, "%s: %s %s\n", opt_name(code), type_name(sc, f, NO_ARTICLE), DISPLAY(code));
		switch (type(f))
		  {
		  case T_C_OBJECT:
		    if (s7_is_aritable(sc, f, 0))
		      set_opt_and_goto_opt_eval(code, f, OP_C_OBJECT);
		    break;

		  case T_GOTO:
		    set_opt_and_goto_opt_eval(code, f, OP_GOTO);

		  case T_CLOSURE:
		    if (has_methods(f)) break;
		    if (is_null(closure_args(f)))
		      set_opt_and_goto_opt_eval(code, f, (is_safe_closure(f)) ? OP_SAFE_THUNK : OP_THUNK);
		    /* we can't ignore the recheck here (i.e. set the hop bit) because the closure, even if a global can be set later:
		     *   (begin (define *x* #f) (define (test) (display (*x*))) (define (setx n) (set! *x* (lambda () n))) (setx 1) (test) (setx 2) (test))
		     * this is a case where the name matters (we need a pristine global), so it's easily missed.
		     */
		    break;
		    
		  case T_CLOSURE_STAR:
		    if (has_methods(f)) break;
		    if (has_simple_args(closure_body(f)))
		      set_opt_and_goto_opt_eval(code, f, ((is_safe_closure(f)) ? OP_SAFE_CLOSURE_STAR : OP_CLOSURE_STAR));
		    break;
		    
		  default:
		    if (UNPRINT) fprintf(stderr, "    un: found %s %s\n", type_name(sc, f, 0), DISPLAY(code));
		    break;
		  }
		break;
	      }
		    
	      
	    case OP_UNKNOWN_G:
	    case HOP_UNKNOWN_G:
	      {
		s7_pointer f;
		bool sym_case;
		f = find_symbol_checked(sc, car(code));
		sym_case = is_symbol(cadr(code));

		if (UNGAD) fprintf(stderr, "%s: %s %s\n", opt_name(code), type_name(sc, f, NO_ARTICLE), DISPLAY(code));
		
		switch (type(f))
		  {
		  case T_C_FUNCTION: case T_C_FUNCTION_STAR: case T_C_ANY_ARGS_FUNCTION: case T_C_OPT_ARGS_FUNCTION: case T_C_RST_ARGS_FUNCTION:
		    if (s7_is_aritable(sc, f, 1))
		      {
			if (sym_case)
			  {
			    set_optimize_data(code, (is_safe_procedure(f)) ? OP_SAFE_C_S : OP_C_S);
			    set_c_function(code, f);
			    goto OPT_EVAL;
			  }
			else 
			  {
			    if (is_safe_procedure(f))
			      {
				set_optimize_data(code, OP_SAFE_C_C);
				set_c_function(code, f);
				goto OPT_EVAL;
			      }
			  }
		      }
		    break;
		    
		  case T_CLOSURE:
		    if (has_methods(f)) break;
		    if (closure_arity_to_int(sc, f) == 1)
		      {
			set_fcdr(code, cadr(code));
			if (sym_case)
			  {
			    if (is_safe_closure(f))
			      set_optimize_data(code, (is_global(car(code))) ? OP_SAFE_GLOSURE_S : OP_SAFE_CLOSURE_S);
			    else set_optimize_data(code, (is_global(car(code))) ? OP_GLOSURE_S : OP_CLOSURE_S);
			  }
			else set_optimize_data(code, (is_safe_closure(f)) ? OP_SAFE_CLOSURE_C : OP_CLOSURE_C);
			set_ecdr(code, f);
			goto OPT_EVAL;
		      }
		    break;

		  case T_CLOSURE_STAR:
		    if (sym_case)
		      {
			if (has_methods(f)) break;
			if ((has_simple_args(closure_body(f))) &&
			    (!is_null(closure_args(f))))
			  {
			    set_fcdr(code, cadr(code));
			    set_opt_and_goto_opt_eval(code, f, (is_safe_closure(f)) ? OP_SAFE_CLOSURE_STAR_S : OP_CLOSURE_STAR_S);
			  }
		      }
		    break;
		    
		  case T_INT_VECTOR:
		  case T_FLOAT_VECTOR:
		  case T_VECTOR:
		    if ((sym_case) ||
			(is_integer(cadr(code))))      /* (v 4/3) */
		      set_opt_and_goto_opt_eval(code, f, (sym_case) ? OP_VECTOR_S : OP_VECTOR_C);
		    break;

		  case T_STRING:
		    set_opt_and_goto_opt_eval(code, f, (sym_case) ? OP_STRING_S : OP_STRING_C);
			
		  case T_PAIR:
		    set_opt_and_goto_opt_eval(code, f, (sym_case) ? OP_PAIR_S : OP_PAIR_C);
			
		  case T_C_OBJECT:
		    if (s7_is_aritable(sc, f, 1))
		      set_opt_and_goto_opt_eval(code, f, (sym_case) ? OP_C_OBJECT_S : OP_C_OBJECT_C);
		    break;
		    
		  case T_ENVIRONMENT:
		    set_opt_and_goto_opt_eval(code, f, (sym_case) ? OP_ENVIRONMENT_S : OP_ENVIRONMENT_C);
		    
		  case T_HASH_TABLE:
		    set_opt_and_goto_opt_eval(code, f, (sym_case) ? OP_HASH_TABLE_S : OP_HASH_TABLE_C);
		    
		  case T_GOTO:
		    set_opt_and_goto_opt_eval(code, f, (sym_case) ? OP_GOTO_S : OP_GOTO_C);
		    
		  default:
		    if (UNPRINT) fprintf(stderr, "    un_s: found %s %s\n", type_name(sc, f, 0), DISPLAY(code)); 
		    break;
		  }
		break;
	      }
						

	    case OP_UNKNOWN_GG:
	    case HOP_UNKNOWN_GG:
	      {
		s7_pointer f;
		bool s1, s2;

		f = find_symbol_checked(sc, car(code));
		s1 = is_symbol(cadr(code));
		s2 = is_symbol(caddr(code));

		if (UNGAD) fprintf(stderr, "%s: %s %s\n", opt_name(code), type_name(sc, f, NO_ARTICLE), DISPLAY(code));

		if (s7_is_aritable(sc, f, 2))
		  {
		    switch (type(f))
		      {
		      case T_CLOSURE:
			if (has_methods(f)) break;
			if (closure_arity_to_int(sc, f) == 2)
			  {
			    if (s1)
			      {
				if (is_safe_closure(f))
				  set_optimize_data(code, (s2) ? OP_SAFE_CLOSURE_SS : OP_SAFE_CLOSURE_SC);
				else 
				  {
				    set_optimize_data(code, (s2) ? OP_CLOSURE_SS : OP_CLOSURE_SC);
				    if ((s2) && (!arglist_has_rest(sc, closure_args(f))))
				      set_optimize_data(code, OP_CLOSURE_SSb);
				  }
			      }
			    else 
			      {
				if (!s2) break;
				set_optimize_data(code, (is_safe_closure(f)) ? OP_SAFE_CLOSURE_CS : OP_CLOSURE_CS);
			      }
			    set_ecdr(code, f);
			    set_fcdr(code, caddr(code));
			    goto OPT_EVAL;
			  }
			break;

		      case T_CLOSURE_STAR:
			if (has_methods(f)) break;
			/* the closure* opts assume args are not keywords, but we can check that! */
			
			if (s1)
			  {
			    if (s2)
			      {
				if ((!is_keyword(cadr(code))) &&
				    (!is_keyword(caddr(code))) &&
				    (has_simple_args(closure_body(f))) &&
				    (closure_star_arity_to_int(sc, f) >= 2))
				  {
				    set_fcdr(code, caddr(code));
				    set_opt_and_goto_opt_eval(code, f, (is_safe_closure(f)) ? OP_SAFE_CLOSURE_STAR_SS : OP_CLOSURE_STAR_SX);
				  }
			      }
			    else
			      {
				if ((!is_keyword(cadr(code))) &&
				    (has_simple_args(closure_body(f))) &&
				    (closure_star_arity_to_int(sc, f) >= 2))
				  set_opt_and_goto_opt_eval(code, f, (is_safe_closure(f)) ? OP_SAFE_CLOSURE_STAR_SC : OP_CLOSURE_STAR_SX);
			      }
			  }
			break;

		      case T_C_FUNCTION: case T_C_FUNCTION_STAR: case T_C_ANY_ARGS_FUNCTION: case T_C_OPT_ARGS_FUNCTION: case T_C_RST_ARGS_FUNCTION:
			if (is_safe_procedure(f))
			  {
			    if (s1)
			      {
				set_optimize_data(code, (s2) ? OP_SAFE_C_SS : OP_SAFE_C_SC);
			      }
			    else
			      {
				set_optimize_data(code, (s2) ? OP_SAFE_C_CS : OP_SAFE_C_C);
			      }
			  }
			else
			  {
			    set_optimize_data(code, OP_C_ALL_X);
			    annotate_args(sc, cdr(code));
			  }
 			set_arglist_length(code, small_int(2));
  			set_c_function(code, f);
  			goto OPT_EVAL;

		      case T_C_OBJECT:
			if (s1 == s2)
			  {
			    set_opt_and_goto_opt_eval(code, f, (s1) ? OP_C_OBJECT_SS : OP_C_OBJECT_CC);
			  }
			break;

		      case T_INT_VECTOR:
		      case T_FLOAT_VECTOR:
		      case T_VECTOR:
			if ((is_integer(cadr(code))) &&       /* !s1 obviously) */
			    (s7_integer(cadr(code)) >= 0) &&
			    (is_integer(caddr(code))) &&
			    (s7_integer(caddr(code)) >= 0))
			  set_opt_and_goto_opt_eval(code, f, OP_VECTOR_CC);
			break;

		      default:
			if (UNPRINT) fprintf(stderr, "    un_ss: found %s %s\n", type_name(sc, f, 0), DISPLAY(code)); 
			break;
		      }
		  }
	      }
	      break;
	      	      
	      
	    case OP_UNKNOWN_ALL_S:
	    case HOP_UNKNOWN_ALL_S:
	      {
		s7_pointer f;
		int num_args;
		f = find_symbol_checked(sc, car(code));
		if (UNGAD) fprintf(stderr, "%s: %s %s\n", opt_name(code), type_name(sc, f, NO_ARTICLE), DISPLAY(code));

		num_args = integer(arglist_length(code));
		if (s7_is_aritable(sc, f, num_args))
		  {
		    switch (type(f))
		      {
		      case T_CLOSURE:
			if (has_methods(f)) break;
			if ((closure_arity_to_int(sc, f) == num_args) &&
			    (!arglist_has_rest(sc, closure_args(f))))
			  {
			    annotate_args(sc, cdr(code));
			    set_opt_and_goto_opt_eval(code, f, (is_safe_closure(f)) ? OP_SAFE_CLOSURE_ALL_X : OP_CLOSURE_ALL_S);
			  }
			break;
			
		      case T_C_FUNCTION: case T_C_FUNCTION_STAR: case T_C_ANY_ARGS_FUNCTION: case T_C_OPT_ARGS_FUNCTION: case T_C_RST_ARGS_FUNCTION:
			if (is_safe_procedure(f))
			  set_optimize_data(code, OP_SAFE_C_ALL_S);
			else
			  {
			    set_optimize_data(code, OP_C_ALL_X);
			    annotate_args(sc, cdr(code));
			  }
			set_c_function(code, f);
			goto OPT_EVAL;
			
		      default:
			if (UNPRINT) fprintf(stderr, "    un_all_s: found %s %s\n", type_name(sc, f, 0), DISPLAY(code));
			break;
		      }
		  }
	      }
	      break;
	      
	      
	    case OP_UNKNOWN_A:
	    case HOP_UNKNOWN_A:
	      {
		s7_pointer f;
		f = find_symbol_checked(sc, car(code));
		if (UNGAD) fprintf(stderr, "%s: %s %s\n", opt_name(code), type_name(sc, f, NO_ARTICLE), DISPLAY(code));
		
		if (s7_is_aritable(sc, f, 1))
		  {
		    set_arglist_length(code, small_int(1));
		    annotate_args(sc, cdr(code));

		    switch (type(f))
		      {
		      case T_INT_VECTOR:
		      case T_FLOAT_VECTOR:
		      case T_VECTOR:
			set_opt_and_goto_opt_eval(code, f, OP_VECTOR_A);
			
		      case T_C_FUNCTION: case T_C_FUNCTION_STAR: case T_C_ANY_ARGS_FUNCTION: case T_C_OPT_ARGS_FUNCTION: case T_C_RST_ARGS_FUNCTION:
			if ((is_safe_procedure(f)) &&
			    (is_optimized(cadr(code))))
			  {
			    int op;
			    op = combine_ops(sc, E_C_P, code, cadr(code));
			    set_optimize_data(code, op);
			    if ((op == OP_SAFE_C_Z) &&
				(is_all_x_op(optimize_data(cadr(code)))))
			      set_optimize_data(code, OP_SAFE_C_A);
			    set_c_function(code, f);
			    goto OPT_EVAL;
			  }
			if ((is_pair(cadr(code))) && (caadr(code) == sc->QUOTE))
			  set_optimize_data(code, (is_safe_procedure(f)) ? OP_SAFE_C_Q : OP_C_A);
			else set_optimize_data(code, (is_safe_procedure(f)) ? OP_SAFE_C_A : OP_C_A);
			set_c_function(code, f);
			goto OPT_EVAL;
			
		      case T_CLOSURE:
			if (has_methods(f)) break;
			if (closure_arity_to_int(sc, f) == 1)
			  {
			    if ((is_pair(cadr(code))) && (caadr(code) == sc->QUOTE))
			      {
				set_opt_and_goto_opt_eval(code, f, (is_safe_closure(f)) ? OP_SAFE_CLOSURE_Q : OP_CLOSURE_Q);
			      }
			    else 
			      {
				if (is_safe_closure(f))
				  set_optimize_data(code, (is_global(car(code))) ? OP_SAFE_GLOSURE_A : OP_SAFE_CLOSURE_A);
				else set_optimize_data(code, (is_global(car(code))) ? OP_GLOSURE_A : OP_CLOSURE_A);
			      }
			    set_ecdr(code, f);
			    goto OPT_EVAL;
			  }
			break;

		      case T_CLOSURE_STAR:
			if (has_methods(f)) break;
			if ((has_simple_args(closure_body(f))) &&
			    (closure_star_arity_to_int(sc, f) >= 1) && 
			    (!arglist_has_keyword(cdr(code))) &&
			    (!arglist_has_rest(sc, closure_args(f))))
			  {
			    set_opt_and_goto_opt_eval(code, f, (is_safe_closure(f)) ? OP_SAFE_CLOSURE_STAR_ALL_X : OP_CLOSURE_STAR_ALL_X);
			  }
			break;

		      case T_STRING:
			set_opt_and_goto_opt_eval(code, f, OP_STRING_A);
			
		      case T_PAIR:
			set_opt_and_goto_opt_eval(code, f, OP_PAIR_A);
			
		      case T_C_OBJECT:
			set_opt_and_goto_opt_eval(code, f, OP_C_OBJECT_A);
			
		      case T_ENVIRONMENT:
			set_opt_and_goto_opt_eval(code, f, ((is_pair(cadr(code))) && (caadr(code) == sc->QUOTE)) ? OP_ENVIRONMENT_Q : OP_ENVIRONMENT_A);
			
		      case T_HASH_TABLE:
			set_opt_and_goto_opt_eval(code, f, OP_HASH_TABLE_A);
			
		      case T_GOTO:
			set_opt_and_goto_opt_eval(code, f, OP_GOTO_A);
			
		      default:
			if (UNPRINT) fprintf(stderr, "    un_a: found %s %s\n", type_name(sc, f, 0), DISPLAY(code));
			break;
		      }
		  }
		break;
	      }


	    case OP_UNKNOWN_AA:
	    case HOP_UNKNOWN_AA:
	      {
		s7_pointer f;
		f = find_symbol_checked(sc, car(code));
		if (UNGAD) fprintf(stderr, "%s: %s %s\n", opt_name(code), type_name(sc, f, NO_ARTICLE), DISPLAY(code));

		if (s7_is_aritable(sc, f, 2))
		  {
		    set_arglist_length(code, small_int(2));
		    annotate_args(sc, cdr(code));

		    switch (type(f))
		      {
		      case T_CLOSURE:
			if (has_methods(f)) break;
			if ((closure_arity_to_int(sc, f) == 2) &&
			    (!arglist_has_rest(sc, closure_args(f))))
			  {
			    set_optimize_data(code, (is_safe_closure(f)) ? OP_SAFE_CLOSURE_AA : OP_CLOSURE_AA);
			    set_ecdr(code, f);
			    goto OPT_EVAL;
			  }
			break;

		      case T_CLOSURE_STAR:
			if (has_methods(f)) break;
			if ((has_simple_args(closure_body(f))) &&
			    (closure_star_arity_to_int(sc, f) >= 2) && 
			    (!arglist_has_keyword(cdr(code))) &&
			    (!arglist_has_rest(sc, closure_args(f))))
			  {
			    set_opt_and_goto_opt_eval(code, f, (is_safe_closure(f)) ? OP_SAFE_CLOSURE_STAR_ALL_X : OP_CLOSURE_STAR_ALL_X);
			  }
			break;

		      case T_C_FUNCTION: case T_C_FUNCTION_STAR: case T_C_ANY_ARGS_FUNCTION: case T_C_OPT_ARGS_FUNCTION: case T_C_RST_ARGS_FUNCTION:
			set_optimize_data(code, (is_safe_procedure(f)) ? OP_SAFE_C_AA : OP_C_ALL_X);
			set_c_function(code, f);
			goto OPT_EVAL;

		      default:
			if (UNPRINT) fprintf(stderr, "    un_all_x: found %s %s\n", type_name(sc, f, 0), DISPLAY(code));
			break;
		      }
		  }
	      }
	      break;

	      
	    case OP_UNKNOWN_ALL_X:
	    case HOP_UNKNOWN_ALL_X:
	      {
		s7_pointer f;
		int num_args;
		f = find_symbol_checked(sc, car(code));
		if (UNGAD) fprintf(stderr, "%s: %s %s\n", opt_name(code), type_name(sc, f, NO_ARTICLE), DISPLAY(code));

		num_args = integer(arglist_length(code));
		if (s7_is_aritable(sc, f, num_args))
		  {
		    switch (type(f))
		      {
		      case T_CLOSURE:
			if (has_methods(f)) break;
			if ((closure_arity_to_int(sc, f) == num_args) &&
			    (!arglist_has_rest(sc, closure_args(f))))
			  {
			    annotate_args(sc, cdr(code));
			    if (is_safe_closure(f))
			      {
				if ((is_symbol(cadr(code))) &&
				    (num_args == 3))
				  set_optimize_data(code, OP_SAFE_CLOSURE_SAA);
				else set_optimize_data(code, OP_SAFE_CLOSURE_ALL_X);
			      }
			    else set_optimize_data(code, OP_CLOSURE_ALL_X);
			    set_ecdr(code, f);
			    goto OPT_EVAL;
			  }
			break;

		      case T_CLOSURE_STAR:
			if (has_methods(f)) break;
			if ((has_simple_args(closure_body(f))) &&
			    (closure_star_arity_to_int(sc, f) >= num_args) && 
			    (!arglist_has_keyword(cdr(code))) &&
			    (!arglist_has_rest(sc, closure_args(f))))
			    /* fcdr is already set -- we use it to get num_args above! */
			  {
			    annotate_args(sc, cdr(code));
			    set_opt_and_goto_opt_eval(code, f, (is_safe_closure(f)) ? OP_SAFE_CLOSURE_STAR_ALL_X : OP_CLOSURE_STAR_ALL_X);
			  }
			break;

		      case T_C_FUNCTION: case T_C_FUNCTION_STAR: case T_C_ANY_ARGS_FUNCTION: case T_C_OPT_ARGS_FUNCTION: case T_C_RST_ARGS_FUNCTION:
			set_optimize_data(code, (is_safe_procedure(f)) ? OP_SAFE_C_ALL_X : OP_C_ALL_X);
			annotate_args(sc, cdr(code));
			set_c_function(code, f);
			goto OPT_EVAL;

		      default:
			if (UNPRINT) fprintf(stderr, "    un_all_x: found %s %s\n", type_name(sc, f, 0), DISPLAY(code));
			break;
		      }
		  }
	      }
	      break;

	      /* -------------------------------------------------------------------------------- */	      

	      
	    case OP_SAFE_C_opVSq_S:
	      if (!c_function_is_ok(sc, code)) break;

	    case HOP_SAFE_C_opVSq_S:
	      {
		s7_pointer f;
		f = find_symbol_checked(sc, car(cadr(code)));
		if ((s7_is_vector(f)) &&
		    (vector_rank(f) == 1))
		  {
		    /* f is a vector, we have (op (f s1) s2)
		     *   get s1 and check int/bounds -> val1
		     *   get s2 -> val2
		     *   c_call(op...)
		     */
		    s7_pointer ind;
		    ind = find_symbol_checked(sc, cadr(cadr(code)));
		    if (s7_is_integer(ind))
		      {
			s7_Int index;
			index = s7_integer(ind);
			if ((index < vector_length(f)) &&
			    (index >= 0))
			  {
			    car(sc->T2_2) = find_symbol_checked(sc, caddr(code));
			    car(sc->T2_1) = vector_getter(f)(sc, f, index);
			    sc->value = c_call(code)(sc, sc->T2_1);
			    goto START; 
			  }
		      }
		  }

		/* fallback on unknown_g */
		set_optimize_data(code, (optimize_data(code) & 1) + OP_SAFE_C_PS);
		goto OPT_EVAL;
	      }


	    case OP_SAFE_C_S_opVSq:
	      if (!c_function_is_ok(sc, code)) break;

	    case HOP_SAFE_C_S_opVSq:
	      {
		s7_pointer f, arg2;
		arg2 = caddr(code);
		f = find_symbol_checked(sc, car(arg2));
		if ((s7_is_vector(f)) &&
		    (vector_rank(f) == 1))
		  {
		    s7_pointer ind;
		    ind = find_symbol_checked(sc, cadr(arg2));
		    if (s7_is_integer(ind))
		      {
			s7_Int index;
			index = s7_integer(ind);
			if ((index < vector_length(f)) &&
			    (index >= 0))
			  {
			    car(sc->T2_1) = find_symbol_checked(sc, cadr(code));
			    car(sc->T2_2) = vector_getter(f)(sc, f, index);
			    sc->value = c_call(code)(sc, sc->T2_1);
			    goto START; 
			  }
		      }
		  }
		set_optimize_data(code, (optimize_data(code) & 1) + OP_SAFE_C_SP);
		goto OPT_EVAL;
	      }


	    case OP_SAFE_C_opVSq_opVSq:
	      if (!c_function_is_ok(sc, code)) break;

	    case HOP_SAFE_C_opVSq_opVSq:
	      {
		s7_pointer f1, arg1;
		arg1 = cadr(code);
		f1 = find_symbol_checked(sc, car(arg1));
		if ((s7_is_vector(f1)) &&
		    (vector_rank(f1) == 1))
		  {
		    s7_pointer f2, arg2;
		    arg2 = caddr(code);
		    f2 = find_symbol_checked(sc, car(arg2));
		    if ((s7_is_vector(f2)) &&
			(vector_rank(f2) == 1))
		      {
			s7_pointer ind;
			ind = find_symbol_checked(sc, cadr(arg1));
			if (s7_is_integer(ind))
			  {
			    s7_Int index1;
			    index1 = s7_integer(ind);
			    if ((index1 < vector_length(f1)) &&
				(index1 >= 0))
			      {
				s7_Int index2;
				if (cadr(arg1) == cadr(arg2))
				  index2 = index1;
				else
				  {
				    ind = find_symbol_checked(sc, cadr(arg2));
				    if (!s7_is_integer(ind))
				      {
					set_optimize_data(code, (optimize_data(code) & 1) + OP_SAFE_C_PP);
					goto OPT_EVAL;
				      }
				    index2 = s7_integer(ind);
				    if ((index2 >= vector_length(f2)) ||
					(index2 < 0))
				      {
					set_optimize_data(code, (optimize_data(code) & 1) + OP_SAFE_C_PP);
					goto OPT_EVAL;
				      }
				  }

				car(sc->T2_1) = vector_getter(f1)(sc, f1, index1);
				car(sc->T2_2) = vector_getter(f2)(sc, f2, index2);
				sc->value = c_call(code)(sc, sc->T2_1);
				goto START; 
			      }
			  }
		      }
		  }
		/* fallback on unknown_g */
		set_optimize_data(code, (optimize_data(code) & 1) + OP_SAFE_C_PP);
		goto OPT_EVAL;
	      }


	    case OP_VECTOR_C:
	    case HOP_VECTOR_C:
	      {
		/* this is the implicit indexing case (vector-ref is a normal safe op) 
		 *    (define (hi) (let ((v (vector 1 2 3))) (v 0)))
		 *    this starts as unknown_g in optimize_expression -> vector_c
		 *    but it still reports itself as unsafe, so there are higher levels possible
		 */
		s7_pointer v; 
		v = find_symbol_checked(sc, car(code));
		if ((!s7_is_vector(v)) ||
		    (!s7_is_integer(cadr(code))))  /* (v 4/3) */
		  break;
		if (vector_rank(v) == 1)
		  {
		    s7_Int index;
		    index = s7_integer(cadr(code));
		    if ((index < vector_length(v)) &&
			(index >= 0))
		      {
			sc->value = vector_getter(v)(sc, v, index);
			goto START;
		      }
		  }
		sc->value = vector_ref_1(sc, v, cdr(code));
		goto START;
	      }
	      
	      
	    case OP_VECTOR_CC:
	    case HOP_VECTOR_CC:
	      {
		s7_pointer v; 
		v = find_symbol_checked(sc, car(code));
		if (!s7_is_vector(v)) break;  /* we've checked that the args are non-negative ints */
		if (vector_rank(v) == 2)
		  {
		    s7_Int index;
		    index = s7_integer(cadr(code)) * vector_offset(v, 0) + s7_integer(caddr(code));
		    if (index < vector_length(v))
		      {
			sc->value = vector_getter(v)(sc, v, index);
			goto START;
		      }
		  }
		sc->value = vector_ref_1(sc, v, cdr(code));
		goto START;
	      }
	      
	      
	    case OP_VECTOR_A:
	      if (!indirect_c_function_is_ok(sc, cadr(code))) break;
	    case HOP_VECTOR_A:
	      {
		s7_pointer v, x; 
		v = find_symbol_checked(sc, car(code));
		if (!s7_is_vector(v))
		  break;
		
		x = ((s7_function)fcdr(cdr(code)))(sc, cadr(code));
		if (s7_is_integer(x))
		  {
		    if (vector_rank(v) == 1)
		      {
			s7_Int index;
			index = s7_integer(x);
			if ((index < vector_length(v)) &&
			    (index >= 0))
			  {
			    sc->value = vector_getter(v)(sc, v, index);
			    goto START;
			  }
			out_of_range(sc, sc->VECTOR_REF, small_int(1), cadr(code), (index < 0) ? ITS_NEGATIVE : ITS_TOO_LARGE);
		      }
		    sc->value = vector_ref_1(sc, v, cons(sc, x, sc->NIL));
		    goto START;
		  }
		return(wrong_type_argument(sc, sc->VECTOR_REF, small_int(1), x, T_INTEGER));
	      }
	      break;
	      
	      
	    case OP_VECTOR_S:
	    case HOP_VECTOR_S:
	      {
		s7_pointer v, ind; 
		v = find_symbol_checked(sc, car(code));
		ind = find_symbol_checked(sc, cadr(code));
		if ((!s7_is_vector(v)) ||
		    (!s7_is_integer(ind)))
		  break;
		
		if (vector_rank(v) == 1)
		  {
		    s7_Int index;
		    index = s7_integer(ind);
		    if ((index < vector_length(v)) &&
			(index >= 0))
		      {
			sc->value = vector_getter(v)(sc, v, index);
			goto START;
		      }
		    out_of_range(sc, sc->VECTOR_REF, small_int(1), cadr(code), (index < 0) ? ITS_NEGATIVE : ITS_TOO_LARGE);
		  }
		sc->value = vector_ref_1(sc, v, cons(sc, ind, sc->NIL));
		goto START;
	      }
	      
	    case OP_STRING_C:
	    case HOP_STRING_C:
	      {
		s7_Int index;
		s7_pointer s;
		s = find_symbol_checked(sc, car(code));
		if ((!is_string(s)) ||
		    (!is_integer(cadr(code))))
		  break;
		
		index = s7_integer(cadr(code));
		if ((index < string_length(s)) &&
		    (index >= 0))
		  {
		    if (is_bytevector(s))
		      sc->value = small_int((unsigned char)string_value(s)[index]);
		    else sc->value = s7_make_character(sc, ((unsigned char *)string_value(s))[index]);
		    goto START;
		  }
		sc->value = string_ref_1(sc, s, cadr(code));
		goto START;
	      }
	      
	      
	    case OP_STRING_A:
	      if (!indirect_c_function_is_ok(sc, cadr(code))) break;
	    case HOP_STRING_A:
	      {
		s7_Int index;
		s7_pointer s, x;
		s = find_symbol_checked(sc, car(code));
		if (!is_string(s))
		  break;
		
		x = ((s7_function)fcdr(cdr(code)))(sc, cadr(code));
		if (s7_is_integer(x))
		  {
		    index = s7_integer(x);
		    if ((index < string_length(s)) &&
			(index >= 0))
		      {
			if (is_bytevector(s))
			  sc->value = small_int((unsigned char)string_value(s)[index]);
			else sc->value = s7_make_character(sc, ((unsigned char *)string_value(s))[index]);
			goto START;
		      }
		    out_of_range(sc, sc->STRING_REF, small_int(2), cadr(code), (index < 0) ? ITS_NEGATIVE : ITS_TOO_LARGE);
		  }
		wrong_type_argument(sc, sc->STRING_REF, small_int(2), x, T_INTEGER);
	      }
	      break;
	      
	      
	    case OP_STRING_S:
	    case HOP_STRING_S:
	      {
		s7_Int index;
		s7_pointer s, ind;
		s = find_symbol_checked(sc, car(code));
		ind = find_symbol_checked(sc, cadr(code));
		if ((!is_string(s)) ||
		    (!s7_is_integer(ind)))
		  break;
		
		index = s7_integer(ind);
		if ((index < string_length(s)) &&
		    (index >= 0))
		  {
		    if (is_bytevector(s))
		      sc->value = small_int((unsigned char)string_value(s)[index]);
		    else sc->value = s7_make_character(sc, ((unsigned char *)string_value(s))[index]);
		    goto START;
		  }
		sc->value = string_ref_1(sc, s, ind);
		goto START;
	      }
	      
	      
	    case OP_HASH_TABLE_C:
	    case HOP_HASH_TABLE_C:
	      {
		s7_pointer s;
		s = find_symbol_checked(sc, car(code));
		if (!is_hash_table(s)) break;
		sc->value = s7_hash_table_ref(sc, s, cadr(code));
		goto START;
	      }
	      
	      
	    case OP_HASH_TABLE_S:
	    case HOP_HASH_TABLE_S:
	      {
		s7_pointer s;
		s = find_symbol_checked(sc, car(code));
		if (!is_hash_table(s)) break;
		sc->value = s7_hash_table_ref(sc, s, find_symbol_checked(sc, cadr(code)));
		goto START;
	      }
	      
	      
	    case OP_HASH_TABLE_A:
	      if (!indirect_c_function_is_ok(sc, cadr(code))) break;
	    case HOP_HASH_TABLE_A:
	      {
		s7_pointer s;
		s = find_symbol_checked(sc, car(code));
		if (!is_hash_table(s)) break;
		sc->value = s7_hash_table_ref(sc, s, ((s7_function)fcdr(cdr(code)))(sc, cadr(code)));
		goto START;
	      }
	      
	      
	    case OP_ENVIRONMENT_C:
	    case HOP_ENVIRONMENT_C:
	      {
		s7_pointer s;
		s = find_symbol_checked(sc, car(code));
		if (!is_let(s)) break;
		sc->value = s7_let_ref(sc, s, cadr(code));
		goto START;
	      }
	      
	      
	    case OP_ENVIRONMENT_S:
	    case HOP_ENVIRONMENT_S:
	      {
		s7_pointer s;
		s = find_symbol_checked(sc, car(code));
		if (!is_let(s)) break;
		sc->value = s7_let_ref(sc, s, find_symbol_checked(sc, cadr(code)));
		goto START;
	      }
	      
	      
	    case OP_ENVIRONMENT_Q:
	    case HOP_ENVIRONMENT_Q:
	      {
		s7_pointer s;
		s = find_symbol_checked(sc, car(code));
		if (!is_let(s)) break;
		sc->value = let_ref_1(sc, s, cadr(cadr(code))); 
		goto START;
	      }
	      
	      
	    case OP_ENVIRONMENT_A:
	      if (!indirect_c_function_is_ok(sc, cadr(code))) break;
	    case HOP_ENVIRONMENT_A:
	      {
		s7_pointer s;
		s = find_symbol_checked(sc, car(code));
		if (!is_let(s)) break;
		sc->value = let_ref_1(sc, s, ((s7_function)fcdr(cdr(code)))(sc, cadr(code)));
		goto START;
	      }
	      
	      
	    case OP_PAIR_C:
	    case HOP_PAIR_C:
	      {
		s7_pointer s;
		s = find_symbol_checked(sc, car(code));
		if (!is_pair(s)) break; /* this used to check is_integer(cadr(code)) but surely an error is correct if s is a pair? */
		sc->value = list_ref_1(sc, s, cadr(code));
		goto START;
	      }
	      
	      
	    case OP_PAIR_A:
	      if (!indirect_c_function_is_ok(sc, cadr(code))) break;
	    case HOP_PAIR_A:
	      {
		s7_pointer s, x;
		s = find_symbol_checked(sc, car(code));
		if (!is_pair(s)) break;
		x = ((s7_function)fcdr(cdr(code)))(sc, cadr(code));
		sc->value = list_ref_1(sc, s, x);
		goto START;
	      }
	      
	      
	    case OP_PAIR_S:
	    case HOP_PAIR_S:
	      {
		s7_pointer s, ind;
		s = find_symbol_checked(sc, car(code));
		if (!is_pair(s)) break;
		ind = find_symbol_checked(sc, cadr(code));
		sc->value = list_ref_1(sc, s, ind);
		goto START;
	      }
	      
	      
	    case OP_C_OBJECT:
	    case HOP_C_OBJECT:
	      {
		s7_pointer c;
		c = find_symbol_checked(sc, car(code));
		if (!is_c_object(c)) break;
		sc->value = (*(c_object_ref(c)))(sc, c, sc->NIL);
		goto START;
	      }


	    case OP_C_OBJECT_C:
	    case HOP_C_OBJECT_C:
	      {
		s7_pointer c;
		c = find_symbol_checked(sc, car(code));
		if (!is_c_object(c)) break;
		sc->value = (*(c_object_ref(c)))(sc, c, cdr(code));
		goto START;
	      }
	      
	      
	    case OP_C_OBJECT_A:
	      if (!indirect_c_function_is_ok(sc, cadr(code))) break;
	    case HOP_C_OBJECT_A:
	      {
		s7_pointer c;
		c = find_symbol_checked(sc, car(code));
		if (!is_c_object(c)) break;
		car(sc->T1_1) = ((s7_function)fcdr(cdr(code)))(sc, cadr(code));
		sc->value = (*(c_object_ref(c)))(sc, c, sc->T1_1);
		goto START;
	      }
	      
	    case OP_C_OBJECT_S:
	    case HOP_C_OBJECT_S:
	      {
		s7_pointer c;
		c = find_symbol_checked(sc, car(code));
		if (!is_c_object(c)) break;
		car(sc->T1_1) = find_symbol_checked(sc, cadr(code));
		sc->value = (*(c_object_ref(c)))(sc, c, sc->T1_1);
		goto START;
	      }
	      
	      
	    case OP_C_OBJECT_SS:
	    case HOP_C_OBJECT_SS:
	      {
		/* perhaps also c_object_ref_2 and direct case? object_cc happens in (m 0 1) where m=mixer
		 */
		s7_pointer c, ind;
		c = find_symbol_checked(sc, car(code));
		if (!is_c_object(c)) break;
		ind = find_symbol_checked(sc, cadr(code));
		car(sc->T2_2) = find_symbol_checked(sc, caddr(code));
		car(sc->T2_1) = ind;
		sc->value = (*(c_object_ref(c)))(sc, c, sc->T2_1);
		goto START;
	      }

	    case OP_C_OBJECT_CC:
	    case HOP_C_OBJECT_CC:
	      {
		s7_pointer c;
		c = find_symbol_checked(sc, car(code));
		if (!is_c_object(c)) break;
		sc->value = (*(c_object_ref(c)))(sc, c, cdr(code));
		goto START;
	      }
	      
	      	      
	    default:
	      fprintf(stderr, "bad op in opt_eval: op %d, is_opt: %d %s, %s\n",
		      optimize_data(code), 
		      is_optimized(code), 
		      ((is_optimized(code)) && (optimize_data(code) < OPT_MAX_DEFINED)) ? opt_names[optimize_data(code)] : "",
		      DISPLAY_80(code));
	      break;
	    }
	  
	  /* else cancel all the optimization info -- someone stepped on our symbol */
	  /* there is a problem with this -- if the caller still insists on goto OPT_EVAL, for example,
	   *   we get here over and over.  (let ((x (list (car y))))...) where list is redefined away.
	   */

	  /* fprintf(stderr, "%s %s\n", DISPLAY(sc->code), opt_name(sc->code)); */
	  clear_all_optimizations(sc, code);
	  /* and fall into the normal evaluator */
	}
      
      /* fprintf(stderr, "trail: %s\n", DISPLAY(sc->code)); */
      {
	s7_pointer code, carc;
	code = sc->code;

	if (is_pair(code))
	  {
	    sc->cur_code = code;
	    carc = car(code);
	    
	    if (typesflag(carc) == SYNTACTIC_TYPE)
	      {
		set_type(code, SYNTACTIC_PAIR);
		car(code) = syntax_symbol(slot_value(initial_slot(carc))); /* clear possible optimization confusion */
		sc->op = (opcode_t)symbol_syntax_op(car(code));
		pair_syntax_op(code) = sc->op;
		sc->code = cdr(code); 
		goto START_WITHOUT_POP_STACK;
	      }
	    
	    /* -------------------------------------------------------------------------------- */
	    /* trailers */
	    
	    if (is_symbol(carc))
	      {
		/* car is a symbol, sc->code a list */
		sc->value = find_global_symbol_checked(sc, carc); 
		sc->code = cdr(code);
		/* sc->args = sc->NIL; */
		/* drop into eval args
		 */
	      }
	    else
	      {
		/* very uncommon case: car is either itself a pair or some non-symbol */
		if (is_pair(carc))
		  {
		    /* evaluate the inner list but that list can be circular: carc: #1=(#1# #1#)!
		     *   and the cycle can be well-hidden -- #1=((#1 2) . 2) and other such stuff
		     */
		    if ((code == carc) || (cdr(code) == carc) || (cdr(code) == code))        /* try a few simple cases */
		      return(eval_error(sc, "attempt to evaluate a circular list: ~A", carc));

		    if (sc->stack_end >= sc->stack_resize_trigger)                           /* ouch -- call in the big guns */
		      {
			if (has_cyclic_sequence(sc, carc))
			  return(eval_error(sc, "attempt to evaluate a circular list: ~A", carc));
#if DEBUGGING
			fprintf(stderr, "stack is about to overflow: %s\n", DISPLAY(carc));
#endif
		      }

		    push_stack(sc, OP_EVAL_ARGS, sc->NIL, cdr(code));
		    if (typesflag(car(carc)) == SYNTACTIC_TYPE) 
		      /* was checking for is_syntactic here but that can be confused by successive optimizer passes:
		       *  (define (hi) (((lambda () list)) 1 2 3)) etc
		       */
		      {
			if ((car(carc) == sc->QUOTE) &&        /* ('and #f) */
			    ((!is_pair(cdr(carc))) ||          /* ((quote . #\h) (2 . #\i)) ! */
			     (is_syntactic(cadr(carc)))))
			  return(apply_error(sc, carc, cdr(code)));
			sc->op = (opcode_t)symbol_syntax_op(car(carc));
			sc->code = cdr(carc);
			goto START_WITHOUT_POP_STACK;
		      }
		    
		    push_stack(sc, OP_EVAL_ARGS, sc->NIL, cdr(carc));
		    sc->code = car(carc);
		    goto EVAL;
		  }
		else
		  {
		    /* car must be the function to be applied */
		    sc->value = carc;
		    sc->code = cdr(code);
		    /* sc->args = sc->NIL; */
		    /* drop into OP_EVAL_ARGS */
		  }
	      }
	  }
	else /* sc->code is not a pair */
	  {
	    if (is_symbol(code))
	      {
		sc->value = find_symbol_checked(sc, code);
		pop_stack(sc);
		if (sc->op != OP_EVAL_ARGS)
		  goto START_WITHOUT_POP_STACK;
		/* drop into OP_EVAL_ARGS */
	      }
	    else
	      {
		/* sc->code is not a pair or a symbol */
		sc->value = code;
		goto START;
	      }
	  }
	/* sc->value is car=something applicable
	 * sc->code = rest of expression
	 * sc->args is nil (set by the drop-through cases above -- perhaps clearer to bring that down?)
	 */
      }
      
    case OP_EVAL_ARGS:
      if (dont_eval_args(sc->value))
	{
	  if (is_any_macro(sc->value))
	    {    
	      /* macro expansion */
	      push_stack(sc, OP_EVAL_MACRO, sc->NIL, sc->code); /* sc->code is here for (vital) GC protection */
	      /* 
	       * pass a list (mac . args) to the macro expander
	       */
	      sc->args = list_1(sc, cons(sc, sc->value, sc->code));
	      sc->code = sc->value;
	      goto APPLY;                      /* not UNSAFE_CLOSURE because it might be a bacro */
	    }
	  
	  /* (define progn begin)
	   * (progn (display "hi") (+ 1 23))
	   */
	  sc->op = (opcode_t)syntax_opcode(sc->value);
	  goto START_WITHOUT_POP_STACK;
	}
      
      /* sc->value is the func
       *   we don't have to delay lookup of the func because arg evaluation order is not specified, so
       *     (let ((func +)) (func (let () (set! func -) 3) 2))
       *   can return 5.
       */
      /* if (is_null(sc->code)) {sc->code = sc->value; goto APPLY;}
       *   this is hit very rarely so it costs more than it saves
       */
      
      push_op_stack(sc, sc->value);
      if (sc->op_stack_now >= sc->op_stack_end)
	resize_op_stack(sc);

      sc->args = sc->NIL;
      goto EVAL_ARGS;
      /* moving eval_args up here (to avoid this goto) was slightly slower, probably by chance.
       */
      
    case OP_EVAL_ARGS5:
      /* sc->value is the last arg, sc->code is the previous
       */
      {
	s7_pointer x, y, lx;
	
	lx = pop_op_stack(sc);
	if (is_safe_procedure(lx))
	  {
	    x = sc->temp_cell_3;
	    y = sc->temp_cell_2;
	  }
	else
	  {
	    NEW_CELL(sc, x); 
	    set_type(x, T_PAIR);
	    NEW_CELL_NO_CHECK(sc, y); 
	    set_type(y, T_PAIR);
	  }
	
	car(x) = sc->code;
	cdr(x) = sc->args;
	car(y) = sc->value;
	cdr(y) = x;
	sc->args = safe_reverse_in_place(sc, y); 
	sc->code = lx;
	goto APPLY;
      }
      
      
    case OP_EVAL_ARGS2:
      /* sc->value is the last arg, [so if is_null(cdr(sc->code) and current is pair, push args2]
       */
      {
	s7_pointer x;
	
	sc->code = pop_op_stack(sc);
	if (is_safe_procedure(sc->code))
	  x = sc->temp_cell_2;
	else
	  {
	    NEW_CELL(sc, x);
	    set_type(x, T_PAIR);
	  }
	car(x) = sc->value;
	cdr(x) = sc->args;
	if (!is_null(sc->args))
	  sc->args = safe_reverse_in_place(sc, x);
	else sc->args = x;
	goto APPLY;
      }
      

      /* tricky cases here all involve values (i.e. multiple-values) */
      
    case OP_EVAL_ARGS_P_1:
      /* only from HOP_SAFE_C_P
       * if multiple values from func, splice_in_values revectors us to OP_EVAL_ARGS_P_1_MV.
       */
      car(sc->T1_1) = sc->value;
      sc->value = c_call(sc->code)(sc, sc->T1_1);
      goto START;
      

    case OP_EVAL_ARGS_P_1_MV:
      /* we can't just call the c function directly here because the values call that
       *   passed us these new arguments might have too many values, so it's simpler
       *   to let OP_APPLY sort that out.
       */
      sc->args = sc->value;
      sc->code = c_function_base(ecdr(sc->code));
      goto APPLY;

      
    case OP_EVAL_ARGS_P_2:
      /* from HOP_SAFE_C_SP||CP|QP, handled like P_1 case above
       *   primarily involves generators: (outa i (nrcos gen)) etc
       */
      car(sc->T2_1) = sc->args;
      car(sc->T2_2) = sc->value;
      sc->value = c_call(sc->code)(sc, sc->T2_1);
      goto START;


    case OP_EVAL_ARGS_P_2_MV:
      sc->args = cons(sc, sc->args, sc->value);
      sc->code = c_function_base(ecdr(sc->code));
      goto APPLY;
      
      
    case OP_EVAL_ARGS_SSP_1:
      /* from HOP_SAFE_C_SSP */
      car(sc->T3_3) = sc->value;
      car(sc->T3_1) = find_symbol_checked(sc, cadr(sc->code));
      car(sc->T3_2) = find_symbol_checked(sc, caddr(sc->code));
      sc->value = c_call(sc->code)(sc, sc->T3_1);
      goto START;


    case OP_EVAL_ARGS_SSP_MV:
      sc->args = cons(sc, find_symbol_checked(sc, cadr(sc->code)), cons(sc, find_symbol_checked(sc, caddr(sc->code)), sc->value));
      sc->code = c_function_base(ecdr(sc->code));
      goto APPLY;
      
      
    case OP_EVAL_ARGS_P_3:
      car(sc->T2_2) = find_symbol_checked(sc, caddr(sc->code)); 
      /* we have to wait because we say the evaluation order is always left to right
       *   and the first arg's evaluation might change the value of the second arg.
       */
      car(sc->T2_1) = sc->value;
      sc->value = c_call(sc->code)(sc, sc->T2_1);
      goto START;

    case OP_EVAL_ARGS_P_3_MV:
      /* (define (hi a) (+ (values 1 2) a)) 
       * (define (hi a) (log (values 1 2) a))
       */
      sc->w = sc->value;
      sc->args = cons(sc, find_symbol_checked(sc, caddr(sc->code)), sc->w);
      sc->code = c_function_base(ecdr(sc->code));
      goto APPLY;
      
      
    case OP_EVAL_ARGS_P_4:
      car(sc->T2_1) = sc->value;
      car(sc->T2_2) = sc->args;
      sc->value = c_call(sc->code)(sc, sc->T2_1);
      goto START;

    case OP_EVAL_ARGS_P_4_MV: /* same as P_2_MV) */
      sc->args = cons(sc, sc->args, sc->value);
      sc->code = c_function_base(ecdr(sc->code));
      goto APPLY; /* (define (hi) (log (values 1 2) 3)) ? */
      

    case OP_SAFE_C_ZS_1:
      car(sc->T2_1) = sc->value;
      car(sc->T2_2) = sc->args;
      sc->value = c_call(sc->code)(sc, sc->T2_1);
      goto START;
      

    case OP_SAFE_C_SZ_1:
      car(sc->T2_1) = sc->args;
      car(sc->T2_2) = sc->value;
      sc->value = c_call(sc->code)(sc, sc->T2_1);
      goto START;
      

    case OP_SAFE_C_SZ_SZ:
      /* S_opSZq actually, in (nominal second, only actual) SZ, S=args, Z=value,
       *   SZ from the SP combiner for SZ
       */
      car(sc->T2_1) = sc->args;
      car(sc->T2_2) = sc->value;
      car(sc->T2_2) = c_call(caddr(sc->code))(sc, sc->T2_1);
      car(sc->T2_1) = find_symbol_checked(sc, cadr(sc->code));
      sc->value = c_call(sc->code)(sc, sc->T2_1);
      goto START;
      

    case OP_SAFE_C_ZA_1:
      car(sc->T2_2) = ((s7_function)fcdr(cddr(sc->code)))(sc, caddr(sc->code));
      car(sc->T2_1) = sc->value;
      sc->value = c_call(sc->code)(sc, sc->T2_1);
      goto START;
      

    case OP_SAFE_C_ZZ_1:
      push_stack(sc, OP_SAFE_C_ZZ_2, sc->value, sc->code);
      sc->code = caddr(sc->code);
      goto OPT_EVAL;
      

    case OP_SAFE_C_ZZ_2:
      car(sc->T2_1) = sc->args;
      car(sc->T2_2) = sc->value;
      sc->value = c_call(sc->code)(sc, sc->T2_1);
      goto START;
      

    case OP_SAFE_C_ZAA_1:
      car(sc->A3_1) = sc->value;
      car(sc->A3_2) = ((s7_function)fcdr(cddr(sc->code)))(sc, caddr(sc->code));
      car(sc->A3_3) = ((s7_function)fcdr(cdddr(sc->code)))(sc, cadddr(sc->code));
      sc->value = c_call(sc->code)(sc, sc->A3_1);
      goto START;
      

    case OP_SAFE_C_AZA_1:
      car(sc->T3_3) = ((s7_function)fcdr(cdddr(sc->code)))(sc, cadddr(sc->code));
      car(sc->T3_2) = sc->value;
      car(sc->T3_1) = sc->args;
      sc->value = c_call(sc->code)(sc, sc->T3_1);
      goto START;
      

    case OP_SAFE_C_SSZ_1:
      car(sc->T3_1) = sc->args;
      car(sc->T3_3) = sc->value;
      car(sc->T3_2) = find_symbol_checked(sc, caddr(sc->code));
      sc->value = c_call(sc->code)(sc, sc->T3_1);
      goto START;
      

    case OP_SAFE_C_AAZ_1:
      car(sc->T3_1) = pop_op_stack(sc);
      car(sc->T3_2) = sc->args;
      car(sc->T3_3) = sc->value;
      sc->value = c_call(sc->code)(sc, sc->T3_1);
      goto START;
      

    case OP_SAFE_C_ZZA_1:
      push_op_stack(sc, sc->value);
      push_stack(sc, OP_SAFE_C_ZZA_2, sc->args, sc->code);
      sc->code = caddr(sc->code);
      goto OPT_EVAL;
      

    case OP_SAFE_C_ZZA_2:
      car(sc->A3_1) = pop_op_stack(sc);
      car(sc->A3_2) = sc->value;
      car(sc->A3_3) = ((s7_function)fcdr(cdddr(sc->code)))(sc, cadddr(sc->code));
      sc->value = c_call(sc->code)(sc, sc->A3_1);
      goto START;
      

    case OP_SAFE_C_ZAZ_1:
      push_op_stack(sc, sc->value);
      push_stack(sc, OP_SAFE_C_ZAZ_2, ((s7_function)fcdr(cddr(sc->code)))(sc, caddr(sc->code)),  sc->code);
      sc->code = cadddr(sc->code);
      goto OPT_EVAL;
      

    case OP_SAFE_C_ZAZ_2:
      car(sc->T3_1) = pop_op_stack(sc);
      car(sc->T3_2) = sc->args;
      car(sc->T3_3) = sc->value;
      sc->value = c_call(sc->code)(sc, sc->T3_1);
      goto START;
      

    case OP_SAFE_C_AZZ_1:
      push_op_stack(sc, sc->value);
      push_stack(sc, OP_SAFE_C_AZZ_2, sc->args, sc->code);
      sc->code = cadddr(sc->code);
      goto OPT_EVAL;
      

    case OP_SAFE_C_AZZ_2:
      car(sc->T3_1) = sc->args;
      car(sc->T3_2) = pop_op_stack(sc);
      car(sc->T3_3) = sc->value;
      sc->value = c_call(sc->code)(sc, sc->T3_1);
      goto START;
      

    case OP_SAFE_C_ZZZ_1:
      push_stack(sc, OP_SAFE_C_ZZZ_2, sc->value, sc->code);
      sc->code = caddr(sc->code);
      goto OPT_EVAL;
      

    case OP_SAFE_C_ZZZ_2:
      push_op_stack(sc, sc->value);
      push_stack(sc, OP_SAFE_C_ZZZ_3, sc->args, sc->code);
      sc->code = cadddr(sc->code);
      goto OPT_EVAL;
      

    case OP_SAFE_C_ZZZ_3:
      car(sc->T3_1) = sc->args;
      car(sc->T3_2) = pop_op_stack(sc);
      car(sc->T3_3) = sc->value;
      sc->value = c_call(sc->code)(sc, sc->T3_1);
      goto START;


    case OP_SAFE_C_ZZZZ_1:
      push_stack(sc, OP_SAFE_C_ZZZZ_2, sc->value, sc->code);
      sc->code = caddr(sc->code);
      goto OPT_EVAL;
      

    case OP_SAFE_C_ZZZZ_2:
      push_op_stack(sc, sc->value);
      push_stack(sc, OP_SAFE_C_ZZZZ_3, sc->args, sc->code);
      sc->code = cadddr(sc->code);
      goto OPT_EVAL;
      

    case OP_SAFE_C_ZZZZ_3:
      push_op_stack(sc, sc->value);
      push_stack(sc, OP_SAFE_C_ZZZZ_4, sc->args, sc->code);
      sc->code = caddddr(sc->code);
      goto OPT_EVAL;
      

    case OP_SAFE_C_ZZZZ_4:
      {
	s7_pointer lx;
	lx = sc->args;
	if (!list_is_in_use(sc->safe_lists[4]))
	  sc->args = sc->safe_lists[4];
	else sc->args = make_list(sc, 4, sc->NIL);

	car(sc->args) = lx;
	cadddr(sc->args) = sc->value;
	caddr(sc->args) = pop_op_stack(sc);
	cadr(sc->args) = pop_op_stack(sc);
	sc->value = c_call(sc->code)(sc, sc->args);
	goto START;
      }


    case OP_SAFE_C_opSq_P_1:
      /* this is the no-multiple-values case */
      car(sc->T2_1) = sc->args;
      car(sc->T2_2) = sc->value;
      sc->value = c_call(sc->code)(sc, sc->T2_1);
      goto START;

    case OP_SAFE_C_opSq_P_MV:
      /* here we need an argnum check since values could have appended any number of args
       */
      sc->args = cons(sc, sc->args, sc->value);
      
      /* can values return an improper or circular list?  I don't think so:
       *   (values 1 . 2) -> improper arg list error (same with apply values)
       *
       * currently (values) does not simply erase itself:
       *
       *   :(let () (define (arg2 a) (let ((b 1)) (set! b (+ a b)) (values))) (define (hi c) (expt (abs c) (arg2 2))) (hi 2))
       *   ;expt power, argument 2, #<unspecified>, is an untyped but should be a number
       *  
       *   :(s7-version (values))
       *   ;s7-version: too many arguments: (#<unspecified>)
       *
       *   :(exp (values) 0.0)
       *   ;exp: too many arguments: (#<unspecified> 0.0)
       *
       * map is explicitly a special case, and surely it is more confusing to have (values) scattered at random.
       * also this is consistent with the unoptimized version
       */
      sc->code = c_function_base(ecdr(sc->code));
      goto APPLY; /* (define (hi a) (+ (abs a) (values 1 2 3))) */
      

    case OP_EVAL_ARGS3:
      /* sc->value is the next-to-last arg, and we know the last arg is not a list (so values can't mess us up!)
       */
      {
	s7_pointer x, y, lx, val;

	val = sc->code;
	if (is_symbol(val))
	  val = find_symbol_checked(sc, val);
	
	lx = pop_op_stack(sc);
	if (is_safe_procedure(lx))
	  {
	    x = sc->temp_cell_3;
	    y = sc->temp_cell_2;
	  }
	else
	  {
	    NEW_CELL(sc, x); 
	    set_type(x, T_PAIR);
	    NEW_CELL_NO_CHECK(sc, y);
	    set_type(y, T_PAIR);
	  }
	car(x) = sc->value;
	cdr(x) = sc->args;
	car(y) = val;
	cdr(y) = x;
	sc->args = safe_reverse_in_place(sc, y); 
	sc->code = lx;
	goto APPLY;
      }
      
      
    case OP_EVAL_ARGS4:
      /* sc->code is a pair, and either cdr(sc->code) is not null or car(sc->code) is a pair 
       *
       * (#f #f) (env #f) etc.  args is very often nil here, so we're looking at 3 simple args
       *   or even just 2 in some cases: (+ req opt) with value 2 and args ()
       */
      {
        s7_pointer x;
	NEW_CELL(sc, x);
	car(x) = sc->value;
	cdr(x) = sc->args;
	set_type(x, T_PAIR);
	sc->args = x; /* all the others reverse -- why not this case? -- reverse is at end? (below) */
	goto EVAL_ARGS_PAIR;
      }
      
      
    case OP_EVAL_ARGS1:
      {
        s7_pointer x;
	NEW_CELL(sc, x);
	car(x) = sc->value;
	cdr(x) = sc->args;
	set_type(x, T_PAIR);
	sc->args = x;
      }
      
      
    EVAL_ARGS:
      /* first time, value = op, args = nil, code is args */
      
      if (is_pair(sc->code))  /* evaluate current arg -- must check for pair here, not sc->NIL (improper list as args) */
	{ 
	  int typ;
	  s7_pointer car_code;
	  
	EVAL_ARGS_PAIR:
	  car_code = car(sc->code);
	  typ = type(car_code);
	  
	  /* switch statement here is much slower for some reason */
	  if (typ == T_PAIR)
	    {
	      if (is_null(cdr(sc->code)))
		push_stack(sc, OP_EVAL_ARGS2, sc->args, sc->NIL);
	      else 
		{
		  if (!is_pair(cdr(sc->code)))            /* (= 0 '(1 . 2) . 3) */
		    improper_arglist_error(sc);
		  
		  if ((is_null(cddr(sc->code))) &&
		      (!is_pair(cadr(sc->code))))
		    push_stack(sc, OP_EVAL_ARGS3, sc->args, cadr(sc->code)); 
		  else push_stack(sc, OP_EVAL_ARGS4, sc->args, cdr(sc->code));
		}
	      sc->code = car_code;
	      if (is_optimized(sc->code))
		goto OPT_EVAL;
	      goto EVAL; 
	    }
	  
	  /* car(sc->code) is not a pair */
	  if (is_pair(cdr(sc->code)))
	    {
	      sc->code = cdr(sc->code);
	      if (typ == T_SYMBOL)
		sc->value = find_symbol_checked(sc, car_code);
	      else sc->value = car_code;
	      /* sc->value is the current arg's value, sc->code is pointing to the next */
	      
	      /* cdr(sc->code) may not be a pair or nil here! 
	       *   (eq? #f . 1) -> sc->code is 1
	       */
	      if (is_null(cdr(sc->code)))
		{
		  s7_pointer x, y, val;
		  /* we're at the last arg, sc->value is the previous one, not yet saved in the args list */
		  car_code = car(sc->code);
		  typ = type(car_code);
		  
		  if (typ == T_PAIR)
		    {
		      push_stack(sc, OP_EVAL_ARGS5, sc->args, sc->value);
		      sc->code = car_code;
		      goto EVAL; 
		    }
		  
		  /* get the last arg */
		  if (typ == T_SYMBOL)
		    val = find_symbol_checked(sc, car_code);
		  else val = car_code;
		  
		  /* get the current arg, which is not a list */
		  sc->code = pop_op_stack(sc);
		  if (is_safe_procedure(sc->code)) /* this check does not currently pay for itself, I think */
		    {
		      x = sc->temp_cell_3; /* these are already pairs */
		      y = sc->temp_cell_2;
		    }
		  else
		    {
		      NEW_CELL(sc, x); 
		      set_type(x, T_PAIR);
		      NEW_CELL_NO_CHECK(sc, y); 
		      set_type(y, T_PAIR);
		    }
		  car(x) = sc->value;
		  cdr(x) = sc->args;
		  car(y) = val;
		  cdr(y) = x;
		  sc->args = safe_reverse_in_place(sc, y); 
		  /* drop into APPLY */
		}
	      else 
		{
		  /* here we know sc->code is a pair, cdr(sc->code) is not null
		   *   sc->value is the previous arg's value
		   */
		  s7_pointer x;
		  NEW_CELL(sc, x); 
		  car(x) = sc->value;
		  cdr(x) = sc->args;
		  set_type(x, T_PAIR);
		  sc->args = x;
		  goto EVAL_ARGS_PAIR;
		}
	    }
	  else
	    {
	      /* here we've reached the last arg (sc->code == nil) 
	       *   it is not a pair (typ == T_PAIR was caught earlier)
	       *   if this is a safe function, we're going straight to
	       *   apply with no complications, so we can use temp_cell_2.
	       */
	      s7_pointer x;
	      
	      if (!is_null(cdr(sc->code)))
		improper_arglist_error(sc);
	      
	      sc->code = pop_op_stack(sc);
	      if (is_safe_procedure(sc->code))
		{
		  x = sc->temp_cell_2;
		  if (typ == T_SYMBOL)
		    car(x) = find_symbol_checked(sc, car_code);
		  else car(x) = car_code;
		  cdr(x) = sc->args;
		}
	      else
		{
		  s7_pointer val;
		  if (typ == T_SYMBOL)
		    val = find_symbol_checked(sc, car_code); /* this has to precede the set_type below */
		  else val = car_code;
		  NEW_CELL(sc, x); 
		  car(x) = val;
		  cdr(x) = sc->args;
		  set_type(x, T_PAIR);
		}
	      
	      if (!is_null(sc->args))
		sc->args = safe_reverse_in_place(sc, x);
	      else sc->args = x;
	      /* drop into APPLY */
	    }
	}
      else                       /* got all args -- go to apply */
	{
	  if (is_not_null(sc->code))
	    improper_arglist_error(sc);
	  else
	    {
	      sc->code = pop_op_stack(sc);
	      sc->args = safe_reverse_in_place(sc, sc->args); 
	      /* fall through  */
	    }
	}
      
      /* we could omit the arg reversal in many cases, but lots of code assumes the args are in order;
       *   adding a bit for this in the type field saves some time in s7test (many + and * tests), but costs
       *   about the same time in other cases, so it's not a clear win.
       */
      
      /*
       * if (sc->stack_end >= sc->stack_resize_trigger)
       *   increase_stack_size(sc);
       *
       * the two places where the stack reaches it maximum size are in read_expression where TOKEN_LEFT_PAREN
       *   pushes OP_READ_LIST, and (the actual max) in OP_EVAL at the push of OP_EVAL_ARGS.  I've moved
       *   the stack size check from here (where it reflects the eval stack size) to read_expression (where
       *   it reflects nested list depth), and added it to the T_CLOSURE(*) parts of apply since (for example) 
       *   extremely deep recursion involving map or for-each can increase the stack size indefinitely:
       *
       * (define (tfe a b)
       *   (format #t "~A ~A -> ~A~%" a b (-s7-stack-size))
       *   (for-each
       *     (lambda (c)
       *       (if (< c b)
       *           (tfe (+ c 1) b)))
       *     (list a)))
       *
       * now (tfe 0 1000) triggers the stack increase.
       */
      
    APPLY:
      /* fprintf(stderr, "apply %s to %s\n", DISPLAY(sc->code), DISPLAY(sc->args)); */

      switch (type(sc->code))
	{
	case T_C_FUNCTION: 	                    /* -------- C-based function -------- */
	  {
	    unsigned int len;
	    len = safe_list_length(sc, sc->args);
	    
	    if (len < c_function_required_args(sc->code))
	      return(s7_error(sc, 
			      sc->WRONG_NUMBER_OF_ARGS, 
			      list_3(sc, sc->NOT_ENOUGH_ARGUMENTS, sc->code, sc->args)));
	    
	    if (c_function_all_args(sc->code) < len)
	      return(s7_error(sc, 
			      sc->WRONG_NUMBER_OF_ARGS, 
			      list_3(sc, sc->TOO_MANY_ARGUMENTS, sc->code, sc->args)));
	  }
	  /* drop into ... */
	  
	case T_C_ANY_ARGS_FUNCTION:                 /* -------- C-based function that can take any number of arguments -------- */
	  sc->value = c_function_call(sc->code)(sc, sc->args);
	  goto START;
	  
	  
	case T_C_FUNCTION_STAR:                     /* -------- C-based function with defaults (lambda*) -------- */
	  sc->value = c_function_call(sc->code)(sc, set_c_function_call_args(sc));
	  goto START;


	case T_C_OPT_ARGS_FUNCTION:                 /* -------- C-based function that has n optional arguments -------- */
	  {
	    unsigned int len;
	    len = safe_list_length(sc, sc->args);
	    if (c_function_all_args(sc->code) < len)
	      return(s7_error(sc, 
			      sc->WRONG_NUMBER_OF_ARGS, 
			      list_3(sc, sc->TOO_MANY_ARGUMENTS, sc->code, sc->args)));
	    sc->value = c_function_call(sc->code)(sc, sc->args);
	    goto START;
	  }
	  
	case T_C_RST_ARGS_FUNCTION:                 /* -------- C-based function that has n required args, then any others -------- */
	  {
	    unsigned int len;
	    len = safe_list_length(sc, sc->args);
	    if (len < c_function_required_args(sc->code))
	      return(s7_error(sc, 
			      sc->WRONG_NUMBER_OF_ARGS, 
			      list_3(sc, sc->NOT_ENOUGH_ARGUMENTS, sc->code, sc->args)));
	    sc->value = c_function_call(sc->code)(sc, sc->args);
	    /* sc->code here need not match sc->code before the function call (map for example) */
	    goto START;
	  }
	  
	case T_C_MACRO: 	                    /* -------- C-based macro -------- */
	  {
	    int len;
	    /* fprintf(stderr, "args: %s\n", DISPLAY(sc->args)); */
	    if (is_pair(car(sc->args)))            /* normally args is ((mac-name ...)) */
	      {                                    /*   but in a case like (call-with-exit quasiquote), args is (#<goto>) */
		if (!s7_is_list(sc, cdar(sc->args)))  /* (quasiquote . 1) */
		  eval_error(sc, "improper list of arguments: ~A", car(sc->args));
		sc->args = cdar(sc->args);         
	      }
	    /* otherwise sc->args is already ok -- I think this can only be triggered by quasiquote */
	    
	    len = s7_list_length(sc, sc->args);
	    if (len <= 0)                          /* (quasiquote 0 . 1) */
	      return(s7_error(sc, sc->SYNTAX_ERROR, 
			      list_3(sc, make_string_wrapper(sc, "~A: improper list of arguments: ~S"), sc->code, sc->args)));
	    
	    if (len < (int)c_macro_required_args(sc->code))
	      return(s7_error(sc, 
			      sc->WRONG_NUMBER_OF_ARGS, 
			      list_3(sc, sc->NOT_ENOUGH_ARGUMENTS, sc->code, sc->args)));
	    
	    if ((int)c_macro_all_args(sc->code) < len)
	      return(s7_error(sc, 
			      sc->WRONG_NUMBER_OF_ARGS, 
			      list_3(sc, sc->TOO_MANY_ARGUMENTS, sc->code, sc->args)));
	    sc->value = c_macro_call(sc->code)(sc, sc->args);
	    goto START;
	  }
	  
	case T_BACRO:                                /* -------- bacro -------- */
	  NEW_FRAME(sc, sc->envir, sc->envir);       /* like let* -- we'll be adding macro args, so might as well sequester things here */
	  goto BACRO;
	  /* another choice is to do expansion and evaluation in the definition env,
	   *   so only the args come (unevaluated) from the current env.
	   *   I can't immediately see any need for this.
	   *
	   * but probably useful would be bacro without the following evaluation, sort of like a function with unevaluated args
	   *  (but expanded in the call-time env): "lacro"?
	   */
	  
	case T_CLOSURE:                              /* -------- normal function (lambda), or macro -------- */
	  /* not often safe closure here, and very confusing if so to get identity macro args handled correctly */
	  CHECK_STACK_SIZE(sc);
	  
	case T_MACRO:
	  NEW_FRAME(sc, closure_let(sc->code), sc->envir);
	  
	BACRO:
	  /* load up the current args into the ((args) (lambda)) layout [via the current environment] */
	  
	  /* (define-macro (hi a b) `(+ ,a ,b))
	   *   -> code: #<macro>, args: ((hi 2 3)), closure args: (hi-9)
	   *   then back again: code: #<closure>, args: (2 3), closure args: (a b)
	   *
	   * (let* ((mac (let () (define-macro (mac1 a) `(+ ,a 1)) mac1)) (lst (list 1))) (set-cdr! lst lst) (apply mac lst ()))
	   * hangs -- this is equivalent to (eval <circular-list>) which also hangs.
	   */
	  
	  {
	    s7_pointer x, z, e;
	    unsigned long long int id;
	    e = sc->envir;
	    id = let_id(e);
	    
	    for (x = closure_args(sc->code), z = sc->args; is_pair(x); x = cdr(x))
	      {
		s7_pointer sym, args, val;
		/* reuse the value cells as the new frame slots */
		
		if (is_null(z))
		  return(s7_error(sc, 
				  sc->WRONG_NUMBER_OF_ARGS, 
				  list_3(sc, sc->NOT_ENOUGH_ARGUMENTS, closure_name(sc, sc->code), sc->cur_code)));
		/* now that args are being reused as slots, the error message can't use sc->args, 
		 *  so fallback on sc->cur_code in this section.
		 *  But that can be #f, and closure_name can be confusing in this context, so we need a better error message!
		 */

		sym = car(x);
		val = car(z);
		args = cdr(z);
		
		slot_symbol(z) = sym;
		symbol_set_local(sym, id, z);
		slot_set_value(z, val);
		set_type(z, T_SLOT | T_IMMUTABLE);
		next_slot(z) = let_slots(e);
		let_slots(e) = z;
		z = args;
	      }
	    if (is_null(x)) 
	      {
		if (is_not_null(z))
		  return(s7_error(sc, 
				  sc->WRONG_NUMBER_OF_ARGS, 
				  list_3(sc, sc->TOO_MANY_ARGUMENTS, closure_name(sc, sc->code), sc->cur_code)));
	      } 
	    else add_slot(sc, x, z); /* the rest arg */
	  }
	  
	  sc->code = closure_body(sc->code);
	  if (is_pair(cdr(sc->code)))
	    push_stack_no_args(sc, OP_BEGIN1, cdr(sc->code));
	  else
	    {
	      if (!is_null(cdr(sc->code)))
		return(eval_error(sc, "closure body: stray dot? ~A", sc->code));		
	    }
	  sc->code = car(sc->code);
	  goto EVAL;
	  
	  
	case T_CLOSURE_STAR:	                  /* -------- define* (lambda*) -------- */
	  { 
	    s7_pointer z, top, nxt;
	    CHECK_STACK_SIZE(sc);
	    sc->envir = new_frame_in_env(sc, closure_let(sc->code)); 
	    
	    /* to check for and fixup unset args from defaults, we need to traverse the slots in left-to-right order
	     *   but they are stored backwards in the environment, so use pending_value as a back-pointer.
	     * We have to build the environment before calling lambda_star_set_args because keywords can
	     *   cause any arg to be set at any point in the arg list.
	     *
	     * the frame-making step below could be precalculated, but where to store it?
	     */
	    top = NULL;
	    nxt = NULL;
	    for (z = closure_args(sc->code); is_pair(z); z = cdr(z))
	      {
		s7_pointer car_z;
		car_z = car(z);
		if (is_pair(car_z))           /* arg has a default value of some sort */
		  {
		    s7_pointer val;
		    val = cadr(car_z);
		    if ((!is_pair(val)) && 
			(!is_symbol(val)))
		      add_slot(sc, car(car_z), val);
		    else
		      {
			s7_pointer y;
			/* y = add_slot(sc, car(car_z), sc->UNDEFINED); */
			ADD_SLOT(sc->envir, car(car_z), sc->UNDEFINED);
			y = let_slots(sc->envir);
			slot_expression(y) = cadr(car_z);
			slot_pending_value(y) = sc->NIL;
			if (!top)
			  {
			    top = y;
			    nxt = top;
			  }
			else
			  {
			    slot_pending_value(nxt) = y;
			    nxt = y;
			  }
		      }
		  }
		else
		  {
		    if (!is_keyword(car_z))
		      add_slot(sc, car_z, sc->F);
		    else
		      {
			if (car_z == sc->KEY_REST)
			  {
			    add_slot(sc, cadr(z), sc->NIL);
			    z = cdr(z);
			  }
		      }
		  }
	      }
	    if (is_symbol(z))
	      add_slot(sc, z, sc->NIL);

	    lambda_star_set_args(sc);                     /* load up current arg vals */	    

	    if (top)
	      {
		/* get default values, which may involve evaluation 
		 */
		push_stack(sc, OP_LAMBDA_STAR_DEFAULT, sc->args, sc->code); /* op is just a placeholder (don't use OP_BARRIER here) */
		sc->args = top;
	    
	      LAMBDA_STAR_DEFAULT:
		z = sc->args;
		if (is_slot(z))
		  {
		    if (slot_value(z) == sc->UNDEFINED)
		      {
			s7_pointer val;
			val = slot_expression(z);
			if (is_symbol(val))
			  {
			    slot_set_value(z, find_symbol_checked(sc, val));
			    if (slot_value(z) == sc->UNDEFINED)
			      return(eval_error(sc, "lambda* defaults: ~A is unbound", slot_symbol(z)));
			    /* but #f is default if no expr, so there's some inconsistency here */
			  }
			else
			  {
			    if (is_pair(val))
			      {
				if (car(val) == sc->QUOTE)
				  {
				    if ((!is_pair(cdr(val))) ||      /* (lambda* ((a (quote))) a) or (lambda* ((a (quote 1 1))) a) etc */
					(is_pair(cddr(val))))
				      return(eval_error(sc, "lambda* default: ~A is messed up", val));
				    slot_set_value(z, cadr(val));
				  }
				else
				  {
				    push_stack(sc, OP_LAMBDA_STAR_DEFAULT, sc->args, sc->code);
				    sc->code = val;
				    goto EVAL; 
				  }
			      }
			    else slot_set_value(z, val);
			  }
		      }
		    sc->args = slot_pending_value(z);
		    goto LAMBDA_STAR_DEFAULT;
		  }
		pop_stack_no_op(sc);              /* get original args and code back */
	      }
	    sc->code = closure_body(sc->code);    /* evaluate the function body */
	    goto BEGIN1;
	  }
	  
	case T_CONTINUATION:	                  /* -------- continuation ("call/cc") -------- */
	  if (!call_with_current_continuation(sc))
	    return(s7_error(sc, make_symbol(sc, "baffled!"),
			    list_1(sc, make_string_wrapper(sc, "continuation can't jump into with-baffle"))));
	  goto START;
	  
	case T_GOTO:	                          /* -------- goto ("call-with-exit") -------- */
	  call_with_exit(sc);
	  goto START;
	  
	case T_C_OBJECT:                          /* -------- applicable (new-type) object -------- */
	  sc->value = (*(c_object_ref(sc->code)))(sc, sc->code, sc->args);
	  goto START;
	  

	case T_INT_VECTOR:
	case T_FLOAT_VECTOR:
	case T_VECTOR:                            /* -------- vector as applicable object -------- */
	  /* sc->code is the vector, sc->args is the list of indices 
	   */
	  if (is_null(sc->args))                  /* (#2d((1 2) (3 4))) */
	    return(s7_wrong_number_of_args_error(sc, "not enough args for vector-ref: ~A", sc->args));

	  if ((is_null(cdr(sc->args))) &&       
	      (s7_is_integer(car(sc->args))) &&
	      (vector_rank(sc->code) == 1))
	    {
	      s7_Int index;
	      index = s7_integer(car(sc->args));
	      if ((index >= 0) &&
		  (index < vector_length(sc->code)))
		{
		  sc->value = vector_getter(sc->code)(sc, sc->code, index);
		  goto START;
		}
	      else out_of_range(sc, sc->VECTOR_REF, small_int(2), car(sc->args), (index < 0) ? ITS_NEGATIVE : ITS_TOO_LARGE);
	    }
	  sc->value = vector_ref_1(sc, sc->code, sc->args);
	  goto START;
	  
	  
	case T_STRING:                            /* -------- string as applicable object -------- */
	  if (is_null(cdr(sc->args)))
	    {
	      if (s7_is_integer(car(sc->args)))
		{
		  s7_Int index;                  /* not int: ("abs" most-negative-fixnum) */
		  index = s7_integer(car(sc->args));
		  if ((index >= 0) &&
		      (index < string_length(sc->code)))
		    {
		      if (is_bytevector(sc->code))
			sc->value = small_int((unsigned char)(string_value(sc->code))[index]);
		      else sc->value = s7_make_character(sc, ((unsigned char *)string_value(sc->code))[index]);
		      goto START;
		    }
		}
	      sc->value = string_ref_1(sc, sc->code, car(sc->args));
	      goto START;
	    }
	  return(s7_error(sc, 
			  sc->WRONG_NUMBER_OF_ARGS, 
			  list_3(sc, (is_null(sc->args)) ? sc->NOT_ENOUGH_ARGUMENTS : sc->TOO_MANY_ARGUMENTS, sc->code, sc->args)));
	  
	  
	case T_PAIR:                              /* -------- list as applicable object -------- */
	  if (is_multiple_value(sc->code))                                  /* ((values 1 2 3) 0) */
	    {
	      /* car of values can be anything, so conjure up a new expression, and apply again */
	      sc->x = multiple_value(sc->code);                             /* ((values + 1 2) 3) */
	      sc->code = car(sc->x);
	      sc->args = s7_append(sc, cdr(sc->x), sc->args);
	      sc->x = sc->NIL;
	      goto APPLY;
	    }
	  if (is_null(sc->args))
	    return(s7_wrong_number_of_args_error(sc, "not enough args for list-ref (via list as applicable object): ~A", sc->args));
	  sc->value = list_ref_1(sc, sc->code, car(sc->args));            /* (L 1) */
	  if (!is_null(cdr(sc->args)))
	    sc->value = implicit_index(sc, sc->value, cdr(sc->args));     /* (L 1 2) */
	  goto START;
	  
	  
	case T_HASH_TABLE:                        /* -------- hash-table as applicable object -------- */
	  if (is_null(sc->args))
	    return(s7_wrong_number_of_args_error(sc, "not enough args for hash-table-ref (via hash table as applicable object): ~A", sc->args));
	  sc->value = s7_hash_table_ref(sc, sc->code, car(sc->args));
	  if (!is_null(cdr(sc->args)))
	    sc->value = implicit_index(sc, sc->value, cdr(sc->args));
	  goto START;
	  
	  
	case T_ENVIRONMENT:                       /* -------- environment as applicable object -------- */
	  if (is_null(sc->args))
	    sc->value = s7_let_ref(sc, sc->code, sc->F); /* why #f and not ()? both are ok in s7test */
	  else sc->value = s7_let_ref(sc, sc->code, car(sc->args));
	  if (is_pair(cdr(sc->args)))
	    sc->value = implicit_index(sc, sc->value, cdr(sc->args));
	  /*    (let ((v #(1 2 3))) (let ((e (curlet))) ((e 'v) 1))) -> 2
	   * so (let ((v #(1 2 3))) (let ((e (curlet))) (e 'v 1))) -> 2
	   */ 
	  goto START;
	  
	  
	case T_SYNTAX:                            /* -------- syntactic keyword as applicable object -------- */
	  sc->op = (opcode_t)syntax_opcode(sc->code);                       /* (apply begin '((define x 3) (+ x 2))) */
	  sc->code = sc->args;
	  if (has_cyclic_sequence(sc, sc->args))
	    return(eval_error(sc, "attempt to evaluate a circular list: ~A", sc->args));
	  fixup_macro_overlay(sc, sc->code);
	  goto START_WITHOUT_POP_STACK;
	  
	  
	default:
	  return(apply_error(sc, sc->code, sc->args));
	}
      /* ---------------- end APPLY/EVAL ---------------- */
      
      
    case OP_LAMBDA_STAR_DEFAULT:
      /* sc->args is the current closure arg list position, sc->value is the default expression's value
       */
      slot_set_value(sc->args, sc->value);
      sc->args = slot_pending_value(sc->args);
      goto LAMBDA_STAR_DEFAULT;
      
      
    case OP_QUOTE:
    case OP_QUOTE_UNCHECKED:
      /* I think a quoted list in another list can be applied to a function, come here and
       *   be changed to unchecked, set-cdr! or something clobbers the argument so we get
       *   here on the next time around with the equivalent of (quote . 0) so unchecked
       *   quote needs more thought.
       */
      check_quote(sc);
      sc->value = car(sc->code);
      goto START;
      
      
    case OP_DEFINE_STAR:
      check_define(sc);

    case OP_DEFINE_STAR_UNCHECKED:
      {
	s7_pointer x;
	/* make_closure* */
	NEW_CELL(sc, x);
	closure_args(x) = cdar(sc->code);
	closure_body(x) = cdr(sc->code);
	closure_let(x) = sc->envir;
	if (is_integer(fcdr(sc->code)))
	  closure_arity(x) = integer(fcdr(sc->code));
	else closure_arity(x) = CLOSURE_ARITY_NOT_SET;
	closure_setter(x) = sc->F;
	if (is_safe_closure(cdr(sc->code)))
	  set_type(x, T_CLOSURE_STAR | T_PROCEDURE | T_SAFE_CLOSURE);
	else set_type(x, T_CLOSURE_STAR | T_PROCEDURE);
	sc->capture_env_counter++;
	sc->value = x;
	sc->code = caar(sc->code);
	goto DEFINE1;
      }


    case OP_DEFINE_FUNCHECKED:
      {
	s7_pointer new_func, new_env, code;
	code = sc->code;
	sc->value = caar(code);

	/* make_closure... */
	NEW_CELL(sc, new_func);
	closure_args(new_func) = cdar(code);
	closure_body(new_func) = cdr(code);
	closure_setter(new_func) = sc->F; 

#if DEBUGGING
	if (!(fcdr(code))) {fprintf(stderr, "%d: no fcdr!\n", __LINE__); set_fcdr(code, small_int(512));}
#endif
	/* fcdr can be nil if we have a function, call it (so check_define+opt), then 
	 *   walk the procedure source while redefining the function, then call it again.
	 *   To be safe, we should actually call check_define, but how to force that?
	 *
	 * later: I think this is fixed now -- we're using fcdr as an int below!
	 */
	closure_arity(new_func) = integer(fcdr(code));
	set_type(new_func, T_CLOSURE | T_PROCEDURE | T_COPY_ARGS); 
	sc->capture_env_counter++; 
	
	if (is_safe_closure(cdr(code)))
	  {
	    s7_pointer arg;
	    set_safe_closure(new_func);
	    
	    NEW_CELL_NO_CHECK(sc, new_env);
	    let_id(new_env) = ++sc->let_number;
	    let_slots(new_env) = sc->NIL;
	    outlet(new_env) = sc->envir;
	    set_type(new_env, T_ENVIRONMENT | T_IMMUTABLE | T_FUNCTION_ENV);
	    closure_let(new_func) = new_env;
	    funclet_function(new_env) = sc->value;
	    
	    for (arg = closure_args(new_func); is_pair(arg); arg = cdr(arg))
	      s7_make_slot(sc, new_env, car(arg), sc->NIL);
	    let_slots(new_env) = reverse_slots(sc, let_slots(new_env));
	  }
	else closure_let(new_func) = sc->envir;
	/* unsafe closures created by other functions do not support __func__ */
	
	ADD_SLOT(sc->envir, sc->value, new_func);
	set_local(sc->value);
	sc->value = new_func; /* 25-Jul-14 */
	
	goto START;
      }

      
    case OP_DEFINE_CONSTANT1:
      /* define-constant -> OP_DEFINE_CONSTANT -> OP_DEFINE..1, then back to here
       *   sc->value is the value of the symbol that we want to be immutable, sc->code is the original pair 
       */
      {
	s7_pointer lx;
	lx = find_symbol(sc, car(sc->code));
	if (is_slot(lx))
	  set_immutable(slot_symbol(lx));
	goto START;
      }

      
    case OP_DEFINE_CONSTANT:
      push_stack(sc, OP_DEFINE_CONSTANT1, sc->NIL, sc->code);
      
      
    case OP_DEFINE:
      check_define(sc);
      
    case OP_DEFINE_UNCHECKED:
      if (!is_pair(car(sc->code)))
	{
	  s7_pointer x;
	  x = car(sc->code);
	  sc->code = cadr(sc->code);
	  if (is_pair(sc->code))
	    {
	      push_stack(sc, OP_DEFINE1, sc->NIL, x);
	      goto EVAL; 
	    }
	  
	  if (is_symbol(sc->code))
	    sc->value = find_global_symbol_checked(sc, sc->code);
	  else sc->value = sc->code;
	  sc->code = x;
	  /* fall through */
	}
      else
	{
	  s7_pointer x;
	  /* a closure.  If we called this same code earlier (a local define), the only thing
	   *   that is new here is the environment -- we can't blithely save the closure object
	   *   in fcdr somewhere, and pick it up the next time around (since call/cc might take
	   *   us back to the previous case).
	   */

	  make_closure_with_let(sc, x, cdar(sc->code), cdr(sc->code), sc->envir);
	  if (is_integer(fcdr(sc->code)))
	    closure_arity(x) = integer(fcdr(sc->code));
	  sc->value = x;
	  sc->code = caar(sc->code);
	  /* fall through */
	}
      

    DEFINE1:
    case OP_DEFINE1:
      /* sc->code is the symbol being defined, sc->value is its value
       *   if sc->value is a closure, car is of the form ((args...) body...)
       *   so the doc string if any is (cadr (car value))
       *   and the arg list gives the number of optional args up to the dot
       */
      
      /* it's not possible to expand and replace macros at this point without evaluating
       *   the body.  Just as examples, say we have a macro "mac", 
       *   (define (hi) (call/cc (lambda (mac) (mac 1))))
       *   (define (hi) (quote (mac 1))) or macroexpand etc
       *   (define (hi mac) (mac 1)) assuming mac here is a function passed as an arg,
       * etc...  
       */
      
      /* the immutable constant check needs to wait until we have the actual new value because
       *   we want to ignore the rebinding (not raise an error) if it is the existing value.
       *   This happens when we reload a file that calls define-constant.
       */
      if (is_immutable(sc->code))                                        /* (define pi 3) or (define (pi a) a) */
	{
	  s7_pointer x;
	  if (!is_symbol(sc->code))                                      /* (define "pi" 3) ? */
	    eval_error_with_name(sc, "~A: ~S is immutable", sc->code);
	  
	  x = find_symbol(sc, sc->code);
	  /* if not a slot, the define-constant must have been local -- this has to still be a constant however:
	   *    (let () (define-constant aaa 2)) (define aaa 3) (set! aaa 4) -> error!
	   *    but in this case, (define aaa 2) is also an error -- inconsistent in a sense.
	   */
	  if ((!is_slot(x)) ||
	      (!s7_is_morally_equal(sc, sc->value, slot_value(x), NULL)))/* if value is unchanged, just ignore this (re)definition */
	    eval_error_with_name(sc, "~A: ~S is immutable", sc->code);   /*   can't use s7_is_equal because value might be NaN, etc */
	}
      if (symbol_has_accessor(sc->code))
	{
	  s7_pointer x;
	  x = find_symbol(sc, sc->code);
	  if ((is_slot(x)) &&
	      (slot_has_accessor(x)))
	    {
	      sc->value = bind_accessed_symbol(sc, OP_DEFINE_WITH_ACCESSOR, sc->code, sc->value);
	      if (sc->value == sc->NO_VALUE)
		goto APPLY;
	      /* if all goes well, OP_DEFINE_WITH_ACCESSOR will jump to DEFINE2 */
	    }
	}
      
    DEFINE2:
      if (is_any_closure(sc->value))
	{
	  s7_pointer new_func, new_env;
	  new_func = sc->value;

	  /* we can get here from let: (define (outer a) (let () (define (inner b) (+ a b)) (inner a)))
	   *   but the port info is not relevant here, so restrict the __func__ list making to top-level
	   *   cases (via sc->envir == sc->NIL).
	   */

	  NEW_CELL_NO_CHECK(sc, new_env);                   
	  let_id(new_env) = ++sc->let_number; 
	  outlet(new_env) = closure_let(new_func);
	  closure_let(new_func) = new_env;		   
	  let_slots(new_env) = sc->NIL;
	  funclet_function(new_env) = sc->code;

	  if ((!is_let(sc->envir)) &&
	      (port_filename(sc->input_port)) &&
	      (port_file(sc->input_port) != stdin))
	    {
	      /* unbound_variable will be called if __func__ is encountered, and will return this info as if __func__ had some meaning */
	      set_type(new_env, T_ENVIRONMENT | T_IMMUTABLE | T_FUNCTION_ENV | T_LINE_NUMBER); 
	      let_file(new_env) = port_file_number(sc->input_port);
	      let_line(new_env) = port_line_number(sc->input_port);
	    }
	  else set_type(new_env, T_ENVIRONMENT | T_IMMUTABLE | T_FUNCTION_ENV); 

	  /* this should happen only if the closure* default values do not refer in any way to
	   *   the enclosing environment (else we can accidentally shadow something that happens
	   *   to share an argument name that is being used as a default value -- kinda dumb!).
	   *   I think I'll check this before setting the safe_closure bit.
	   */
	  if (is_safe_closure(new_func))
	    {
	      int i;
	      s7_pointer arg;
	      for (i = 0, arg = closure_args(new_func); is_pair(arg); i++, arg = cdr(arg))
		{
		  if (is_pair(car(arg)))
		    s7_make_slot(sc, new_env, caar(arg), sc->NIL);
		  else s7_make_slot(sc, new_env, car(arg), sc->NIL);
		}
	      let_slots(new_env) = reverse_slots(sc, let_slots(new_env));
	    }
	  /* add the newly defined thing to the current environment */
	  if (is_let(sc->envir))
	    {
	      ADD_SLOT(sc->envir, sc->code, new_func);
	      set_local(sc->code);
	      /* so funchecked is always local already -- perhaps reset below? */
	    }
	  else s7_make_slot(sc, sc->envir, sc->code, new_func);
	  sc->value = new_func; /* 25-Jul-14 so define returns the value not the name */
	}
      else
	{
	  s7_pointer lx;
	  /* add the newly defined thing to the current environment */
	  lx = find_local_symbol(sc, sc->code, sc->envir);
	  if (is_slot(lx))
	    slot_set_value(lx, sc->value); 
	  else s7_make_slot(sc, sc->envir, sc->code, sc->value);
	}
      IF_BEGIN_POP_STACK(sc);
      
      
    case OP_DEFINE_WITH_ACCESSOR:
      if (sc->value == sc->ERROR) /* backwards compatibility... */
	return(s7_error(sc, sc->ERROR,
			list_3(sc, make_string_wrapper(sc, "can't define ~S to ~S"), car(sc->args), cadr(sc->args))));
      goto DEFINE2;
      
      
      /* -------------------------------- SET! -------------------------------- */

    case OP_SET_FV_SCALED:
      /* (set! (fv i) (* s (fv i))) -- if not float-vector, change to use set_pair_p
       */
      {
	s7_pointer f1, settee, ind1, scl;
	settee = car(sc->code);
	f1 = find_symbol_checked(sc, car(settee));
	if ((is_float_vector(f1)) &&
	    (vector_rank(f1) == 1))
	  {
	    ind1 = find_symbol_checked(sc, cadr(settee));
	    if (is_integer(ind1))
	      {
		s7_Int ind;
		ind = integer(ind1);
		if ((ind < 0) ||
		    (ind >= vector_length(f1)))
		  out_of_range(sc, sc->VECTOR_REF, small_int(2), ind1, (ind < 0) ? ITS_NEGATIVE : ITS_TOO_LARGE);
		else
		  {
		    scl = find_symbol_checked(sc, cadr(cadr(sc->code)));
		    if (is_real(scl))
		      {
			float_vector_element(f1, ind) *= number_to_double(sc, scl, "float-vector-set!");
			/* what value here?? we want to avoid the make-real allocation! */
			if (main_stack_op(sc) == OP_BEGIN1) goto POP_BEGIN;
			sc->value = make_real(sc, float_vector_element(f1, ind));
			goto START;
		      }
		  }
	      }
	  }
	/* back out of the initial guess */
	pair_set_syntax_symbol(sc->code, sc->SET_PAIR_P);
	goto PAIR_P;
      }


    case OP_SET_PAIR_UNKNOWN_G:
      /* look for (set! (fv1 s1) (fv2 s2)) with no complications.  If not that case, drop into SET_PAIR_P
       */
      {
	s7_pointer f1, f2, settee, value, ind1, ind2;
	settee = car(sc->code);
	value = cadr(sc->code);
	f1 = find_symbol_checked(sc, car(settee));
	ind1 = cadr(settee);
	if (is_symbol(ind1))
	  ind1 = find_symbol_checked(sc, ind1);
	f2 = find_symbol_checked(sc, car(value));
	ind2 = cadr(value);
	if (is_symbol(ind2))
	  ind2 = find_symbol_checked(sc, ind2);
	if ((is_float_vector(f1)) &&
	    (is_float_vector(f2)) &&
	    (is_integer(ind1)) &&
	    (is_integer(ind2)) &&
	    (vector_rank(f1) == 1) &&
	    (vector_rank(f2) == 1))
	  {
	    s7_Int index1, index2;
	    index1 = s7_integer(ind1);
	    index2 = s7_integer(ind2);
	    if ((index1 < vector_length(f1)) && (index1 >= 0))
	      {
		if ((index2 < vector_length(f2)) && (index2 >= 0))
		  {
		    float_vector_element(f1, index1) = float_vector_element(f2, index2);
		    IF_BEGIN_POP_STACK(sc); 
		  }
		else out_of_range(sc, sc->VECTOR_REF, small_int(1), cadr(value), (index2 < 0) ? ITS_NEGATIVE : ITS_TOO_LARGE);
	      }
	    else out_of_range(sc, sc->VECTOR_REF, small_int(1), cadr(settee), (index1 < 0) ? ITS_NEGATIVE : ITS_TOO_LARGE);
	  }
	pair_set_syntax_symbol(sc->code, sc->SET_PAIR_P);
	/* fall through */
      }
      
    PAIR_P:
    case OP_SET_PAIR_P:
      /* ([set!] (car a) (cadr a)) */
      /* here the pair can't generate multiple values, or if it does, it's an error (caught below) 
       *  splice_in_values will notice the OP_SET_PAIR_P_1 and complain.
       * (let () (define (hi) (let ((str "123")) (set! (str 0) (values #\a)) str)) (hi) (hi)) is "a23"
       * (let () (define (hi) (let ((str "123")) (set! (str 0) (values #\a #\b)) str)) (hi) (hi)) is an error from the first call (caught elsewhere)
       * (let () (define (hi) (let ((str "123")) (set! (str 0) (values #\a #\b)) str)) (catch #t hi (lambda a a)) (hi)) is an error from the second call
       */
      push_stack_no_args(sc, OP_SET_PAIR_P_1, sc->code);
      sc->code = cadr(sc->code);
      goto EVAL;


    case OP_SET_PAIR_Z:
      push_stack_no_args(sc, OP_SET_PAIR_P_1, sc->code);
      sc->code = cadr(sc->code);
      goto OPT_EVAL;


    case OP_SET_PAIR_A:
      {
	s7_pointer obj, val;
	obj = find_symbol_checked(sc, caar(sc->code));
	val = ((s7_function)fcdr(cdr(sc->code)))(sc, cadr(sc->code)); /* this call can step on sc->Tx_x */
	car(sc->T2_1) = cadar(sc->code);  /* might be a constant: (set! (mus-sound-srate "oboe.snd") 12345) */
	if (is_symbol(car(sc->T2_1)))
	  car(sc->T2_1) = find_symbol_checked(sc, cadar(sc->code));
	car(sc->T2_2) = val;
	sc->value = c_function_call(c_function_setter(obj))(sc, sc->T2_1);
	IF_BEGIN_POP_STACK(sc);
      }


    case OP_SET_PAIR_C_P:
      /* ([set!] (name (+ i 1)) (if (eq? (car a) 'car) #\a #\d)) */
      push_stack_no_args(sc, OP_SET_PAIR_C_P_1, sc->code);
      sc->code = cadr(sc->code);
      goto EVAL;

      {
	s7_pointer obj, arg, value;

      case OP_SET_PAIR_C_P_1:
	/* code: ((name (+ i 1)) ...) for example, so cadar is the c_c expr 
	 *   and its args are cdr(cadar)
	 */
	value = sc->value;
	arg = c_call(cadar(sc->code))(sc, cdadar(sc->code));
	obj = find_symbol(sc, caar(sc->code));
	if (is_slot(obj))
	  obj = slot_value(obj);
	else eval_error(sc, "no generalized set for ~A", caar(sc->code));
	goto SET_PAIR_P_3;
	
	
      case OP_SET_PAIR_C:
	/* ([set!] (name (+ len 1)) #\r) */
	value = cadr(sc->code);
	if (is_symbol(value))
	  value = find_symbol_checked(sc, value);
	arg = c_call(cadar(sc->code))(sc, cdadar(sc->code));
	obj = find_symbol(sc, caar(sc->code));
	if (is_slot(obj))
	  obj = slot_value(obj);
	else eval_error(sc, "no generalized set for ~A", caar(sc->code));
	goto SET_PAIR_P_3;
	
	
      case OP_SET_ENV_S:
	value = find_symbol_checked(sc, cadr(sc->code));
	obj = find_symbol(sc, caar(sc->code));
	if (is_slot(obj))
	  obj = slot_value(obj);
	else eval_error(sc, "no generalized set for ~A", caar(sc->code));
	arg = cadr(cadar(sc->code));
	if (is_let(obj))
	  {
	    sc->value = s7_let_set(sc, obj, arg, value);
	    IF_BEGIN_POP_STACK(sc); 
	  }
	goto SET_PAIR_P_3;


      case OP_SET_ENV_ALL_X:
	value = ((s7_function)fcdr(cdr(sc->code)))(sc, cadr(sc->code));
	obj = find_symbol(sc, caar(sc->code));
	if (is_slot(obj))
	  obj = slot_value(obj);
	else eval_error(sc, "no generalized set for ~A", caar(sc->code));
	arg = cadr(cadar(sc->code));
	if (is_let(obj))
	  {
	    sc->value = s7_let_set(sc, obj, arg, value);
	    IF_BEGIN_POP_STACK(sc); 
	  }
	goto SET_PAIR_P_3;


      case OP_SET_PAIR_ZA:
	/* unknown setter pair, but value is easy */
	sc->value = ((s7_function)fcdr(cdr(sc->code)))(sc, cadr(sc->code)); 
	/* fall through */


      case OP_SET_PAIR_P_1:
	/* car(sc->code) is a pair, caar(code) is the object with a setter, it has one (safe) argument, and one safe value to set
	 *   (set! (str i) #\a) in a function (both inner things need to be symbols (or the 2nd can be a quoted symbol) to get here)
	 *   the inner list is a proper list, with no embedded list at car.
	 */
	value = sc->value;
	
	arg = cadar(sc->code);
	if (is_symbol(arg))
	  arg = find_symbol_checked(sc, arg);
	else
	  {
	    if (is_pair(arg))
	      arg = cadr(arg); /* can only be (quote ...) in this case */
	  }
	obj = find_symbol(sc, caar(sc->code));
	if (is_slot(obj))
	  obj = slot_value(obj);
	else eval_error(sc, "no generalized set for ~A", caar(sc->code));
	goto SET_PAIR_P_3;
	
	

      case OP_SET_PAIR:
	/* ([set!] (procedure-setter g) s) or ([set!] (str 0) #\a) */
	value = cadr(sc->code);
	if (is_symbol(value))
	  value = find_symbol_checked(sc, value);
	
	arg = cadar(sc->code);
	if (is_symbol(arg)) 
	  arg = find_symbol_checked(sc, arg);
	else
	  {
	    if (is_pair(arg))
	      arg = cadr(arg); /* can only be (quote ...) in this case */
	  }
	obj = caar(sc->code);
	if (is_symbol(obj))
	  {
	    obj = find_symbol(sc, obj);
	    if (is_slot(obj))
	      obj = slot_value(obj);
	    else eval_error(sc, "no generalized set for ~A", caar(sc->code));
	  }
	
	
      SET_PAIR_P_3:
	switch (type(obj))
	  {
	  case T_C_OBJECT:
	    car(sc->T2_1) = arg;
	    car(sc->T2_2) = value;
	    sc->value = (*(c_object_set(obj)))(sc, obj, sc->T2_1);
	    break;
	    
	    /* some of these are wasteful -- we know the object type! (list hash-table)
	     */
	  case T_INT_VECTOR:
	  case T_FLOAT_VECTOR:
	  case T_VECTOR:
#if WITH_GMP
	    car(sc->T3_1) = obj;
	    car(sc->T3_2) = arg;
	    car(sc->T3_3) = value;
	    sc->value = g_vector_set(sc, sc->T3_1);
#else
	    if (vector_rank(obj) > 1)
	      {
		car(sc->T3_1) = obj;
		car(sc->T3_2) = arg;
		car(sc->T3_3) = value;
		sc->value = g_vector_set(sc, sc->T3_1);
	      }
	    else
	      {
		s7_Int index;
		if (!is_integer(arg))
		  eval_type_error(sc, "vector-set!: index must be an integer: ~S", sc->code);
		index = integer(arg);
		if (index < 0)
		  eval_range_error(sc, "vector-set!: index must not be negative: ~S", sc->code);
		if (index >= vector_length(obj))
		  eval_range_error(sc, "vector-set!: index must be less than vector length: ~S", sc->code);
		vector_setter(obj)(sc, obj, index, value);
		sc->value = value;
	      }
#endif
	    break;
	    
	  case T_STRING:
#if WITH_GMP
	    car(sc->T3_1) = obj;
	    car(sc->T3_2) = arg;
	    car(sc->T3_3) = value;
	    sc->value = g_string_set(sc, sc->T3_1);
#else
	    {
	      s7_Int index;
	      if (!is_integer(arg))
		eval_type_error(sc, "string-set!: index must be an integer: ~S", sc->code);
	      index = integer(arg);
	      if (index < 0)
		eval_range_error(sc, "string-set!: index must not be negative: ~S", sc->code);
	      if (index >= string_length(obj))
		eval_range_error(sc, "string-set!: index must be less than string length: ~S", sc->code);
	      if (s7_is_character(value))
		{
		  string_value(obj)[index] = (char)s7_character(value);
		  sc->value = value;
		}
	      else
		{
		  if ((is_bytevector(obj)) &&
		      (s7_is_integer(value)))
		    {
		      int ic;
		      ic = s7_integer(value);
		      if ((ic < 0) || (ic > 255))
			eval_type_error(sc, "string-set!: value must be a character: ~S", sc->code);
		      string_value(obj)[index] = (char)ic;
		      sc->value = value;
		    }
		  else eval_type_error(sc, "string-set!: value must be a character: ~S", sc->code);
		}
	    }
#endif
	    break;
	    
	  case T_PAIR:
	    car(sc->T3_1) = obj;
	    car(sc->T3_2) = arg;
	    car(sc->T3_3) = value;
	    sc->value = g_list_set(sc, sc->T3_1);
	    break;
	    
	  case T_HASH_TABLE:
	    car(sc->T3_1) = obj;
	    car(sc->T3_2) = arg;
	    car(sc->T3_3) = value;
	    sc->value = g_hash_table_set(sc, sc->T3_1);
	    break;
	    
	  case T_ENVIRONMENT:
	    /* (set! (env sym) val) */
	    sc->value = s7_let_set(sc, obj, arg, value);
	    break;
	    
	  case T_C_OPT_ARGS_FUNCTION:
	  case T_C_RST_ARGS_FUNCTION:
	  case T_C_ANY_ARGS_FUNCTION:                       /* (let ((lst (list 1 2))) (set! (list-ref lst 1) 2) lst) */
	  case T_C_FUNCTION:
	  case T_C_FUNCTION_STAR:
	    /* obj here is a c_function, but its setter could be a closure and vice versa below
	     */
	    if (is_procedure(c_function_setter(obj)))
	      {
		if (is_c_function(c_function_setter(obj)))
		  {
		    car(sc->T2_1) = arg;
		    car(sc->T2_2) = value;
		    sc->value = c_function_call(c_function_setter(obj))(sc, sc->T2_1);
		  }
		else
		  {
		    sc->code = c_function_setter(obj);
		    sc->args = list_2(sc, arg, value);
		    goto APPLY;
		  }
	      }
	    else
	      {
		if (is_any_macro(c_function_setter(obj)))
		  {
		    push_stack(sc, OP_EVAL_MACRO, sc->NIL, sc->args);
		    if (is_null(cdar(sc->code)))
		      sc->args = list_1(sc, cons(sc, c_function_setter(obj), cdr(sc->code)));
		    else sc->args = list_1(sc, cons(sc, c_function_setter(obj), s7_append(sc, cdar(sc->code), cdr(sc->code))));
		    sc->code = c_function_setter(obj);
		    goto APPLY;
		  }
		else eval_error(sc, "no generalized set for ~A", obj); 
	      }
	    break;
	    
	  case T_MACRO:
	  case T_BACRO:
	  case T_CLOSURE:
	  case T_CLOSURE_STAR:
	    if (is_procedure(closure_setter(obj)))
	      {
		if (is_c_function(closure_setter(obj)))
		  {
		    car(sc->T2_1) = arg;
		    car(sc->T2_2) = value;
		    sc->value = c_function_call(closure_setter(obj))(sc, sc->T2_1);
		  }
		else
		  {
		    sc->code = closure_setter(obj);
		    sc->args = list_2(sc, arg, value);
		    goto APPLY;
		  }
	      }
	    else
	      {
		if (is_any_macro(closure_setter(obj)))
		  {
		    push_stack(sc, OP_EVAL_MACRO, sc->NIL, sc->args);
		    if (is_null(cdar(sc->code)))
		      sc->args = list_1(sc, cons(sc, closure_setter(obj), cdr(sc->code)));
		    else sc->args = list_1(sc, cons(sc, closure_setter(obj), s7_append(sc, cdar(sc->code), cdr(sc->code))));
		    sc->code = closure_setter(obj);
		    goto APPLY;
		  }
		else eval_error(sc, "no generalized set for ~A", obj); 
	      }
	    break;
	    
	  default:                                         /* (set! (1 2) 3) */
	    eval_error(sc, "no generalized set for ~A", obj);
	  }
	IF_BEGIN_POP_STACK(sc); 
      }
      

      /* this is (set! (getter) val) where getter is a global c_function (a built-in pws) and val is not a pair
       */
    case OP_SET_PWS:
      {
	/* ([set!] (save-dir) "/home/bil/zap/snd") */
	s7_pointer obj;
	
	obj = caar(sc->code);
	if (is_symbol(obj))
	  {
	    obj = find_symbol(sc, obj);
	    if (is_slot(obj))
	      obj = slot_value(obj);
	    else eval_error(sc, "no generalized set for ~A", caar(sc->code));
	  }
	
	if ((is_c_function(obj)) &&
	    (is_procedure(c_function_setter(obj))))
	  {
	    s7_pointer value;
	    value = cadr(sc->code);
	    if (is_symbol(value))
	      value = find_symbol_checked(sc, value);
	    
	    car(sc->T1_1) = value;
	    sc->value = c_function_call(c_function_setter(obj))(sc, sc->T1_1);
	  }
	else eval_error(sc, "no generalized set for ~A", obj); 
	IF_BEGIN_POP_STACK(sc);
      }
      
      
    case OP_INCREMENT_1:
      {
	/* ([set!] ctr (+ ctr 1)) */
	s7_pointer y;

	y = find_symbol(sc, car(sc->code));
	if (is_slot(y))
	  {
	    s7_pointer val;	    
	    val = slot_value(y);
	    switch (type(val))
	      {
	      case T_INTEGER:
		slot_set_value(y, make_integer(sc, integer(val) + 1));
		/* this can't be optimized to treat y's value as a mutable integer 
		 * also, we have to set sc->value, since s7.html says set! returns the value.
		 */
		IF_BEGIN_POP_STACK_ELSE_SET_VALUE(sc, y);
		
	      case T_REAL:
		sc->value = make_real(sc, real(val) + 1.0);
		slot_set_value(y, sc->value);
		goto START;
		
	      default:
		break;
	      }
	  }
	/* else use standard set */
	push_stack_no_args(sc, OP_SET_SAFE, car(sc->code));
	sc->code = cadr(sc->code);
	goto EVAL; 
      }

      
    case OP_DECREMENT_1:
      {
	/* ([set!] ctr (- ctr 1)) */
	s7_pointer y;
	y = find_symbol(sc, car(sc->code));
	if (is_slot(y))
	  {
	    s7_pointer val;	    
	    val = slot_value(y);
	    switch (type(val))
	      {
	      case T_INTEGER:
		sc->value = make_integer(sc, integer(val) - 1);
		slot_set_value(y, sc->value);
		goto START;
		
	      case T_REAL:
		sc->value = make_real(sc, real(val) - 1.0);
		slot_set_value(y, sc->value);
		goto START;
		
	      default:
		break;
	      }
	  }
	push_stack_no_args(sc, OP_SET_SAFE, car(sc->code)); 
	sc->code = cadr(sc->code);
	goto EVAL; 
      }

      
    case OP_SET_CONS:
      /* it's safe if hop_safe_c_ss, so if op_safe_c_ss we check? */
      /* ([set!] bindings (cons v bindings)) */
      {
	s7_pointer y;
	y = find_symbol(sc, car(sc->code)); 
	if (is_slot(y))
	  {
	    sc->value = cons(sc, find_symbol_checked(sc, fcdr(sc->code)), slot_value(y));
	    slot_set_value(y, sc->value);
	    goto START;
	  }
	push_stack_no_args(sc, OP_SET2, car(sc->code)); 
	sc->code = cadr(sc->code);
	goto EVAL; 
      }


    case OP_SET_SYMBOL_SAFE_C:
      {
	s7_pointer lx;
	/* ([set!] ctr (- ctr 11)) */
	lx = find_symbol(sc, car(sc->code));
	if (is_slot(lx)) 
	  {
	    slot_set_value(lx, c_call(cadr(sc->code))(sc, fcdr(sc->code)));
	    IF_BEGIN_POP_STACK_ELSE_SET_VALUE(sc, lx); 
	  }
	eval_type_error(sc, "set! ~A: unbound variable", sc->code);
      }


    case OP_INCREMENT_C_TEMP:
      {
	s7_pointer sym, arg1, arg2, code;

	code = sc->code;
	sym = find_symbol(sc, car(code));
	if (is_slot(sym)) 
	  {
	    arg2 = c_call(fcdr(code))(sc, cdr(fcdr(code)));
	    arg1 = slot_value(sym);
	    if (is_simple_real(arg1))
	      sc->value = remake_real(sc, arg2, real(arg1) + real(arg2));
	    else sc->value = g_a_s_t(sc, arg1, arg2, fcdr(code));
	    slot_set_value(sym, sc->value);
	    goto START; 
	  }
      }
      eval_type_error(sc, "set! ~A: unbound variable", sc->code);
      

    case OP_SET_SYMBOL_SAFE_SS:
      {
	s7_pointer sym, code;
	code = sc->code;
	sym = find_symbol(sc, car(code));
	if (is_slot(sym))
	  {
	    car(sc->T2_1) = find_symbol_checked(sc, car(fcdr(code)));
	    car(sc->T2_2) = find_symbol_checked(sc, cadr(fcdr(code)));
	    slot_set_value(sym, c_call(cadr(code))(sc, sc->T2_1));
	    IF_BEGIN_POP_STACK_ELSE_SET_VALUE(sc, sym); 
	  }
      }
      eval_type_error(sc, "set! ~A: unbound variable", sc->code);


    case OP_INCREMENT_SS:
      {
	s7_pointer lx;
	/* ([set!] x (+ x i)) */
	lx = find_symbol(sc, car(sc->code));
	if (is_slot(lx)) 
	  {
	    car(sc->T2_1) = slot_value(lx);
	    car(sc->T2_2) = find_symbol_checked(sc, cadr(fcdr(sc->code)));
	    slot_set_value(lx, c_call(cadr(sc->code))(sc, sc->T2_1));
	    IF_BEGIN_POP_STACK_ELSE_SET_VALUE(sc, lx);
	  }
	eval_type_error(sc, "set! ~A: unbound variable", sc->code);
      }

    case OP_INCREMENT_SSS:
      {
	s7_pointer lx;
	/* ([set!] x (+ x y z)) -- nearly always involves reals */
	lx = find_symbol(sc, car(sc->code));
	if (is_slot(lx))
	  {
	    s7_pointer x1, x2, x3;
	    x1 = slot_value(lx);
	    x2 = find_symbol_checked(sc, ecdr(fcdr(sc->code)));
	    x3 = find_symbol_checked(sc, fcdr(fcdr(sc->code)));
	    if ((type(x1) == T_REAL) &&
		(type(x2) == T_REAL) &&
		(type(x3) == T_REAL))
	      slot_set_value(lx, make_real(sc, real(x1) + real(x2) + real(x3)));
	    else
	      {
		car(sc->T3_1) = x1;
		car(sc->T3_2) = x2;
		car(sc->T3_3) = x3;
		slot_set_value(lx, g_add(sc, sc->T3_1));
	      }
	    IF_BEGIN_POP_STACK_ELSE_SET_VALUE(sc, lx);
	  }
	eval_type_error(sc, "set! ~A: unbound variable", sc->code);
      }

    case OP_SET_SYMBOL_SAFE_SSS:
      {
	s7_pointer sym, code;
	code = sc->code;
	sym = find_symbol(sc, car(code));
	if (is_slot(sym))
	  {
	    car(sc->T3_1) = find_symbol_checked(sc, car(fcdr(code))); /* checking car=car(fcdr) here is slightly slower */
	    car(sc->T3_2) = find_symbol_checked(sc, ecdr(fcdr(code))); 
	    car(sc->T3_3) = find_symbol_checked(sc, fcdr(fcdr(code)));
	    slot_set_value(sym, c_call(cadr(code))(sc, sc->T3_1));
	    IF_BEGIN_POP_STACK_ELSE_SET_VALUE(sc, sym); 
	  }
	eval_type_error(sc, "set! ~A: unbound variable", sc->code);
      }


    case OP_SET_SYMBOL_SAFE_S:
      {
	s7_pointer sym;
	sym = car(sc->code);
	car(sc->T1_1) = find_symbol_checked(sc, fcdr(sc->code));
	sc->value = c_call(cadr(sc->code))(sc, sc->T1_1);
	/*
	 * I think this is supposed to be a safe c function (is_h_safe_c_s above), but
	 *   it's possible for the call to change sc->code?  
	 *   in snd-test: 
	 *           (set! snd-output (open-sound output-1)) 
	 *   becomes (set! (hook-functions *error-hook*) ())
	 *   because open-sound is calling itself safe, but it can call all kinds of hooks and whatnot,
	 *   and these can hit errors, setting *error-hook*, fallling into s7_call and so on.
	 */
	sc->code = sym;
	/* goto SET_SAFE; */
      }


    SET_SAFE:
    case OP_SET_SAFE:  
      {
	s7_pointer lx;
	lx = find_symbol(sc, sc->code);
	if (is_slot(lx)) 
	  {
	    slot_set_value(lx, sc->value); 
	    IF_BEGIN_POP_STACK_ELSE_SET_VALUE(sc, lx); 
	  }
	eval_type_error(sc, "set! ~A: unbound variable", sc->code);
      }
      
    case OP_SET_SYMBOL_C:      /* ([set!] x 3) */
      sc->value = cadr(sc->code);
      sc->code = car(sc->code);
      goto SET_SAFE;
      

    case OP_SET_SYMBOL_Q:      /* ([set!] incr '+) */
      sc->value = cadr(cadr(sc->code));
      sc->code = car(sc->code);
      goto SET_SAFE;
      

    case OP_SET_SYMBOL_P:      /* ([set!] f (lambda () 1)) */
      push_stack_no_args(sc, OP_SET_SAFE, car(sc->code)); 
      sc->code = cadr(sc->code);
      goto EVAL; 
      /* possible optimization: if types match, and "P" part is direct, we could
       *   just set the current slot, and free the direct value.
       */


    case OP_SET_SYMBOL_UNKNOWN_G:
      {
	s7_pointer f, value;
	/* (set! x (v1 i)) */
	value = cadr(sc->code);
	f = find_symbol_checked(sc, car(value));
	if ((type(f) == T_FLOAT_VECTOR) &&
	    (vector_rank(f) == 1))
	  {
	    s7_pointer ind;
	    s7_Int index;

	    ind = cadr(value);
	    if (is_symbol(ind))
	      ind = find_symbol_checked(sc, ind);
	    if (type(ind) == T_INTEGER)
	      {
		index = s7_integer(ind);
		if ((index < vector_length(f)) &&
		    (index >= 0))
		  {
		    s7_pointer lx;
		    lx = find_symbol(sc, car(sc->code));
		    if (is_slot(lx)) 
		      {
			slot_set_value(lx, float_vector_getter(sc, f, index));
			IF_BEGIN_POP_STACK_ELSE_SET_VALUE(sc, lx);
		      }
		    eval_type_error(sc, "set! ~A: unbound variable", car(sc->code));
		  }
		else out_of_range(sc, sc->VECTOR_REF, small_int(1), cadr(value), (index < 0) ? ITS_NEGATIVE : ITS_TOO_LARGE);
	      }
	  }
	pair_set_syntax_symbol(sc->code, sc->SET_SYMBOL_P);
	/* fall through */
      }


    case OP_SET_SYMBOL_Z:
      /* ([set!] sum (+ sum n)) */
      push_stack_no_args(sc, OP_SET_SAFE, car(sc->code)); 
      sc->code = cadr(sc->code);
      goto OPT_EVAL;


    case OP_SET_SYMBOL_A:
      {
	s7_pointer lx;
	lx = find_symbol(sc, car(sc->code));
	if (is_slot(lx)) 
	  {
	    slot_set_value(lx, ((s7_function)fcdr(cdr(sc->code)))(sc, cadr(sc->code)));
	    IF_BEGIN_POP_STACK_ELSE_SET_VALUE(sc, lx); 
	  }
	eval_type_error(sc, "set! ~A: unbound variable", sc->code);
      }
      
    case OP_INCREMENT_SA:
      {
	s7_pointer sym;

	sym = find_symbol(sc, car(sc->code));
	if (is_slot(sym))
	  {
	    s7_pointer arg;
	    arg = fcdr(sc->code); /* cddr(value) */
	    car(sc->T2_2) = ((s7_function)fcdr(arg))(sc, car(arg));
	    car(sc->T2_1) = slot_value(sym);
	    sc->value = c_call(cadr(sc->code))(sc, sc->T2_1);
	    slot_set_value(sym, sc->value);
	    goto START; 
	  }
	eval_type_error(sc, "set! ~A: unbound variable", sc->code);
      }


    case OP_INCREMENT_SAA:
      {
	s7_pointer sym;
	/* (set! sum (+ sum (expt k i) (expt (- k) i))) */
	sym = find_symbol(sc, car(sc->code));
	if (is_slot(sym))
	  {
	    s7_pointer arg;
	    arg = fcdr(sc->code); /* cddr(value) */
	    car(sc->A3_3) = ((s7_function)fcdr(cdr(arg)))(sc, cadr(arg));
	    car(sc->A3_2) = ((s7_function)fcdr(arg))(sc, car(arg));
	    car(sc->A3_1) = slot_value(sym);
	    sc->value = c_call(cadr(sc->code))(sc, sc->A3_1);
	    slot_set_value(sym, sc->value);
	    goto START; 
	  }
	eval_type_error(sc, "set! ~A: unbound variable", sc->code);
      }


    case OP_INCREMENT_SZ:
      {
	s7_pointer sym;

	sym = find_symbol(sc, car(sc->code));
	if (is_slot(sym))
	  {
	    push_stack(sc, OP_INCREMENT_SZ_1, sym, sc->code);
	    sc->code = fcdr(sc->code); /* caddr(cadr(sc->code)); */
	    goto OPT_EVAL;
	  }
	eval_type_error(sc, "set! ~A: unbound variable", sc->code);
      }

    case OP_INCREMENT_SZ_1:
      car(sc->T2_1) = slot_value(sc->args);
      car(sc->T2_2) = sc->value;
      sc->value = c_call(cadr(sc->code))(sc, sc->T2_1);
      slot_set_value(sc->args, sc->value);
      goto START;


    case OP_SET_SYMBOL_S:
      {
	s7_pointer sym;
	sym = find_symbol(sc, car(sc->code));
	if (is_slot(sym))
	  {
	    slot_set_value(sym, find_symbol_checked(sc, cadr(sc->code)));
	    IF_BEGIN_POP_STACK_ELSE_SET_VALUE(sc, sym);
	  }
	eval_type_error(sc, "set! ~A: unbound variable", sc->code);
      }
      

    case OP_SET2:
      if (is_pair(sc->value))
	{
	  /* (let ((L '((1 2 3)))) (set! ((L 0) 1) 32) L)
	   * (let ((L '(((1 2 3))))) (set! ((L 0) 0 1) 32) L)
	   * any deeper nesting was handled already by the first eval
	   *   set! looks at its first argument, if it's a symbol, it sets the associated value,
	   *   if it's a list, it looks at the car of that list to decide which setter to call,
	   *   if it's a list of lists, it passes the embedded lists to eval, then looks at the
	   *   car of the result.  This means that we can do crazy things like:
	   *   (let ((x '(1)) (y '(2))) (set! ((if #t x y) 0) 32) x)
	   *
	   * the other args need to be evaluated (but not the list as if it were code):
	   *   (let ((L '((1 2 3))) (index 1)) (set! ((L 0) index) 32) L)
	   */
	  
	  if (!is_proper_list(sc, sc->args))                                 /* (set! ('(1 2) 1 . 2) 1) */
	    eval_error(sc, "set! target arguments are an improper list: ~A", sc->args);
	  
	  /* in all of these cases, we might need to GC protect the temporary lists */
	  
	  if (is_multiple_value(sc->value))
	    sc->code = cons(sc, sc->SET, s7_append(sc, multiple_value(sc->value), s7_append(sc, sc->args, sc->code))); /* drop into OP_SET */
	  else
	    {
	      if (sc->args != sc->NIL)
		{
		  push_op_stack(sc, sc->List_Set);
		  push_stack(sc, OP_EVAL_ARGS1, list_1(sc, sc->value), s7_append(sc, cdr(sc->args), sc->code));
		  sc->code = car(sc->args);
		}
	      else eval_error(sc, "list set!: not enough arguments: ~S", sc->code);
	      goto EVAL;
	    }
	}
      else
	{
	  if (s7_is_vector(sc->value))
	    {
	      /* (let ((L #(#(1 2 3) #(4 5 6)))) (set! ((L 1) 0) 32) L)
	       * bad case when args is nil: (let ((L #(#(1 2 3) #(4 5 6)))) (set! ((L 1)) 32) L)
	       */
	      if (sc->args != sc->NIL)
		{
		  push_op_stack(sc, sc->Vector_Set);
		  push_stack(sc, OP_EVAL_ARGS1, list_1(sc, sc->value), s7_append(sc, cdr(sc->args), sc->code));
		  sc->code = car(sc->args);
		}
	      else eval_error(sc, "vector set!: not enough arguments: ~S", sc->code);
	      goto EVAL;
	    }
	  sc->code = cons_unchecked(sc, cons(sc, sc->value, sc->args), sc->code);
	}
      /* fall through */
      
      
    case OP_SET:                                                             /* entry for set! */
      check_set(sc);
      
    case OP_SET_UNCHECKED:
      if (is_pair(car(sc->code)))                                            /* has accessor */
	{
	  s7_pointer caar_code, cx;
	  caar_code = caar(sc->code);
	  if (is_pair(caar_code))
	    {
	      push_stack(sc, OP_SET2, cdar(sc->code), cdr(sc->code));
	      sc->code = caar_code;
	      goto EVAL; 
	    }
	  
	  if (is_symbol(caar_code))
	    {
	      /* this was cx = s7_symbol_value(sc, caar_code) but the function call overhead is noticeable */
	      cx = find_symbol(sc, caar_code);
	      if (is_slot(cx))
		cx = slot_value(cx);
	      else eval_error(sc, "no generalized set for ~A", caar_code);
	    }
	  else cx = caar_code;
	  
	  /* code here is the accessor and the value without the "set!": ((window-width) 800) */
	  /*    (set! (hi 0) (* 2 3)) -> ((hi 0) (* 2 3)) */
	  
	  /* for these kinds of objects, some Schemes restrict set!
	   * (list-set! '(1 2 3) 1 32) is accepted but does it make sense?
	   * (set-car! '(1 . 2) 32)
	   * (string-set! "hiho" 1 #\z)
	   * (vector-set! #(1 2 3) 1 32)
	   * (let ((x (lambda () "hiho"))) (string-set! (x) 1 #\a))
	   * (let ((x (lambda () #(1 2 3)))) (vector-set! (x) 1 32))
	   * (let ((str "hiho")) (string-set! str 1 #\x) str)
	   * (let ((v #(1 2 3))) (vector-set! v 1 32) v)
	   * (let ((x (lambda () "hiho"))) (string-set! (x) 1 #\x) (x))
	   *
	   *   It seems weird that we can reach into both the function body, and its closure:
	   *   (let ((xx (let ((x '(1 2 3))) (lambda () x)))) (list-set! (xx) 1 32) (xx)) -> '(1 32 3)
	   *
	   * (let* ((x '(1 2)) (y (list x)) (z (car y))) (list-set! z 1 32) (list x y z))
	   * ((1 32) ((1 32)) (1 32))
	   *
	   * (string-set! (symbol->string 'symbol->string) 1 #\X) -> error currently also in Guile "string is read-only"
	   * (setf (elt (symbol-name 'xyz) 1) #\X) -> error in CL "read-only string"
	   */
	  /* for gmp case, indices need to be decoded via s7_integer, not just integer */

	  switch (type(cx))
	    {
	    case T_C_OBJECT:
	      {
		s7_pointer settee, index, val;
		
		if (is_null(cdr(sc->code)))
		  s7_wrong_number_of_args_error(sc, "no value for object-set!: ~S", sc->code);
		if (!is_null(cddr(sc->code)))
		  s7_wrong_number_of_args_error(sc, "too many values for object-set!: ~S", sc->code);
		
		settee = car(sc->code);
		if ((is_null(cdr(settee))) ||
		    (!is_null(cddr(settee))))
		  {
		    /* no-index or multi-index case -- use slow version */
		    push_op_stack(sc, sc->Object_Set);
		    if (is_null(cdr(settee)))
		      {
			push_stack(sc, OP_EVAL_ARGS1, list_1(sc, cx), cddr(sc->code));
			sc->code = cadr(sc->code);
		      }
		    else
		      {
			push_stack(sc, OP_EVAL_ARGS1, list_1(sc, cx), s7_append(sc, cddr(settee), cdr(sc->code)));
			sc->code = cadr(settee);
		      }
		    goto EVAL;
		  }

		index = cadr(settee);
		if (!is_pair(index))
		  {
		    if (is_symbol(index))
		      index = find_symbol_checked(sc, index);

		    val = cadr(sc->code);
		    if (!is_pair(val))
		      {
			if (is_symbol(val))
			  val = find_symbol_checked(sc, val);
			car(sc->T2_1) = index;
			car(sc->T2_2) = val;
			sc->value = (*(c_object_set(cx)))(sc, cx, sc->T2_1);
			goto START;
		      }
		    push_op_stack(sc, sc->Object_Set);
		    sc->args = list_2(sc, index, cx);
		    sc->code = cdr(sc->code);
		    goto EVAL_ARGS;
		  }
		else
		  {
		    push_stack(sc, OP_EVAL_ARGS1, list_1(sc, cx), cdr(sc->code));
		    push_op_stack(sc, sc->Object_Set);
		    sc->code = cadr(settee);
		  }
		goto EVAL;
	      }
	      break;
	      
	    case T_INT_VECTOR:
	    case T_FLOAT_VECTOR:
	    case T_VECTOR:
	      {
		/* cx is the vector, sc->code is expr without the set! */
		/*  args have not been evaluated! */

		s7_pointer settee, index, val;
		
		if (is_null(cdr(sc->code)))
		  s7_wrong_number_of_args_error(sc, "no value for vector-set!: ~S", sc->code);
		if (!is_null(cddr(sc->code)))
		  s7_wrong_number_of_args_error(sc, "too many values for vector-set!: ~S", sc->code);
		
		settee = car(sc->code);
		if (is_null(cdr(settee)))
		  s7_wrong_number_of_args_error(sc, "no index for vector-set!: ~S", sc->code);
		if ((!is_null(cddr(settee))) ||
		    (vector_rank(cx) > 1))
		  {
		    /* multi-index case -- use slow version */
		    push_op_stack(sc, sc->Vector_Set);
		    push_stack(sc, OP_EVAL_ARGS1, list_1(sc, cx), s7_append(sc, cddr(settee), cdr(sc->code)));
		    sc->code = cadr(settee);
		    goto EVAL;
		  }

		index = cadr(settee);
		if (!is_pair(index))
		  {
		    s7_Int ind;
		    
		    if (is_symbol(index))
		      index = find_symbol_checked(sc, index);
		    if (!s7_is_integer(index))
		      eval_type_error(sc, "vector-set!: index must be an integer: ~S", sc->code);
		    ind = s7_integer(index);
		    if ((ind < 0) ||
			(ind >= vector_length(cx)))
		      out_of_range(sc, sc->VECTOR_SET, small_int(2), index, (ind < 0) ? ITS_NEGATIVE : ITS_TOO_LARGE);
		    val = cadr(sc->code);
		    if (!is_pair(val))
		      {
			if (is_symbol(val))
			  val = find_symbol_checked(sc, val);
			vector_setter(cx)(sc, cx, ind, val);
			sc->value = val;
			goto START;
		      }
		    push_op_stack(sc, sc->Vector_Set);
		    sc->args = list_2(sc, index, cx);
		    sc->code = cdr(sc->code);
		    goto EVAL_ARGS;
		  }
		else
		  {
		    /* here the index calc might be trivial -- (+ i 1) or (- j 1) but this branch hardly ever happens
		     */
		    push_stack(sc, OP_EVAL_ARGS1, list_1(sc, cx), cdr(sc->code));
		    push_op_stack(sc, sc->Vector_Set);
		    sc->code = cadr(settee);
		  }
	      }
	      break;
	      
	    case T_STRING:
	      {
		/* sc->code = cons(sc, sc->String_Set, s7_append(sc, car(sc->code), cdr(sc->code))); 
		 *
		 * here only one index makes sense, and it is required, so 
		 *   (set! ("str") #\a), (set! ("str" . 1) #\a) and (set! ("str" 1 2) #\a)
		 *   are all errors (but see below!).
		 */
		s7_pointer settee, index, val;
		
		if (is_null(cdr(sc->code)))
		  s7_wrong_number_of_args_error(sc, "no value for string-set!: ~S", sc->code);
		if (!is_null(cddr(sc->code)))
		  s7_wrong_number_of_args_error(sc, "too many values for string-set!: ~S", sc->code);
		
		settee = car(sc->code);
		if (is_null(cdr(settee))) /* there's an index: (set! (str i) #\a), code is ((str 0) #\1) */
		  s7_wrong_number_of_args_error(sc, "no index for string-set!: ~S", sc->code);
		if (!is_null(cddr(settee)))
		  s7_wrong_number_of_args_error(sc, "too many indices for string-set!: ~S", sc->code);
		
		/* if there's one index (the standard case), and it is not a pair, and there's one value (also standard)
		 *    and it is not a pair, let's optimize this thing!
		 *    cx is what we're setting, cadar is the index, cadr is the new value
		 */
		index = cadr(settee);
		if (!is_pair(index))
		  {
		    s7_Int ind;
		    
		    if (is_symbol(index))
		      index = find_symbol_checked(sc, index);
		    if (!s7_is_integer(index))
		      eval_type_error(sc, "string-set!: index must be an integer: ~S", sc->code);
		    ind = s7_integer(index);
		    if ((ind < 0) ||
			(ind >= string_length(cx)))
		      out_of_range(sc, sc->STRING_SET, small_int(2), index, (ind < 0) ? ITS_NEGATIVE : ITS_TOO_LARGE);
		    
		    val = cadr(sc->code);
		    if (!is_pair(val))
		      {
			if (is_symbol(val))
			  val = find_symbol_checked(sc, val);
			if (s7_is_character(val))
			  {
			    string_value(cx)[ind] = character(val);
			    sc->value = val;
			    goto START;
			  }
			else
			  {
			    if ((is_bytevector(cx)) &&
				(s7_is_integer(val)))
			      {
				int ic;
				ic = s7_integer(val);
				if ((ic < 0) || (ic > 255))
				  eval_type_error(sc, "string-set!: value must be a character: ~S", sc->code);
				string_value(cx)[ind] = (char)ic;
				sc->value = val;
				goto START;
			      }
			  }
			eval_type_error(sc, "string-set!: value must be a character: ~S", sc->code);
		      }
		    push_op_stack(sc, sc->String_Set);
		    sc->args = list_2(sc, index, cx);
		    sc->code = cdr(sc->code);
		    goto EVAL_ARGS;
		  }
		else
		  {
		    push_stack(sc, OP_EVAL_ARGS1, list_1(sc, cx), cdr(sc->code));
		    push_op_stack(sc, sc->String_Set);
		    sc->code = cadar(sc->code);
		  }
	      }
	      break;
	      
	    case T_PAIR:
	      /* code: ((lst 1) 32) from (let ((lst '(1 2 3))) (set! (lst 1) 32))
	       * old form:
	       *    sc->code = cons(sc, sc->List_Set, s7_append(sc, car(sc->code), cdr(sc->code))); 
	       *    old: 2.32, new: 1.77 (1.50 direct), (12.2): 1.07
	       *    (time (let ((lst '(1 2 3))) (do ((i 0 (+ i 1))) ((= i 10000000)) (set! (lst 1) 32))))
	       */
	      {
		s7_pointer settee, index, val;
		
		if (is_null(cdr(sc->code)))
		  s7_wrong_number_of_args_error(sc, "no value for list-set!: ~S", sc->code);
		if (!is_null(cddr(sc->code)))
		  s7_wrong_number_of_args_error(sc, "too many values for list-set!: ~S", sc->code);
		
		settee = car(sc->code);
		if (is_null(cdr(settee)))
		  s7_wrong_number_of_args_error(sc, "no index for list-set!: ~S", sc->code);

		index = cadr(settee);
		val = cadr(sc->code);

		if ((!is_null(cddr(settee))) ||
		    (is_pair(index)) ||
		    (is_pair(val)))
		  {
		    push_op_stack(sc, sc->List_Set);
		    push_stack(sc, OP_EVAL_ARGS1, list_1(sc, cx), s7_append(sc, cddr(settee), cdr(sc->code)));
		    sc->code = index;
		    goto EVAL;
		  }

		if (is_symbol(index))
		  index = find_symbol_checked(sc, index);
		if (is_symbol(val))
		  val = find_symbol_checked(sc, val);

		car(sc->T2_1) = index;
		car(sc->T2_2) = val;
		sc->value = g_list_set_1(sc, cx, sc->T2_1, 2);
		goto START;
	      }
	      break;

	      
	    case T_HASH_TABLE:
	      {
		s7_pointer settee, key, val;
		
		if (is_null(cdr(sc->code)))
		  s7_wrong_number_of_args_error(sc, "no value for hash-table-set!: ~S", sc->code);
		if (!is_null(cddr(sc->code)))
		  s7_wrong_number_of_args_error(sc, "too many values for hash-table-set!: ~S", sc->code);
		
		settee = car(sc->code);
		if (is_null(cdr(settee)))
		  s7_wrong_number_of_args_error(sc, "no key for hash-table-set!: ~S", sc->code);
		if (!is_null(cddr(settee)))
		  s7_wrong_number_of_args_error(sc, "too many keys for hash-table-set!: ~S", sc->code);
		
		key = cadr(settee);
		if (!is_pair(key))
		  {
		    if (is_symbol(key))
		      key = find_symbol_checked(sc, key);
		    val = cadr(sc->code);
		    if (!is_pair(val))
		      {
			if (is_symbol(val))
			  val = find_symbol_checked(sc, val);
			sc->value = s7_hash_table_set(sc, cx, key, val);
			goto START;
		      }
		    push_op_stack(sc, sc->Hash_Table_Set);
		    sc->args = list_2(sc, key, cx);
		    sc->code = cdr(sc->code);
		    goto EVAL_ARGS;
		  }
		else
		  {
		    push_stack(sc, OP_EVAL_ARGS1, list_1(sc, cx), cdr(sc->code));
		    push_op_stack(sc, sc->Hash_Table_Set);
		    sc->code = cadar(sc->code);
		  }
	      }
	      break;
	      
	      
	    case T_ENVIRONMENT:
	      /* sc->code = cons(sc, sc->Let_Set, s7_append(sc, car(sc->code), cdr(sc->code))); */
	      {
		s7_pointer settee, key, val;
		/* code: ((gen 'input) input) from (set! (gen 'input) input)
		 */

		if (is_null(cdr(sc->code)))
		  s7_wrong_number_of_args_error(sc, "no value for let-set!: ~S", sc->code);
		if (!is_null(cddr(sc->code)))
		  s7_wrong_number_of_args_error(sc, "too many values for let-set!: ~S", sc->code);
		
		settee = car(sc->code);
		if (is_null(cdr(settee)))
		  s7_wrong_number_of_args_error(sc, "no identifier for let-set!: ~S", sc->code);
		if (!is_null(cddr(settee)))
		  s7_wrong_number_of_args_error(sc, "too many identifiers for let-set!: ~S", sc->code);
		
		key = cadr(settee);
		if ((is_pair(key)) &&
		    (car(key) == sc->QUOTE))
		  {
		    key = cadr(key);
		    val = cadr(sc->code);
		    if (!is_pair(val))
		      {
			if (is_symbol(val))
			  val = find_symbol_checked(sc, val);
			sc->value = s7_let_set(sc, cx, key, val);
			goto START;
		      }
		    push_op_stack(sc, sc->Let_Set);
		    sc->args = list_2(sc, key, cx);
		    sc->code = cdr(sc->code);
		    goto EVAL_ARGS;
		  }
		else
		  {
		    push_stack(sc, OP_EVAL_ARGS1, list_1(sc, cx), cdr(sc->code));
		    push_op_stack(sc, sc->Let_Set);
		    sc->code = cadar(sc->code);
		  }
	      }
	      break;
	      
	      
	    case T_C_MACRO:
	    case T_C_OPT_ARGS_FUNCTION:
	    case T_C_RST_ARGS_FUNCTION:
	    case T_C_ANY_ARGS_FUNCTION:                       /* (let ((lst (list 1 2))) (set! (list-ref lst 0) 2) lst) */
	    case T_C_FUNCTION:
	    case T_C_FUNCTION_STAR:
	      /* perhaps it has a setter */
	      
	      if (is_procedure(c_function_setter(cx)))
		{
		  /* sc->code = cons(sc, c_function_setter(cx), s7_append(sc, cdar(sc->code), cdr(sc->code))); */
		  if (is_pair(cdar(sc->code)))
		    {
		      if ((is_symbol(cadr(sc->code))) &&
			  (is_symbol(cadar(sc->code))))
			{
			  if (is_null(cddar(sc->code)))
			    {
			      car(sc->T2_1) = find_symbol_checked(sc, cadar(sc->code));
			      car(sc->T2_2) = find_symbol_checked(sc, cadr(sc->code));
			      sc->args = sc->T2_1;
			      sc->code = c_function_setter(cx);
			      goto APPLY; /* check arg num etc */
			    }
			  if ((is_symbol(caddar(sc->code))) &&
			      (is_null(cdddar(sc->code))))
			    {
			      car(sc->T3_1) = find_symbol_checked(sc, cadar(sc->code));
			      car(sc->T3_2) = find_symbol_checked(sc, caddar(sc->code));
			      car(sc->T3_3) = find_symbol_checked(sc, cadr(sc->code));
			      sc->args = sc->T3_1;
			      sc->code = c_function_setter(cx);
			      goto APPLY; /* check arg num etc */
			    }
			}

		      push_op_stack(sc, c_function_setter(cx));
		      push_stack(sc, OP_EVAL_ARGS1, sc->NIL, s7_append(sc, cddar(sc->code), cdr(sc->code)));
		      sc->code = cadar(sc->code);
		    }
		  else
		    {
		      if ((is_null(cddr(sc->code))) &&
			  (!is_pair(cadr(sc->code))))
			{
			  if (is_symbol(cadr(sc->code)))
			    car(sc->T1_1) = find_symbol_checked(sc, cadr(sc->code));
			  else car(sc->T1_1) = cadr(sc->code);
			  sc->args = sc->T1_1;
			  sc->code = c_function_setter(cx);
			  goto APPLY; /* check arg num etc */
			}
		      push_op_stack(sc, c_function_setter(cx));
		      push_stack(sc, OP_EVAL_ARGS1, sc->NIL, cddr(sc->code));
		      sc->code = cadr(sc->code);
		    }
		}
	      else
		{
		  if (is_any_macro(c_function_setter(cx)))
		    {
		      push_stack(sc, OP_EVAL_MACRO, sc->NIL, sc->args);
		      if (is_null(cdar(sc->code)))
			sc->args = list_1(sc, cons(sc, c_function_setter(cx), cdr(sc->code)));
		      else sc->args = list_1(sc, cons(sc, c_function_setter(cx), s7_append(sc, cdar(sc->code), cdr(sc->code))));
		      sc->code = c_function_setter(cx);
		      goto APPLY;
		    }
		  else eval_error(sc, "no generalized set for ~A", caar_code);
		}
	      break;
	      
	    case T_MACRO:
	    case T_BACRO:
	    case T_CLOSURE:
	    case T_CLOSURE_STAR:
	      if (is_procedure(closure_setter(cx))) /* appears to be caar_code */
		{
		  /* (set! (o g) ...), here cx = o, sc->code = ((o g) ...) */
		  
		  push_op_stack(sc, closure_setter(cx));
		  if (is_null(cdar(sc->code)))
		    {
		      push_stack(sc, OP_EVAL_ARGS1, sc->NIL, cddr(sc->code));
		      sc->code = cadr(sc->code);
		    }
		  else 
		    {
		      if (is_null(cddar(sc->code)))
			push_stack(sc, OP_EVAL_ARGS1, sc->NIL, cdr(sc->code));
		      else push_stack(sc, OP_EVAL_ARGS1, sc->NIL, s7_append(sc, cddar(sc->code), cdr(sc->code)));
		      sc->code = cadar(sc->code);
		    }
		}
	      else
		{
		  if (is_any_macro(closure_setter(cx)))
		    {
		      push_stack(sc, OP_EVAL_MACRO, sc->NIL, sc->args);
		      if (is_null(cdar(sc->code)))
			sc->args = list_1(sc, cons(sc, closure_setter(cx), cdr(sc->code)));
		      else sc->args = list_1(sc, cons(sc, closure_setter(cx), s7_append(sc, cdar(sc->code), cdr(sc->code))));
		      sc->code = closure_setter(cx);
		      goto APPLY;
		    }
		  else eval_error(sc, "no generalized set for ~A", caar_code);
		}
	      break;
	      
	    default:                                         /* (set! (1 2) 3) */
	      eval_error(sc, "no generalized set for ~A", caar_code);
	    }
	  goto EVAL;
	}
      

    case OP_SET_NORMAL:
      {
	s7_pointer x;
	x = cadr(sc->code);
	if (is_pair(x))
	  {
	    push_stack_no_args(sc, OP_SET1, car(sc->code));
	    sc->code = x;
	    goto EVAL; 
	  }
	
	if (is_symbol(x))
	  sc->value = find_symbol_checked(sc, x);
	else sc->value = x;
	sc->code = car(sc->code);
      }
      

    case OP_SET1:  
      {
	s7_pointer lx;
	/* if unbound variable hook here, we need the binding, not the current value */
	lx = find_symbol(sc, sc->code);
	if (is_slot(lx)) 
	  {
	    if (slot_has_accessor(lx))
	      {
		s7_pointer func;
		func = slot_accessor(lx);
		if (is_procedure(func))
		  {
		    if (is_c_function(func))
		      {
			car(sc->T2_1) = sc->code;
			car(sc->T2_2) = sc->value;
			sc->value = c_function_call(func)(sc, sc->T2_1);
			if (sc->value == sc->ERROR) /* backwards compatibility... */
			  return(s7_error(sc, sc->ERROR,
					  list_3(sc, make_string_wrapper(sc, "can't set ~S to ~S"), car(sc->T2_1), car(sc->T2_2))));
		      }
		    else
		      {
			sc->args = list_2(sc, sc->code, sc->value);
			push_stack(sc, OP_SET_WITH_ACCESSOR, sc->args, lx); /* op, args, code */
			sc->code = func;
			goto APPLY;
		      }
		  }
		else
		  {
		    if (is_any_macro(func))
		      {
			push_stack(sc, OP_SET_WITH_ACCESSOR, sc->args, lx); /* args only for error (see below) */
			sc->args = list_1(sc, cons(sc, func, list_2(sc, sc->code, sc->value)));
			sc->code = func;
			push_stack(sc, OP_EVAL_MACRO, sc->NIL, sc->args);
			goto APPLY;
		      }
		  }
	      }
	    else
	      {
		if (is_syntax(slot_value(lx)))
		  eval_error(sc, "can't set! ~A", sc->code);
	      }
	    slot_set_value(lx, sc->value); 
	    goto START;
	  }
	eval_type_error(sc, "set! ~A: unbound variable", sc->code); 
      }
      
    case OP_SET_WITH_ACCESSOR:
      if (sc->value == sc->ERROR) /* backwards compatibility... */
	return(s7_error(sc, sc->ERROR,
			list_3(sc, make_string_wrapper(sc, "can't set ~S to ~S"), car(sc->args), cadr(sc->args))));
      slot_set_value(sc->code, sc->value); 
      goto START;
      
      
      
      /* -------------------------------- IF -------------------------------- */
    case OP_IF:
      check_if(sc);

    case OP_IF_UNCHECKED:
      push_stack_no_args(sc, OP_IF1, cdr(sc->code));
      sc->code = car(sc->code);
      goto EVAL;
      

    case OP_IF1:
      if (is_true(sc, sc->value))
	sc->code = car(sc->code);
      else sc->code = cadr(sc->code);   /* even pre-optimization, (if #f #f) ==> #<unspecified> because car(sc->NIL) = sc->UNSPECIFIED */
      if (is_pair(sc->code))
	goto EVAL; 
      if (is_symbol(sc->code))
	sc->value = find_symbol_checked(sc, sc->code);
      else sc->value = sc->code;
      goto START;
      
      
    case OP_IF_P_P_P:
      push_stack_no_args(sc, OP_IF_PPP, cdr(sc->code));
      sc->code = car(sc->code);
      goto EVAL; 
      

    case OP_IF_Z_P_P:
      push_stack_no_args(sc, OP_IF_PPP, cdr(sc->code));
      sc->code = car(sc->code);
      goto OPT_EVAL;
      

    case OP_IF_A_P_P:
      if (is_true(sc, ((s7_function)fcdr(sc->code))(sc, car(sc->code))))
	sc->code = cadr(sc->code);
      else sc->code = caddr(sc->code);
      goto EVAL;
      

    case OP_IF_ANDP_P_P:
      push_stack_no_args(sc, OP_IF_PPP, cdr(sc->code));
      sc->code = cdar(sc->code);
      goto AND_P;
      
      
    case OP_IF_ORP_P_P:
      push_stack_no_args(sc, OP_IF_PPP, cdr(sc->code));
      sc->code = cdar(sc->code);
      goto OR_P;
      
      
    case OP_IF_PPP:
      if (is_true(sc, sc->value))
	sc->code = car(sc->code);
      else sc->code = cadr(sc->code);
      goto EVAL;
      
      
    case OP_IF_P_P:
      push_stack_no_args(sc, OP_IF_PP, cadr(sc->code));
      sc->code = car(sc->code);
      goto EVAL; 
      

    case OP_IF_Z_P:
      push_stack_no_args(sc, OP_IF_PP, fcdr(sc->code));
      sc->code = car(sc->code);
      goto OPT_EVAL;
      
      
    case OP_IF_A_P:
      if (is_true(sc, ((s7_function)fcdr(sc->code))(sc, car(sc->code))))
	{
	  sc->code = cadr(sc->code);
	  goto EVAL;
	}
      sc->value = sc->UNSPECIFIED;
      goto START;
      
      
    case OP_IF_B_P:
      {
	s7_pointer code;
	code = sc->code;
	push_stack_no_args(sc, OP_IF_PP, cadr(code));
	code = car(code);
	sc->op = (opcode_t)pair_syntax_op(code);
	sc->code = cdr(code);
	goto START_WITHOUT_POP_STACK;
      }
      
      
    case OP_IF_ANDP_P:
      push_stack_no_args(sc, OP_IF_PP, cadr(sc->code));
      sc->code = cdar(sc->code);
      goto AND_P;
      
      
    case OP_IF_ORP_P:
      push_stack_no_args(sc, OP_IF_PP, cadr(sc->code));
      sc->code = cdar(sc->code);
      goto OR_P;
      
      
    case OP_IF_PP:
      if (is_true(sc, sc->value))
	goto EVAL;  /* almost never happens */
      sc->value = sc->UNSPECIFIED;  
      IF_BEGIN_POP_STACK(sc); 
      

    case OP_IF_A_P_X:
      if (is_true(sc, ((s7_function)fcdr(sc->code))(sc, car(sc->code))))
	{
	  sc->code = cadr(sc->code);
	  goto EVAL;
	}
      sc->value = caddr(sc->code);
      if (is_symbol(sc->value))
	sc->value = find_symbol_checked(sc, sc->value);
      goto START;


    case OP_IF_P_P_X:
      push_stack_no_args(sc, OP_IF_PPX, cdr(sc->code));
      sc->code = car(sc->code);
      if (is_optimized(sc->code))
	goto OPT_EVAL;
      goto EVAL; 


    case OP_IF_PPX:
      if (is_true(sc, sc->value))
	{
	  sc->code = car(sc->code);
	  goto EVAL; 
	}
      sc->value = cadr(sc->code);
      if (is_symbol(sc->value))
	sc->value = find_symbol_checked(sc, sc->value);
      goto START;
      
      
    case OP_IF_P_X_P:
      push_stack_no_args(sc, OP_IF_PXP, cdr(sc->code));
      sc->code = car(sc->code);
      goto EVAL; 

    case OP_IF_PXP:
      if (is_true(sc, sc->value))
	{
	  sc->value = car(sc->code);
	  if (is_symbol(sc->value))
	    sc->value = find_symbol_checked(sc, sc->value);
	  goto START;
	}
      sc->code = cadr(sc->code);
      goto EVAL; 
      
      
    case OP_IF_S_P_P:
      if (is_true(sc, find_symbol_checked(sc, car(sc->code))))
	sc->code = cadr(sc->code);
      else sc->code = caddr(sc->code);
      goto EVAL; 
      
      
    case OP_IF_S_P:
      if (is_true(sc, find_symbol_checked(sc, car(sc->code))))
	{
	  sc->code = cadr(sc->code);
	  goto EVAL; 
	}
      sc->value = sc->UNSPECIFIED;
      goto START;
      
      
    case OP_IF_S_P_X:
      if (is_true(sc, find_symbol_checked(sc, car(sc->code))))
	{
	  sc->code = cadr(sc->code);
	  goto EVAL; 
	}
      if (is_symbol(caddr(sc->code)))
	sc->value = find_symbol_checked(sc, caddr(sc->code));
      else sc->value = caddr(sc->code);
      goto START;
      
      
    case OP_IF_S_X_P:
      if (is_true(sc, find_symbol_checked(sc, car(sc->code))))
	{
	  sc->value = cadr(sc->code);
	  if (is_symbol(sc->value))
	    sc->value = find_symbol_checked(sc, sc->value);
	  goto START;
	}
      sc->code = caddr(sc->code);
      goto EVAL; 


    case OP_IF_P_FEED:
      /* actually cond right now: (cond (expr => p)) where p is (lambda (s) ...) -- see check_cond -- this could be extended a lot
       */
      push_stack_no_args(sc, OP_IF_P_FEED_1, sc->code);
      sc->code = caar(sc->code);
      goto EVAL;

    case OP_IF_P_FEED_1:
      if (is_true(sc, sc->value))
	{
	  if (is_multiple_value(sc->value))
	    sc->code = cons(sc, fcdr(sc->code), multiple_value(sc->value));
	  else 
	    {
	      NEW_FRAME_WITH_SLOT(sc, sc->envir, sc->envir, car(cadr(fcdr(sc->code))), sc->value);
	      sc->code = caddr(fcdr(sc->code));
	    }
	  goto EVAL; 
	}
      sc->value = sc->NIL; /* since it's actually cond -- perhaps push as sc->args above */
      goto START;
      
      
    case OP_SAFE_IF_Z_Z:
      push_stack_no_args(sc, OP_SAFE_IF_Z_Z_1, fcdr(sc->code));
      sc->code = car(sc->code); 
      goto OPT_EVAL;

    case OP_SAFE_IF_Z_Z_1:
      if (is_true(sc, sc->value))
	goto OPT_EVAL; 
      sc->value = sc->UNSPECIFIED;
      goto START;
      
      
    case OP_SAFE_IF_CC_X_P:
      {
	s7_pointer code;
	code = sc->code;
	if (is_true(sc, c_call(car(code))(sc, cdar(code))))
	  {
	    sc->value = cadr(code);
	    if (is_symbol(sc->value))
	      sc->value = find_symbol_checked(sc, sc->value);
	    goto START;
	  }
	sc->code = caddr(code);
	goto EVAL; 
      }
      
      
    case OP_SAFE_IF_CC_P:
      {
	s7_pointer code;
	code = sc->code;
	if (is_true(sc, c_call(car(code))(sc, cdar(code))))
	  {
	    sc->code = cadr(code);
	    goto EVAL; 
	  }
	else sc->value = sc->UNSPECIFIED;
	IF_BEGIN_POP_STACK(sc); 
      }
      
      
    case OP_SAFE_IF_CC_P_P:
      {
	s7_pointer code;
	code = sc->code;
	if (is_true(sc, c_call(car(code))(sc, cdar(code))))
	  sc->code = cadr(code);
	else sc->code = caddr(code);
	goto EVAL; 
      }
      
      
    case OP_SAFE_IF_CS_X_P:
      {
	s7_pointer code;
	code = sc->code;
	car(sc->T1_1) = find_symbol_checked(sc, fcdr(code));
	if (is_true(sc, c_call(car(code))(sc, sc->T1_1)))
	  {
	    sc->value = cadr(code);
	    if (is_symbol(sc->value))
	      sc->value = find_symbol_checked(sc, sc->value);
	    goto START;
	  }
	sc->code = caddr(code);
	goto EVAL;
      }
      

    case OP_SAFE_IF_CSS_X_P:
      {
	s7_pointer code;
	code = sc->code;
	car(sc->T2_1) = find_symbol_checked(sc, cadar(code));
	car(sc->T2_2) = find_symbol_checked(sc, fcdr(code));
	if (is_true(sc, c_call(car(code))(sc, sc->T2_1)))
	  {
	    sc->value = cadr(code);
	    if (is_symbol(sc->value))
	      sc->value = find_symbol_checked(sc, sc->value);
	    goto START;
	  }
	sc->code = caddr(code);
	goto EVAL; 
      }
      

    case OP_SAFE_IF_CSC_X_P:
      {
	s7_pointer code;
	code = sc->code;
	car(sc->T2_1) = find_symbol_checked(sc, cadar(code));
	car(sc->T2_2) = fcdr(code);
	if (is_true(sc, c_call(car(code))(sc, sc->T2_1)))
	  {
	    sc->value = cadr(code);
	    if (is_symbol(sc->value))
	      sc->value = find_symbol_checked(sc, sc->value);
	    goto START;
	  }
	sc->code = caddr(code);
	goto EVAL; 
      }
      

    case OP_SAFE_IF_CSC_X_O_A:
      {
	/* (if (< n 2) n (f ...)) */
	s7_pointer code;
	code = sc->code;
	sc->value = find_symbol_checked(sc, cadar(code)); /* tricky -- the same symbol is used twice, and if expr true, it is the returned value */
	car(sc->T2_1) = sc->value;
	car(sc->T2_2) = fcdr(code);
	if (is_true(sc, c_call(car(code))(sc, sc->T2_1)))
	  goto START;
	sc->code = caddr(code);
	goto OPT_EVAL; 
      }
      
      
    case OP_SAFE_IF_CS_P_X:
      {
	s7_pointer code;
	code = sc->code;
	car(sc->T1_1) = find_symbol_checked(sc, fcdr(code));
	if (is_true(sc, c_call(car(code))(sc, sc->T1_1)))
	  {
	    sc->code = cadr(code);
	    goto EVAL; 
	  }
	sc->value = caddr(code);
	if (is_symbol(sc->value))
	  sc->value = find_symbol_checked(sc, sc->value);
	goto START;
      }
      
      
    case OP_SAFE_IF_IS_PAIR_P_X:
      if (is_pair(find_symbol_checked(sc, fcdr(sc->code))))
	{
	  sc->code = cadr(sc->code);
	  goto EVAL; 
	}
      sc->value = caddr(sc->code);
      if (is_symbol(sc->value))
	sc->value = find_symbol_checked(sc, sc->value);
      goto START;
      
      
    case OP_SAFE_IF_CS_P_P:
      car(sc->T1_1) = find_symbol_checked(sc, fcdr(sc->code));
      if (is_true(sc, c_call(car(sc->code))(sc, sc->T1_1)))
	sc->code = cadr(sc->code);
      else sc->code = caddr(sc->code);
      goto EVAL; 
      
      
    case OP_SAFE_IF_IS_PAIR_P_P:
      if (is_pair(find_symbol_checked(sc, fcdr(sc->code))))
	sc->code = cadr(sc->code);
      else sc->code = caddr(sc->code);
      goto EVAL; 
      

    case OP_SAFE_IF_IS_SYMBOL_P_P:
      if (is_symbol(find_symbol_checked(sc, fcdr(sc->code))))
	sc->code = cadr(sc->code);
      else sc->code = caddr(sc->code);
      goto EVAL; 
      

    case OP_SAFE_IF_IS_NULL_Q_P:
      {
	s7_pointer code;
	code = sc->code;
	if (is_null(find_symbol_checked(sc, fcdr(code))))
	  {
	    sc->value = cadr(cadr(code));
	    goto START;
	  }
	code = caddr(code);
	sc->op = (opcode_t)pair_syntax_op(code);
	sc->code = cdr(code);
	goto START_WITHOUT_POP_STACK;
      }
      
      
    case OP_SAFE_IF_IS_NULL_S_P:
      {
	s7_pointer code;
	code = sc->code;
	if (is_null(find_symbol_checked(sc, fcdr(code))))
	  {
	    sc->value = find_symbol_checked(sc, cadr(code));
	    goto START;
	  }
	code = caddr(code);
	sc->op = (opcode_t)pair_syntax_op(code);
	sc->code = cdr(code);
	goto START_WITHOUT_POP_STACK;
      }

      
    case OP_SAFE_IF_CS_P:
      car(sc->T1_1) = find_symbol_checked(sc, fcdr(sc->code));
      if (is_true(sc, c_call(car(sc->code))(sc, sc->T1_1)))
	{
	  sc->code = cadr(sc->code);
	  goto EVAL; 
	}
      sc->value = sc->UNSPECIFIED;
      goto START;
      
      
    case OP_SAFE_IF_NOT_S_P:
      if (is_false(sc, find_symbol_checked(sc, fcdr(sc->code))))
	{
	  sc->code = cadr(sc->code);
	  goto EVAL; 
	}
      sc->value = sc->UNSPECIFIED;
      goto START;
      
      
    case OP_SAFE_IF_CSQ_P:
      car(sc->T2_1) = find_symbol_checked(sc, cadr(car(sc->code)));
      car(sc->T2_2) = fcdr(sc->code);
      if (is_true(sc, c_call(car(sc->code))(sc, sc->T2_1)))
	{
	  sc->code = cadr(sc->code);
	  goto EVAL; 
	}
      sc->value = sc->UNSPECIFIED;
      goto START;
      
      
    case OP_SAFE_IF_CSQ_P_P:
      car(sc->T2_1) = find_symbol_checked(sc, cadr(car(sc->code)));
      car(sc->T2_2) = fcdr(sc->code);
      if (is_true(sc, c_call(car(sc->code))(sc, sc->T2_1)))
	sc->code = cadr(sc->code);
      else sc->code = caddr(sc->code);
      goto EVAL; 
      
      
    case OP_SAFE_IF_CSS_P:
      car(sc->T2_1) = find_symbol_checked(sc, cadr(car(sc->code)));
      car(sc->T2_2) = find_symbol_checked(sc, fcdr(sc->code));
      if (is_true(sc, c_call(car(sc->code))(sc, sc->T2_1)))
	{
	  sc->code = cadr(sc->code);
	  if (is_optimized(sc->code))
	    goto OPT_EVAL;
	  goto EVAL; 
	}
      sc->value = sc->UNSPECIFIED;
      IF_BEGIN_POP_STACK(sc); 
      
      
    case OP_SAFE_IF_CSS_P_P:
      car(sc->T2_1) = find_symbol_checked(sc, cadr(car(sc->code)));
      car(sc->T2_2) = find_symbol_checked(sc, fcdr(sc->code));
      if (is_true(sc, c_call(car(sc->code))(sc, sc->T2_1)))
	sc->code = cadr(sc->code);
      else sc->code = caddr(sc->code);
      goto EVAL; 
      
      
    case OP_SAFE_IF_CSC_P:
      car(sc->T2_1) = find_symbol_checked(sc, cadr(car(sc->code)));
      car(sc->T2_2) = fcdr(sc->code);
      if (is_true(sc, c_call(car(sc->code))(sc, sc->T2_1)))
	{
	  sc->code = cadr(sc->code);
	  goto EVAL; 
	}
      sc->value = sc->UNSPECIFIED;
      goto START;
      

    case OP_SAFE_IF_CSC_P_P:
      car(sc->T2_1) = find_symbol_checked(sc, cadr(car(sc->code)));
      car(sc->T2_2) = fcdr(sc->code);
      if (is_true(sc, c_call(car(sc->code))(sc, sc->T2_1)))
	sc->code = cadr(sc->code);
      else sc->code = caddr(sc->code);
      goto EVAL; 
      
      
    case OP_SAFE_IF_IS_PAIR_P:
      if (is_pair(find_symbol_checked(sc, fcdr(sc->code))))
	{
	  sc->code = cadr(sc->code);
	  goto EVAL; 
	}
      sc->value = sc->UNSPECIFIED;
      IF_BEGIN_POP_STACK(sc); 
      

    case OP_SAFE_IF_IS_SYMBOL_P:
      if (is_symbol(find_symbol_checked(sc, fcdr(sc->code))))
	{
	  sc->code = cadr(sc->code);
	  goto EVAL; 
	}
      sc->value = sc->UNSPECIFIED;
      goto START;
      

    case OP_SAFE_IF_C_SS_P:
      {
	s7_pointer args, val1;
	args = fcdr(sc->code);
	val1 = find_symbol_checked(sc, cadr(args));
	car(sc->T2_2) = find_symbol_checked(sc, caddr(args));
	car(sc->T2_1) = val1;
	car(sc->T1_1) = c_call(args)(sc, sc->T2_1);
	if (is_true(sc, c_call(car(sc->code))(sc, sc->T1_1)))
	  {
	    sc->code = cadr(sc->code);
	    goto EVAL; 
	  }
	sc->value = sc->UNSPECIFIED;
	goto START;
      }


    case OP_SAFE_IF_A_Z:
      if (is_true(sc, ((s7_function)fcdr(sc->code))(sc, car(sc->code))))
	{
	  sc->code = cadr(sc->code);
	  goto OPT_EVAL;
	}
      sc->value = sc->UNSPECIFIED;
      goto START;
      
      
    case OP_WHEN:
      check_when(sc);
      
    case OP_WHEN_UNCHECKED:
      push_stack_no_args(sc, OP_WHEN1, cdr(sc->code));
      sc->code = car(sc->code);
      goto EVAL; 

    case OP_WHEN1:
      if (is_true(sc, sc->value)) goto BEGIN1;
      sc->value = sc->UNSPECIFIED;
      goto START;

    case OP_WHEN_S:
      if (is_true(sc, find_symbol_checked(sc, car(sc->code))))
	{
	  sc->code = cdr(sc->code);
	  goto BEGIN1;
	}
      sc->value = sc->UNSPECIFIED;
      goto START;


    case OP_UNLESS:
      check_unless(sc);
      
    case OP_UNLESS_UNCHECKED:
      push_stack_no_args(sc, OP_UNLESS1, cdr(sc->code));
      sc->code = car(sc->code);
      goto EVAL; 

    case OP_UNLESS1:
      if (is_false(sc, sc->value)) goto BEGIN1;
      sc->value = sc->UNSPECIFIED;
      goto START;
      
    case OP_UNLESS_S:
      if (is_false(sc, find_symbol_checked(sc, car(sc->code))))
	{
	  sc->code = cdr(sc->code);
	  goto BEGIN1;
	}
      sc->value = sc->UNSPECIFIED;
      goto START;


    case OP_SAFE_C_P_1:
      car(sc->T1_1) = sc->value;
      sc->value = c_call(sc->code)(sc, sc->T1_1);
      goto START;
      
      
    case OP_SAFE_C_PP_1:
      /* unless multiple values from last call (first arg), sc->args == sc->NIL because we pushed that.
       *   we get here only from OP_SAFE_C_PP.
       *
       * currently splice_in_values changes the operator so if we get here, sc->value is the result of the first arg
       *
       * safe_c_pp -> 1, but if mv, -> 3
       * 1: -> 2, if mv -> 4
       * 2: done (both normal)
       * 3: -> 5, but if mv, -> 6
       * 4: done (1 normal, 2 mv)
       * 5: done (1 mv, 2 normal)
       * 6: done (both mv)
       *
       * I think safe_c_ppp would require 18 branches
       */
      push_stack(sc, OP_SAFE_C_PP_2, sc->value, sc->code); /* mv -> 3 */
      sc->code = caddr(sc->code);
      if (is_optimized(sc->code))
	goto OPT_EVAL;
      goto EVAL; 
      

    case OP_SAFE_C_PP_2:
      /* we get here only if neither arg returned multiple values, so sc->args is the first value, and sc->value the second
       */
      car(sc->T2_1) = sc->args;
      car(sc->T2_2) = sc->value;
      sc->value = c_call(sc->code)(sc, sc->T2_1);
      goto START;


    case OP_SAFE_C_PP_3:
      /* we get here if the first arg returned multiple values
       */
      push_stack(sc, OP_SAFE_C_PP_5, sc->value, sc->code);
      sc->code = caddr(sc->code);
      if (is_optimized(sc->code))
	goto OPT_EVAL;
      goto EVAL; 


    case OP_SAFE_C_PP_4:
      /* we get here if the first arg result was normal, but the second had multiple values */
      sc->args = cons(sc, sc->args, sc->value);
      goto PP_MM;

    case OP_SAFE_C_PP_5:
      /* 1 mv, 2, normal */
      sc->args = s7_append(sc, sc->args, list_1(sc, sc->value));
      goto PP_MM;

    case OP_SAFE_C_PP_6:
      /* both mv */
      sc->args = s7_append(sc, sc->args, sc->value);
      
    PP_MM:
      /* 
       * c_call(sc->code) here refers to the fcdr field which is g_add_2, but we have any number of args from a values call
       *   the original (unoptimized) function is (hopefully) c_function_base(ecdr(sc->code))?
       *
       *   (let () (define (ho a) (+ a 2)) (define (hi) (+ (ho 1) (ho 2))) (hi)) -> 7
       *   (let () (define (ho a) (+ a 2)) (define (hi) (+ (ho 1) (values 3 4))) (hi)) -> 10
       *   (let () (define (ho a) (+ a 2)) (define (hi) (+ (values 3 4) (ho 1))) (hi)) -> 10
       *   (let () (define (hi) (+ (values 1 2) (values 3 4))) (hi)) -> 10
       */
      sc->code = c_function_base(ecdr(sc->code));
      goto APPLY;

      
    case OP_C_P_1:
      sc->value = c_call(sc->code)(sc, list_1(sc, sc->value));
      goto START;

    case OP_C_P_2:
      /* op_c_p_1 -> mv case: (define (hi) (format (values #f "~A ~D" 1 2)))
       */
      sc->code = c_function_base(ecdr(sc->code)); /* see comment above */
      sc->args = copy_list(sc, sc->value);
      goto APPLY;
      

    case OP_SAFE_CLOSURE_P_1:
      sc->envir = old_frame_with_slot(sc, closure_let(ecdr(sc->code)), sc->value);
      sc->code = closure_body(ecdr(sc->code));
      goto BEGIN1;

    case OP_CLOSURE_P_1:
      /* sc->value is presumably the argument value */
      CHECK_STACK_SIZE(sc);
      sc->code = ecdr(sc->code);
      NEW_FRAME_WITH_CHECKED_SLOT(sc, closure_let(sc->code), sc->envir, car(closure_args(sc->code)), sc->value);
      sc->code = closure_body(sc->code);
      goto BEGIN1;

    case OP_CLOSURE_P_2:
      /* here we got multiple values */
      sc->code = ecdr(sc->code);
      sc->args = copy_list(sc, sc->value);
      goto APPLY;


    case OP_C_SP_1:
      sc->value = c_call(sc->code)(sc, list_2(sc, sc->args, sc->value));
      goto START;

    case OP_C_SP_2:
      /* op_c_sp_1 -> mv case: (map + (values '(1 2 3) #(1 2 3)))
       */
      sc->code = c_function_base(ecdr(sc->code)); 
      sc->args = cons(sc, sc->args, copy_list(sc, sc->value));
      goto APPLY;
      
      
      /* -------------------------------- LET -------------------------------- */
      
    case OP_LET_NO_VARS:
      NEW_FRAME(sc, sc->envir, sc->envir); 
      sc->code = cdr(sc->code);            /* ignore the () */
      goto BEGIN1;
      
      
    case OP_NAMED_LET_NO_VARS:
      NEW_FRAME(sc, sc->envir, sc->envir); 
      sc->args = make_closure(sc, sc->NIL, cddr(sc->code), T_CLOSURE); /* sc->args is a temp here */
      add_slot(sc, car(sc->code), sc->args); 
      sc->code = cddr(sc->code);
      goto BEGIN1;
      
      
    case OP_LET_C:
      /* one var, init is constant, incoming sc->code is '(((var val))...)!
       */
      NEW_FRAME_WITH_SLOT(sc, sc->envir, sc->envir, gcdr(sc->code), fcdr(sc->code));
      sc->code = cdr(sc->code);
      goto BEGIN1;
      
      
    case OP_LET_C_D:
      /* one var, init is constant, incoming sc->code is '(((var val))...)!
       *   body is one optimized statement
       * here the frame+slot can be prebuilt and reused, but the local gain is lost later because the
       *   frame+slot has to be marked all the time -- the same trade-off as in the safe-let cases.
       */
      NEW_FRAME_WITH_SLOT(sc, sc->envir, sc->envir, gcdr(sc->code), fcdr(sc->code));
      sc->code = cadr(sc->code);
      goto OPT_EVAL;
      
      
    case OP_LET_S:
      /* one var, init is symbol, incoming sc->code is '(((var sym))...)
       */
      sc->value = find_symbol_checked(sc, fcdr(sc->code));
      NEW_FRAME_WITH_SLOT(sc, sc->envir, sc->envir, gcdr(sc->code), sc->value);
      sc->code = cdr(sc->code);
      goto BEGIN1;
      
      
    case OP_LET_Q:
      /* one var, init is quoted, incoming sc->code is '(((var 'val))...)
       */
      {
	s7_pointer binding;
	binding = caar(sc->code);
	NEW_FRAME_WITH_SLOT(sc, sc->envir, sc->envir, car(binding), cadr(cadr(binding)));
	sc->code = cdr(sc->code);
	goto BEGIN1;
      }
      
    case OP_LET_R:
      {
	s7_pointer binding;
	binding = caar(sc->code);
	car(sc->T1_1) = find_symbol_checked(sc, fcdr(sc->code));
	sc->value = c_call(cadr(binding))(sc, sc->T1_1);
	NEW_FRAME_WITH_SLOT(sc, sc->envir, sc->envir, car(binding), sc->value);
	push_stack_no_args(sc, OP_BEGIN1, cddr(sc->code));
	sc->code = cadr(sc->code);
	goto EVAL;
      }
      

    case OP_LET_R_P:
      {
	s7_pointer binding;
	binding = caar(sc->code);
	car(sc->T1_1) = find_symbol_checked(sc, fcdr(sc->code));
	sc->value = c_call(cadr(binding))(sc, sc->T1_1);
	NEW_FRAME_WITH_SLOT(sc, sc->envir, sc->envir, car(binding), sc->value);
	sc->code = cadr(sc->code);
	goto EVAL; 
      }
      

    case OP_LET_opCq:
      /* one var, init is safe_c_c */
      sc->value = c_call(fcdr(sc->code))(sc, cdr(fcdr(sc->code)));
      NEW_FRAME_WITH_SLOT(sc, sc->envir, sc->envir, gcdr(sc->code), sc->value);
      sc->code = cdr(sc->code);
      goto BEGIN1;
      

    case OP_LET_opSSq:
      /* one var, init is safe_c_ss
       */
      {
	s7_pointer binding, largs, in_val;
	binding = caar(sc->code);
	largs = cadr(binding);
	in_val = find_symbol_checked(sc, cadr(largs));
	car(sc->T2_2) = find_symbol_checked(sc, caddr(largs));
	car(sc->T2_1) = in_val;
	sc->value = c_call(largs)(sc, sc->T2_1);
	NEW_FRAME_WITH_SLOT(sc, sc->envir, sc->envir, car(binding), sc->value);
	sc->code = cdr(sc->code);
	goto BEGIN1;
      }
      

    case OP_LET_CAR_P:
      {
	s7_pointer val, code;
	opcode_t op;
	
	code = sc->code;
	val = find_symbol_checked(sc, fcdr(code));
	if (!is_pair(val)) 
	  {
	    val = g_car(sc, list_1(sc, val));
	    NEW_FRAME_WITH_SLOT(sc, sc->envir, sc->envir, gcdr(code), val);
	  }
	else 
	  {
	    NEW_FRAME_WITH_SLOT(sc, sc->envir, sc->envir, gcdr(code), car(val));
	  }
	
	code = cadr(code);
	op = (opcode_t)pair_syntax_op(code);
	if (op == OP_SAFE_IF_CC_X_P)
	  {
	    code = cdr(code);
	    if (is_true(sc, c_call(car(code))(sc, cdar(code))))
	      {
		sc->value = cadr(code);
		if (is_symbol(sc->value))
		  sc->value = find_symbol_checked(sc, sc->value);
		goto START;
	      }
	    sc->code = caddr(code);
	    goto EVAL;
	  }
	sc->op = op;
	sc->code = cdr(code);
	goto START_WITHOUT_POP_STACK;
      }
      

    case OP_LET_Z:
      push_stack(sc, OP_LET_Z_1, fcdr(cdr(sc->code)), cadr(sc->code));
      sc->code = fcdr(sc->code);
      goto OPT_EVAL;

    case OP_LET_Z_1:
      NEW_FRAME_WITH_SLOT(sc, sc->envir, sc->envir, sc->args, sc->value);
      goto EVAL;


    case OP_LET_ONE:
      /* one var */
      {
	s7_pointer p;
	p = caar(sc->code);
	sc->value = cadr(p);
	if (is_pair(sc->value))
	  {
	    push_stack(sc, OP_LET_ONE_1, car(p), cdr(sc->code)); /* args code */
	    sc->code = sc->value;
	    goto EVAL;	
	  }
	if (is_symbol(sc->value))
	  sc->value = find_symbol_checked(sc, sc->value);
	sc->code = cdr(sc->code);
	sc->args = car(p);
	/* drop through */
      }

    case OP_LET_ONE_1:
      /* fprintf(stderr, "val: %s %s, code: %s\n", DISPLAY(sc->args), DISPLAY(sc->value), DISPLAY(sc->code)); */
      NEW_FRAME_WITH_SLOT(sc, sc->envir, sc->envir, sc->args, sc->value);
      goto BEGIN1;

      
    case OP_LET_ALL_C:
      {
	s7_pointer p;
	NEW_FRAME(sc, sc->envir, sc->envir);
	for (p = car(sc->code); is_pair(p); p = cdr(p))
	  ADD_SLOT(sc->envir, caar(p), cadar(p)); 
	sc->code = cdr(sc->code);
	goto BEGIN1;
      }
      
      
    case OP_LET_ALL_S:
      /* n vars, all inits are symbols.  We need to GC-protect the new frame-list as it is being
       *    created without tying the new frame into sc->envir until the end.
       */
      {
	s7_pointer p, frame;
	frame = make_simple_let(sc);
	sc->args = frame;
	for (p = car(sc->code); is_pair(p); p = cdr(p))
	  ADD_SLOT(frame, caar(p), find_symbol_checked(sc, cadar(p)));
	sc->let_number++;
	sc->envir = frame;
	sc->code = cdr(sc->code);
	goto BEGIN1;
      }
      

    case OP_LET_ALL_R:
      {
	s7_pointer p, frame;
	frame = make_simple_let(sc);   	
	sc->args = frame;
	for (p = car(sc->code); is_pair(p); p = cdr(p))
	  {
	    s7_pointer cp;
	    cp = cadar(p);
	    car(sc->T1_1) = find_symbol_checked(sc, cadr(cp));
	    ADD_SLOT(frame, caar(p), c_call(cp)(sc, sc->T1_1)); 
	  }
	sc->let_number++;
	sc->envir = frame;
	sc->code = cdr(sc->code);
	goto BEGIN1;
      }
      
      /* it is possible to save the frame+slots in a copied symbol+syntax pair, then reuse them
       *  on every call here, but the savings in GC+allocation+setup is less than the cost in
       *  marking the saved stuff past its actual life!  (If the code is removed from the heap,
       *  the frame has to be saved on the permanent_objects list).
       */
    case OP_LET_ALL_X:
      {
	s7_pointer p, frame;
	frame = make_simple_let(sc);   		
	sc->args = frame; 
	for (p = car(sc->code); is_pair(p); p = cdr(p))
	  {
	    s7_pointer arg;
	    arg = cdar(p);
	    arg = ((s7_function)fcdr(arg))(sc, car(arg));
	    ADD_SLOT(frame, caar(p), arg);
	  }
	sc->let_number++;
	sc->envir = frame;
	sc->code = cdr(sc->code);
	goto BEGIN1;
      }
      
      
    case OP_NAMED_LET:
      sc->args = sc->NIL;
      sc->value = sc->code;
      sc->code = cadr(sc->code);
      goto LET1;
      

    case OP_LET_UNCHECKED:
      /* not named, but has vars */
      {
	s7_pointer x;
	NEW_CELL(sc, x);
	car(x) = sc->code;
	cdr(x) = sc->NIL;
	set_type(x, T_PAIR);
	sc->args = x;
	sc->code = car(sc->code);
	goto LET1A;
      }
      
      
    case OP_LET:
      /* sc->code is everything after the let: (let ((a 1)) a) so sc->code is (((a 1)) a) */
      /*   car can be either a list or a symbol ("named let") */
      {
	bool named_let;
	check_let(sc);
	sc->args = sc->NIL;
	sc->value = sc->code;
	named_let = is_symbol(car(sc->code));
	
	sc->code = (named_let) ? cadr(sc->code) : car(sc->code);
	if (is_null(sc->code))                    /* (let [name] () ...):  no bindings, so skip that step */
	  {
	    sc->code = sc->value;
	    NEW_FRAME(sc, sc->envir, sc->envir);
	    if (named_let)
	      {    
		sc->x = make_closure(sc, sc->NIL, cddr(sc->code), T_CLOSURE); /* args = () in new closure, see NAMED_LET_NO_VARS above */
		/* if this is a safe closure, we can build its env in advance and name it (a thunk in this case) */
		set_function_env(closure_let(sc->x));
		funclet_function(closure_let(sc->x)) = car(sc->code);
		add_slot(sc, car(sc->code), sc->x); 
		sc->code = cddr(sc->code);
		sc->x = sc->NIL;
	      }
	    else sc->code = cdr(sc->code);
	    goto BEGIN1;
	  }
      }
      
      
    LET1:
    case OP_LET1:
      {
	s7_pointer x, y;
	
	NEW_CELL(sc, x);
	car(x) = sc->value; /* the first time (now handled above), this saves the entire let body across the evaluations -- we pick it up later */
	cdr(x) = sc->args;
	set_type(x, T_PAIR);
	sc->args = x;
	
	if (is_pair(sc->code)) 
	  { 
	  LET1A:
	    x = cadar(sc->code);
	    if (is_pair(x))
	      {
		push_stack(sc, OP_LET1, sc->args, cdr(sc->code));
		sc->code = x;
		if (is_optimized(x))
		  goto OPT_EVAL;
		goto EVAL;  
		/* this push_stack/goto can't be optimized away via a local optimize_data case statement
		 *    because any c_call can trigger an embedded call on the evaluator (for example,
		 *    open-sound involves both hooks, and s7_load if the corresponding .scm code exists),
		 *    so we have to protect sc->code and sc->args via the stack.  (I subsequently added
		 *    some protection here, but debugging this is hard, and the gain is not huge).
		 */
	      }
	    if (is_symbol(x))
	      sc->value = find_symbol_checked(sc, x);
	    else sc->value = x;
	    sc->code = cdr(sc->code);
	    goto LET1;
	  }
	
	x = safe_reverse_in_place(sc, sc->args);
	sc->code = car(x); /* restore the original form */
	y = cdr(x);        /* use sc->args as the new frame */
	sc->envir = old_frame_in_env(sc, x, sc->envir);
	
	{
	  bool named_let;
	  named_let = is_symbol(car(sc->code));
	  if (named_let)
	    {
	      /* we need to check the current environment for ridiculous cases like
	       *    (let hiho ((hiho 4)) hiho) -- I guess hiho is 4
	       */
	      s7_pointer let_name;
	      let_name = car(sc->code);
	      sc->envir = new_frame_in_env(sc, sc->envir);
	      
	      sc->w = sc->NIL;
	      for (x = cadr(sc->code); is_pair(x); x = cdr(x)) 
		sc->w = cons(sc, caar(x), sc->w);
	      
	      sc->x = make_closure(sc, sc->w = safe_reverse_in_place(sc, sc->w), cddr(sc->code), T_CLOSURE);
	      sc->w = sc->NIL;
	      if (is_safe_closure(sc->x))
		{
		  s7_pointer arg, new_env;
		  new_env = new_frame_in_env(sc, sc->envir);
		  closure_let(sc->x) = new_env;
		  for (arg = closure_args(sc->x); is_pair(arg); arg = cdr(arg))
		    s7_make_slot(sc, new_env, car(arg), sc->NIL);
		  let_slots(new_env) = reverse_slots(sc, let_slots(new_env));
		}
	      add_slot(sc, let_name, sc->x); 
	      sc->x = sc->NIL;
	      
	      sc->envir = new_frame_in_env(sc, sc->envir);
	      for (x = cadr(sc->code); is_not_null(y); x = cdr(x)) 
		{
		  s7_pointer sym, args, val;
		  /* reuse the value cells as the new frame slots */
		  
		  sym = caar(x);
		  if (sym == let_name) let_name = sc->NIL;
		  val = car(y);
		  args = cdr(y);
		  
		  slot_symbol(y) = sym;
		  slot_set_value(y, val);
		  set_type(y, T_SLOT | T_IMMUTABLE);
		  next_slot(y) = let_slots(sc->envir);
		  let_slots(sc->envir) = y;
		  symbol_set_local(sym, let_id(sc->envir), y);
		  
		  y = args;
		}
	      sc->code = cddr(sc->code);
	    }
	  else
	    {
	      s7_pointer e;
	      unsigned long long int id;
	      
	      e = sc->envir;
	      id = let_id(e);
	      
	      for (x = car(sc->code); is_not_null(y); x = cdr(x)) 
		{
		  s7_pointer sym, args, val;
		  /* reuse the value cells as the new frame slots */
		  
		  sym = caar(x);
		  val = car(y);
		  args = cdr(y);
		  
		  slot_symbol(y) = sym;
		  symbol_set_local(sym, id, y);
		  slot_set_value(y, val);
		  set_type(y, T_SLOT | T_IMMUTABLE);
		  next_slot(y) = let_slots(e);
		  let_slots(e) = y;
		  
		  y = args;
		}
	      sc->code = cdr(sc->code);
	    }
	}
	goto BEGIN1;
      }
      
      
      /* -------------------------------- LET* -------------------------------- */      

    case OP_LET_STAR_ALL_X:
      {
	s7_pointer p;
	for (p = car(sc->code); is_pair(p); p = cdr(p))
	  {
	    s7_pointer arg;
	    arg = cdar(p);
	    arg = ((s7_function)fcdr(arg))(sc, car(arg));
	    NEW_FRAME_WITH_SLOT(sc, sc->envir, sc->envir, caar(p), arg);
	  }
	sc->code = cdr(sc->code);
	goto BEGIN1;
      }


    case OP_NAMED_LET_STAR:
      push_stack(sc, OP_LET_STAR1, sc->code, cadr(sc->code));
      sc->code = fcdr(sc->code);
      goto EVAL;

      
    case OP_LET_STAR2:
      push_stack(sc, OP_LET_STAR1, sc->code, car(sc->code));
      sc->code = fcdr(sc->code);
      goto EVAL;


    case OP_LET_STAR:
      check_let_star(sc);

      
    case OP_LET_STAR_UNCHECKED:
      if (is_symbol(car(sc->code)))
	{
	  s7_pointer cx;
	  cx = car(sc->code);
	  sc->value = cdr(sc->code);
	  if (is_null(car(sc->value)))
	    {
	      sc->envir = new_frame_in_env(sc, sc->envir);
	      sc->code = cdr(sc->value);
	      add_slot(sc, cx, make_closure(sc, sc->NIL, sc->code, T_CLOSURE_STAR));
	      goto BEGIN1;
	    }
	}
      else
	{
	  if (is_null(car(sc->code)))
	    {
	      sc->envir = new_frame_in_env(sc, sc->envir);
	      sc->code = cdr(sc->code);
	      goto BEGIN1;
	    }
	}

      if (is_symbol(car(sc->code)))
	{
	  push_stack(sc, OP_LET_STAR1, sc->code, cadr(sc->code));
	  sc->code = cadr(caadr(sc->code));
	}
      else
	{
	  push_stack(sc, OP_LET_STAR1, sc->code, car(sc->code));
	  /* args is the let body, saved for later, code is the list of vars+initial-values */
	  sc->code = cadr(caar(sc->code));
	  /* caar(code) = first var/val pair, we've checked that all these guys are legit, so cadr of that is the value */
	}
      goto EVAL;
      
      
    LET_STAR1:
    case OP_LET_STAR1:    /* let* -- calculate parameters */
      /* we can't skip (or reuse) this new frame -- we have to imitate a nested let, otherwise
       *
       *   (let ((f1 (lambda (arg) (+ arg 1))))
       *     (let* ((x 32)
       *            (f1 (lambda (arg) (f1 (+ x arg)))))
       *       (f1 1)))
       *
       * will hang.  (much later -- this worries me... Could we defer making the slot?)
       */
      NEW_FRAME_WITH_CHECKED_SLOT(sc, sc->envir, sc->envir, caar(sc->code), sc->value);
      
      sc->code = cdr(sc->code);
      if (is_pair(sc->code)) 
	{ 
	  s7_pointer x;
	  x = cadar(sc->code);
	  if (is_pair(x))
	    {
	      push_stack(sc, OP_LET_STAR1, sc->args, sc->code); 
	      sc->code = x;
	      if (is_optimized(x))
		goto OPT_EVAL;
	      goto EVAL; 
	    }
	  
	  if (is_symbol(x))
	    sc->value = find_symbol_checked(sc, x);
	  else sc->value = x;
	  goto LET_STAR1;
	} 

      sc->code = sc->args; /* original sc->code set in push_stack above */
      if (is_symbol(car(sc->code)))
	{
	  /* now we need to declare the new function */
	  add_slot(sc, car(sc->code), make_closure(sc, cadr(sc->code), cddr(sc->code), T_CLOSURE_STAR));
	  sc->code = cddr(sc->code);
	}
      else sc->code = cdr(sc->code);
      goto BEGIN1;
      
      
      /* -------------------------------- LETREC -------------------------------- */
      
    case OP_LETREC:
      check_letrec(sc, true);
      
    case OP_LETREC_UNCHECKED:
      /* get all local vars and set to #<undefined>
       * get parallel list of values
       * eval each member of values list with env still full of #<undefined>'s
       * assign each value to its variable
       * eval body
       *
       * which means that (letrec ((x x)) x) is not an error!
       * but this assumes the environment is not changed by evaluating the exprs?
       * (letrec ((a (define b 1))) b) -- if let, the define takes place in the calling env, not the current env
       * I think I need to check here that slot_pending_value is set (using the is_checked bit below).
       */
      sc->envir = new_frame_in_env(sc, sc->envir); 
      if (is_pair(car(sc->code)))
	{
	  s7_pointer x;
	  for (x = car(sc->code); is_not_null(x); x = cdr(x))
	    {
	      s7_pointer slot;
	      slot = add_slot(sc, caar(x), sc->UNDEFINED);
	      slot_pending_value(slot) = sc->UNDEFINED;
	      slot_expression(slot) = cadar(x);
	      set_checked(slot);
	    }
	  sc->args = let_slots(sc->envir);
	  push_stack(sc, OP_LETREC1, sc->args, sc->code);
	  sc->code = slot_expression(sc->args);
	  goto EVAL;
	}
      sc->code = cdr(sc->code);
      goto BEGIN1;
      

    case OP_LETREC1:
      slot_pending_value(sc->args) = sc->value;
      sc->args = next_slot(sc->args);
      if (is_slot(sc->args))
	{
	  push_stack(sc, OP_LETREC1, sc->args, sc->code);
	  sc->code = slot_expression(sc->args);
	  goto EVAL;
	}
      else
	{
	  s7_pointer slot;
	  slot = let_slots(sc->envir);
	  for (slot = let_slots(sc->envir); is_slot(slot); slot = next_slot(slot))
	    {
	      if (is_checked(slot))
		slot_set_value(slot, slot_pending_value(slot));
	    }
	  sc->code = cdr(sc->code);
	  goto BEGIN1;
	}
      

      /* -------------------------------- LETREC* -------------------------------- */
      
    case OP_LETREC_STAR:
      check_letrec(sc, false);
      
    case OP_LETREC_STAR_UNCHECKED:
      /* get all local vars and set to #<undefined>
       * eval each member of values list and assign immediately, as in let*
       * eval body
       */
      sc->envir = new_frame_in_env(sc, sc->envir); 
      if (is_pair(car(sc->code)))
	{
	  s7_pointer x, p, q;
	  for (x = car(sc->code); is_not_null(x); x = cdr(x))
	    {
	      s7_pointer slot;
	      slot = add_slot(sc, caar(x), sc->UNDEFINED);
	      slot_expression(slot) = cadar(x);
	    }
	  /* these are reversed, and for letrec*, they need to be in order, so... (reverse_in_place on the slot list) */
	  p = let_slots(sc->envir);
	  x = sc->NIL;
	  while (is_slot(p))
	    {
	      q = next_slot(p);
	      next_slot(p) = x;
	      x = p;
	      p = q;
	    }
	  let_slots(sc->envir) = x;
	  sc->args = let_slots(sc->envir);
	  push_stack(sc, OP_LETREC_STAR1, sc->args, sc->code);
	  sc->code = slot_expression(sc->args);
	  goto EVAL;
	}
      sc->code = cdr(sc->code);
      goto BEGIN1;
      

    case OP_LETREC_STAR1:
      {
	s7_pointer slot;
	slot = sc->args;
	slot_set_value(slot, sc->value);
	slot = next_slot(slot);
	if (is_slot(slot))
	  {
	    push_stack(sc, OP_LETREC_STAR1, slot, sc->code);
	    sc->code = slot_expression(slot);
	    goto EVAL;
	  }
	else
	  {
	    sc->code = cdr(sc->code);
	    goto BEGIN1;
	  }
      }


      /* -------------------------------- COND -------------------------------- */      
    case OP_COND:
      check_cond(sc);

    case OP_COND_UNCHECKED:
      push_stack(sc, OP_COND1, sc->NIL, sc->code);
      sc->code = caar(sc->code);
      goto EVAL;
      
      
    case OP_COND1:
      if (is_true(sc, sc->value))     /* got a hit (is_true -> not false, so else is true even though it has no value) */
	{
	  sc->code = cdar(sc->code);
	  if (is_null(sc->code))
	    {
	      if (is_multiple_value(sc->value))                             /* (+ 1 (cond ((values 2 3)))) */
		sc->value = splice_in_values(sc, multiple_value(sc->value));
	      /* no result clause, so return test, (cond (#t)) -> #t, (cond ((+ 1 2))) -> 3 */
	      goto START;
	    }

	  if (is_pair(sc->code))
	    {
	      if ((car(sc->code) == sc->FEED_TO) &&
		  (s7_symbol_value(sc, sc->FEED_TO) == sc->UNDEFINED))
		{
		  if (is_multiple_value(sc->value))                             /* (cond ((values 1 2) => +)) */
		    sc->code = cons(sc, cadr(sc->code), multiple_value(sc->value));
		  else sc->code = list_2(sc, cadr(sc->code), list_2(sc, sc->QUOTE, sc->value)); 
		  goto EVAL; 
		}
	      goto BEGIN1;
	    }
	  eval_error_with_name(sc, "~A: unexpected dot? ~A", sc->code); /* (cond (#t . 1)) etc */
	}
      sc->code = cdr(sc->code);
      if (is_null(sc->code))
	{
	  sc->value = sc->NIL;
	  goto START;
	} 
	  
      push_stack_no_args(sc, OP_COND1, sc->code);
      sc->code = caar(sc->code);
      goto EVAL;
      
      
    case OP_COND_SIMPLE:
      push_stack_no_args(sc, OP_COND1_SIMPLE, sc->code);
      sc->code = caar(sc->code);
      goto EVAL;
      
      
    COND1_SIMPLE:
    case OP_COND1_SIMPLE:
      if (is_true(sc, sc->value))
	{
	  sc->code = cdar(sc->code);
	  if (is_null(sc->code))
	    {
	      if (is_multiple_value(sc->value))
		sc->value = splice_in_values(sc, multiple_value(sc->value));
	      goto START;
	    }
	  goto BEGIN1;
	}

      sc->code = cdr(sc->code);
      if (is_null(sc->code))
	{
	  sc->value = sc->NIL;
	  goto START;
	} 
      if (is_pair(caar(sc->code)))
	{
	  push_stack_no_args(sc, OP_COND1_SIMPLE, sc->code);
	  sc->code = caar(sc->code);
	  goto EVAL; 
	}
      sc->value = caar(sc->code);
      if (is_symbol(sc->value))
	sc->value = find_symbol_checked(sc, sc->value);
      goto COND1_SIMPLE;


    case OP_COND_S:
      {
	s7_pointer val = NULL, p;
	if (is_pair(caar(sc->code)))
	  val = find_symbol_checked(sc, cadaar(sc->code));
	for (p = sc->code; is_pair(p); p = cdr(p))
	  {
	    s7_pointer ap;
	    ap = caar(p);
	    if (is_pair(ap))
	      {
		car(sc->T1_1) = val;
		sc->value = c_call(ap)(sc, sc->T1_1);
	      }
	    else sc->value = sc->T;
	    if (is_true(sc, sc->value))
	      {
		sc->code = cdar(p);
		if (is_null(sc->code))
		  {
		    if (is_multiple_value(sc->value))
		      sc->value = splice_in_values(sc, multiple_value(sc->value));
		    goto START;
		  }
		goto BEGIN1;
	      }
	  }
	sc->value = sc->NIL;
	goto START;
      }

    case OP_COND_ALL_X:
      {
	s7_pointer p;
	for (p = sc->code; is_pair(p); p = cdr(p))
	  {
	    sc->value = ((s7_function)fcdr(car(p)))(sc, caar(p));
	    if (is_true(sc, sc->value))
	      {
		sc->code = cdar(p);
		if (is_null(sc->code))
		  {
		    if (is_multiple_value(sc->value))
		      sc->value = splice_in_values(sc, multiple_value(sc->value));
		    goto START;
		  }
		goto BEGIN1;
	      }
	  }
	sc->value = sc->NIL;
	goto START;
      }


      /* -------------------------------- AND -------------------------------- */
    case OP_AND:
      check_and(sc);
      if (is_null(sc->code))
	{
	  sc->value = sc->T;
	  goto START;
	}
      goto AND1;

    case OP_AND1:
      if ((is_false(sc, sc->value)) ||
	  (is_null(sc->code)))
	goto START;

    AND1:
    case OP_AND_UNCHECKED:
      {
	s7_pointer p;
	p = car(sc->code);
	if (!is_pair(p))
	  {
	    if (is_symbol(p))
	      sc->value = find_global_symbol_checked(sc, p); 
	    else sc->value = p;

	    if ((is_false(sc, sc->value)) ||
		(is_null(cdr(sc->code))))
	      goto START;

	    sc->code = cdr(sc->code);
	    goto AND1;
	  }

	if (is_not_null(cdr(sc->code)))
	  push_stack_no_args(sc, OP_AND1, cdr(sc->code));
	sc->code = p;
	if (is_optimized(p))
	  goto OPT_EVAL;
	goto EVAL; 
      }


    case OP_AND_P1:
      if ((is_false(sc, sc->value)) ||
	  (is_null(sc->code)))
	goto START; 
      /* fall through */

    AND_P:
    case OP_AND_P:
      if (fcdr(sc->code))
	{
	  sc->value = ((s7_function)fcdr(sc->code))(sc, car(sc->code));
	  if (is_false(sc, sc->value))
	    goto START;
	  sc->code = cdr(sc->code);
	  if (is_null(sc->code))
	    goto START;
	  goto AND_P;
	}
      else
	{
	  if (is_not_null(cdr(sc->code)))
	    push_stack_no_args(sc, OP_AND_P1, cdr(sc->code)); 
	  sc->code = car(sc->code);
	  goto EVAL; 
	}

      
      /* -------------------------------- OR -------------------------------- */      
    case OP_OR:
      check_or(sc);
      if (is_null(sc->code)) 
	{
	  sc->value = sc->F;
	  goto START;
	}
      goto OR1;

    case OP_OR1:
      if ((is_true(sc, sc->value)) ||
	  (is_null(sc->code)))
	goto START;

    OR1:
    case OP_OR_UNCHECKED:
      if (!is_pair(car(sc->code)))
	{
	  sc->value = car(sc->code);
	  if (is_symbol(sc->value))
	    sc->value = find_symbol_checked(sc, sc->value);

	  if ((is_true(sc, sc->value)) ||
	      (is_null(cdr(sc->code))))
	    goto START;

	  sc->code = cdr(sc->code);
	  goto OR1;
	}

      if (is_not_null(cdr(sc->code)))
	push_stack_no_args(sc, OP_OR1, cdr(sc->code)); 
      sc->code = car(sc->code);
      goto EVAL; 


    case OP_OR_P1:
      if ((is_true(sc, sc->value)) ||
	  (is_null(sc->code)))
	goto START;
      /* fall through */

    OR_P:
    case OP_OR_P:
      if (fcdr(sc->code))
	{
	  sc->value = ((s7_function)fcdr(sc->code))(sc, car(sc->code));
	  if (is_true(sc, sc->value))
	    goto START;
	  sc->code = cdr(sc->code);
	  if (is_null(sc->code))
	    goto START;
	  goto OR_P;
	}
      else
	{
	  if (is_not_null(cdr(sc->code)))
	    push_stack_no_args(sc, OP_OR_P1, cdr(sc->code)); 
	  sc->code = car(sc->code);
	  goto EVAL; 
	}

      /* by going direct without a push_stack on the last one we get "tail recursion",
       *   but if the last arg (also in "and" above) is "values", there is a slight
       *   inconsistency: the values are returned and spliced into the caller if trailing, but
       *   are spliced into the "or" if not trailing, so
       *
       *   (+ 10 (or (values 1 2) #f))
       *   11
       *   (+ 10 (or #f (values 1 2)))
       *   13
       *   (+ 10 (or (or #f (values 1 2)) #f))
       *   11
       *
       * The tail recursion is more important.  This behavior matches that of "begin" -- if the
       * values statement is last, it splices into the next outer arglist.
       *
       * I tried implementing dynamic-binding by walking up the stack looking at envs, but
       *   for that to work in some cases, every new frame needs to be on the stack, hence
       *   OP_LET_UNWIND.  But that means the stack grows on every let, so tail recursion is
       *   defeated.  I'd have to notice tail calls and pop the stack, but I decided that
       *   was too much work for a minor (unneeded) feature.  Although the stack does not
       *   grow, we're depending on the GC to take care of the old local envs after the
       *   tail call.  My test:
       *
       * (let ((max-stack 0))
       *   (define (tc-1 a c) 
       *     (let ((b (+ a 1)) 
       *           (d (make-vector 1000)))
       *       (if (> (s7-stack-size) max-stack)
       *           (set! max-stack (s7-stack-size)))
       *       (if (< b c) 
       *           (tc-1 b c))))
       * (tc-1 0 3200000) max-stack)
       * 4
       *
       * indicated (via "top") that the memory footprint was not growing, and the "4"
       * shows the stack was ok, so I currently believe that s7 is doing tail calls
       * correctly.
       */


      /* -------------------------------- macro evaluation -------------------------------- */

    case OP_EVAL_MACRO:    /* after (scheme-side) macroexpansion, evaluate the resulting expression */
      /* 
       * (define-macro (hi a) `(+ ,a 1))
       * (hi 2)
       * here with value: (+ 2 1)
       */
      if (is_multiple_value(sc->value))
	{
	  /* a normal macro's result is evaluated (below) and its value replaces the macro invocation,
	   *   so if a macro returns multiple values, evaluate each one, then replace the macro 
	   *   invocation with (apply values evaluated-results-in-a-list).  We need to save the
	   *   new list of results, and where we are in the macro's output list, so code=macro output,
	   *   args=new list.  If it returns (values), should we use #<unspecified>?  I think that
	   *   happens now without generating a multiple_value object:
	   *       (define-macro (hi) (values)) (hi) -> #<unspecified>
	   *
	   *   (define-macro (ho) (values '(+ 1 2) '(* 3 4))) (+ 1 (ho) 3) -> 19
	   *   (define-macro (ha) (values '(define a 1) '(define b 2))) (let () (ha) (+ a b)) -> 3
	   */
	  push_stack(sc, OP_EVAL_MACRO_MV, sc->NIL, cdr(sc->value));
	  sc->code = car(sc->value);
	}
      else sc->code = sc->value;
      goto EVAL;


    case OP_EVAL_MACRO_MV:
      if (is_null(sc->code)) /* end of values list */
	{
	  sc->value = splice_in_values(sc, multiple_value(safe_reverse_in_place(sc, cons(sc, sc->value, sc->args))));
	  goto START;
	}
      push_stack(sc, OP_EVAL_MACRO_MV, cons(sc, sc->value, sc->args), cdr(sc->code));
      sc->code = car(sc->code);
      goto EVAL;


    case OP_EXPANSION:
      /* after the expander has finished, if a list was returned, we need to add some annotations.
       *   if the expander returned (values), the list-in-progress vanishes! (This mimics map and *#readers*).
       */
      if (sc->value == sc->NO_VALUE)
	sc->stack_end[-1] = (s7_pointer)OP_READ_NEXT;
      else annotate_expansion(sc, sc->value);
      goto START;


    case OP_DEFINE_MACRO_WITH_ACCESSOR:
      if (sc->value == sc->ERROR) /* backwards compatibility... */
	return(s7_error(sc, sc->ERROR,
			list_3(sc, make_string_wrapper(sc, "can't define-macro ~S to ~S"), car(sc->args), cadr(sc->args))));
      sc->code = sc->value;
      goto DEFINE_MACRO2;


    case OP_DEFINE_BACRO:
    case OP_DEFINE_BACRO_STAR:
    case OP_DEFINE_EXPANSION:
    case OP_DEFINE_MACRO:
    case OP_DEFINE_MACRO_STAR:
      check_define_macro(sc);

      if (symbol_has_accessor(caar(sc->code)))
	{
	  s7_pointer x;
	  x = find_symbol(sc, caar(sc->code));
	  if ((is_slot(x)) &&
	      (slot_has_accessor(x)))
	    {
	      sc->value = bind_accessed_symbol(sc, OP_DEFINE_MACRO_WITH_ACCESSOR, caar(sc->code), sc->code);
	      if (sc->value == sc->NO_VALUE)
		goto APPLY;
	      sc->code = sc->value;
	    }
	}

    DEFINE_MACRO2:
      {
	s7_pointer lx, cx, zx;
	cx = caar(sc->code);
	zx = cdr(sc->code);
	lx = s7_gensym(sc, symbol_name(cx));
	/* (define-macro (hi a b) `(+ ,a ,b)) becomes:
	 *   cx: hi
	 *   sc->code: (lambda ({hi}-14) (apply (lambda (a b) ({list} '+ a b)) (cdr {hi}-14)))
	 */
	NEW_CELL_NO_CHECK(sc, sc->value);
	closure_args(sc->value) = list_1(sc, lx);
	closure_body(sc->value) = list_1(sc, cons(sc, 
						  sc->Apply,
						  cons(sc, 
						       cons(sc, 
							    ((sc->op == OP_DEFINE_MACRO_STAR) || 
							     (sc->op == OP_DEFINE_BACRO_STAR)) ? sc->LAMBDA_STAR : sc->LAMBDA,
							    cons(sc,                /* cdar(sc->code) is the parameter list */
								 ((sc->op == OP_DEFINE_MACRO_STAR) || 
								  (sc->op == OP_DEFINE_BACRO_STAR)) ? quotify(sc, cdar(sc->code)) : cdar(sc->code),
								 zx)),
						       list_1(sc, list_2(sc, sc->CDR, lx)))));
	closure_let(sc->value) = sc->envir;
	closure_setter(sc->value) = sc->F;
	closure_arity(sc->value) = CLOSURE_ARITY_NOT_SET;
	sc->capture_env_counter++;
	sc->code = cx; 
	
	if ((sc->op == OP_DEFINE_BACRO) ||
	    (sc->op == OP_DEFINE_BACRO_STAR))
	  set_type(sc->value, T_BACRO | T_DONT_EVAL_ARGS | T_COPY_ARGS);
	else
	  {
	    if (sc->op == OP_DEFINE_EXPANSION)
	      {
		/* perhaps define-expansion should be define-reader-macro but that collides with historical uses. 
		 */
		set_type(sc->value, T_MACRO | T_EXPANSION | T_DONT_EVAL_ARGS | T_COPY_ARGS);
		set_type(sc->code, EXPANSION_TYPE);
	      }
	    else set_type(sc->value, T_MACRO | T_DONT_EVAL_ARGS | T_COPY_ARGS); 
	  }

	fixup_macro_overlay(sc, closure_body(sc->value));
	/* symbol? macro name has already been checked */
	/* find name in environment, and define it */
	cx = find_local_symbol(sc, sc->code, sc->envir); 
	if (is_slot(cx))
	  slot_set_value(cx, sc->value); 
	else s7_make_slot(sc, sc->envir, sc->code, sc->value); /* was current but we've checked immutable already */
	
	optimize(sc, closure_body(sc->value), 0, sc->NIL);
	
	/* here sc->value is the macro, sc->code is its name (a symbol),
	 *   (symbol? (define (f a) (+ a 1))) -> #t, so I guess define-macro should follow suit.
	 */
	/* sc->value = sc->code; */ /* 25-Jul-14 */
	goto START;
      }
      
    case OP_LAMBDA: 
      /* code is a lambda form minus the "lambda": ((a b) (+ a b)) */
      /* this includes unevaluated symbols (direct symbol table refs) in macro arg list */
      {
	s7_pointer code, body;
	code = sc->code;
	body = cdr(code);

	if ((!is_pair(code)) ||
	    (!is_pair(body)))                               /* (lambda) or (lambda #f) or (lambda . 1) */
	  eval_error(sc, "lambda: no args or no body? ~A", code);
	
	/* in many cases, this is a no-op -- we already checked at define */
	/* why can't we optimize hop=1 here?  
	 *    First because 'safety might be checking sort functions for infinite loops.
	 *    Second because the body might not be at top level, so optimizations might interpose global for local functions.
	 *    Third because something is broken in the optimizer: ((sc->safety == 0) && (sc->envir == sc->NIL)) should be sufficient!
	 */
	check_lambda_args(sc, car(code), NULL);
	clear_syms_in_list(sc);
	optimize(sc, body, 0, sc->NIL);

	if ((is_overlaid(code)) &&
	    (cdr(ecdr(code)) == code))
	  pair_set_syntax_symbol(code, sc->LAMBDA_UNCHECKED);
      }

    case OP_LAMBDA_UNCHECKED:
      make_closure_with_let(sc, sc->value, car(sc->code), cdr(sc->code), sc->envir);
      goto START;


    case OP_LAMBDA_STAR:
      if ((!is_pair(sc->code)) ||
	  (!is_pair(cdr(sc->code))))                                          /* (lambda*) or (lambda* #f) */
	eval_error(sc, "lambda*: no args or no body? ~A", sc->code);

      car(sc->code) = check_lambda_star_args(sc, car(sc->code), NULL);
      clear_syms_in_list(sc);
      optimize(sc, cdr(sc->code), 0, sc->NIL);

      if ((is_overlaid(sc->code)) &&
	  (cdr(ecdr(sc->code)) == sc->code))
	pair_set_syntax_symbol(sc->code, sc->LAMBDA_STAR_UNCHECKED);


    case OP_LAMBDA_STAR_UNCHECKED:
      sc->value = make_closure(sc, car(sc->code), cdr(sc->code), T_CLOSURE_STAR);
      goto START;
      


      /* -------------------------------- CASE -------------------------------- */

    case OP_CASE:      /* case, car(sc->code) is the selector */
      check_case(sc);


    case OP_CASE_UNCHECKED:
      {
	s7_pointer carc;
	carc = car(sc->code);
	if (!is_pair(carc))
	  {
	    if (is_symbol(carc))
	      sc->value = find_symbol_checked(sc, carc);
	    else sc->value = carc;
	    sc->code = cdr(sc->code);
	    /* fall through */
	  }
	else
	  {
	    push_stack_no_args(sc, OP_CASE1, cdr(sc->code));
	    sc->code = carc;
	    goto EVAL; 
	  }
      }
      
    case OP_CASE1: 
      {
	s7_pointer x, y;
	if (is_simple(sc->value))
	  {
	    for (x = sc->code; is_pair(x); x = cdr(x))
	      {
		y = caar(x);
		if (!is_pair(y))
		  goto ELSE_CASE;
		do {
		  if (car(y) == sc->value)
		    goto ELSE_CASE;
		  y = cdr(y);
		} while (is_pair(y));
	      }
	  }
	else
	  {
	    for (x = sc->code; is_pair(x); x = cdr(x)) 
	      {
		y = caar(x);
		if (!is_pair(y))
		  goto ELSE_CASE;
		for (; is_pair(y); y = cdr(y)) 
		  if (s7_is_eqv(car(y), sc->value)) 
		    goto ELSE_CASE;
	      }
	  }
	/* x is the entire matching clause, (case 2 ((2) 3)), x: (((2) 3)) */
      ELSE_CASE:
	if (is_not_null(x))
	  {
	    sc->code = cdar(x);
	    
	    /* check for => */
	    if ((car(sc->code) == sc->FEED_TO) &&
		(s7_symbol_value(sc, sc->FEED_TO) == sc->UNDEFINED))
	      {
		sc->code = list_2(sc, cadr(sc->code), list_2(sc, sc->QUOTE, sc->value)); 
		goto EVAL; 
	      }
	    goto BEGIN1;
	  }
	
	/* no match found */
	sc->value = sc->UNSPECIFIED; /* this was sc->NIL but the spec says case value is unspecified if no clauses match */
	goto START;
      }


    case OP_CASE_SIMPLE: 
      /* assume symbol as selector, all keys are simple, and no =>
       */
      {
	s7_pointer x, y, selector;
	selector = find_symbol_checked(sc, car(sc->code));
	for (x = cdr(sc->code); is_pair(x); x = cdr(x))
	  {
	    y = fcdr(x);
	    if (!is_pair(y)) /* else? */
	      {
		sc->code = cdar(x);
		goto BEGIN1;
	      }
	    do {
	      if (car(y) == selector)
		{
		  sc->code = cdar(x);
		  goto BEGIN1;
		}
	      y = cdr(y);
	    } while (is_pair(y));
	  }
	sc->value = sc->UNSPECIFIED;
	goto START;
      }


    case OP_CASE_SIMPLER: 
      /* assume symbol as selector, all keys are simple, and no => and no else
       */
      {
	s7_pointer x, y, selector;
	selector = find_symbol_checked(sc, car(sc->code));
	for (x = cdr(sc->code); is_pair(x); x = cdr(x))
	  {
	    y = fcdr(x);
	    do {
	      if (car(y) == selector)
		{
		  sc->code = cdar(x);
		  goto BEGIN1;
		}
	      y = cdr(y);
	    } while (is_pair(y));
	  }
	sc->value = sc->UNSPECIFIED;
	IF_BEGIN_POP_STACK(sc);
      }


    case OP_CASE_SIMPLER_1: 
      /* assume symbol as selector, all keys are simple, and no => and no else, bodies are 1 liners
       */
      {
	s7_pointer x, y, selector;
	selector = find_symbol_checked(sc, car(sc->code));
	for (x = cdr(sc->code); is_pair(x); x = cdr(x))
	  {
	    y = fcdr(x);
	    do {
	      if (car(y) == selector)
		{
		  sc->code = ecdr(x); /* cadar(x); */
		  goto EVAL;
		}
	      y = cdr(y);
	    } while (is_pair(y));
	  }
	sc->value = sc->UNSPECIFIED;
	goto START;
      }


    case OP_CASE_SIMPLER_SS: 
      /* assume hop_safe_ss as selector, all keys are simple, and no => and no else, bodies are 1 liners
       */
      {
	s7_pointer x, y, selector, args;
	args = cdar(sc->code);
	x = find_symbol_checked(sc, car(args));
	car(sc->T2_2) = find_symbol_checked(sc, cadr(args));
	car(sc->T2_1) = x;
	selector = c_call(car(sc->code))(sc, sc->T2_1);

	for (x = cdr(sc->code); is_pair(x); x = cdr(x))
	  {
	    y = fcdr(x);
	    do {
	      if (car(y) == selector)
		{
		  sc->code = ecdr(x); /* cadar(x); */
		  goto EVAL;
		}
	      y = cdr(y);
	    } while (is_pair(y));
	  }
	sc->value = sc->UNSPECIFIED;
	IF_BEGIN_POP_STACK(sc);
      }


    case OP_CASE_SIMPLEST_SS:
      {
	s7_pointer x, selector, args;
	args = cdar(sc->code);
	x = find_symbol_checked(sc, car(args));
	car(sc->T2_2) = find_symbol_checked(sc, cadr(args));
	car(sc->T2_1) = x;
	selector = c_call(car(sc->code))(sc, sc->T2_1);
	for (x = cdr(sc->code); is_pair(x); x = cdr(x))
	  if (fcdr(x) == selector)
	    {
	      sc->code = cdar(x);
	      goto BEGIN1;
	    }
	sc->value = sc->UNSPECIFIED;
	goto START;
      }
      

    case OP_CASE_SIMPLEST: 
      /* assume symbol as selector, all keys are simple and singletons, and no => and no else, bodies are 1 liners
       */
      {
	s7_pointer x, selector;
	selector = find_symbol_checked(sc, car(sc->code));
	for (x = cdr(sc->code); is_pair(x); x = cdr(x))
	  if (fcdr(x) == selector)
	    {
	      sc->code = ecdr(x); /* cadar(x); */ 
	      goto EVAL;
	    }
	sc->value = sc->UNSPECIFIED;
	goto START;
      }


    case OP_CASE_SIMPLEST_ELSE: 
      /* assume symbol as selector, all keys are simple and singletons, and no =>, bodies are 1 liners
       */
      {
	s7_pointer x, selector;
	selector = find_symbol_checked(sc, car(sc->code));
	for (x = cdr(sc->code); is_pair(x); x = cdr(x))
	  if (fcdr(x) == selector)
	    {
	      sc->code = cadar(x); /* ecdr(x); */
	      goto EVAL;
	    }
	sc->code = gcdr(sc->code);
	goto EVAL;
      }


    case OP_CASE_SIMPLEST_ELSE_C: 
      /* assume symbol as selector, all keys are simple and singletons, and no =>, bodies are constants
       */
      {
	s7_pointer x, selector;
	selector = find_symbol_checked(sc, car(sc->code));
	for (x = cdr(sc->code); is_pair(x); x = cdr(x))
	  if (fcdr(x) == selector)
	    {
	      sc->value = cadar(x); /* ecdr(x); but that requires setup in optimizer */
	      if (is_pair(sc->value))
		sc->value = cadr(sc->value);
	      goto START;
	    }
	sc->code = gcdr(sc->code);
	goto EVAL;
      }


    case OP_ERROR_QUIT: 
    case OP_EVAL_DONE:
      /* this is the "time to quit" operator */
      return(sc->F);
      break;

    case OP_BARRIER:
    case OP_CATCH_ALL:
    case OP_CATCH:
    case OP_CATCH_1:
    case OP_CATCH_2:
      goto START;

    case OP_DEACTIVATE_GOTO:
      call_exit_active(sc->args) = false;      /* as we leave the call-with-exit body, deactivate the exiter */
      goto START;


    case OP_ERROR_HOOK_QUIT:
      sc->error_hook = sc->code;  /* restore old value */

      /* now mimic the end of the normal error handler.  Since this error hook evaluation can happen
       *   in an arbitrary s7_call nesting, we can't just return from the current evaluation --
       *   we have to jump to the original (top-level) call.  Otherwise '#<unspecified> or whatever
       *   is simply treated as the (non-error) return value, and the higher level evaluations
       *   get confused.
       */
      stack_reset(sc);
      sc->op = OP_ERROR_QUIT;
      if (sc->longjmp_ok) longjmp(sc->goto_start, 1);
      return(sc->value); /* not executed I hope */

      
    case OP_GET_OUTPUT_STRING:          /* from get-output-string -- return a new string */
      sc->value = s7_make_string_with_length(sc, (const char *)port_data(sc->code), port_position(sc->code));
      goto START;


    case OP_GET_OUTPUT_STRING_1:        /* from call-with-output-string and with-output-to-string -- return the port string directly */
      if ((!is_output_port(sc->code)) || 
	  (port_is_closed(sc->code)))
	simple_wrong_type_argument_with_type(sc, sc->WITH_OUTPUT_TO_STRING, sc->code, make_string_wrapper(sc, "an open string output port"));	

      sc->value = make_string_uncopied_with_length(sc, (char *)port_data(sc->code), port_position(sc->code));
      string_value(sc->value)[port_position(sc->code)] = 0;
      port_data(sc->code) = NULL;
      port_data_size(sc->code) = 0;
      port_needs_free(sc->code) = false;
      /* fall through */

    case OP_UNWIND_OUTPUT:
      {
	bool is_file;
	is_file = is_file_port(sc->code);

	if ((is_output_port(sc->code)) &&
	    (!port_is_closed(sc->code)))
	  s7_close_output_port(sc, sc->code); /* may call fflush */

	if ((is_output_port(sc->args)) &&
	    (!port_is_closed(sc->args)))
	  sc->output_port = sc->args;
       
	if ((is_file) &&
	    (is_multiple_value(sc->value)))
	  sc->value = splice_in_values(sc, multiple_value(sc->value));
       goto START;
      }


    case OP_UNWIND_INPUT:
      if ((is_input_port(sc->code)) &&
	  (!port_is_closed(sc->code)))
	s7_close_input_port(sc, sc->code);

      if ((is_input_port(sc->args)) &&
	  (!port_is_closed(sc->args)))
	sc->input_port = sc->args;

      if (is_multiple_value(sc->value)) 
	sc->value = splice_in_values(sc, multiple_value(sc->value));
      goto START;


    case OP_DYNAMIC_WIND:
      if (dynamic_wind_state(sc->code) == DWIND_INIT)
	{
	  dynamic_wind_state(sc->code) = DWIND_BODY;
	  push_stack(sc, OP_DYNAMIC_WIND, sc->NIL, sc->code);
	  sc->code = dynamic_wind_body(sc->code);
	  if (is_any_macro(sc->code))
	    {
	      push_stack(sc, OP_EVAL_MACRO, sc->NIL, sc->NIL);
	      sc->args = list_1(sc, list_1(sc, sc->code));
	    }
	  else sc->args = sc->NIL;
	  goto APPLY;
	}
      else
	{
	  if (dynamic_wind_state(sc->code) == DWIND_BODY)
	    {
	      dynamic_wind_state(sc->code) = DWIND_FINISH;
	      if (dynamic_wind_out(sc->code) != sc->F)
		{
		  push_stack(sc, OP_DYNAMIC_WIND, sc->value, sc->code);
		  sc->code = dynamic_wind_out(sc->code);
		  if (is_any_macro(sc->code))
		    {
		      push_stack(sc, OP_EVAL_MACRO, sc->NIL, sc->NIL);
		      sc->args = list_1(sc, list_1(sc, sc->code));
		    }
		  else sc->args = sc->NIL;
		  goto APPLY;
		}
	      else
		{
		  if (is_multiple_value(sc->value))
		    sc->value = splice_in_values(sc, multiple_value(sc->value));
		  goto START;
		}
	    }
	  /* (+ 1 (dynamic-wind (lambda () #f) (lambda () (values 2 3 4)) (lambda () #f)) 5) */
	  if (is_multiple_value(sc->args))
	    sc->value = splice_in_values(sc, multiple_value(sc->args));
	  else sc->value = sc->args;                         /* value saved above */ 
	  goto START;
	}
      break;


      /* -------------------------------- with-let --------------------------------
       *
       * the extra set! to pull in args, or fixup the outlet is annoying, but 
       *   but with-let is hard to do right -- what if env is chained as in class/objects?
       *
       * also, currently a mock-let is an error -- perhaps add the method checks?
       *   but unless 'values, that would require a 'with-let method (it's not a function)
       */

    case OP_WITH_LET_S:
      {
	s7_pointer e;
	e = find_symbol_checked(sc, car(sc->code));
	if (e == sc->rootlet)
	  sc->envir = sc->NIL;
	else 
	  {
	    s7_pointer p;
	    if (!is_let(e))
	      eval_type_error(sc, "with-let takes an environment argument: ~A", e);
	    let_id(e) = ++sc->let_number;
	    sc->envir = e;
	    /* if the let in question has 10,000 names (e.g. *gtk*) this loop (which can't be avoided currently)
	     *   will dominate the computation.  So, instead of saying (with-let *gtk* ...) use something
	     *   equivalent to (with-let (sublet *gtk*) ...) which is cleaner anyway.
	     */
	    for (p = let_slots(e); is_slot(p); p = next_slot(p))
	      {
		s7_pointer sym;
		sym = slot_symbol(p);
		symbol_set_local(sym, sc->let_number, p);
	      }
	  }
	sc->code = cdr(sc->code);
	goto BEGIN1;
      }
      

    case OP_WITH_LET:
      check_with_let(sc);


    case OP_WITH_LET_UNCHECKED:
      sc->value = car(sc->code);
      if (!is_pair(sc->value))
	{
	  if (is_symbol(sc->value))
	    sc->value = find_symbol_checked(sc, sc->value);
	  sc->code = cdr(sc->code);

	  if (!is_pair(sc->code))
	    {
	      if (!is_let(sc->value))            /* (with-let e abs) */
		eval_type_error(sc, "with-let takes an environment argument: ~A", sc->value);
	      if (is_symbol(sc->code))
		sc->value = s7_symbol_local_value(sc, sc->code, sc->value);
	      else sc->value = sc->code;
	      goto START;
	    }
	  /* else fall through */
	}
      else
	{
	  push_stack(sc, OP_WITH_LET1, sc->NIL, cdr(sc->code));
	  sc->code = sc->value;                          /* eval env arg */
	  goto EVAL;
	}

    case OP_WITH_LET1:
      {
	s7_pointer e;
	e = sc->value;
	if (!is_let(e))                    /* (with-let . "hi") */
	  eval_type_error(sc, "with-let takes an environment argument: ~A", e);
	if (e == sc->rootlet)
	  sc->envir = sc->NIL;                             /* (with-let (rootlet) ...) */
	else 
	  {
	    s7_pointer p;
	    let_id(e) = ++sc->let_number;
	    sc->envir = e;
	    for (p = let_slots(e); is_slot(p); p = next_slot(p))
	      symbol_set_local(slot_symbol(p), sc->let_number, p);
	  }
	goto BEGIN1;
      }


    case OP_WITH_BAFFLE:
      if (!is_proper_list(sc, sc->code)) 
	eval_error_with_name(sc, "~A: unexpected dot? ~A", sc->code);

      if ((!is_null(sc->code)) &&
	  (is_overlaid(sc->code)) &&
	  (cdr(ecdr(sc->code)) == sc->code))
	pair_set_syntax_symbol(sc->code, sc->WITH_BAFFLE_UNCHECKED);

    case OP_WITH_BAFFLE_UNCHECKED:
      if (is_null(sc->code))
	{
	  sc->value = sc->NIL;
	  goto START;
	}
      NEW_FRAME(sc, sc->envir, sc->envir);
      add_slot(sc, sc->BAFFLE, make_baffle(sc));
      goto BEGIN1;



      /* -------------------------------- the reader -------------------------------- */

    READ_LIST:
    case OP_READ_LIST: 
      /* sc->args is sc->NIL at first */
      /*    was: sc->args = cons(sc, sc->value, sc->args); */ 
      {
	s7_pointer x;
	NEW_CELL(sc, x);
	car(x) = sc->value;
	cdr(x) = sc->args;
	set_type(x, T_PAIR);
	sc->args = x;
      }
      
    case OP_READ_NEXT:
      /* this is 75% of the token calls, so expanding it saves lots of time */
      {
	int c;
	s7_pointer pt;

	pt = sc->input_port;
	c = port_read_white_space(pt)(sc, pt);
	
	switch (c) 
	  {
	  case '(':
	    c = port_read_white_space(pt)(sc, pt);  /* sc->tok = token(sc) */
	    switch (c) 
	      {
	      case '(':	 sc->tok = TOKEN_LEFT_PAREN;                break;
	      case ')':	 sc->tok = TOKEN_RIGHT_PAREN;               break;
	      case '.':	 sc->tok = read_dot(sc, pt);                break;
	      case '\'': sc->tok = TOKEN_QUOTE;                     break;
	      case ';':  sc->tok = port_read_semicolon(pt)(sc, pt); break;
	      case '"':  sc->tok = TOKEN_DOUBLE_QUOTE;              break;
	      case '`':  sc->tok = TOKEN_BACK_QUOTE;                break;
	      case ',':  sc->tok = read_comma(sc, pt);              break;
	      case '#':  sc->tok = read_sharp(sc, pt);              break;
	      case '\0': case EOF: sc->tok = TOKEN_EOF;             break;
	      default:
		sc->strbuf[0] = c;
		push_stack_no_code(sc, OP_READ_LIST, sc->args);
		CHECK_STACK_SIZE(sc);
		sc->value = port_read_name(pt)(sc, pt);
		sc->args = sc->NIL;
		goto READ_LIST;
	      }

	    if (sc->tok == TOKEN_ATOM)
	      {
		push_stack_no_code(sc, OP_READ_LIST, sc->args);
		CHECK_STACK_SIZE(sc);
		sc->value = port_read_name(pt)(sc, pt);
		sc->args = sc->NIL;
		goto READ_LIST;
	      }

	    if (sc->tok == TOKEN_RIGHT_PAREN)
	      {
		sc->value = sc->NIL;
		goto READ_LIST;
	      }
	    
	    if (sc->tok == TOKEN_DOT) 
	      {
		do {c = inchar(pt);} while ((c != ')') && (c != EOF));
		return(read_error(sc, "stray dot after '('?"));      /* (car '( . )) */
	      }
	    
	    if (sc->tok == TOKEN_EOF)
	      return(missing_close_paren_error(sc));
	    
	    push_stack_no_code(sc, OP_READ_LIST, sc->args);
	    CHECK_STACK_SIZE(sc);
	    push_stack_no_code(sc, OP_READ_LIST, sc->NIL);
	    sc->value = read_expression(sc);
	    goto START;
      
	  case ')':
	    sc->tok = TOKEN_RIGHT_PAREN;
	    break;
	    
	  case '.':
	    sc->tok = read_dot(sc, pt); /* dot or atom */
	    break;
	    
	  case '\'':
	    sc->tok = TOKEN_QUOTE;
	    push_stack_no_code(sc, OP_READ_LIST, sc->args);
	    sc->value = read_expression(sc);
	    goto START;
	    
	  case ';':
	    sc->tok = port_read_semicolon(pt)(sc, pt);
	    break;
	    
	  case '"':
	    sc->tok = TOKEN_DOUBLE_QUOTE;
	    sc->value = read_string_constant(sc, pt);
	    if (sc->value == sc->F)                                /* can happen if input code ends in the middle of a string */
	      return(string_read_error(sc, "end of input encountered while in a string"));
	    if (sc->value == sc->T)
	      return(read_error(sc, "unknown backslash usage -- perhaps you meant two backslashes?"));
	    goto READ_LIST;
	    
	  case '`':
	    sc->tok = TOKEN_BACK_QUOTE;
	    push_stack_no_code(sc, OP_READ_LIST, sc->args);
	    sc->value = read_expression(sc);
	    goto START;
	    
	  case ',':
	    sc->tok = read_comma(sc, pt); /* at_mark or comma */
	    push_stack_no_code(sc, OP_READ_LIST, sc->args);
	    sc->value = read_expression(sc);
	    goto START;
	    
	  case '#':
	    sc->tok = read_sharp(sc, pt);
	    break;

	  case '\0':
	  case EOF:
	    return(missing_close_paren_error(sc));
	  
	  default: 
	    sc->strbuf[0] = c; 
	    sc->value = port_read_name(pt)(sc, pt);
	    goto READ_LIST;
	  }
      }

    READ_TOK:
      switch (sc->tok)
	{
	case TOKEN_RIGHT_PAREN:
	  /* sc->args can't be null here */
	  /* read-time macro expansion:
	   *   (define-macro (hi a) (format #t "hi...") `(+ ,a 1))
	   *   (define (ho b) (+ 1 (hi b)))
	   * here sc->value is: (ho b), (hi b), (+ 1 (hi b)), (define (ho b) (+ 1 (hi b)))
	   * but... first we can't tell for sure at this point that "hi" really is a macro
	   *   (letrec ((hi ... (hi...))) will be confused about the second hi,
	   *   or (call/cc (lambda (hi) (hi 1))) etc.
	   * second, figuring out that we're quoted is not easy -- we have to march all the
	   * way to the bottom of the stack looking for op_read_quote or op_read_vector
	   *    #(((hi)) 2) or '(((hi)))
	   * or op_read_list with args not equal (quote) or (macroexapand)
	   *    '(hi 3) or (macroexpand (hi 3) or (quote (hi 3))
	   * and those are only the problems I noticed!
	   *
	   * The hardest of these problems involve shadowing, so Rick asked for "define-expansion"
	   *   which is just like define-macro, but the programmer guarantees that the macro
	   *   name will not be shadowed. 
	   */
	  sc->value = safe_reverse_in_place(sc, sc->args);
	  if (is_symbol(car(sc->value)))
	    {
	      pair_line_number(sc->value) = port_line_number(sc->input_port) | (port_file_number(sc->input_port) << 20);
	      set_has_line_number(sc->value);	      /* sc->input_port above can't be nil(?) -- it falls back on stdin now */

	      if (is_expansion(car(sc->value)))
		{
		  int loc;
		  s7_pointer caller;
		  loc = s7_stack_top(sc) - 1;
		  caller = car(stack_args(sc->stack, loc));
		  if ((loc >= 3) &&
		      (stack_op(sc->stack, loc) != OP_READ_QUOTE) &&             /* '(hi 1) for example */
		      (caller != sc->QUOTE) &&          /* (quote (hi 1)) */
		      (caller != sc->MACROEXPAND) &&    /* (macroexpand (hi 1)) */
		      (caller != sc->DEFINE_EXPANSION)) /* (define-expansion ...) being reloaded */
		    {
		      push_stack(sc, OP_EXPANSION, sc->NIL, sc->NIL);
		      sc->code = slot_value(find_symbol(sc, car(sc->value)));
		      sc->args = list_1(sc, sc->value); 
		      goto APPLY;
		    }
		}
	      if (is_pair(cdr(sc->value)))
		{
		  set_ecdr(cdr(sc->value), sc->value);
		  set_overlay(cdr(sc->value));
		}
	    }
	  /* I think it's too soon to scan every list at this point for symbols and pairs */
	  break;

	case TOKEN_EOF:
	  /* can't happen, I believe */
	  return(missing_close_paren_error(sc));

	case TOKEN_ATOM:
	  sc->value = port_read_name(sc->input_port)(sc, sc->input_port);
	  goto READ_LIST;
	  break;

	case TOKEN_SHARP_CONST:
	  sc->value = port_read_sharp(sc->input_port)(sc, sc->input_port);
	  if (is_null(sc->value))
	    return(read_error(sc, "undefined # expression"));
	  if (sc->value == sc->NO_VALUE)
	    {
	      /* (set! *#readers* (cons (cons #\; (lambda (s) (read) (values))) *#readers*))
	       * (+ 1 #;(* 2 3) 4)
	       * so we need to get the next token, act on it without any assumptions about read list
	       */
	      sc->tok = token(sc);
	      goto READ_TOK;
	    }
	  goto READ_LIST;
	  break;

	case TOKEN_DOUBLE_QUOTE:
	  sc->value = read_string_constant(sc, sc->input_port);
	  if (sc->value == sc->F)                                /* can happen if input code ends in the middle of a string */
	    return(string_read_error(sc, "end of input encountered while in a string"));
	  if (sc->value == sc->T)
	    return(read_error(sc, "unknown backslash usage -- perhaps you meant two backslashes?"));
	  goto READ_LIST;
	  break;

	case TOKEN_DOT:
	  push_stack_no_code(sc, OP_READ_DOT, sc->args);
	  sc->tok = token(sc);
	  sc->value = read_expression(sc);
	  break;

	default:
	  /* by far the main case here is TOKEN_LEFT_PAREN, but it doesn't save anything to move it to this level */
	  push_stack_no_code(sc, OP_READ_LIST, sc->args);
	  sc->value = read_expression(sc);
	  /* check for op_read_list here and explicit pop_stack are slower */
	  break;
	}
      goto START;

      
    case OP_READ_DOT:
      if (token(sc) != TOKEN_RIGHT_PAREN)
	{
	  back_up_stack(sc);
	  read_error(sc, "stray dot?");            /* (+ 1 . 2 3) or (list . ) */
	}
      /* args = previously read stuff, value = thing just after the dot and before the ')':
       *   (list 1 2 . 3)
       *   value: 3, args: (2 1 list) 
       *   '(1 . 2)
       *   value: 2, args: (1)
       *
       * but we also get here in a lambda arg list:
       *   (lambda (a b . c) #f)
       *   value: c, args: (b a)
       *
       * so we have to leave any error checks until later, I guess
       *   -- in eval_args1, if we end with non-pair-not-nil then
       *      something is fishy
       */
      sc->value = reverse_in_place(sc, sc->value, sc->args);
      goto START;
      
      
    case OP_READ_QUOTE:
      /* can't check for sc->value = sc->NIL here because we want ''() to be different from '() */
      sc->value = list_2(sc, sc->QUOTE, sc->value);
      set_ecdr(cdr(sc->value), sc->value);
      set_overlay(cdr(sc->value));
      goto START;      
      
      
    case OP_READ_QUASIQUOTE:
      /* this was pushed when the backquote was seen, then eventually we popped back to it */
      sc->value = g_quasiquote_1(sc, sc->value);
      /* doing quasiquote at read time means there are minor inconsistencies in 
       *    various combinations or quote/' and quasiquote/`.  A quoted ` will expand
       *    but quoted quasiquote will not (` can't be redefined, but quasiquote can).
       *    see s7test.scm for examples.
       */
      goto START;
      
      
    case OP_READ_VECTOR:
      if (!is_proper_list(sc, sc->value))       /* #(1 . 2) */
	return(read_error(sc, "vector constant data is not a proper list"));
      if (sc->args == small_int(1))             /* sc->args was sc->w earlier from read_sharp */
	sc->value = g_vector(sc, sc->value);
      else sc->value = g_multivector(sc, s7_integer(sc->args), sc->value);
      goto START;

      
    case OP_READ_BYTEVECTOR:
      if (!is_proper_list(sc, sc->value))       /* #u8(1 . 2) */
	return(read_error(sc, "bytevector constant data is not a proper list"));
      sc->value = g_bytevector(sc, sc->value);
      goto START;

      
#if WITH_QUASIQUOTE_VECTOR
    case OP_READ_QUASIQUOTE_VECTOR:
      /* this works only if the quasiquoted list elements can be evaluated in the read-time environment.
       *
       *    `#(1 ,@(list 1 2) 4) -> (apply vector ({list} 1 ({apply_values} (list 1 2)) 4)) -> #(1 1 2 4)
       *
       * Originally, I used:
       *
       *   sc->value = list_3(sc, sc->Apply, sc->Vector, g_quasiquote_1(sc, sc->value));
       *   goto START;
       *
       * which means that #(...) makes a vector at read time, but `#(...) is just like (vector ...).
       *
       *   :(let ((f1 (lambda () (let ((x 32)) #(x 0))))
       *          (f2 (lambda () (let ((x 32)) `#(,x 0)))))
       *      (eq? (f1) (f1)))
       *   #t
       *   :(let ((f1 (lambda () (let ((x 32)) #(x 0))))
       *          (f2 (lambda () (let ((x 32)) `#(,x 0)))))
       *      (eq? (f2) (f2)))
       *   #f
       *
       * The tricky part in s7 is that we might have quasiquoted multidimensional vectors
       */
      if (sc->args == small_int(1))
	sc->code = list_3(sc, sc->Apply, sc->Vector, g_quasiquote_1(sc, sc->value)); /* qq result will be evaluated (might include {list} etc) */
      else sc->code = list_4(sc, sc->Apply, sc->Multivector, sc->args, g_quasiquote_1(sc, sc->value));
      goto EVAL; 
#endif

      
    case OP_READ_UNQUOTE:
      /* here if sc->value is a constant, the unquote is pointless (what about ,pi?) */
      if ((is_pair(sc->value)) ||
	  (is_symbol(sc->value)))
	sc->value = list_2(sc, sc->UNQUOTE, sc->value);
      goto START;
      
      
    case OP_READ_APPLY_VALUES:
      if (is_symbol(sc->value))
	{
	  s7_pointer lst;
	  lst = list_2(sc, sc->QQ_Apply_Values, sc->value);
	  set_unsafely_optimized(lst);
	  set_optimize_data(lst, HOP_C_S);
	  set_c_function(lst, sc->QQ_Apply_Values);
	  sc->value = list_2(sc, sc->UNQUOTE, lst);
	}
      else sc->value = list_2(sc, sc->UNQUOTE, list_2(sc, sc->QQ_Apply_Values, sc->value));
      goto START;
      
      
    default:
      fprintf(stderr, "unknown operator: %d, %s in %s\n", 
	      (int)(sc->op), 
	      ((int)(sc->op) < OP_MAX_DEFINED) ? op_names[(int)(sc->op)] : "?",
	      DISPLAY(sc->cur_code));
      return(sc->F);
    }
  return(sc->F);
}

#if WITH_GCC
#undef NEW_CELL
#define NEW_CELL(Sc, Obj) \
  do {						\
    if (Sc->free_heap_top <= Sc->free_heap_trigger) try_to_call_gc(Sc); \
    Obj = (*(--(Sc->free_heap_top))); \
    } while (0)
#endif



/* -------------------------------- multiprecision arithmetic -------------------------------- */

#if WITH_GMP
static mp_prec_t mpc_precision = DEFAULT_BIGNUM_PRECISION; /* global for libs */
static mp_prec_t mpc_set_default_precision(mp_prec_t prec) {mpc_precision = prec; return(prec);}

#define mpc_init(Z) mpc_init2(Z, mpc_precision)

static void mpc_init_set(mpc_ptr z, mpc_ptr y, mpc_rnd_t rnd)
{
  mpc_init(z);
  mpc_set(z, y, rnd);
}


mpfr_t *s7_big_real(s7_pointer x)    {return(&big_real(x));}
mpz_t  *s7_big_integer(s7_pointer x) {return(&big_integer(x));}
mpq_t  *s7_big_ratio(s7_pointer x)   {return(&big_ratio(x));}
mpc_t  *s7_big_complex(s7_pointer x) {return(&big_complex(x));}

static char *mpfr_to_string(mpfr_t val, int radix)
{
  char *str, *tmp, *str1;
  mp_exp_t expptr;
  int i, len, ep;

  if (mpfr_zero_p(val))
    return(copy_string("0.0"));

  if (mpfr_nan_p(val))
    return(copy_string("nan.0"));
  if (mpfr_inf_p(val))
    {
      if (mpfr_signbit(val) == 0)
	return(copy_string("inf.0"));
      return(copy_string("-inf.0"));
    }

  str1 = mpfr_get_str(NULL, &expptr, radix, 0, val, GMP_RNDN);
  /* 0 -> full precision, but it's too hard to make this look like C formatted output.
   *  
   *  :(format #f "~,3F" pi)
   *  "3.141592653589793238462643383279502884195E0" 
   *  :(format #f "~,3F" 1.1234567890123)  ; not a bignum
   *  "1.123"
   *  :(format #f "~,3F" 1.12345678901234) ; a bignum
   *  "1.123456789012339999999999999999999999999E0"
   * 
   * but we don't know the exponent or the string length until after we call mpfr_get_str.
   */

  str = str1;
  ep = (int)expptr;
  len = safe_strlen(str);

  if (radix <= 10)
    {
      /* remove trailing 0's */
      for (i = len - 1; i > 3; i--)
	if (str[i] != '0')
	  break;
      if (i < len - 1)
	str[i + 1] = '\0';

      len += 64;
      tmp = (char *)malloc(len * sizeof(char));
  
      if (str[0] == '-')
	snprintf(tmp, len, "-%c.%sE%d", str[1], (char *)(str + 2), ep - 1);
      else snprintf(tmp, len, "%c.%sE%d", str[0], (char *)(str + 1), ep - 1);
    }
  else
    {
      /* if radix > 10, we should not be using the 'E' business -- need to move the decimal point */
      /* (number->string 1234.5678909876543212345 16) "4d2.91614dc3ab1f80e55a563311b8f308"
       * (number->string -1234.5678909876543212345 16) "-4d2.91614dc3ab1f80e55a563311b8f308"
       * (number->string 1234.5678909876543212345e8 16) "1cbe991a6a.c3f35c11868cb7e3fb75536"
       * (number->string 1234.5678909876543212345e-8 16) "0.0000cf204983a27e1eff701c562a870641e50"
       * (number->string 123456789098765432.12345e-8 16) "499602d2.fcd6e9e1748ba5adccc12c5a8"
       * (number->string 123456789098765432.1e20 16) "949b0f70beeac8895e74b18b9680000.00"
       */
      int loc = 0;
      tmp = (char *)calloc(len + ep + 64, sizeof(char));
      if (str[0] == '-')
	tmp[loc++] = (*str++);
      if (ep < 0)
	{
	  ep = -ep;
	  tmp[loc++] = '0';
	  tmp[loc++] = '.';
	  for (i = 0; i < ep; i++)
	    tmp[loc++] = '0';
	}
      else
	{
	  for (i = 0; i < ep; i++)
	    tmp[loc++] = (*str++);
	  tmp[loc++] = '.';
	}
      while (*str) tmp[loc++] = (*str++);
    }
  mpfr_free_str(str1);
  return(tmp);
}


static char *mpc_to_string(mpc_t val, int radix, use_write_t use_write)
{
  char *rl, *im, *tmp;
  int len;
  mpfr_t a, b;

  mpfr_init(a);
  mpc_real(a, val, GMP_RNDN);
  rl = mpfr_to_string(a, radix);
  mpfr_init(b);
  mpc_imag(b, val, GMP_RNDN);
  im = mpfr_to_string(b, radix);

  len = safe_strlen(rl) + safe_strlen(im) + 128;
  tmp = (char *)malloc(len * sizeof(char));
  
  if (use_write == USE_READABLE_WRITE)
    snprintf(tmp, len, "(make-rectangular %s %s)", rl, im);
  else snprintf(tmp, len, "%s%s%si", rl, (im[0] == '-') ? "" : "+", im); 

  free(rl);
  free(im);
  return(tmp);
}


static char *big_number_to_string_with_radix(s7_pointer p, int radix, int width, int *nlen, use_write_t use_write)
{
  char *str = NULL;

  switch (type(p))
    {
    case T_BIG_INTEGER:
      str = mpz_get_str(NULL, radix, big_integer(p));
      break;

    case T_BIG_RATIO:
      str = mpq_get_str(NULL, radix, big_ratio(p));
      break;

    case T_BIG_REAL:
      str = mpfr_to_string(big_real(p), radix);
      break;

    default:
      str = mpc_to_string(big_complex(p), radix, use_write);
      break;
    }

  if (width > 0)
    {
      int len;
      len = safe_strlen(str);
      if (width > len)
	{
	  int spaces;
	  str = (char *)realloc(str, (width + 1) * sizeof(char));
	  spaces = width - len;
	  str[width] = '\0';
	  memmove((void *)(str + spaces), (void *)str, len);    
	  memset((void *)str, (int)' ', spaces);
	  (*nlen) = width;
	}
      else (*nlen) = len;
    }
  else (*nlen) = safe_strlen(str);
  return(str);
}


static bool s7_is_one_or_big_one(s7_pointer p)
{
  bool result = false;

  if (!is_big_number(p))
    return(s7_is_one(p));

  if (type(p) == T_BIG_INTEGER)
    {
      mpz_t n;
      mpz_init_set_si(n, 1);
      result = (mpz_cmp(n, big_integer(p)) == 0);
      mpz_clear(n);
    }
  else
    {
      if (type(p) == T_BIG_REAL)
	{
	  mpfr_t n;
	  mpfr_init_set_d(n, 1.0, GMP_RNDN);
	  result = (mpfr_cmp(n, big_real(p)) == 0);
	  mpfr_clear(n);
	}
    }
  return(result);
}


static s7_pointer string_to_big_integer(s7_scheme *sc, const char *str, int radix)
{
  s7_pointer x;

  NEW_CELL(sc, x);
  set_type(x, T_BIG_INTEGER);
  add_bigint(sc, x);
  mpz_init_set_str(big_integer(x), (str[0] == '+') ? (const char *)(str + 1) : str, radix);

  return(x);
}


static s7_pointer mpz_to_big_integer(s7_scheme *sc, mpz_t val)
{
  s7_pointer x;

  NEW_CELL(sc, x);
  set_type(x, T_BIG_INTEGER);
  add_bigint(sc, x);
  mpz_init_set(big_integer(x), val);

  return(x);
}


s7_pointer s7_make_big_integer(s7_scheme *sc, mpz_t *val)
{
  return(mpz_to_big_integer(sc, *val));
}


static s7_pointer string_to_big_ratio(s7_scheme *sc, const char *str, int radix)
{
  s7_pointer x;
  mpq_t n;

  mpq_init(n);
  mpq_set_str(n, str, radix);
  mpq_canonicalize(n);

  if (mpz_cmp_ui(mpq_denref(n), 1) == 0)
    x = mpz_to_big_integer(sc, mpq_numref(n));
  else
    {
      NEW_CELL(sc, x);
      set_type(x, T_BIG_RATIO);
      add_bigratio(sc, x);
      mpq_init(big_ratio(x));
      mpq_set_num(big_ratio(x), mpq_numref(n));
      mpq_set_den(big_ratio(x), mpq_denref(n));
    }

  mpq_clear(n);
  return(x);
}


static s7_pointer mpq_to_big_ratio(s7_scheme *sc, mpq_t val)
{
  s7_pointer x;

  NEW_CELL(sc, x);
  set_type(x, T_BIG_RATIO);
  add_bigratio(sc, x);
  mpq_init(big_ratio(x));
  mpq_set_num(big_ratio(x), mpq_numref(val));
  mpq_set_den(big_ratio(x), mpq_denref(val));
  
  return(x);
}


s7_pointer s7_make_big_ratio(s7_scheme *sc, mpq_t *val)
{
  return(mpq_to_big_ratio(sc, *val));
}


static s7_pointer mpz_to_big_ratio(s7_scheme *sc, mpz_t val)
{
  s7_pointer x;

  NEW_CELL(sc, x);
  set_type(x, T_BIG_RATIO);
  add_bigratio(sc, x);
  mpq_init(big_ratio(x));
  mpq_set_num(big_ratio(x), val);
  
  return(x);
}


static s7_pointer make_big_integer_or_ratio(s7_scheme *sc, s7_pointer z)
{
  if (mpz_cmp_ui(mpq_denref(big_ratio(z)), 1) == 0)
    return(mpz_to_big_integer(sc, mpq_numref(big_ratio(z))));
  return(z);
}


static s7_pointer string_to_big_real(s7_scheme *sc, const char *str, int radix)
{
  s7_pointer x;

  NEW_CELL(sc, x);
  set_type(x, T_BIG_REAL);
  add_bigreal(sc, x);
  mpfr_init_set_str(big_real(x), str, radix, GMP_RNDN);

  return(x);
}

static void mpz_init_set_s7_Int(mpz_t n, s7_Int uval);

static s7_pointer s7_number_to_big_real(s7_scheme *sc, s7_pointer p)
{
  s7_pointer x;

  NEW_CELL(sc, x);
  set_type(x, T_BIG_REAL);
  add_bigreal(sc, x);

  switch (type(p))
    {
    case T_INTEGER: 
      {
	s7_Int val;
	val = s7_integer(p);
	if ((val <= S7_LONG_MAX) && (val >= S7_LONG_MIN))
	  mpfr_init_set_si(big_real(x), (int)val, GMP_RNDN);
	else mpfr_init_set_ld(big_real(x), (long double)val, GMP_RNDN);
      }
      break;

    case T_RATIO:
      /* here we can't use fraction(number(p)) even though that uses long double division because
       *   there are lots of long long int ratios that will still look the same.
       *   We have to do the actual bignum divide by hand.
       */
      {
	mpq_t rat;
	mpz_t n1, d1;

	mpz_init_set_s7_Int(n1, numerator(p));
	mpz_init_set_s7_Int(d1, denominator(p));
	mpq_init(rat);

	mpq_set_num(rat, n1);
	mpq_set_den(rat, d1);
	mpq_canonicalize(rat);
	mpfr_init_set_q(big_real(x), rat, GMP_RNDN);

	mpz_clear(n1);
	mpz_clear(d1);
	mpq_clear(rat);
      }
      break;

    default:
      mpfr_init_set_d(big_real(x), s7_real(p), GMP_RNDN);
      break;
    }
  return(x);
}


static s7_pointer mpz_to_big_real(s7_scheme *sc, mpz_t val)
{
  s7_pointer x;

  NEW_CELL(sc, x);
  set_type(x, T_BIG_REAL);
  add_bigreal(sc, x);
  mpfr_init_set_z(big_real(x), val, GMP_RNDN);

  return(x);
}


static s7_pointer mpq_to_big_real(s7_scheme *sc, mpq_t val)
{
  s7_pointer x;

  NEW_CELL(sc, x);
  set_type(x, T_BIG_REAL);
  add_bigreal(sc, x);
  mpfr_init_set_q(big_real(x), val, GMP_RNDN);

  return(x);
}


static s7_pointer mpfr_to_big_real(s7_scheme *sc, mpfr_t val)
{
  s7_pointer x;

  NEW_CELL(sc, x);
  set_type(x, T_BIG_REAL);
  add_bigreal(sc, x);
  mpfr_init_set(big_real(x), val, GMP_RNDN);

  return(x);
}


s7_pointer s7_make_big_real(s7_scheme *sc, mpfr_t *val)
{
  return(mpfr_to_big_real(sc, *val));
}


static s7_pointer big_pi(s7_scheme *sc)
{
  s7_pointer x;

  NEW_CELL(sc, x);
  set_type(x, T_BIG_REAL);
  add_bigreal(sc, x);
  mpfr_init(big_real(x));
  mpfr_const_pi(big_real(x), GMP_RNDN);
  
  return(x);
}


static s7_pointer s7_number_to_big_complex(s7_scheme *sc, s7_pointer p)
{
  s7_pointer x;

  NEW_CELL(sc, x);
  set_type(x, T_BIG_COMPLEX);
  add_bignumber(sc, x);
  mpc_init(big_complex(x));

  switch (type(p))
    {
    case T_INTEGER:
      {
	s7_Int val;
	val = s7_integer(p);
	if ((val <= S7_LONG_MAX) && (val >= S7_LONG_MIN))
	  mpc_set_si(big_complex(x), (long int)val, MPC_RNDNN);
	else mpc_set_d(big_complex(x), (double)val, MPC_RNDNN);
      }
      break;

    case T_RATIO:
      /* can't use fraction here */
      {
	mpfr_t temp;
	mpq_t rat;
	mpz_t n1, d1;

	mpz_init_set_s7_Int(n1, numerator(p));
	mpz_init_set_s7_Int(d1, denominator(p));
	mpq_init(rat);

	mpq_set_num(rat, n1);
	mpq_set_den(rat, d1);
	mpq_canonicalize(rat);
	mpfr_init_set_q(temp, rat, GMP_RNDN);
	mpc_set_fr(big_complex(x), temp, MPC_RNDNN);

	mpz_clear(n1);
	mpz_clear(d1);
	mpq_clear(rat);
	mpfr_clear(temp);
      }
      break;

    case T_REAL:
      mpc_set_d(big_complex(x), s7_real(p), MPC_RNDNN);
      break;

    default:
      mpc_set_d_d(big_complex(x), real_part(p), imag_part(p), MPC_RNDNN);
      break;
    }
  return(x);
}


static s7_pointer make_big_real_or_complex(s7_scheme *sc, s7_pointer z)
{
  double ipart;

  ipart = mpfr_get_d(mpc_imagref(big_complex(z)), GMP_RNDN);
  /* not mpfr_cmp_ui to 0 here because that misleads us when imag_part is NaN or inf */
  if (ipart == 0.0)
    return(mpfr_to_big_real(sc, mpc_realref(big_complex(z))));
  return(z);
}


static s7_pointer mpz_to_big_complex(s7_scheme *sc, mpz_t val)
{
  mpfr_t temp;
  s7_pointer x;

  NEW_CELL(sc, x);
  set_type(x, T_BIG_COMPLEX);
  add_bignumber(sc, x);
  mpc_init(big_complex(x));
  mpfr_init_set_z(temp, val, GMP_RNDN);
  mpc_set_fr(big_complex(x), temp, MPC_RNDNN);

  mpfr_clear(temp);
  return(x);
}


static s7_pointer mpq_to_big_complex(s7_scheme *sc, mpq_t val)
{
  mpfr_t temp;
  s7_pointer x;

  NEW_CELL(sc, x);
  set_type(x, T_BIG_COMPLEX);
  add_bignumber(sc, x);
  mpc_init(big_complex(x));
  mpfr_init_set_q(temp, val, GMP_RNDN);
  mpc_set_fr(big_complex(x), temp, MPC_RNDNN);

  mpfr_clear(temp);
  return(x);
}


static s7_pointer mpfr_to_big_complex(s7_scheme *sc, mpfr_t val)
{
  s7_pointer x;

  NEW_CELL(sc, x);
  set_type(x, T_BIG_COMPLEX);
  add_bignumber(sc, x);
  mpc_init(big_complex(x));
  mpc_set_fr(big_complex(x), val, MPC_RNDNN);

  return(x);
}


static s7_pointer mpc_to_big_complex(s7_scheme *sc, mpc_t val)
{
  s7_pointer x;

  NEW_CELL(sc, x);
  set_type(x, T_BIG_COMPLEX);
  add_bignumber(sc, x);
  mpc_init(big_complex(x));
  mpc_set(big_complex(x), val, MPC_RNDNN);

  return(x);
}


s7_pointer s7_make_big_complex(s7_scheme *sc, mpc_t *val)
{
  return(mpc_to_big_complex(sc, *val));
}


static s7_pointer make_big_complex(s7_scheme *sc, mpfr_t rl, mpfr_t im)
{
  /* there is no mpc_get_str equivalent, so we need to split up str,
   *   use make_big_real to get the 2 halves, then mpc_init, then
   *   mpc_set_fr_fr.
   */
  s7_pointer x;

  NEW_CELL(sc, x);
  set_type(x, T_BIG_COMPLEX);
  add_bignumber(sc, x);
  mpc_init(big_complex(x));
  mpc_set_fr_fr(big_complex(x), rl ,im, MPC_RNDNN);

  return(x);
}


/* gmp.h mpz_init_set_si the "si" part is "signed long int", so in the 64-bit case,
 *   s7_Int already fits.  I guess we can catch this (since no configure script)
 *   by noticing that sizeof(s7_Int) == sizeof(long int).
 */

static void mpz_init_set_s7_Int(mpz_t n, s7_Int uval)
{
  if (sizeof(s7_Int) == sizeof(long int))
    mpz_init_set_si(n, uval);
  else
    {
      if ((uval <= S7_LONG_MAX) && (uval >= S7_LONG_MIN))
	mpz_init_set_si(n, (int)uval);
      else
	{ 
	  /* long long int to gmp mpz_t */
	  bool need_sign;
	  long long int val;
	  val = (long long int)uval;
	  /* handle one special case (sigh) */
	  if (val == S7_LLONG_MIN)
	    mpz_init_set_str(n, "-9223372036854775808", 10);
	  else
	    {
	      need_sign = (val < 0);
	      if (need_sign) val = -val;
	      mpz_init_set_si(n, val >> 32);
	      mpz_mul_2exp(n, n, 32);
	      mpz_add_ui(n, n, (unsigned int)(val & 0xffffffff));
	      if (need_sign) mpz_neg(n, n);
	    }
	}
    }
}


static s7_pointer s7_Int_to_big_integer(s7_scheme *sc, s7_Int val)
{
  s7_pointer x;

  NEW_CELL(sc, x);
  set_type(x, T_BIG_INTEGER);
  add_bigint(sc, x);
  mpz_init_set_s7_Int(big_integer(x), val);

  return(x);
}


static s7_Int big_integer_to_s7_Int(mpz_t n)
{
  long long int high, low;
  mpz_t x;
  bool need_sign = false;

  if (sizeof(s7_Int) == sizeof(long int))
    return(mpz_get_si(n));

  if (mpz_fits_sint_p(n))
    return((s7_Int)mpz_get_si(n));
  /* special case as always is most-negative-fixnum */

  mpz_init_set(x, n);
  if (mpz_cmp_ui(x, 0) < 0)
    {
      need_sign = true;
      mpz_neg(x, x);
    }
  low = mpz_get_ui(x);
  if (low == S7_LLONG_MIN)
    return(S7_LLONG_MIN);

  mpz_fdiv_q_2exp(x, x, 32);
  high = mpz_get_ui(x);
  mpz_clear(x);
  if (need_sign)
    return(-(low + (high << 32)));
  return(low + (high << 32));
}

/* (expt 1/2 9223372036854775807) */


static mpq_t *s7_Ints_to_mpq(s7_Int num, s7_Int den)
{
  /* den here always comes from denominator(x) so it is not negative */
  mpq_t *n;
  n = (mpq_t *)malloc(sizeof(mpq_t));
  mpq_init(*n);
  if ((num <= S7_LONG_MAX) && (num >= S7_LONG_MIN) && (den <= S7_LONG_MAX))
    mpq_set_si(*n, (long int)num, (long int)den);
  else
    {
      mpz_t n1, d1;
      mpz_init_set_s7_Int(n1, num);
      mpz_init_set_s7_Int(d1, den);
      mpq_set_num(*n, n1);
      mpq_set_den(*n, d1);
      mpq_canonicalize(*n);
      mpz_clear(n1);
      mpz_clear(d1);
    }
  return(n);
}


static mpfr_t *s7_Double_to_mpfr(s7_Double val)
{
  mpfr_t *n;
  n = (mpfr_t *)malloc(sizeof(mpfr_t));
  mpfr_init_set_d(*n, val, GMP_RNDN);
  return(n);
}


static mpc_t *s7_Doubles_to_mpc(s7_Double rl, s7_Double im)
{
  mpc_t *n;
  n = (mpc_t *)malloc(sizeof(mpc_t));
  mpc_init(*n);
  mpc_set_d_d(*n, rl, im, MPC_RNDNN);
  return(n);
}


static s7_pointer s7_ratio_to_big_ratio(s7_scheme *sc, s7_Int num, s7_Int den)
{
  /* den here always comes from denominator(x) or some positive constant so it is not negative */
  s7_pointer x;

  NEW_CELL(sc, x);
  set_type(x, T_BIG_RATIO);
  add_bigratio(sc, x);
  mpq_init(big_ratio(x));

  if ((num <= S7_LONG_MAX) && (num >= S7_LONG_MIN) && (den <= S7_LONG_MAX))
    mpq_set_si(big_ratio(x), (long int)num, (long int)den);
  else
    {
      mpz_t n1, d1;
      mpz_init_set_s7_Int(n1, num);
      mpz_init_set_s7_Int(d1, den);
      mpq_set_num(big_ratio(x), n1);
      mpq_set_den(big_ratio(x), d1);
      mpq_canonicalize(big_ratio(x));
      mpz_clear(n1);
      mpz_clear(d1);
    }
  return(x);
}


static bool big_numbers_are_eqv(s7_pointer a, s7_pointer b)
{
  bool result;
  /* either or both can be big here, but not neither */

  if (s7_is_integer(a))
    {
      mpz_t a1, b1;
      if (!(s7_is_integer(b))) return(false);

      if ((is_big_number(a)) && (is_big_number(b)))
	return(mpz_cmp(big_integer(a), big_integer(b)) == 0);

      if (is_big_number(a))
	mpz_init_set(a1, big_integer(a));
      else mpz_init_set_s7_Int(a1, s7_integer(a));

      if (is_big_number(b))
	mpz_init_set(b1, big_integer(b));
      else mpz_init_set_s7_Int(b1, s7_integer(b));
      result = (mpz_cmp(a1, b1) == 0);

      mpz_clear(a1);
      mpz_clear(b1);
      return(result);
    }

  if (s7_is_ratio(a))
    {
      mpq_t *a1, *b1;
      if (!s7_is_ratio(b)) return(false);

      if ((is_big_number(a)) && (is_big_number(b)))
	return(mpq_cmp(big_ratio(a), big_ratio(b)) == 0);

      if (is_big_number(a))
	a1 = &big_ratio(a);
      else a1 = s7_Ints_to_mpq(numerator(a), denominator(a));
      if (is_big_number(b))
	b1 = &big_ratio(b);
      else b1 = s7_Ints_to_mpq(numerator(b), denominator(b));

      result = (mpq_cmp(*a1, *b1) == 0);

      if (!is_big_number(a))
	{
	  mpq_clear(*a1);
	  free(a1);
	}
      if (!is_big_number(b))
	{
	  mpq_clear(*b1);
	  free(b1);
	}
      return(result);
    }

  if (s7_is_real(a))
    {
      mpfr_t *a1, *b1;

      /* s7_is_real is not finicky enough here -- (eqv? 1.0 1) should return #f */
      if (is_big_number(b))
	{
	  if (type(b) != T_BIG_REAL)
	    return(false);
	}
      else
	{
	  if (type(b) != T_REAL)
	    return(false);
	}

      if ((is_big_number(a)) && (is_big_number(b)))
	return(mpfr_equal_p(big_real(a), big_real(b)));

      if (is_big_number(a))
	a1 = &big_real(a);
      else a1 = s7_Double_to_mpfr(s7_real(a));

      if (is_big_number(b))
	b1 = &big_real(b);
      else b1 = s7_Double_to_mpfr(s7_real(b));

      result = (mpfr_cmp(*a1, *b1) == 0);

      if (!is_big_number(a))
	{
	  mpfr_clear(*a1);
	  free(a1);
	}
      if (!is_big_number(b))
	{
	  mpfr_clear(*b1);
	  free(b1);
	}
      return(result);
    }

  if (s7_is_complex(a))
    {
      mpc_t *a1, *b1;
      /* s7_is_complex is not finicky enough here */

      if ((type(b) != T_BIG_COMPLEX) &&
	  (type(b) != T_COMPLEX))
	return(false);

      /* (eqv? (bignum "1+i") 1+1i) */
      if ((is_big_number(a)) && (is_big_number(b)))
	return(mpc_cmp(big_complex(a), big_complex(b)) == 0);

      if (is_big_number(a))
	a1 = &big_complex(a);
      else a1 = s7_Doubles_to_mpc(real_part(a), imag_part(a));

      if (is_big_number(b))
	b1 = &big_complex(b);
      else b1 = s7_Doubles_to_mpc(real_part(b), imag_part(b));

      result = (mpc_cmp(*a1, *b1) == 0);

      if (!is_big_number(a))
	{
	  mpc_clear(*a1);
	  free(a1);
	}
      if (!is_big_number(b))
	{
	  mpc_clear(*b1);
	  free(b1);
	}
      return(result);
    }
  return(false);
}


static s7_pointer string_to_either_integer(s7_scheme *sc, const char *str, int radix)
{
  s7_Int val;
  bool overflow = false;

  val = string_to_integer(str, radix, &overflow);
  if (!overflow)
    return(make_integer(sc, val));
  
  return(string_to_big_integer(sc, str, radix));
}


static s7_pointer string_to_either_ratio(s7_scheme *sc, const char *nstr, const char *dstr, int radix)
{
  s7_Int n, d;
  bool overflow = false;

  /* gmp segfaults if passed a bignum/0 so this needs to check first that
   *   the denominator is not 0 before letting gmp screw up.  Also, if the
   *   first character is '+', gmp returns 0!
   */
  d = string_to_integer(dstr, radix, &overflow);
  if (!overflow)
    {
      if (d == 0)
	return(real_NaN);

      n = string_to_integer(nstr, radix, &overflow);
      if (!overflow)
	return(s7_make_ratio(sc, n, d));
    }
  if (nstr[0] == '+')
    return(string_to_big_ratio(sc, (const char *)(nstr + 1), radix));
  return(string_to_big_ratio(sc, nstr, radix));
}


static s7_pointer string_to_either_real(s7_scheme *sc, const char *str, int radix)
{
  bool overflow = false;
  s7_Double val;

  val = string_to_double_with_radix((char *)str, radix, &overflow);
  if (!overflow)
    return(make_real(sc, val));

  return(string_to_big_real(sc, str, radix));
}


static s7_pointer string_to_either_complex_1(s7_scheme *sc, char *q, char *slash1, char *ex1, bool has_dec_point1, int radix, s7_Double *d_rl)
{
  bool overflow = false;
  /* there's a real problem here -- we don't want to promote s7_Double .1 to a bignum because
   *    its low order digits are garbage, causing (rationalize .1 0) to return 3602879701896397/36028797018963968
   *    no matter what the bignum-precision.  But we can't just fallback on gmp's reader because (for example)
   *    it reads 1/2+i or 1+0/0i as 1.0.  Also format gets screwed up.  And string->number signals an error
   *    where it should return #f.  I wonder what to do.
   */
  if ((has_dec_point1) ||
      (ex1))
    {
      (*d_rl) = string_to_double_with_radix(q, radix, &overflow);
      if (overflow)
	return(string_to_big_real(sc, q, radix));
    }
  else
    {
      if (slash1)
	{
	  s7_Int n, d;

	  /* q can include the slash and denominator */
	  n = string_to_integer(q, radix, &overflow);
	  if (overflow)
	    return(string_to_big_ratio(sc, q, radix));
	  else
	    {
	      d = string_to_integer(slash1, radix, &overflow);
	      if (!overflow)
		(*d_rl) = (s7_Double)n / (s7_Double)d;
	      else return(string_to_big_ratio(sc, q, radix));
	    }
	}
      else
	{
	  s7_Int val;

	  val = string_to_integer(q, radix, &overflow);
	  if (overflow)
	    return(string_to_big_integer(sc, q, radix));
	  (*d_rl) = (s7_Double)val;
	}
    }
  if ((*d_rl) == -0.0) (*d_rl) = 0.0;

  return(NULL);
}


static s7_pointer string_to_either_complex(s7_scheme *sc,
					   char *q, char *slash1, char *ex1, bool has_dec_point1, 
					   char *plus, char *slash2, char *ex2, bool has_dec_point2, 
					   int radix, int has_plus_or_minus)
{
  /* this can be just about anything involving 2 real/ratio/int portions, +/- in between and 'i' at the end */
  double d_rl = 0.0, d_im = 0.0;
  s7_pointer p_rl = NULL, p_im = NULL, result;
  mpfr_t m_rl, m_im;

  p_rl = string_to_either_complex_1(sc, q, slash1, ex1, has_dec_point1, radix, &d_rl);
  p_im = string_to_either_complex_1(sc, plus, slash2, ex2, has_dec_point2, radix, &d_im);
  
  if (d_im == 0.0)
    {
      /* 1.0+0.0000000000000000000000000000i */
      if ((!p_im) ||
	  (s7_is_zero(p_im)))
	{
	  if (!p_rl)
	    return(make_real(sc, d_rl));
	  return(p_rl);
	}
    }

  if ((!p_rl) && (!p_im))
    return(s7_make_complex(sc, d_rl, (has_plus_or_minus == -1) ? (-d_im) : d_im));

  if (p_rl)
    mpfr_init_set(m_rl, big_real(promote_number(sc, T_BIG_REAL, p_rl)), GMP_RNDN);
  else mpfr_init_set_d(m_rl, d_rl, GMP_RNDN);

  if (p_im)
    mpfr_init_set(m_im, big_real(promote_number(sc, T_BIG_REAL, p_im)), GMP_RNDN);
  else mpfr_init_set_d(m_im, d_im, GMP_RNDN);

  if (has_plus_or_minus == -1) 
    mpfr_neg(m_im, m_im, GMP_RNDN);

  result = make_big_complex(sc, m_rl, m_im);
  
  mpfr_clear(m_rl);
  mpfr_clear(m_im);
  return(result);
}


static int big_type_to_result_type(int cur_type, int next_type)
{
  if ((cur_type == T_BIG_COMPLEX) || 
      (cur_type == T_COMPLEX) ||
      (next_type == T_BIG_COMPLEX))
    return(T_BIG_COMPLEX);

  if ((cur_type == T_BIG_REAL) ||
      (cur_type == T_REAL) ||
      (next_type == T_BIG_REAL))
    return(T_BIG_REAL);

  if ((cur_type == T_BIG_RATIO) ||
      (cur_type == T_RATIO) ||
      (next_type == T_BIG_RATIO))
    return(T_BIG_RATIO);

  return(T_BIG_INTEGER);
}


static int normal_type_to_result_type(int cur_type, int next_type)
{
  if (cur_type > T_COMPLEX)
    next_type += 4;
  if (cur_type > next_type)
    return(cur_type);
  return(next_type);
}


static s7_pointer promote_number_1(s7_scheme *sc, int type, s7_pointer x, bool copy)
{
  /* x can be any number -- need to convert it to the current result type */

  switch (type)
    {
    case T_BIG_INTEGER:
      if (is_big_number(x)) 
	{
	  if (copy)
	    return(mpz_to_big_integer(sc, big_integer(x)));
	  return(x);                       /* can only be T_BIG_INTEGER here */
	}
      return(s7_Int_to_big_integer(sc, s7_integer(x))); /* can only be integer here */
      break;

    case T_BIG_RATIO:
      if (is_big_number(x))
	{
	  if (type(x) == T_BIG_RATIO) 
	    {
	      if (copy)
		return(mpq_to_big_ratio(sc, big_ratio(x)));
	      return(x);
	    }
	  return(mpz_to_big_ratio(sc, big_integer(x)));
	}
      if (type(x) == T_INTEGER)
	return(s7_ratio_to_big_ratio(sc, integer(x), 1));
      return(s7_ratio_to_big_ratio(sc, numerator(x), denominator(x)));
      break;

    case T_BIG_REAL:
      if (is_big_number(x))
	{
	  if (type(x) == T_BIG_REAL) 
	    {
	      if (copy)
		return(mpfr_to_big_real(sc, big_real(x)));
	      return(x);
	    }
	  if (type(x) == T_BIG_RATIO) 
	    return(mpq_to_big_real(sc, big_ratio(x)));
	  return(mpz_to_big_real(sc, big_integer(x)));
	}
      return(s7_number_to_big_real(sc, x));
      break;

    default:
      if (is_big_number(x))
	{
	  if (type(x) == T_BIG_COMPLEX) 
	    {
	      if (copy)
		return(mpc_to_big_complex(sc, big_complex(x)));
	      return(x);
	    }
	  if (type(x) == T_BIG_REAL) 
	    return(mpfr_to_big_complex(sc, big_real(x)));
	  if (type(x) == T_BIG_RATIO) 
	    return(mpq_to_big_complex(sc, big_ratio(x)));
	  return(mpz_to_big_complex(sc, big_integer(x)));
	}
      return(s7_number_to_big_complex(sc, x));
      break;
    }
  return(sc->NIL);
}


static s7_pointer promote_number(s7_scheme *sc, int type, s7_pointer x)
{
  return(promote_number_1(sc, type, x, false));
}


static s7_pointer to_big(s7_scheme *sc, s7_pointer x)
{
  if (is_big_number(x))
    return(x);
  switch (type(x))
    {
    case T_INTEGER:
      return(s7_Int_to_big_integer(sc, integer(x)));
      
    case T_RATIO:
      return(s7_ratio_to_big_ratio(sc, numerator(x), denominator(x)));

    case T_REAL:
      return(s7_number_to_big_real(sc, x));

    default:
      return(s7_number_to_big_complex(sc, x));
    }
}


static s7_pointer copy_and_promote_number(s7_scheme *sc, int type, s7_pointer x)
{
  return(promote_number_1(sc, type, x, true));
}


void s7_vector_fill(s7_scheme *sc, s7_pointer vec, s7_pointer obj) 
{
  /* if the same bignum object is assigned to each element, different vector elements
   *    are actually the same -- we need to make a copy of obj for each one
   */
  if ((type(vec) == T_VECTOR) && (is_big_number(obj)))
    {
      int gc_loc;
      s7_Int i, len;
      s7_pointer *tp;

      len = vector_length(vec);
      tp = (s7_pointer *)(vector_elements(vec));

      /* we'll be calling new_cell below, hence the GC, so make sure the elements are markable,
       *   and the vector itself is GC protected (we can be called within make-vector).
       */
      gc_loc = s7_gc_protect(sc, vec);
      vector_fill(sc, vec, sc->NIL); 

      switch (type(obj))
	{
	case T_BIG_INTEGER:
	  for (i = 0; i < len; i++) 
	    tp[i] = mpz_to_big_integer(sc, big_integer(obj));
	  break;

	case T_BIG_RATIO:
	  for (i = 0; i < len; i++) 
	    tp[i] = mpq_to_big_ratio(sc, big_ratio(obj));
	  break;

	case T_BIG_REAL:
	  for (i = 0; i < len; i++) 
	    tp[i] = mpfr_to_big_real(sc, big_real(obj));
	  break;

	default:
	  for (i = 0; i < len; i++) 
	    tp[i] = mpc_to_big_complex(sc, big_complex(obj));
	  break;
	}

      s7_gc_unprotect_at(sc, gc_loc);
    }
  else vector_fill(sc, vec, obj);
}


static s7_pointer g_bignum(s7_scheme *sc, s7_pointer args)
{
  #define H_bignum "(bignum val (radix 10)) returns a multiprecision version of the string 'val'"
  s7_pointer p;

  p = g_string_to_number_1(sc, args, sc->BIGNUM); 
  if (is_false(sc, p))                                       /* (bignum "1/3.0") */
    s7_error(sc, make_symbol(sc, "bignum-error"),
	     list_2(sc, make_string_wrapper(sc, "bignum argument does not represent a number: ~S"), car(args)));

  switch (type(p))
    {
    case T_INTEGER:  
      return(promote_number(sc, T_BIG_INTEGER, p));

    case T_RATIO:
      return(promote_number(sc, T_BIG_RATIO, p));

      /* we can't use promote_number here because it propogates C-double inaccuracies
       *    (rationalize (bignum "0.1") 0) should return 1/10 not 3602879701896397/36028797018963968
       */
    case T_REAL:
      if (is_NaN(real(p))) return(p);
      return(string_to_big_real(sc, string_value(car(args)), (is_pair(cdr(args))) ? s7_integer(cadr(args)) : 10));

    case T_COMPLEX:
      return(promote_number(sc, T_BIG_COMPLEX, p));

    default:
      return(p);
    }
}


bool s7_is_bignum(s7_pointer obj)
{
  return(is_big_number(obj));
}


static s7_pointer g_is_bignum(s7_scheme *sc, s7_pointer args)
{
  #define H_is_bignum "(bignum? obj) returns #t if obj is a multiprecision number."
  return(s7_make_boolean(sc, is_big_number(car(args))));
}

#define get_result_type(Sc, Type, P) \
  ((is_number(P)) ? normal_type_to_result_type(Type, type(p)) : ((is_big_number(P)) ? big_type_to_result_type(Type, type(p)) : result_type_via_method(Sc, Type, P)))

static int result_type_via_method(s7_scheme *sc, int result_type, s7_pointer p)
{
  s7_pointer f;
  f = find_method(sc, find_let(sc, p), sc->IS_INTEGER);
  if ((f != sc->UNDEFINED) &&
      (is_true(sc, s7_apply_function(sc, f, cons(sc, p, sc->NIL)))))
    return(big_type_to_result_type(result_type, T_BIG_INTEGER));
  
  f = find_method(sc, find_let(sc, p), sc->IS_RATIONAL);
  if ((f != sc->UNDEFINED) &&
      (is_true(sc, s7_apply_function(sc, f, cons(sc, p, sc->NIL)))))
    return(big_type_to_result_type(result_type, T_BIG_RATIO));
  
  f = find_method(sc, find_let(sc, p), sc->IS_REAL);
  if ((f != sc->UNDEFINED) &&
      (is_true(sc, s7_apply_function(sc, f, cons(sc, p, sc->NIL)))))
    return(big_type_to_result_type(result_type, T_BIG_REAL));
  
  /* might be a number, but not complex (quaternion) */
  f = find_method(sc, find_let(sc, p), sc->IS_COMPLEX);
  if ((f != sc->UNDEFINED) &&
      (is_true(sc, s7_apply_function(sc, f, cons(sc, p, sc->NIL)))))
    return(big_type_to_result_type(result_type, T_BIG_COMPLEX));

  return(-1);
}
      

static s7_pointer big_add(s7_scheme *sc, s7_pointer args)
{
  int result_type = T_INTEGER;
  s7_pointer x, result;

  if (is_null(args))
    return(small_int(0));

  if ((is_null(cdr(args))) && (s7_is_number(car(args))))
    return(car(args));

  for (x = args; is_not_null(x); x = cdr(x)) 
    {
      s7_pointer p;
      p = car(x);
      result_type = get_result_type(sc, result_type, p);
      if (result_type < 0)
	return(g_add(sc, args));
    }

  if (result_type < T_BIG_INTEGER)
    return(g_add(sc, args));

  if (!s7_is_number(car(args)))
    check_method(sc, car(args), sc->ADD, args);

  result = copy_and_promote_number(sc, result_type, car(args));

  for (x = cdr(args); is_not_null(x); x = cdr(x)) 
    {
      s7_pointer arg;
      if (!s7_is_number(car(x)))
	check_method(sc, car(x), sc->ADD, cons(sc, result, x));

      arg = promote_number(sc, result_type, car(x));

      switch (result_type)
	{
	case T_BIG_INTEGER:
	  mpz_add(big_integer(result), big_integer(result), big_integer(arg));
	  break;
	  
	case T_BIG_RATIO:
	  mpq_add(big_ratio(result), big_ratio(result), big_ratio(arg));
	  break;
	  
	case T_BIG_REAL:
	  mpfr_add(big_real(result), big_real(result), big_real(arg), GMP_RNDN);
	  break;
	  
	case T_BIG_COMPLEX:
	  mpc_add(big_complex(result), big_complex(result), big_complex(arg), MPC_RNDNN);
	  break;
	}
    }
  
  switch (result_type)
      {
      case T_BIG_RATIO:
	return(make_big_integer_or_ratio(sc, result));

      case T_BIG_COMPLEX:
	return(make_big_real_or_complex(sc, result));
      }
  return(result);
}


static s7_pointer big_negate(s7_scheme *sc, s7_pointer args)
{
  /* assume cdr(args) is nil and we're called from subtract, so check for big num else call g_subtract */
  s7_pointer p, x;

  p = car(args);
  switch (type(p))
    {
    case T_BIG_INTEGER:
      x = mpz_to_big_integer(sc, big_integer(p));
      mpz_neg(big_integer(x), big_integer(x));
      return(x);

    case T_BIG_RATIO:
      x = mpq_to_big_ratio(sc, big_ratio(p));
      mpq_neg(big_ratio(x), big_ratio(x));
      return(x);

    case T_BIG_REAL:
      x = mpfr_to_big_real(sc, big_real(p));
      mpfr_neg(big_real(x), big_real(x), GMP_RNDN);
      return(x);

    case T_BIG_COMPLEX:
      x = mpc_to_big_complex(sc, big_complex(p));
      mpc_neg(big_complex(x), big_complex(x), MPC_RNDNN);
      return(x);

    case T_INTEGER: 
      if (integer(p) == S7_LLONG_MIN)
	{
	  x = s7_Int_to_big_integer(sc, integer(p));
	  mpz_neg(big_integer(x), big_integer(x));
	  return(x);
	}
      return(make_integer(sc, -integer(p)));
      
    case T_RATIO:
      return(s7_make_ratio(sc, -numerator(p), denominator(p)));
      
    case T_REAL:
      return(make_real(sc, -real(p)));
      
    default:
      return(s7_make_complex(sc, -real_part(p), -imag_part(p)));
    }
}


static s7_pointer big_subtract(s7_scheme *sc, s7_pointer args)
{
  int result_type = T_INTEGER;
  s7_pointer x, result;

  if (!s7_is_number(car(args)))
    {
      check_method(sc, car(args), sc->MINUS, args);
      return(wrong_type_argument_with_type(sc, sc->MINUS, small_int(1), car(args), A_NUMBER));
    }
  if (is_null(cdr(args))) 
    return(big_negate(sc, args));

  for (x = args; is_not_null(x); x = cdr(x)) 
    {
      s7_pointer p;
      p = car(x);
      result_type = get_result_type(sc, result_type, p);
      if (result_type < 0)
	return(g_subtract(sc, args));
    }

  if (result_type < T_BIG_INTEGER)
    return(g_subtract(sc, args));

  if (!s7_is_number(car(args)))
    check_method(sc, car(args), sc->MINUS, args);

  result = copy_and_promote_number(sc, result_type, car(args));

  for (x = cdr(args); is_not_null(x); x = cdr(x)) 
    {
      s7_pointer arg;
      if (!s7_is_number(car(x)))
	check_method(sc, car(x), sc->MINUS, cons(sc, result, x));

      arg = promote_number(sc, result_type, car(x));

      switch (result_type)
	{
	case T_BIG_INTEGER:
	  mpz_sub(big_integer(result), big_integer(result), big_integer(arg));
	  break;
	  
	case T_BIG_RATIO:
	  mpq_sub(big_ratio(result), big_ratio(result), big_ratio(arg));
	  break;
	  
	case T_BIG_REAL:
	  mpfr_sub(big_real(result), big_real(result), big_real(arg), GMP_RNDN);
	  break;
	  
	case T_BIG_COMPLEX:
	  mpc_sub(big_complex(result), big_complex(result), big_complex(arg), MPC_RNDNN);
	  break;
	}
    }

  switch (result_type)
    {
      case T_BIG_RATIO:
	return(make_big_integer_or_ratio(sc, result));

      case T_BIG_COMPLEX:
	return(make_big_real_or_complex(sc, result));
      }
  return(result);
}


static s7_pointer big_multiply(s7_scheme *sc, s7_pointer args)
{
  int result_type = T_INTEGER;
  s7_pointer x, result;

  if (is_null(args))
    return(small_int(1));

  if ((is_null(cdr(args))) && (s7_is_number(car(args))))
    return(car(args));

  for (x = args; is_not_null(x); x = cdr(x)) 
    {
      s7_pointer p;
      p = car(x);
      result_type = get_result_type(sc, result_type, p);
      if (result_type < 0)
	return(g_multiply(sc, args));
    }

  if (result_type < T_BIG_INTEGER)
    return(g_multiply(sc, args));

  if (!s7_is_number(car(args)))
    check_method(sc, car(args), sc->MULTIPLY, args);

  result = copy_and_promote_number(sc, result_type, car(args));

  for (x = cdr(args); is_not_null(x); x = cdr(x)) 
    {
      s7_pointer arg;
      if (!s7_is_number(car(x)))
	check_method(sc, car(x), sc->MULTIPLY, cons(sc, result, x));

      arg = promote_number(sc, result_type, car(x));
      switch (result_type)
	{
	case T_BIG_INTEGER:
	  mpz_mul(big_integer(result), big_integer(result), big_integer(arg));
	  break;
	  
	case T_BIG_RATIO:
	  mpq_mul(big_ratio(result), big_ratio(result), big_ratio(arg));
	  break;

	case T_BIG_REAL:
	  mpfr_mul(big_real(result), big_real(result), big_real(arg), GMP_RNDN);
	  break;

	case T_BIG_COMPLEX:
	  mpc_mul(big_complex(result), big_complex(result), big_complex(arg), MPC_RNDNN);
	  break;
	}
    }

  switch (result_type)
    {
    case T_BIG_RATIO:
      return(make_big_integer_or_ratio(sc, result));

    case T_BIG_COMPLEX:
      return(make_big_real_or_complex(sc, result));
    }
  return(result);
}


static s7_pointer big_invert(s7_scheme *sc, s7_pointer args)
{
  /* assume cdr(args) is nil and we're called from divide, so check for big num else call g_divide */
  s7_pointer p, x;

  p = car(args);
  if (s7_is_zero(p))
    return(division_by_zero_error(sc, sc->DIVIDE, p));
  
  switch (type(p))
    {
    case T_INTEGER:
      if (integer(p) == S7_LLONG_MIN)
	{
	  mpz_t n1, d1;

	  NEW_CELL(sc, x);
	  set_type(x, T_BIG_RATIO);
	  add_bigratio(sc, x);

	  mpz_init_set_s7_Int(n1, 1);
	  mpz_init_set_s7_Int(d1, S7_LLONG_MIN);
	  mpq_set_num(big_ratio(x), n1);
	  mpq_set_den(big_ratio(x), d1);
	  mpq_canonicalize(big_ratio(x));
	  mpz_clear(n1);
	  mpz_clear(d1);

	  return(x);
	}
      return(s7_make_ratio(sc, 1, integer(p)));      /* a already checked, not 0 */
      
    case T_RATIO:
      return(s7_make_ratio(sc, denominator(p), numerator(p)));

    case T_REAL:
      return(make_real(sc, 1.0 / real(p)));

    case T_COMPLEX:
      {
	s7_Double r2, i2, den;
	r2 = real_part(p);
	i2 = imag_part(p);
	den = (r2 * r2 + i2 * i2);
	return(s7_make_complex(sc, r2 / den, -i2 / den));
      }

    case T_BIG_INTEGER:
      /* p might be 1 or -1 */
      {
	mpz_t n;

	mpz_init_set_si(n, 1);
	if (mpz_cmp(n, big_integer(p)) == 0)
	  {
	    mpz_clear(n);
	    return(small_int(1));
	  }
	mpz_set_si(n, -1);
	if (mpz_cmp(n, big_integer(p)) == 0)
	  {
	    mpz_clear(n);
	    return(s7_make_integer(sc, -1));
	  }

	NEW_CELL(sc, x);
	set_type(x, T_BIG_RATIO);
	add_bigratio(sc, x);
	mpq_init(big_ratio(x));

	mpz_set_ui(n, 1);
	mpq_set_num(big_ratio(x), n);
	mpz_clear(n);

	mpq_set_den(big_ratio(x), big_integer(p));
	mpq_canonicalize(big_ratio(x));
	return(x);
      }

    case T_BIG_RATIO:
      {
	mpz_t n;

	mpz_init_set_si(n, 1);
	if (mpz_cmp(n, mpq_numref(big_ratio(p))) == 0)
	  {
	    mpz_clear(n);
	    return(mpz_to_big_integer(sc, mpq_denref(big_ratio(p))));
	  }
	mpz_set_si(n, -1);
	if (mpz_cmp(n, mpq_numref(big_ratio(p))) == 0)
	  {
	    mpz_clear(n);
	    x = mpz_to_big_integer(sc, mpq_denref(big_ratio(p)));
	    mpz_neg(big_integer(x), big_integer(x));
	    return(x);
	  }
	mpz_clear(n);

	NEW_CELL(sc, x);
	set_type(x, T_BIG_RATIO);
	add_bigratio(sc, x);

	mpq_init(big_ratio(x));
	mpq_set_num(big_ratio(x), mpq_denref(big_ratio(p)));
	mpq_set_den(big_ratio(x), mpq_numref(big_ratio(p)));
	mpq_canonicalize(big_ratio(x));
	return(x);
      }

    case T_BIG_REAL:
      x = mpfr_to_big_real(sc, big_real(p));
      mpfr_ui_div(big_real(x), 1, big_real(x), GMP_RNDN);
      return(x);

    default:
      x = mpc_to_big_complex(sc, big_complex(p));
      mpc_ui_div(big_complex(x), 1, big_complex(x), MPC_RNDNN);
      return(x);
    }
}


static s7_pointer big_divide(s7_scheme *sc, s7_pointer args)
{
  int result_type = T_INTEGER;
  s7_pointer x, divisor, result;

  if (!s7_is_number(car(args)))
    {
      check_method(sc, car(args), sc->DIVIDE, args);
      return(wrong_type_argument_with_type(sc, sc->DIVIDE, small_int(1), car(args), A_NUMBER));
    }
  if (is_null(cdr(args))) 
    return(big_invert(sc, args));

  for (x = args; is_not_null(x); x = cdr(x)) 
    {
      s7_pointer p;
      p = car(x);
      /* if divisor is 0, gmp throws an exception and halts s7!
       *   I don't think we can trap gmp errors, and the abort is built into the library code.
       */
      result_type = get_result_type(sc, result_type, p);
      if (result_type < 0)
	return(g_divide(sc, args));

      if ((x != args) &&
	  (s7_is_zero(p)))
	return(division_by_zero_error(sc, sc->DIVIDE, args));
    }

  if (result_type < T_BIG_INTEGER)
    return(g_divide(sc, args));

  if (!s7_is_number(car(args)))
    check_method(sc, car(args), sc->DIVIDE, args);

  if (!s7_is_number(cadr(args)))
    check_method(sc, cadr(args), sc->DIVIDE, args);

  divisor = copy_and_promote_number(sc, result_type, cadr(args));

  for (x = cddr(args); is_not_null(x); x = cdr(x)) 
    {
      s7_pointer arg;
      if (!s7_is_number(car(x)))
	{
	  s7_pointer func;
	  if ((has_methods(car(x))) && ((func = find_method(sc, find_let(sc, car(x)), sc->MULTIPLY)) != sc->UNDEFINED))
	    {
	      divisor = s7_apply_function(sc, func, cons(sc, divisor, x));
	      break;
	    }
	}

      arg = promote_number(sc, result_type, car(x));
      switch (result_type)
	{
	case T_BIG_INTEGER:
	  mpz_mul(big_integer(divisor), big_integer(divisor), big_integer(arg));
	  break;
	  
	case T_BIG_RATIO:
	  mpq_mul(big_ratio(divisor), big_ratio(divisor), big_ratio(arg));
	  break;
	  
	case T_BIG_REAL:
	  mpfr_mul(big_real(divisor), big_real(divisor), big_real(arg), GMP_RNDN);
	  break;
	  
	case T_BIG_COMPLEX:
	  mpc_mul(big_complex(divisor), big_complex(divisor), big_complex(arg), MPC_RNDNN);
	  break;
	}
    }
    
  if (s7_is_zero(divisor))
    return(division_by_zero_error(sc, sc->DIVIDE, args));

  /* it's possible for the divisor to be the wrong type here (if complex multiply -> real for example */
  divisor = promote_number_1(sc, result_type, divisor, false);

  result = copy_and_promote_number(sc, result_type, car(args));

  switch (result_type)
    {
    case T_BIG_INTEGER:
      {
	NEW_CELL(sc, x);
	set_type(x, T_BIG_RATIO);
	add_bigratio(sc, x);

	mpq_init(big_ratio(x));
	mpq_set_num(big_ratio(x), big_integer(result));
	mpq_set_den(big_ratio(x), big_integer(divisor));
	mpq_canonicalize(big_ratio(x));

	if (mpz_cmp_ui(mpq_denref(big_ratio(x)), 1) == 0)
	  return(mpz_to_big_integer(sc, mpq_numref(big_ratio(x))));
	return(x);
      }
      
    case T_BIG_RATIO:
      mpq_div(big_ratio(result), big_ratio(result), big_ratio(divisor));
      return(make_big_integer_or_ratio(sc, result));
      break;
      
    case T_BIG_REAL:
      mpfr_div(big_real(result), big_real(result), big_real(divisor), GMP_RNDN);
      break;
      
    case T_BIG_COMPLEX:
      mpc_div(big_complex(result), big_complex(result), big_complex(divisor), MPC_RNDNN);
      return(make_big_real_or_complex(sc, result));
      break;
    }
  return(result);
}


static s7_pointer big_abs(s7_scheme *sc, s7_pointer args)
{
  #define H_abs "(abs x) returns the absolute value of the real number x"
  s7_pointer p, x;

  p = car(args);
  switch (type(p))
    {
    case T_INTEGER:
      if (integer(p) < 0)
	{
	  if (integer(p) == S7_LLONG_MIN)
	    {
	      x = s7_Int_to_big_integer(sc, integer(p));
	      mpz_neg(big_integer(x), big_integer(x));
	      return(x);
	    }
	  return(make_integer(sc, -integer(p)));
	}
      return(p);

    case T_RATIO:
      if (numerator(p) < 0)
	return(s7_make_ratio(sc, -numerator(p), denominator(p)));
      return(p);

    case T_REAL:
      if (real(p) < 0.0)
	return(make_real(sc, -real(p)));
      return(p);

    case T_BIG_INTEGER:
      x = mpz_to_big_integer(sc, big_integer(p));
      mpz_abs(big_integer(x), big_integer(x));
      return(x);

    case T_BIG_RATIO:
      x = mpq_to_big_ratio(sc, big_ratio(p));
      mpq_abs(big_ratio(x), big_ratio(x));
      return(x);

    case T_BIG_REAL:
      x = mpfr_to_big_real(sc, big_real(p));
      mpfr_abs(big_real(x), big_real(x), GMP_RNDN);
      return(x);

#if WITH_MAKE_COMPLEX
    case T_COMPLEX:
      return(make_real(sc, hypot(imag_part(p), real_part(p))));

    case T_BIG_COMPLEX:
      {
	mpfr_t n;
	mpfr_init(n);
	mpc_abs(n, big_complex(p), GMP_RNDN);
	p = mpfr_to_big_real(sc, n);
	mpfr_clear(n);
	return(p);
      }
#endif

    default:
      check_method(sc, p, sc->ABS, args);
      return(simple_wrong_type_argument(sc, sc->ABS, p, T_REAL));
    }
}


#if (!WITH_MAKE_COMPLEX)
static s7_pointer big_magnitude(s7_scheme *sc, s7_pointer args)
{
  #define H_magnitude "(magnitude z) returns the magnitude of z"
  s7_pointer p;

  p = car(args);
  if (!s7_is_number(p))
    {
      check_method(sc, p, sc->MAGNITUDE, args);
      return(simple_wrong_type_argument_with_type(sc, sc->MAGNITUDE, p, A_NUMBER));
    }

  if (type(p) == T_BIG_COMPLEX)
    {
      mpfr_t n;
      mpfr_init(n);
      mpc_abs(n, big_complex(p), GMP_RNDN);
      p = mpfr_to_big_real(sc, n);
      mpfr_clear(n);
      return(p);
    }

  if (type(p) == T_COMPLEX)
    return(make_real(sc, hypot(imag_part(p), real_part(p))));

  return(big_abs(sc, args));
}
#endif

static s7_pointer big_angle(s7_scheme *sc, s7_pointer args)
{
  #define H_angle "(angle z) returns the angle of z"
  s7_pointer p;

  p = car(args);
  switch (type(p))
    {
    case T_INTEGER:
      if (integer(p) < 0)
	return(real_pi);
      return(small_int(0));

    case T_RATIO:
      if (numerator(p) < 0)
	return(real_pi);
      return(small_int(0));

    case T_REAL:
      if (is_NaN(real(p))) return(p);
      if (real(p) < 0.0)
	return(real_pi);
      return(real_zero);

    case T_COMPLEX:
      return(make_real(sc, atan2(imag_part(p), real_part(p))));

    case T_BIG_INTEGER:
      if (mpz_cmp_ui(big_integer(p), 0) >= 0)
	return(small_int(0));
      return(big_pi(sc));

    case T_BIG_RATIO:
      if (mpq_cmp_ui(big_ratio(p), 0, 1) >= 0)
	return(small_int(0));
      return(big_pi(sc));

    case T_BIG_REAL:
      {
	double x;
	x = mpfr_get_d(big_real(p), GMP_RNDN);
	/* mpfr_get_d returns inf or -inf if the arg is too large for a double */
	if (is_NaN(x)) return(p);
	if (x >= 0.0)
	  return(real_zero);
	return(big_pi(sc));
      }

    case T_BIG_COMPLEX:
      {
	s7_pointer x;
	NEW_CELL(sc, x);
	set_type(x, T_BIG_REAL);
	add_bigreal(sc, x);
	mpfr_init(big_real(x));
	mpc_arg(big_real(x), big_complex(p), GMP_RNDN);
	return(x);
      }
      
    default:
      check_method(sc, p, sc->ANGLE, args);
      return(simple_wrong_type_argument_with_type(sc, sc->ANGLE, p, A_NUMBER));
    }
}


static s7_pointer big_make_rectangular(s7_scheme *sc, s7_pointer args)
{
#if WITH_MAKE_COMPLEX
  #define H_make_rectangular "(make-complex x1 x2) returns a complex number with real-part x1 and imaginary-part x2"
#else
  #define H_make_rectangular "(make-rectangular x1 x2) returns a complex number with real-part x1 and imaginary-part x2"
#endif

  s7_pointer p0, p1, p;
  mpfr_t rl, im;
  double x;

  p0 = car(args);
  if (!s7_is_real(p0))
    {
      check_method(sc, p0, sc->MAKE_RECTANGULAR, args);
      return(wrong_type_argument(sc, sc->MAKE_RECTANGULAR, small_int(1), p0, T_REAL));
    }
  p1 = cadr(args);
  if (!s7_is_real(p1))
    {
      check_method(sc, p1, sc->MAKE_RECTANGULAR, args);
      return(wrong_type_argument(sc, sc->MAKE_RECTANGULAR, small_int(2), p1, T_REAL));
    }
  if ((!is_big_number(p1)) && (s7_real(p1) == 0.0)) /* imag-part is not bignum and is 0.0 */
    return(p0);

  mpfr_init_set(im, big_real(promote_number(sc, T_BIG_REAL, p1)), GMP_RNDN);
  x = mpfr_get_d(im, GMP_RNDN);
  if (x == 0.0)                                   /* imag-part is bignum 0.0 */
    {
      mpfr_clear(im);
      return(p0);
    }

  mpfr_init_set(rl, big_real(promote_number(sc, T_BIG_REAL, p0)), GMP_RNDN);

  NEW_CELL(sc, p);
  set_type(p, T_BIG_COMPLEX);
  add_bignumber(sc, p);
  mpc_init(big_complex(p));
  mpc_set_fr_fr(big_complex(p), rl, im, MPC_RNDNN);

  mpfr_clear(rl);
  mpfr_clear(im);
  return(p);
}


/* (make-polar 0 (real-part (log 0))) = 0? or nan?
 */

#if (!WITH_MAKE_COMPLEX)
static s7_pointer big_make_polar(s7_scheme *sc, s7_pointer args)
{
  #define H_make_polar "(make-polar mag ang) returns a complex number with magnitude mag and angle ang"

  s7_pointer p0, p1, p;
  mpfr_t ang, mag, rl, im;
  double x, y;

  p0 = car(args);
  if (!s7_is_real(p0))
    {
      check_method(sc, p0, sc->MAKE_POLAR, args);
      return(wrong_type_argument(sc, sc->MAKE_POLAR, small_int(1), p0, T_REAL));
    }
  p1 = cadr(args);
  if (!s7_is_real(p1))
    {
      check_method(sc, p1, sc->MAKE_POLAR, args);
      return(wrong_type_argument(sc, sc->MAKE_POLAR, small_int(2), p1, T_REAL));
    }
  mpfr_init_set(ang, big_real(promote_number(sc, T_BIG_REAL, p1)), GMP_RNDN);
  y = mpfr_get_d(ang, GMP_RNDN);

  if (is_NaN(y))
    {
      mpfr_clear(ang);
      return(real_NaN);
    }

  mpfr_init_set(mag, big_real(promote_number(sc, T_BIG_REAL, p0)), GMP_RNDN);
  x = mpfr_get_d(mag, GMP_RNDN);

  if (is_NaN(x))
    {
      mpfr_clear(ang);
      mpfr_clear(mag);
      return(real_NaN);
    }

  if ((x == 0.0) || (y == 0.0))
    {
      mpfr_clear(ang);
      mpfr_clear(mag);
      return(p0);
    }

  mpfr_init_set(im, ang, GMP_RNDN);
  mpfr_sin(im, im, GMP_RNDN);
  mpfr_mul(im, im, mag, GMP_RNDN);
      
  x = mpfr_get_d(im, GMP_RNDN);
  if (x == 0.0)
    {
      mpfr_clear(im);
      mpfr_clear(ang);
      mpfr_clear(mag);
      return(p0);
    }

  mpfr_init_set(rl, ang, GMP_RNDN);
  mpfr_cos(rl, rl, GMP_RNDN);
  mpfr_mul(rl, rl, mag, GMP_RNDN);

  NEW_CELL(sc, p);
  set_type(p, T_BIG_COMPLEX);
  add_bignumber(sc, p);
  mpc_init(big_complex(p));
  mpc_set_fr_fr(big_complex(p), rl, im, MPC_RNDNN);

  mpfr_clear(rl);
  mpfr_clear(im);
  mpfr_clear(ang);
  mpfr_clear(mag);
  return(p);
}
#endif


static s7_pointer big_log(s7_scheme *sc, s7_pointer args)
{
  #define H_log "(log z1 (z2 e)) returns log(z1) / log(z2) where z2 (the base) defaults to e: (log 8 2) = 3"
  /* either arg can be big, second is optional */
  s7_pointer p0, p1 = NULL, p;

  p0 = car(args);
  if (!s7_is_number(p0))
    {
      check_method(sc, p0, sc->LOG, args);
      return(wrong_type_argument_with_type(sc, sc->LOG, small_int(1), p0, A_NUMBER));
    }
  if (is_not_null(cdr(args)))
    {
      p1 = cadr(args);
      if (!s7_is_number(p1))
	{
	  check_method(sc, p1, sc->LOG, args);
	  return(wrong_type_argument_with_type(sc, sc->LOG, small_int(2), p1, A_NUMBER));
	}
    }

  if ((s7_is_real(p0)) &&
      ((!p1) || (s7_is_real(p1))))
    {
      double x, y = 0.0;

      p0 = promote_number(sc, T_BIG_REAL, p0);
      x = mpfr_get_d(big_real(p0), GMP_RNDN);
      if (is_NaN(x))
	return(real_NaN);

      if (p1) 
	{
	  p1 = promote_number(sc, T_BIG_REAL, p1);
	  y = mpfr_get_d(big_real(p1), GMP_RNDN);

	  /* we can't check y here for 1.0 (check for 0.0 apparently is ok):
	   *  :(log 100.0 (+ 1.0 (bignum "1e-16")))
	   *  ;log base, argument 2, 1.000000000000000100000000000000000000002E0, is out of range (can't be 0.0 or 1.0)
	   *  :(= 1.0 (+ 1.0 (bignum "1e-16")))
	   *  #f
	   */
	  if (is_NaN(y))
	    return(real_NaN);
	  if (y == 0.0)
	    return(out_of_range(sc, sc->LOG, small_int(2), p1, make_string_wrapper(sc, "argument can't be 0.0")));
	}
      if (x == 0.0)
	return(s7_make_complex(sc, -INFINITY, M_PI));

      if ((x > 0.0) && (y >= 0.0))
	{
	  mpfr_t n, base;

	  mpfr_init_set(n, big_real(p0), GMP_RNDN);
	  mpfr_log(n, n, GMP_RNDN);

	  if (!p1)
	    {
	      /* presumably log is safe with regard to real-part overflow giving a bogus int? */
	      if ((s7_is_rational(car(args))) &&
		  (mpfr_integer_p(n) != 0))
		{
		  NEW_CELL(sc, p);
		  set_type(p, T_BIG_INTEGER);
		  add_bigint(sc, p);
		  mpz_init(big_integer(p));
		  mpfr_get_z(big_integer(p), n, GMP_RNDN);
		}
	      else p = mpfr_to_big_real(sc, n);
	      mpfr_clear(n);
	      return(p);
	    }

	  mpfr_init_set(base, big_real(p1), GMP_RNDN);
	  mpfr_log(base, base, GMP_RNDN);
	  mpfr_div(n, n, base, GMP_RNDN);
	  mpfr_clear(base);

	  if ((s7_is_rational(car(args))) &&
	      (s7_is_rational(cadr(args))) &&
	      (mpfr_integer_p(n) != 0))
	    {
	      NEW_CELL(sc, p);
	      set_type(p, T_BIG_INTEGER);
	      add_bigint(sc, p);
	      mpz_init(big_integer(p));
	      mpfr_get_z(big_integer(p), n, GMP_RNDN);
	    }
	  else p = mpfr_to_big_real(sc, n);
	  mpfr_clear(n);
	  return(p);
	}
    }

  p0 = promote_number(sc, T_BIG_COMPLEX, p0);
  if (p1) p1 = promote_number(sc, T_BIG_COMPLEX, p1);
  {
    mpc_t n, base;
    double x;

    mpc_init(n);
    mpc_set(n, big_complex(p0), MPC_RNDNN);
    mpc_log(n, n, MPC_RNDNN);
    if (!p1)
      {
	p = mpc_to_big_complex(sc, n);
	mpc_clear(n);
	return(p);
      }

    mpc_init(base);
    mpc_set(base, big_complex(p1), MPC_RNDNN);
    mpc_log(base, base, MPC_RNDNN);
    mpc_div(n, n, base, MPC_RNDNN);
    mpc_clear(base);

    x = mpfr_get_d(mpc_imagref(n), GMP_RNDN);
    if (x == 0.0)
      p = mpfr_to_big_real(sc, mpc_realref(n));
    else p = mpc_to_big_complex(sc, n);

    mpc_clear(n);
    return(p);
  }
}


static s7_pointer big_sqrt(s7_scheme *sc, s7_pointer args)
{
  /* real >= 0 -> real, else complex */
  #define H_sqrt "(sqrt z) returns the square root of z"

  s7_pointer p;

  p = car(args);
  if (!s7_is_number(p))
    {
      check_method(sc, p, sc->SQRT, args);
      return(simple_wrong_type_argument_with_type(sc, sc->SQRT, p, A_NUMBER));
    }
  p = to_big(sc, p);
    
  /* if big integer, try to return int if perfect square */
  if (type(p) == T_BIG_INTEGER)
    {
      if (mpz_cmp_ui(big_integer(p), 0) < 0)
	p = promote_number(sc, T_BIG_COMPLEX, p);
      else
	{
	  mpz_t n, rem;

	  mpz_init(rem);
	  mpz_init_set(n, big_integer(p));
	  mpz_sqrtrem(n, rem, n);

	  if (mpz_cmp_ui(rem, 0) == 0)
	    {
	      p = mpz_to_big_integer(sc, n);
	      mpz_clear(n);
	      mpz_clear(rem);
	      return(p);
	    }
	  mpz_clear(n);
	  mpz_clear(rem);
	  p = promote_number(sc, T_BIG_REAL, p);
	}
    }
  
  /* if big ratio, check both num and den for squares */
  if (type(p) == T_BIG_RATIO)
    {
      if (mpq_cmp_ui(big_ratio(p), 0, 1) < 0)
	p = promote_number(sc, T_BIG_COMPLEX, p);
      else
	{
	  mpz_t n1, rem;
	  mpz_init(rem);
	  mpz_init_set(n1, mpq_numref(big_ratio(p)));
	  mpz_sqrtrem(n1, rem, n1);

	  if (mpz_cmp_ui(rem, 0) == 0)
	    {
	      mpz_t d1;
	      mpz_init_set(d1, mpq_denref(big_ratio(p)));
	      mpz_sqrtrem(d1, rem, d1);

	      if (mpz_cmp_ui(rem, 0) == 0)
		{
		  mpq_t n;
		  mpq_init(n);
		  mpq_set_num(n, n1);
		  mpq_set_den(n, d1);
		  mpq_canonicalize(n);
		  p = mpq_to_big_ratio(sc, n);
		  mpz_clear(n1);
		  mpz_clear(d1);
		  mpz_clear(rem);
		  mpq_clear(n);
		  return(p);
		}
	      mpz_clear(d1);
	    }

	  mpz_clear(n1);
	  mpz_clear(rem);
	  p = promote_number(sc, T_BIG_REAL, p);
	}
    }
  
  /* if real and not negative, use mpfr_sqrt */
  if (type(p) == T_BIG_REAL)
    {
      if (mpfr_cmp_ui(big_real(p), 0) < 0)
	p = promote_number(sc, T_BIG_COMPLEX, p);
      else
	{
	  mpfr_t n;
	  mpfr_init_set(n, big_real(p), GMP_RNDN);
	  mpfr_sqrt(n, n, GMP_RNDN);
	  p = mpfr_to_big_real(sc, n);
	  mpfr_clear(n);
	  return(p);
	}
    }
  
  /* p is a big number, so it must be complex at this point */
  {
    mpc_t n;
    mpc_init(n);
    mpc_set(n, big_complex(p), MPC_RNDNN);
    mpc_sqrt(n, n, MPC_RNDNN);
    p = mpc_to_big_complex(sc, n);
    mpc_clear(n);
    return(p);
  }
}


/* (define (diff f a) (magnitude (- (f a) (f (bignum (number->string a))))))
 * (sin 1e15+1e15i) hangs in mpc 0.8.2, but appears to be fixed in the current svn sources
 */

enum {TRIG_NO_CHECK, TRIG_TAN_CHECK, TRIG_TANH_CHECK};

static s7_pointer big_trig(s7_scheme *sc, s7_pointer args, 
			   int (*mpfr_trig)(mpfr_ptr, mpfr_srcptr, mpfr_rnd_t),
			   int (*mpc_trig)(mpc_ptr, mpc_srcptr, mpc_rnd_t),
			   int tan_case, s7_pointer sym)
/* these declarations mimic the mpfr.h and mpc.h declarations.  It seems to me that
 *   they ought to be:
 *			   int (*mpfr_trig)(mpfr_t rop, mpfr_t op, mp_rnd_t rnd),
 *			   void (*mpc_trig)(mpc_t rop, mpc_t op, mpc_rnd_t rnd))
 */
{
  s7_pointer p;
  p = car(args);

  /* I think here we should always promote to bignum (otherwise, for example, (exp 800) -> inf)
   */
  if (!s7_is_number(p))
    {
      check_method(sc, p, sym, args);
      return(simple_wrong_type_argument_with_type(sc, sym, p, A_NUMBER));
    }
  if (s7_is_real(p))
    {
      mpfr_t n;
      mpfr_init_set(n, big_real(promote_number(sc, T_BIG_REAL, p)), GMP_RNDN);
      mpfr_trig(n, n, GMP_RNDN);
      /* it's confusing to check for ints here via mpfr_integer_p because it
       *   is dependent on the precision!  (exp 617/5) returns an integer if
       *   precision is 128, but a float if 512.
       */
      p = mpfr_to_big_real(sc, n);
      mpfr_clear(n);
      return(p);
    }

  if (!is_big_number(p))
    p = promote_number(sc, T_BIG_COMPLEX, p);

  if (tan_case == TRIG_TAN_CHECK)
    {
      if ((MPC_INEX_IM(mpc_cmp_si_si(big_complex(p), 1, 350))) > 0)
	return(s7_make_complex(sc, 0.0, 1.0));	
      if ((MPC_INEX_IM(mpc_cmp_si_si(big_complex(p), 1, -350))) < 0)
	return(s7_make_complex(sc, 0.0, -1.0));	
    }
  
  if (tan_case == TRIG_TANH_CHECK)
    {
      if ((MPC_INEX_RE(mpc_cmp_si_si(big_complex(p), 350, 1))) > 0)
	return(real_one);
      if ((MPC_INEX_RE(mpc_cmp_si_si(big_complex(p), -350, 1))) < 0)
	return(make_real(sc, -1.0));
    }
  
  {
    mpc_t n;
    double ix;

    mpc_init(n);
    mpc_trig(n, big_complex(p), MPC_RNDNN);  
    /* (sin (bignum "1e15+1e15i")) causes mpc to hang (e9 is ok, but e10 hangs) 
     *   (sin (bignum "0+1e10i")) -> 0+inf (sin (bignum "1+1e10i")) hangs
     *
     * before comparing imag-part to 0, we need to look for NaN and inf, else:
     *    (sinh 0+0/0i) -> 0.0
     *    (sinh (log 0.0)) -> inf.0
     */

    ix = mpfr_get_d(mpc_imagref(n), GMP_RNDN);
    if (ix == 0.0)
      {
	mpfr_t z;

	mpfr_init_set(z, mpc_realref(n), GMP_RNDN);
	p = mpfr_to_big_real(sc, z);
	mpfr_clear(z);
      }
    else p = mpc_to_big_complex(sc, n);
    mpc_clear(n);
    return(p);
  }
}


static s7_pointer big_sin(s7_scheme *sc, s7_pointer args)
{
  #define H_sin "(sin z) returns sin(z)"
  return(big_trig(sc, args, mpfr_sin, mpc_sin, TRIG_NO_CHECK, sc->SIN));
}


static s7_pointer big_cos(s7_scheme *sc, s7_pointer args)
{
  #define H_cos "(cos z) returns cos(z)"
  return(big_trig(sc, args, mpfr_cos, mpc_cos, TRIG_NO_CHECK, sc->COS));
}


s7_pointer s7_cos(s7_scheme *sc, s7_pointer x)
{
  return(big_cos(sc, cons(sc, x, sc->NIL)));
}


static s7_pointer big_tan(s7_scheme *sc, s7_pointer args)
{
  #define H_tan "(tan z) returns tan(z)"
  return(big_trig(sc, args, mpfr_tan, mpc_tan, TRIG_TAN_CHECK, sc->TAN));
}


static s7_pointer big_sinh(s7_scheme *sc, s7_pointer args)
{
  #define H_sinh "(sinh z) returns sinh(z)"
  /* currently (sinh 0+0/0i) -> 0.0? */
  return(big_trig(sc, args, mpfr_sinh, mpc_sinh, TRIG_NO_CHECK, sc->SINH));
}


static s7_pointer big_cosh(s7_scheme *sc, s7_pointer args)
{
  #define H_cosh "(cosh z) returns cosh(z)"
  return(big_trig(sc, args, mpfr_cosh, mpc_cosh, TRIG_NO_CHECK, sc->COSH));
}


static s7_pointer big_tanh(s7_scheme *sc, s7_pointer args)
{
  #define H_tanh "(tanh z) returns tanh(z)"
  return(big_trig(sc, args, mpfr_tanh, mpc_tanh, TRIG_TANH_CHECK, sc->TANH));
}


static s7_pointer big_exp(s7_scheme *sc, s7_pointer args)
{
  #define H_exp "(exp z) returns e^z, (exp 1) is 2.718281828459"
  return(big_trig(sc, args, mpfr_exp, mpc_exp, TRIG_NO_CHECK, sc->EXP));
}


static s7_pointer big_expt(s7_scheme *sc, s7_pointer args)
{
  #define H_expt "(expt z1 z2) returns z1^z2"
  s7_pointer x, y, p;

  /* see comment under g_expt
   *  if (is_not_null(cddr(args)))
   *    return(big_expt(sc, list_2(sc, car(args), big_expt(sc, cdr(args)))));
   */

  x = car(args);
  if (!s7_is_number(x))
    {
      check_method(sc, x, sc->EXPT, args);
      return(wrong_type_argument_with_type(sc, sc->EXPT, small_int(1), x, A_NUMBER));
    }
  y = cadr(args);
  if (!s7_is_number(y))
    {
      check_method(sc, y, sc->EXPT, args);
      return(wrong_type_argument_with_type(sc, sc->EXPT, small_int(2), y, A_NUMBER));
    }
  if (s7_is_zero(x))
    {
      if ((s7_is_integer(x)) && 
	  (s7_is_integer(y)) &&
	  (s7_is_zero(y)))
	return(small_int(1));

      if (s7_is_real(y))
	{
	  if (s7_is_negative(y))
	    return(division_by_zero_error(sc, sc->EXPT, args));
	}
      else
	{
	  if (s7_is_negative(g_real_part(sc, cdr(args))))
	    return(division_by_zero_error(sc, sc->EXPT, args));
	}

      if ((s7_is_rational(x)) && 
	  (s7_is_rational(y)))
	return(small_int(0));

      return(real_zero);
    }

  if (s7_is_integer(y))
    {
      s7_Int yval;

      if (!is_big_number(y))
	yval = s7_integer(y);
      else yval = big_integer_to_s7_Int(big_integer(y));

      if (yval == 0)
	{
	  if (s7_is_rational(x))
	    return(small_int(1));
	  return(real_one);
	}

      if (yval == 1)
	return(x);

      if (!is_big_number(x))
	{
	  if ((s7_is_one(x)) || (s7_is_zero(x)))
	    return(x);
	}

      if ((yval < S7_LONG_MAX) &&
	  (yval > S7_LONG_MIN))
	{
	  /* from here yval can fit in an unsigned int 
	   *    (protect against gmp exception if for example (expt 1/9223372036854775807 -9223372036854775807) 
	   */
	  if (s7_is_integer(x))
	    {
	      mpz_t n;
	      mpq_t r;

	      x = promote_number(sc, T_BIG_INTEGER, x);
	      mpz_init_set(n, big_integer(x));
	      if (yval >= 0)
		{
		  mpz_pow_ui(n, n, (unsigned int)yval);
		  p = mpz_to_big_integer(sc, n);
		  mpz_clear(n);
		  return(p);
		}

	      mpz_pow_ui(n, n, (unsigned int)(-yval));
	      mpq_init(r);
	      mpq_set_z(r, n);
	      mpq_inv(r, r);
	      if (mpz_cmp_ui(mpq_denref(r), 1) == 0)
		{
		  mpz_t z;
		  mpz_init_set(z, mpq_numref(r));
		  mpq_clear(r);
		  mpz_clear(n);
		  p = mpz_to_big_integer(sc, z);
		  mpz_clear(z);
		  return(p);
		}
	      mpz_clear(n);
	      p = mpq_to_big_ratio(sc, r);
	      mpq_clear(r);
	      return(p);
	    }

	  if (s7_is_ratio(x)) /* (here y is an integer */
	    {
	      mpz_t n, d;
	      mpq_t r;
	      
	      x = promote_number(sc, T_BIG_RATIO, x);
	      mpz_init_set(n, mpq_numref(big_ratio(x)));
	      mpz_init_set(d, mpq_denref(big_ratio(x)));
	      mpq_init(r);
	      if (yval >= 0)
		{
		  mpz_pow_ui(n, n, (unsigned int)yval);
		  mpz_pow_ui(d, d, (unsigned int)yval);
		  mpq_set_num(r, n);
		  mpq_set_den(r, d);
		}
	      else
		{
		  yval = -yval;
		  mpz_pow_ui(n, n, (unsigned int)yval);
		  mpz_pow_ui(d, d, (unsigned int)yval);
		  mpq_set_num(r, d);
		  mpq_set_den(r, n);	      
		  mpq_canonicalize(r);
		}
	      mpz_clear(n);
	      mpz_clear(d);
	      if (mpz_cmp_ui(mpq_denref(r), 1) == 0)
		{
		  mpz_t z;
		  mpz_init_set(z, mpq_numref(r));
		  mpq_clear(r);
		  p = mpz_to_big_integer(sc, z);
		  mpz_clear(z);
		  return(p);
		}
	      p = mpq_to_big_ratio(sc, r);
	      mpq_clear(r);
	      return(p);
	    }
	  
	  if (s7_is_real(x))
	    {
	      mpfr_t z;
	      x = promote_number(sc, T_BIG_REAL, x);
	      mpfr_init_set(z, big_real(x), GMP_RNDN);
	      mpfr_pow_si(z, z, yval, GMP_RNDN);
	      p = mpfr_to_big_real(sc, z);
	      mpfr_clear(z);
	      return(p);
	    }
	}
    }

  if ((s7_is_ratio(y)) &&
      (numerator(y) == 1))
    {
      if (denominator(y) == 2)
	return(big_sqrt(sc, args));
      
      if ((s7_is_real(x)) &&
	  (denominator(y) == 3))
	{
	  mpfr_t z;
	  mpfr_init_set(z, big_real(promote_number(sc, T_BIG_REAL, x)), GMP_RNDN);
	  mpfr_cbrt(z, z, GMP_RNDN);
	  p = mpfr_to_big_real(sc, z);
	  mpfr_clear(z);
	  return(p);
	}
    }

  if ((s7_is_real(x)) &&
      (s7_is_real(y)) &&
      (s7_is_positive(x)))
    {
      mpfr_t z;
      mpfr_init_set(z, big_real(promote_number(sc, T_BIG_REAL, x)), GMP_RNDN);
      mpfr_pow(z, z, big_real(promote_number(sc, T_BIG_REAL, y)), GMP_RNDN);
      p = mpfr_to_big_real(sc, z);
      mpfr_clear(z);
      return(p);
    }

  {
    mpc_t cy;
    mpc_t z;
    
    x = promote_number(sc, T_BIG_COMPLEX, x);
    y = promote_number(sc, T_BIG_COMPLEX, y);
    
    mpc_init(z);
    mpc_set(z, big_complex(x), MPC_RNDNN);
    
    if (mpc_cmp_si_si(z, 0, 0) == 0)
      {
	mpc_clear(z);
	return(small_int(0));
      }
    
    if (mpc_cmp_si_si(z, 1, 0) == 0)
      {
	mpc_clear(z);
	return(small_int(1));
      }
    
    mpc_init(cy);
    mpc_set(cy, big_complex(y), MPC_RNDNN);
    mpc_pow(z, z, cy, MPC_RNDNN);
    mpc_clear(cy);
    
    if (mpfr_cmp_ui(mpc_imagref(z), 0) == 0)
      {
	mpfr_t n;
	if ((s7_is_rational(car(args))) &&
	    (s7_is_rational(cadr(args))) &&
	    (mpfr_integer_p(mpc_realref(z)) != 0))
	  {
	    /* mpfr_integer_p can be confused: (expt 2718/1000 (bignum "617/5")) returns an int if precision=128, float if 512 */
	    /*   so first make sure we're within (say) 31 bits */
	    mpfr_t zi;
	    mpfr_init_set_ui(zi, S7_LONG_MAX, GMP_RNDN);
	    if (mpfr_cmpabs(mpc_realref(z), zi) < 0)
	      {
		mpz_t k;
		mpz_init(k);
		mpfr_get_z(k, mpc_realref(z), GMP_RNDN);
		mpc_clear(z);
		mpfr_clear(zi);
		p = mpz_to_big_integer(sc, k);
		mpz_clear(k);
		return(p);
	      }
	    mpfr_clear(zi);
	  }

	mpfr_init_set(n, mpc_realref(z), GMP_RNDN);
	mpc_clear(z);
	p = mpfr_to_big_real(sc, n);
	mpfr_clear(n);
	return(p);
      }
    
    p = mpc_to_big_complex(sc, z);
    mpc_clear(z);
    return(p);
  }
}


static s7_pointer big_asinh(s7_scheme *sc, s7_pointer args)
{
  #define H_asinh "(asinh z) returns asinh(z)"
  s7_pointer p;

  p = car(args);
  if (!s7_is_number(p))
    {
      check_method(sc, p, sc->ASINH, args);
      return(simple_wrong_type_argument_with_type(sc, sc->ASINH, p, A_NUMBER));
    }
  if (s7_is_real(p))
    {
      mpfr_t n;
      p = promote_number(sc, T_BIG_REAL, p);
      mpfr_init_set(n, big_real(p), GMP_RNDN);
      mpfr_asinh(n, n, GMP_RNDN);
      p = mpfr_to_big_real(sc, n);
      mpfr_clear(n);
      return(p);
    }

  {
    mpc_t n;
    p = promote_number(sc, T_BIG_COMPLEX, p);
    mpc_init(n);
    mpc_set(n, big_complex(p), MPC_RNDNN);
    mpc_asinh(n, n, MPC_RNDNN);
    p = mpc_to_big_complex(sc, n);
    mpc_clear(n);
    return(p);
  }
}


static s7_pointer big_acosh(s7_scheme *sc, s7_pointer args)
{
  #define H_acosh "(acosh z) returns acosh(z)"
  s7_pointer p;
  double x;
  mpc_t n;

  p = car(args);
  if (!s7_is_number(p))
    {
      check_method(sc, p, sc->ACOSH, args);
      return(simple_wrong_type_argument_with_type(sc, sc->ACOSH, p, A_NUMBER));
    }
  p = promote_number(sc, T_BIG_COMPLEX, p);
  
  mpc_init(n);
  mpc_set(n, big_complex(p), MPC_RNDNN);
  mpc_acosh(n, n, MPC_RNDNN);
  
  x = mpfr_get_d(mpc_imagref(n), GMP_RNDN);
  if (x == 0.0)
    p = mpfr_to_big_real(sc, mpc_realref(n));
  else p = mpc_to_big_complex(sc, n);
  mpc_clear(n);
  return(p);
}


static s7_pointer big_atanh(s7_scheme *sc, s7_pointer args)
{
  #define H_atanh "(atanh z) returns atanh(z)"
  s7_pointer p;

  p = car(args);
  if (!s7_is_number(p))
    {
      check_method(sc, p, sc->ATANH, args);
      return(simple_wrong_type_argument_with_type(sc, sc->ATANH, p, A_NUMBER));
    }
  if (s7_is_real(p))
    {
      bool ok;
      mpfr_t temp;
      p = promote_number(sc, T_BIG_REAL, p);
      mpfr_init_set_ui(temp, 1, GMP_RNDN);
      ok = (mpfr_cmpabs(big_real(p), temp) < 0);
      mpfr_clear(temp);
      if (ok)
	{
	  mpfr_t n;
	  mpfr_init_set(n, big_real(p), GMP_RNDN);
	  mpfr_atanh(n, n, GMP_RNDN);
	  p = mpfr_to_big_real(sc, n);
	  mpfr_clear(n);
	  return(p);
	}
    }

  {
    mpc_t n;
    p = promote_number(sc, T_BIG_COMPLEX, p);
    mpc_init(n);
    mpc_set(n, big_complex(p), MPC_RNDNN);
    mpc_atanh(n, n, MPC_RNDNN);
    p = mpc_to_big_complex(sc, n);
    mpc_clear(n);
    return(p);
  }
}


static s7_pointer big_atan(s7_scheme *sc, s7_pointer args)
{
  #define H_atan "(atan z) returns atan(z), (atan y x) returns atan(y/x)"
  s7_pointer p0, p1 = NULL, p;

  p0 = car(args);
  if (!s7_is_number(p0))
    {
      check_method(sc, p0, sc->ATAN, args);
      return(simple_wrong_type_argument_with_type(sc, sc->ATAN, p0, A_NUMBER));
    }
  if (is_not_null(cdr(args)))
    {
      p1 = cadr(args);
      if (!s7_is_real(p1))
	{
	  check_method(sc, p1, sc->ATAN, args);
	  return(wrong_type_argument(sc, sc->ATAN, small_int(2), p1, T_REAL));
	}
      if (!s7_is_real(p0))
	return(wrong_type_argument(sc, sc->ATAN, small_int(1), p0, T_REAL));

      p1 = promote_number(sc, T_BIG_REAL, p1);
    }

  if (s7_is_real(p0))
    {
      mpfr_t n;
      p0 = promote_number(sc, T_BIG_REAL, p0);
      mpfr_init_set(n, big_real(p0), GMP_RNDN);
      if (!p1)
	mpfr_atan(n, n, GMP_RNDN);
      else mpfr_atan2(n, n, big_real(p1), GMP_RNDN);
      p = mpfr_to_big_real(sc, n);
      mpfr_clear(n);
      return(p);
    }

  {
    mpc_t n;
    p0 = promote_number(sc, T_BIG_COMPLEX, p0);
    mpc_init_set(n, big_complex(p0), MPC_RNDNN);
    mpc_atan(n, n, MPC_RNDNN);
    p = mpc_to_big_complex(sc, n);
    mpc_clear(n);
    return(p);
  }
}


static s7_pointer big_acos(s7_scheme *sc, s7_pointer args)
{
  #define H_acos "(acos z) returns acos(z); (cos (acos 1)) = 1"
  s7_pointer p;

  p = car(args);
  if (!s7_is_number(p))
    {
      check_method(sc, p, sc->ACOS, args);
      return(simple_wrong_type_argument_with_type(sc, sc->ACOS, p, A_NUMBER));
    }
  if (s7_is_real(p))
    {
      bool ok;
      mpfr_t temp;
      mpfr_t n;
      p = promote_number(sc, T_BIG_REAL, p);
      mpfr_init_set(n, big_real(p), GMP_RNDN);
      mpfr_init_set_ui(temp, 1, GMP_RNDN);
      ok = (mpfr_cmpabs(n, temp) <= 0);
      mpfr_clear(temp);
      if (ok)
	{
	  mpfr_acos(n, n, GMP_RNDN);
	  p = mpfr_to_big_real(sc, n);
	  mpfr_clear(n);
	  return(p);
	}
      mpfr_clear(n);
    }

  {
    mpc_t n;
    p = promote_number(sc, T_BIG_COMPLEX, p);
    mpc_init_set(n, big_complex(p), MPC_RNDNN);
    mpc_acos(n, n, MPC_RNDNN);
    p = mpc_to_big_complex(sc, n);
    mpc_clear(n);
    return(p);
  }
}


static s7_pointer big_asin(s7_scheme *sc, s7_pointer args)
{
  #define H_asin "(asin z) returns asin(z); (sin (asin 1)) = 1"
  s7_pointer p;

  p = car(args);
  if (!s7_is_number(p))
    {
      check_method(sc, p, sc->ASIN, args);
      return(simple_wrong_type_argument_with_type(sc, sc->ASIN, p, A_NUMBER));
    }
  if (s7_is_real(p))
    {
      bool ok;
      mpfr_t temp;
      mpfr_t n;
      p = promote_number(sc, T_BIG_REAL, p);
      mpfr_init_set(n, big_real(p), GMP_RNDN);
      mpfr_init_set_ui(temp, 1, GMP_RNDN);
      ok = (mpfr_cmpabs(n, temp) <= 0);
      mpfr_clear(temp);
      if (ok)
	{
	  mpfr_asin(n, n, GMP_RNDN);
	  p = mpfr_to_big_real(sc, n);
	  mpfr_clear(n);
	  return(p);
	}
      mpfr_clear(n);
    }

  {
    mpc_t n;
    p = promote_number(sc, T_BIG_COMPLEX, p);
    mpc_init_set(n, big_complex(p), MPC_RNDNN);
    mpc_asin(n, n, MPC_RNDNN);
    p = mpc_to_big_complex(sc, n);
    mpc_clear(n);
    return(p);
  }
}


static s7_pointer big_lognot(s7_scheme *sc, s7_pointer args)
{
  if (type(car(args)) == T_BIG_INTEGER)
    {
      s7_pointer p;
      mpz_t n;
      mpz_init(n);
      mpz_com(n, big_integer(car(args)));
      p = mpz_to_big_integer(sc, n);
      mpz_clear(n);
      return(p);
    }
  return(g_lognot(sc, args));
}


static s7_pointer big_integer_length(s7_scheme *sc, s7_pointer args)
{
  if (type(car(args)) == T_BIG_INTEGER)
    {
      s7_pointer result;
      mpfr_t n;
      mpfr_init_set_z(n, big_integer(car(args)), GMP_RNDN);
      if (mpfr_cmp_ui(n, 0) < 0)
	mpfr_neg(n, n, GMP_RNDN);
      else mpfr_add_ui(n, n, 1, GMP_RNDN);
      mpfr_log2(n, n, GMP_RNDU);
      result = make_integer(sc, mpfr_get_si(n, GMP_RNDU));
      mpfr_clear(n);
      return(result);
    }
  return(g_integer_length(sc, args));
}


static s7_pointer big_ash(s7_scheme *sc, s7_pointer args)
{
  s7_pointer p0, p1;

  p0 = car(args);
  p1 = cadr(args);
  /* here, as in expt, there are cases like (ash 1 63) which need to be handled as bignums
   *   so there's no way to tell when it's safe to drop into g_ash instead.
   */
  if ((s7_is_integer(p0)) && /* this includes bignum ints... */
      (s7_is_integer(p1)))
    {
      mpz_t n;
      s7_Int shift;
      s7_pointer p;
      bool p0_is_big;
      int p0_compared_to_zero = 0;

      p0_is_big = is_big_number(p0);
      if (p0_is_big)
	p0_compared_to_zero = mpz_cmp_ui(big_integer(p0), 0);
      else 
	{
	  if (s7_integer(p0) > 0)
	    p0_compared_to_zero = 1;
	  else
	    {
	      if (s7_integer(p0) < 0)
		p0_compared_to_zero = -1;
	      else p0_compared_to_zero = 0;
	    }
	}

      if (p0_compared_to_zero == 0)
	return(small_int(0));

      if (is_big_number(p1))
	{
	  if (!mpz_fits_sint_p(big_integer(p1)))
	    {
	      if (mpz_cmp_ui(big_integer(p1), 0) > 0)
		return(out_of_range(sc, sc->ASH, small_int(2), p1, ITS_TOO_LARGE));

	      /* here if p0 is negative, we need to return -1 */
	      if (p0_compared_to_zero == 1)
		return(small_int(0));
	      return(make_integer(sc, -1));
	    }
	  shift = mpz_get_si(big_integer(p1));
	}
      else 
	{
	  shift = s7_integer(p1);
	  if (shift < S7_LONG_MIN)
	    {
	      if (p0_compared_to_zero == 1)
		return(small_int(0));
	      return(make_integer(sc, -1));
	    }
	}

      mpz_init_set(n, big_integer(promote_number(sc, T_BIG_INTEGER, p0)));
      if (shift > 0)     /* left */
	mpz_mul_2exp(n, n, shift);
      else
	{
	  if (shift < 0) /* right */
	    mpz_fdiv_q_2exp(n, n, (unsigned int)(-shift));
	}
      p = mpz_to_big_integer(sc, n);
      mpz_clear(n);
      return(p);
    }
  return(g_ash(sc, args));
}


static bool is_integer_via_method(s7_scheme *sc, s7_pointer p)
{
  if (s7_is_integer(p))
    return(true);
  if (has_methods(p))
    {
      s7_pointer f;
      f = find_method(sc, find_let(sc, p), sc->IS_INTEGER);
      if (f != sc->UNDEFINED)
	return(is_true(sc, s7_apply_function(sc, f, cons(sc, p, sc->NIL))));
    }
  return(false);
}

static s7_pointer big_bits(s7_scheme *sc, s7_pointer args, s7_pointer sym, int start, s7_function g_bits, 
			   void (*mpz_bits)(mpz_ptr, mpz_srcptr, mpz_srcptr))
{
  s7_pointer x, lst;
  bool use_bigs = false;
  for (x = args; is_not_null(x); x = cdr(x))
    {
      if (!is_integer_via_method(sc, car(x)))
	return(wrong_type_argument_n(sc, sym, position_of(x, args), car(x), T_INTEGER));  
      if (!use_bigs) use_bigs = (type(car(x)) != T_INTEGER);
    }
  if (use_bigs)
    {
      mpz_t n;
      mpz_init_set_si(n, 0);
      if (start == -1)
	mpz_sub_ui(n, n, 1);
      for (x = args; is_not_null(x); x = cdr(x))
	{
	  s7_pointer i;
	  i = car(x);
	  switch (type(i))
	    {
	    case T_BIG_INTEGER:
	      mpz_bits(n, n, big_integer(i));
	      break;

	    case T_INTEGER:
	      mpz_bits(n, n, big_integer(s7_Int_to_big_integer(sc, integer(i))));
	      break;

	    default:
	      /* we know it's an integer of some sort, but what about the method */
	      lst = cons(sc, mpz_to_big_integer(sc, n), x);
	      mpz_clear(n);
	      check_method(sc, i, sym, lst);
	      return(wrong_type_argument_n(sc, sym, position_of(x, args), i, T_INTEGER));  
	      break;
	    }
	}
      x = mpz_to_big_integer(sc, n);
      mpz_clear(n);
      return(x);
    }
  return(g_bits(sc, args));
}


static s7_pointer big_logand(s7_scheme *sc, s7_pointer args)
{
  if (is_null(args))
    return(make_integer(sc, -1));
  return(big_bits(sc, args, sc->LOGAND, -1, g_logand, mpz_and));
}


static s7_pointer big_logior(s7_scheme *sc, s7_pointer args)
{
  if (is_null(args))
    return(small_int(0));
  return(big_bits(sc, args, sc->LOGIOR, 0, g_logior, mpz_ior));
}


static s7_pointer big_logxor(s7_scheme *sc, s7_pointer args)
{
  if (is_null(args))
    return(small_int(0));
  return(big_bits(sc, args, sc->LOGXOR, 0, g_logxor, mpz_xor));
}


static s7_pointer big_rationalize(s7_scheme *sc, s7_pointer args)
{
  #define H_rationalize "(rationalize x err) returns the ratio with lowest denominator within err of x"

  /* currently (rationalize 1/0 1e18) -> 0
   * remember to pad with many trailing zeros:
   *
   *    : (rationalize 0.1 0)
   *    3602879701896397/36028797018963968
   *    :(rationalize 0.1000000000000000 0)
   *    1/10
   *
   * perhaps gmp number reader used if gmp -- could this be the trailing zeros problem?  (why is the non-gmp case ok?)
   *         also the bignum function is faking it.
   *         (rationalize (real-part (bignum "0.1+i")) 0) -> 3602879701896397/36028797018963968
   *
   * a confusing case:
   *      > (rationalize 5925563891587147521650777143.74135805596e05)
   *   should be 148139097289678688041269428593533951399/250000
   *   but that requires more than 128 bits of bignum-precision.
   */

  s7_pointer p0, p1 = NULL, p;
  mpfr_t error, ux, x0, x1;
  mpz_t i, i0, i1;
  double xx;

  p0 = car(args);
  if (!s7_is_real(p0))
    {
      check_method(sc, p0, sc->RATIONALIZE, args);
      return(wrong_type_argument(sc, sc->RATIONALIZE, small_int(1), p0, T_REAL));
    }
  /* p0 can be exact, but we still have to check it for simplification */
  if (is_not_null(cdr(args)))
    {
      double err_x;
      p1 = cadr(args);
      if (!s7_is_real(p1))              /* (rationalize (expt 2 60) -) */
	{
	  check_method(sc, p1, sc->RATIONALIZE, args);
	  return(wrong_type_argument(sc, sc->RATIONALIZE, small_int(2), p1, T_REAL));
	}
      if (is_big_number(p1))
	mpfr_init_set(error, big_real(promote_number(sc, T_BIG_REAL, p1)), GMP_RNDN);
      else mpfr_init_set_d(error, number_to_double(sc, p1, "rationalize"), GMP_RNDN);

      err_x = mpfr_get_d(error, GMP_RNDN);
      if (is_NaN(err_x))
	{
	  mpfr_clear(error);
	  return(out_of_range(sc, sc->RATIONALIZE, small_int(2), cadr(args), ITS_NAN));
	}
      if (mpfr_inf_p(error) != 0)
	{
	  mpfr_clear(error);
	  return(small_int(0));
	}
      mpfr_abs(error, error, GMP_RNDN);
    }
  else mpfr_init_set_d(error, sc->default_rationalize_error, GMP_RNDN);
  
  if (is_big_number(p0))
    mpfr_init_set(ux, big_real(promote_number(sc, T_BIG_REAL, p0)), GMP_RNDN);
  else mpfr_init_set_d(ux, number_to_double(sc, p0, "rationalize"), GMP_RNDN);

  xx = mpfr_get_d(ux, GMP_RNDN);  
  if (is_NaN(xx))
    {
      mpfr_clear(ux);
      mpfr_clear(error);
      return(out_of_range(sc, sc->RATIONALIZE, small_int(1), car(args), ITS_NAN));
    }
  if (mpfr_inf_p(ux) != 0)
    {
      mpfr_clear(ux);
      mpfr_clear(error);
      return(out_of_range(sc, sc->RATIONALIZE, small_int(1), car(args), ITS_INFINITE));
    }

  mpfr_init_set(x0, ux, GMP_RNDN);        /* x0 = ux - error */
  mpfr_sub(x0, x0, error, GMP_RNDN);
  mpfr_init_set(x1, ux, GMP_RNDN);        /* x1 = ux + error */
  mpfr_add(x1, x1, error, GMP_RNDN);
  mpz_init(i);
  mpfr_get_z(i, x0, GMP_RNDU);            /* i = ceil(x0) */
  
  if (mpfr_cmp_ui(error, 1) >= 0)         /* if (error >= 1.0) */
    {
      mpz_t n;
      
      if (mpfr_cmp_ui(x0, 0) < 0)                /* if (x0 < 0) */
	{
	  if (mpfr_cmp_ui(x1, 0) < 0)            /*   if (x1 < 0) */
	    {
	      mpz_init(n);
	      mpfr_get_z(n, x1, GMP_RNDD);       /*     num = floor(x1) */
	    }
	  else mpz_init_set_ui(n, 0);            /*   else num = 0 */
	}
      else mpz_init_set(n, i);                   /* else num = i */
      
      mpz_clear(i);
      mpfr_clear(ux);
      mpfr_clear(x0);
      mpfr_clear(x1);
      mpfr_clear(error);
      p = mpz_to_big_integer(sc, n);
      mpz_clear(n);
      return(p);
    }
  
  if (mpfr_cmp_z(x1, i) >= 0)                /* if (x1 >= i) */
    {
      mpz_t n;
      
      if (mpz_cmp_ui(i, 0) >= 0)            /* if (i >= 0) */
	mpz_init_set(n, i);                 /*   num = i */
      else
	{
	  mpz_init(n);
	  mpfr_get_z(n, x1, GMP_RNDD);      /* else num = floor(x1) */
	}
      
      mpz_clear(i);
      mpfr_clear(ux);
      mpfr_clear(x0);
      mpfr_clear(x1);
      mpfr_clear(error);
      p = mpz_to_big_integer(sc, n);
      mpz_clear(n);
      return(p);
    }
  
  {
    mpz_t p0, q0, r, r1, p1, q1, old_p1, old_q1;
    mpfr_t val, e0, e1, e0p, e1p, old_e0, old_e1, old_e0p;
    
    mpz_init(i0);
    mpz_init(i1);
    mpfr_get_z(i0, x0, GMP_RNDD);            /* i0 = floor(x0) */
    mpfr_get_z(i1, x1, GMP_RNDU);            /* i1 = ceil(x1) */
    
    mpz_init_set(p0, i0);                    /* p0 = i0 */
    mpz_init_set_ui(q0, 1);                  /* q0 = 1 */
    mpz_init_set(p1, i1);                    /* p1 = i1 */
    mpz_init_set_ui(q1, 1);                  /* q1 = 1 */
    mpfr_init(e0);
    mpfr_init(e1);
    mpfr_init(e0p);
    mpfr_init(e1p);
    mpfr_sub_z(e0, x0, i1, GMP_RNDN);        /* e0 = i1 - x0 */
    mpfr_neg(e0, e0, GMP_RNDN);
    mpfr_sub_z(e1, x0, i0, GMP_RNDN);        /* e1 = x0 - i0 */
    mpfr_sub_z(e0p, x1, i1, GMP_RNDN);       /* e0p = i1 - x1 */
    mpfr_neg(e0p, e0p, GMP_RNDN);
    mpfr_sub_z(e1p, x1, i0, GMP_RNDN);       /* e1p = x1 - i0 */
    
    mpfr_init(val);
    
    mpfr_init(old_e0);
    mpfr_init(old_e1);
    mpfr_init(old_e0p);
    
    mpz_init(r);
    mpz_init(r1);
    mpz_init(old_p1);
    mpz_init(old_q1);
    
    while (true)
      {
	mpfr_set_z(val, p0, GMP_RNDN);
	mpfr_div_z(val, val, q0, GMP_RNDN);  /* val = p0/q0 */
	
	if (((mpfr_cmp(x0, val) <= 0) &&      /* if ((x0 <= val) && (val <= x1)) */
	     (mpfr_cmp(val, x1) <= 0)) ||
	    (mpfr_cmp_ui(e1, 0) == 0) ||
	    (mpfr_cmp_ui(e1p, 0) == 0)) 
	  /* these last 2 are probably not needed -- they protect against running out of bits in the non-gmp case above */
	  {
	    mpq_t q;
	    mpq_init(q);
	    mpq_set_num(q, p0);            /* return(p0/q0) */
	    mpq_set_den(q, q0);
	    
	    mpz_clear(i);
	    mpz_clear(i0);
	    mpz_clear(i1);
	    mpfr_clear(ux);
	    mpfr_clear(x0);
	    mpfr_clear(x1);
	    mpfr_clear(error);
	    
	    mpz_clear(p0);
	    mpz_clear(q0);
	    mpz_clear(r);
	    mpz_clear(r1);
	    mpz_clear(p1);
	    mpz_clear(q1);
	    mpz_clear(old_p1);
	    mpz_clear(old_q1);
	    
	    mpfr_clear(val);
	    mpfr_clear(e0);
	    mpfr_clear(e1);
	    mpfr_clear(e0p);
	    mpfr_clear(e1p);
	    mpfr_clear(old_e0);
	    mpfr_clear(old_e1);
	    mpfr_clear(old_e0p);
	    
	    p = mpq_to_big_ratio(sc, q);
	    mpq_clear(q);
	    return(p);
	  }
	
	mpfr_div(val, e0, e1, GMP_RNDN);
	mpfr_get_z(r, val, GMP_RNDD);           /* r = floor(e0/e1) */
	mpfr_div(val, e0p, e1p, GMP_RNDN);
	mpfr_get_z(r1, val, GMP_RNDU);          /* r1 = ceil(e0p/e1p) */
	if (mpz_cmp(r1, r) < 0)                 /* if (r1 < r) */
	  mpz_set(r, r1);                       /*   r = r1 */
	
	mpz_set(old_p1, p1);                    /* old_p1 = p1 */
	mpz_set(p1, p0);                        /* p1 = p0 */
	mpz_set(old_q1, q1);                    /* old_q1 = q1 */
	mpz_set(q1, q0);                        /* q1 = q0 */
	
	mpfr_set(old_e0, e0, GMP_RNDN);         /* old_e0 = e0 */
	mpfr_set(e0, e1p, GMP_RNDN);            /* e0 = e1p */
	mpfr_set(old_e0p, e0p, GMP_RNDN);       /* old_e0p = e0p */
	mpfr_set(e0p, e1, GMP_RNDN);            /* e0p = e1 */
	mpfr_set(old_e1, e1, GMP_RNDN);         /* old_e1 = e1 */
	
	mpz_mul(p0, p0, r);                     /* p0 = old_p1 + r * p0 */
	mpz_add(p0, p0, old_p1); 
	
	mpz_mul(q0, q0, r);                     /* q0 = old_q1 + r * q0 */
	mpz_add(q0, q0, old_q1); 
	
	mpfr_mul_z(e1, e1p, r, GMP_RNDN);       /* e1 = old_e0p - r * e1p */
	mpfr_sub(e1, old_e0p, e1, GMP_RNDN); 
	
	mpfr_mul_z(e1p, old_e1, r, GMP_RNDN);   /* e1p = old_e0 - r * old_e1 */
	mpfr_sub(e1p, old_e0, e1p, GMP_RNDN); 
      }
  }
}


static s7_pointer big_exact_to_inexact(s7_scheme *sc, s7_pointer args)
{
  #define H_exact_to_inexact "(exact->inexact num) converts num to an inexact number; (exact->inexact 3/2) = 1.5"
  s7_pointer p;

  p = car(args);
  if (!s7_is_number(p))   /* apparently (exact->inexact 1+i) is not an error */
    {
      check_method(sc, p, sc->EXACT_TO_INEXACT, args);
      return(simple_wrong_type_argument_with_type(sc, sc->EXACT_TO_INEXACT, p, A_NUMBER));
    }
  if (!s7_is_rational(p))
    return(p);

  return(promote_number(sc, T_BIG_REAL, to_big(sc, p)));
}


static s7_pointer big_inexact_to_exact(s7_scheme *sc, s7_pointer args)
{
  #define H_inexact_to_exact "(inexact->exact num) converts num to an exact number; (inexact->exact 1.5) = 3/2"

  s7_pointer p;
  p = car(args);

  if (s7_is_rational(p))
    return(p);

  if (!s7_is_real(p))
    {
      check_method(sc, p, sc->INEXACT_TO_EXACT, args);
      return(simple_wrong_type_argument(sc, sc->INEXACT_TO_EXACT, p, T_REAL));
    }
  return(big_rationalize(sc, args));
}


static s7_pointer big_convert_to_int(s7_scheme *sc, s7_pointer args,
				     s7_pointer sym,
				     void (*div_func)(mpz_ptr, mpz_srcptr, mpz_srcptr),
				     mp_rnd_t mode)
{
  /* we can't go to the normal (non-gmp) functions here */
  s7_pointer p;
  mpz_t n;

  p = car(args);
  if (!s7_is_real(p))
    {
      check_method(sc, p, sym, args);
      return(simple_wrong_type_argument(sc, sym, p, T_REAL));
    }
  if (s7_is_integer(p))
    return(p);

  p = to_big(sc, p);
  if (type(p) == T_BIG_RATIO)
    {
      /* apparently we have to do the divide by hand */
      mpz_t d;
      mpz_init_set(n, mpq_numref(big_ratio(p)));
      mpz_init_set(d, mpq_denref(big_ratio(p)));
      div_func(n, n, d);
      mpz_clear(d);
    }
  else
    {
      if ((g_is_nan(sc, args) == sc->T) ||
	  (g_is_infinite(sc, args)) == sc->T)
	return(simple_out_of_range(sc, sym, p, (g_is_nan(sc, args) == sc->T) ? ITS_NAN : ITS_INFINITE));

      mpz_init(n);
      mpfr_get_z(n, big_real(p), mode);
    }
  p = mpz_to_big_integer(sc, n);
  mpz_clear(n);
  return(p);
}


static s7_pointer big_floor(s7_scheme *sc, s7_pointer args)
{
  #define H_floor "(floor x) returns the integer closest to x toward -inf"
  return(big_convert_to_int(sc, args, sc->FLOOR, mpz_fdiv_q, GMP_RNDD));
}


static s7_pointer big_ceiling(s7_scheme *sc, s7_pointer args)
{
  #define H_ceiling "(ceiling x) returns the integer closest to x toward inf"
  return(big_convert_to_int(sc, args, sc->CEILING, mpz_cdiv_q, GMP_RNDU));
}


static s7_pointer big_truncate(s7_scheme *sc, s7_pointer args)
{
  #define H_truncate "(truncate x) returns the integer closest to x toward 0"
  return(big_convert_to_int(sc, args, sc->TRUNCATE, mpz_tdiv_q, GMP_RNDZ));
}


static s7_pointer big_round(s7_scheme *sc, s7_pointer args)
{
  #define H_round "(round x) returns the integer closest to x"
  s7_pointer p;
  mpz_t n;

  p = car(args);
  if (!s7_is_real(p))
    {
      check_method(sc, p, sc->ROUND, args);
      return(simple_wrong_type_argument(sc, sc->ROUND, p, T_REAL));
    }
  if (s7_is_integer(p))
    return(p);

  p = to_big(sc, p);
  if (type(p) == T_BIG_INTEGER)
    return(p);

  if (type(p) == T_BIG_RATIO)
    {
      int rnd;
      mpz_t rm;
      mpz_init_set(n, mpq_numref(big_ratio(p)));
      mpz_init(rm);
      mpz_fdiv_qr(n, rm, n, mpq_denref(big_ratio(p)));
      mpz_mul_ui(rm, rm, 2);
      rnd = mpz_cmpabs(rm, mpq_denref(big_ratio(p)));
      mpz_fdiv_q(rm, rm, mpq_denref(big_ratio(p)));
      if (rnd > 0)
	mpz_add(n, n, rm);
      else
	{
	  if (rnd == 0)
	    {
	      if (mpz_odd_p(n))
		mpz_add_ui(n, n, 1);
	    }
	}
      mpz_clear(rm);
      p = mpz_to_big_integer(sc, n);
      mpz_clear(n);
      return(p);
    }

  if ((g_is_nan(sc, args) == sc->T) ||
      (g_is_infinite(sc, args)) == sc->T)
    return(simple_out_of_range(sc, sc->ROUND, p, (g_is_nan(sc, args) == sc->T) ? ITS_NAN : ITS_INFINITE));

  {
    int cmp_res;
    mpz_t fl, ce;
    mpfr_t x, dfl, dce;
    mpfr_init_set(x, big_real(p), GMP_RNDN);
    mpz_init(fl);
    mpfr_get_z(fl, x, GMP_RNDD);           /* fl = floor(x) */
    mpz_init(ce);
    mpfr_get_z(ce, x, GMP_RNDU);           /* ce = ceil(x) */
    mpfr_init(dfl);
    mpfr_sub_z(dfl, x, fl, GMP_RNDN);      /* dfl = x - fl */
    mpfr_init(dce);
    mpfr_sub_z(dce, x, ce, GMP_RNDN);      /* dce = -(ce - x) */
    mpfr_neg(dce, dce, GMP_RNDN);          /*    and reversed */
    cmp_res = mpfr_cmp(dfl, dce);
    if (cmp_res > 0)                       /* if (dfl > dce) return(ce) */
      mpz_init_set(n, ce);
    else
      {
	if (cmp_res < 0)                   /* if (dfl < dce) return(fl) */
	  mpz_init_set(n, fl);
	else
	  {
	    if (mpz_even_p(fl))
	      mpz_init_set(n, fl);         /* if (mod(fl, 2) == 0) return(fl) */
	    else mpz_init_set(n, ce);      /* else return(ce) */
	  }
      }
    mpz_clear(fl);
    mpz_clear(ce);
    mpfr_clear(dfl);
    mpfr_clear(dce);
    mpfr_clear(x);
    p = mpz_to_big_integer(sc, n);
    mpz_clear(n);
    return(p);
  }
}


static s7_pointer big_quotient(s7_scheme *sc, s7_pointer args)
{
  #define H_quotient "(quotient x1 x2) returns the integer quotient of x1 and x2; (quotient 4 3) = 1"

  s7_pointer x, y, p;
  x = car(args);
  y = cadr(args);

  if (!s7_is_real(x))
    {
      check_method(sc, x, sc->QUOTIENT, args);
      return(wrong_type_argument(sc, sc->QUOTIENT, small_int(1), x, T_REAL));
    }
  if (!s7_is_real(y))
    {
      check_method(sc, y, sc->QUOTIENT, args);
      return(wrong_type_argument(sc, sc->QUOTIENT, small_int(2), y, T_REAL));
    }
  if ((s7_is_integer(x)) &&
      (s7_is_integer(y)))
    {
      mpz_t n;
      x = to_big(sc, x);
      y = to_big(sc, y);

      if (s7_is_zero(y))
	return(division_by_zero_error(sc, sc->QUOTIENT, args));
	  
      mpz_init_set(n, big_integer(x));
      mpz_tdiv_q(n, n, big_integer(y));

      p = mpz_to_big_integer(sc, n);
      mpz_clear(n);
      return(p);
    }
  return(big_truncate(sc, list_1(sc, big_divide(sc, args))));
}


static s7_pointer big_remainder(s7_scheme *sc, s7_pointer args)
{
  #define H_remainder "(remainder x1 x2) returns the integer remainder of x1 and x2; (remainder 10 3) = 1"
  s7_pointer x, y, p;
  x = car(args);
  y = cadr(args);

  if (!s7_is_real(x))
    {
      check_method(sc, x, sc->REMAINDER, args);
      return(wrong_type_argument(sc, sc->REMAINDER, small_int(1), x, T_REAL));
    }
  if (!s7_is_real(y))
    {
      check_method(sc, y, sc->REMAINDER, args);
      return(wrong_type_argument(sc, sc->REMAINDER, small_int(2), y, T_REAL));
    }
  if ((s7_is_integer(x)) &&
      (s7_is_integer(y)))
    {
      mpz_t n;
      x = to_big(sc, x);
      y = to_big(sc, y);

      if (s7_is_zero(y))
	return(division_by_zero_error(sc, sc->REMAINDER, args));
	  
      mpz_init_set(n, big_integer(x));
      mpz_tdiv_r(n, n, big_integer(y));

      p = mpz_to_big_integer(sc, n);
      mpz_clear(n);
      return(p);
    }
  return(big_subtract(sc, 
          list_2(sc, x, 
           big_multiply(sc, 
            list_2(sc, y, 
             big_quotient(sc, args))))));
}


static s7_pointer big_modulo(s7_scheme *sc, s7_pointer args)
{
  #define H_modulo "(modulo x1 x2) returns x1 mod x2; (modulo 4 3) = 1.  The arguments can be real numbers."
  s7_pointer a, b, p;

  a = car(args);
  if (!s7_is_real(a))
    {
      check_method(sc, a, sc->MODULO, args);
      return(wrong_type_argument(sc, sc->MODULO, small_int(1), a, T_REAL));
    }
  b = cadr(args);
  if (!s7_is_real(b))
    {
      check_method(sc, b, sc->MODULO, args);
      return(wrong_type_argument(sc, sc->MODULO, small_int(2), b, T_REAL));
    }
  a = to_big(sc, a);
  b = to_big(sc, b);

  if ((s7_is_integer(a)) &&
      (s7_is_integer(b)))
    {
      s7_pointer x, y;
      int cy, cz;
      mpz_t n;
      
      y = promote_number(sc, T_BIG_INTEGER, b);
      if (mpz_cmp_ui(big_integer(y), 0) == 0)
	return(a);
      
      x = promote_number(sc, T_BIG_INTEGER, a);
      /* mpz_mod is too tricky here */

      mpz_init_set(n, big_integer(x));
      mpz_fdiv_r(n, n, big_integer(y));
      cy = mpz_cmp_ui(big_integer(y), 0);
      cz = mpz_cmp_ui(n, 0);
      if (((cy < 0) && (cz > 0)) ||
	  ((cy > 0) && (cz < 0)))
	mpz_add(n, n, big_integer(y));

      p = mpz_to_big_integer(sc, n);
      mpz_clear(n);
      return(p);
    }
  return(big_subtract(sc, 
          list_2(sc, a, 
           big_multiply(sc, 
            list_2(sc, b, 
    	     big_floor(sc, 
              list_1(sc, 
               big_divide(sc, 
	        list_2(sc, a, b)))))))));
}


static int big_real_scan_args(s7_scheme *sc, s7_pointer args)
{
  int i, result_type = T_INTEGER;
  s7_pointer arg;

  for (i = 1, arg = args; is_not_null(arg); i++, arg = cdr(arg)) 
    {
      s7_pointer p;
      p = car(arg);
      if (!is_real_via_method(sc, p))
	return(-i);
      result_type = get_result_type(sc, result_type, p);
    }
  return(result_type);
}


static s7_pointer big_max(s7_scheme *sc, s7_pointer args)
{
  int result_type;
  s7_pointer x, result, arg;
  
  result_type = big_real_scan_args(sc, args);
  if (result_type < 0)
    return(wrong_type_argument_n(sc, sc->MAX, -result_type, s7_list_ref(sc, args, -1 - result_type), T_REAL));

  if (result_type < T_BIG_INTEGER)
    return(g_max(sc, args));

  if (!s7_is_number(car(args)))
    check_method(sc, car(args), sc->MAX, args);

  result = promote_number(sc, result_type, car(args));

  for (x = cdr(args); is_not_null(x); x = cdr(x)) 
    {
      if (!s7_is_number(car(x)))
	check_method(sc, car(x), sc->MAX, cons(sc, result, x));

      arg = promote_number(sc, result_type, car(x));
      switch (result_type)
	{
	case T_BIG_INTEGER:
	  if (mpz_cmp(big_integer(result), big_integer(arg)) < 0)
	    result = arg;
	  break;
	  
	case T_BIG_RATIO:
	  if (mpq_cmp(big_ratio(result), big_ratio(arg)) < 0)
	    result = arg;
	  break;

	case T_BIG_REAL:
	  if (mpfr_cmp(big_real(result), big_real(arg)) < 0)
	    result = arg;
	  break;
	}
    }
  if (result_type == T_BIG_RATIO) /* maybe actual result was an int */
    {
      if (mpz_cmp_ui(mpq_denref(big_ratio(result)), 1) == 0)
	{
	  mpz_t n;
	  s7_pointer p;
	  mpz_init_set(n, mpq_numref(big_ratio(result)));
	  p = mpz_to_big_integer(sc, n);
	  mpz_clear(n);
	  return(p);
	}
    }
  return(result);
}


static s7_pointer big_min(s7_scheme *sc, s7_pointer args)
{
  int result_type;
  s7_pointer x, result, arg;
  
  result_type = big_real_scan_args(sc, args);
  if (result_type < 0)
    return(wrong_type_argument_n(sc, sc->MIN, -result_type, s7_list_ref(sc, args, -1 - result_type), T_REAL));

  if (result_type < T_BIG_INTEGER)
    return(g_min(sc, args));

  if (!s7_is_number(car(args)))
    check_method(sc, car(args), sc->MIN, args);

  result = promote_number(sc, result_type, car(args));

  for (x = cdr(args); is_not_null(x); x = cdr(x)) 
    {
      if (!s7_is_number(car(x)))
	check_method(sc, car(x), sc->MIN, cons(sc, result, x));

      arg = promote_number(sc, result_type, car(x));
      switch (result_type)
	{
	case T_BIG_INTEGER:
	  if (mpz_cmp(big_integer(result), big_integer(arg)) > 0)
	    result = arg;
	  break;
	  
	case T_BIG_RATIO:
	  if (mpq_cmp(big_ratio(result), big_ratio(arg)) > 0)
	    result = arg;
	  break;

	case T_BIG_REAL:
	  if (mpfr_cmp(big_real(result), big_real(arg)) > 0)
	    result = arg;
	  break;
	}
    }
  if (result_type == T_BIG_RATIO) /* maybe actual result was an int */
    {
      if (mpz_cmp_ui(mpq_denref(big_ratio(result)), 1) == 0)
	{
	  mpz_t n;
	  s7_pointer p;
	  mpz_init_set(n, mpq_numref(big_ratio(result)));
	  p = mpz_to_big_integer(sc, n);
	  mpz_clear(n);
	  return(p);
	}
    }
  return(result);
}


static s7_pointer big_less(s7_scheme *sc, s7_pointer args)
{
  #define H_less "(< x1 ...) returns #t if its arguments are in increasing order"
  int result_type;
  s7_pointer x, previous, current;
  
  result_type = big_real_scan_args(sc, args);
  if (result_type < 0)
    return(wrong_type_argument_n(sc, sc->LT, -result_type, s7_list_ref(sc, args, -1 - result_type), T_REAL));

  /* don't try to use g_less here */
  if (result_type < T_BIG_INTEGER)
    result_type += 4;

  if (!s7_is_number(car(args)))
    check_method(sc, car(args), sc->LT, args);

  previous = promote_number(sc, result_type, car(args));

  for (x = cdr(args); is_not_null(x); x = cdr(x)) 
    {
      if (!s7_is_number(car(x)))
	check_method(sc, car(x), sc->LT, cons(sc, previous, x));

      current = promote_number(sc, result_type, car(x));
      switch (result_type)
	{
	case T_BIG_INTEGER:
	  if (mpz_cmp(big_integer(previous), big_integer(current)) >= 0) return(sc->F);
	  break;
	  
	case T_BIG_RATIO:
	  if (mpq_cmp(big_ratio(previous), big_ratio(current)) >= 0) return(sc->F);
	  break;

	case T_BIG_REAL:
	  if (mpfr_cmp(big_real(previous), big_real(current)) >= 0) return(sc->F);
	  break;
	}
      previous = current;
    }
  return(sc->T);
}


static s7_pointer big_less_or_equal(s7_scheme *sc, s7_pointer args)
{
  #define H_less_or_equal "(<= x1 ...) returns #t if its arguments are in increasing order"
  int result_type;
  s7_pointer x, previous, current;
  
  result_type = big_real_scan_args(sc, args);
  if (result_type < 0)
    return(wrong_type_argument_n(sc, sc->LEQ, -result_type, s7_list_ref(sc, args, -1 - result_type), T_REAL));

  if (result_type < T_BIG_INTEGER)
    result_type += 4;

  if (!s7_is_number(car(args)))
    check_method(sc, car(args), sc->LEQ, args);

  previous = promote_number(sc, result_type, car(args));

  for (x = cdr(args); is_not_null(x); x = cdr(x)) 
    {
      if (!s7_is_number(car(x)))
	check_method(sc, car(x), sc->LEQ, cons(sc, previous, x));

      current = promote_number(sc, result_type, car(x));
      switch (result_type)
	{
	case T_BIG_INTEGER:
	  if (mpz_cmp(big_integer(previous), big_integer(current)) > 0) return(sc->F);
	  break;
	  
	case T_BIG_RATIO:
	  if (mpq_cmp(big_ratio(previous), big_ratio(current)) > 0) return(sc->F);
	  break;

	case T_BIG_REAL:
	  if (mpfr_cmp(big_real(previous), big_real(current)) > 0) return(sc->F);
	  break;
	}
      previous = current;
    }
  return(sc->T);
}


static s7_pointer big_greater(s7_scheme *sc, s7_pointer args)
{
  #define H_greater "(> x1 ...) returns #t if its arguments are in decreasing order"
  int result_type;
  s7_pointer x, previous, current;

  result_type = big_real_scan_args(sc, args);
  if (result_type < 0)
    return(wrong_type_argument_n(sc, sc->GT, -result_type, s7_list_ref(sc, args, -1 - result_type), T_REAL));

  if (result_type < T_BIG_INTEGER)
    result_type += 4;

  if (!s7_is_number(car(args)))
    check_method(sc, car(args), sc->GT, args);

  previous = promote_number(sc, result_type, car(args));

  for (x = cdr(args); is_not_null(x); x = cdr(x)) 
    {
      if (!s7_is_number(car(x)))
	check_method(sc, car(x), sc->GT, cons(sc, previous, x));
      current = promote_number(sc, result_type, car(x));
      switch (result_type)
	{
	case T_BIG_INTEGER:
	  if (mpz_cmp(big_integer(previous), big_integer(current)) <= 0) return(sc->F);
	  break;
	  
	case T_BIG_RATIO:
	  if (mpq_cmp(big_ratio(previous), big_ratio(current)) <= 0) return(sc->F);
	  break;

	case T_BIG_REAL:
	  if (mpfr_cmp(big_real(previous), big_real(current)) <= 0) return(sc->F);
	  break;
	}
      previous = current;
    }
  return(sc->T);
}


static s7_pointer big_greater_or_equal(s7_scheme *sc, s7_pointer args)
{
  #define H_greater_or_equal "(>= x1 ...) returns #t if its arguments are in decreasing order"
  int result_type;
  s7_pointer x, previous, current;
  
  result_type = big_real_scan_args(sc, args);
  if (result_type < 0)
    return(wrong_type_argument_n(sc, sc->GEQ, -result_type, s7_list_ref(sc, args, -1 - result_type), T_REAL));

  if (result_type < T_BIG_INTEGER)
    result_type += 4;

  if (!s7_is_number(car(args)))
    check_method(sc, car(args), sc->GEQ, args);
  previous = promote_number(sc, result_type, car(args));

  for (x = cdr(args); is_not_null(x); x = cdr(x)) 
    {
      if (!s7_is_number(car(x)))
	check_method(sc, car(x), sc->GEQ, cons(sc, previous, x));
      current = promote_number(sc, result_type, car(x));
      switch (result_type)
	{
	case T_BIG_INTEGER:
	  if (mpz_cmp(big_integer(previous), big_integer(current)) < 0) return(sc->F);
	  break;
	  
	case T_BIG_RATIO:
	  if (mpq_cmp(big_ratio(previous), big_ratio(current)) < 0) return(sc->F);
	  break;

	case T_BIG_REAL:
	  if (mpfr_cmp(big_real(previous), big_real(current)) < 0) return(sc->F);
	  break;
	}
      previous = current;
    }
  return(sc->T);
}


static s7_pointer big_equal(s7_scheme *sc, s7_pointer args)
{
  int result_type = T_INTEGER;
  s7_pointer x, y, result;

  for (x = args; is_not_null(x); x = cdr(x)) 
    {
      s7_pointer p;
      p = car(x);

      result_type = get_result_type(sc, result_type, p);
      if (result_type < 0)
	return(wrong_type_argument_n_with_type(sc, sc->EQ, position_of(x, args), p, A_NUMBER));

      switch (type(p))
	{
	case T_REAL:
	  if (is_NaN(real(p)))  /* (= (bignum "3") 1/0) */
	    return(sc->F);

	case T_COMPLEX:
	      if ((is_NaN(real_part(p))) ||
		  (is_NaN(imag_part(p))))
		return(sc->F);
	}
    }

  if (result_type < T_BIG_INTEGER)
    return(g_equal(sc, args));

  if (!s7_is_number(car(args)))
    check_method(sc, car(args), sc->EQ, args);

  result = promote_number(sc, result_type, car(args));

  for (y = cdr(args); is_not_null(y); y = cdr(y)) 
    {
      s7_pointer arg;
      if (!s7_is_number(car(y)))
	check_method(sc, car(y), sc->EQ, cons(sc, result, y));

      arg = promote_number(sc, result_type, car(y));
      switch (result_type)
	{
	case T_BIG_INTEGER:
	  if (mpz_cmp(big_integer(result), big_integer(arg)) != 0) return(sc->F);
	  break;
	  
	case T_BIG_RATIO:
	  if (mpq_cmp(big_ratio(result), big_ratio(arg)) != 0) return(sc->F);
	  break;
	  
	case T_BIG_REAL:
	  if (mpfr_cmp(big_real(result), big_real(arg)) != 0) return(sc->F);
	  break;
	  
	case T_BIG_COMPLEX:
	  if (mpc_cmp(big_complex(result), big_complex(arg)) != 0) return(sc->F);
	  break;
	}
    }
  return(sc->T);
}


static s7_pointer big_gcd(s7_scheme *sc, s7_pointer args)
{
  #define H_gcd "(gcd ...) returns the greatest common divisor of its rational arguments"
  bool rats = false;
  s7_pointer x, lst;

  for (x = args; is_not_null(x); x = cdr(x)) 
    {
      if (!is_rational_via_method(sc, car(x)))
	return(wrong_type_argument_n_with_type(sc, sc->GCD, position_of(x, args), car(x), A_RATIONAL));
      if (!rats)
	rats = (!is_integer_via_method(sc, car(x)));
    }

  if (is_null(cdr(args)))    /* (gcd -2305843009213693951/4611686018427387903) */
    return(big_abs(sc, args)); 

  if (!rats)
    {
      mpz_t n;
      mpz_init(n);
      for (x = args; is_not_null(x); x = cdr(x)) 
	{
	  if (!s7_is_number(car(x)))
	    {
	      lst = cons(sc, mpz_to_big_integer(sc, n), x);
	      mpz_clear(n);
	      check_method(sc, car(x), sc->GCD, lst);
	      return(wrong_type_argument_n(sc, sc->GCD, position_of(x, args), car(x), T_INTEGER));  
	    }
	  mpz_gcd(n, n, big_integer(promote_number(sc, T_BIG_INTEGER, car(x))));
	  if (mpz_cmp_ui(n, 1) == 0)
	    {
	      mpz_clear(n);
	      return(small_int(1));
	    }
	}
      x = mpz_to_big_integer(sc, n);
      mpz_clear(n);
      return(x);
    }

  {
    s7_pointer rat;
    mpq_t q;
    mpz_t n, d;

    if (!s7_is_number(car(args)))
      check_method(sc, car(args), sc->GCD, args);
    
    rat = promote_number(sc, T_BIG_RATIO, car(args));
    mpz_init_set(n, mpq_numref(big_ratio(rat)));
    mpz_init_set(d, mpq_denref(big_ratio(rat)));
    for (x = cdr(args); is_not_null(x); x = cdr(x))
      {
	if (!s7_is_number(car(x)))
	  {
	    mpq_init(q);
	    mpq_set_num(q, n);
	    mpq_set_den(q, d);
	    lst = cons(sc, mpq_to_big_ratio(sc, q), x);
	    mpz_clear(n);
	    mpz_clear(d);
	    mpq_clear(q);
	    check_method(sc, car(x), sc->GCD, lst);
	    return(wrong_type_argument_n_with_type(sc, sc->GCD, position_of(x, args), car(x), A_RATIONAL));  
	  }
	rat = promote_number(sc, T_BIG_RATIO, car(x));
	mpz_gcd(n, n, mpq_numref(big_ratio(rat)));
	mpz_lcm(d, d, mpq_denref(big_ratio(rat)));
      }
    if (mpz_cmp_ui(d, 1) == 0)
      {
	rat = mpz_to_big_integer(sc, n);
	mpz_clear(n);
	mpz_clear(d);
	return(rat);
      }

    mpq_init(q);
    mpq_set_num(q, n);
    mpq_set_den(q, d);
    mpz_clear(n);
    mpz_clear(d);

    x = mpq_to_big_ratio(sc, q);
    mpq_clear(q);
    return(x);
  }
}


static s7_pointer big_lcm(s7_scheme *sc, s7_pointer args)
{
  #define H_lcm "(lcm ...) returns the least common multiple of its rational arguments"
  s7_pointer x, lst;
  bool rats = false;

  for (x = args; is_not_null(x); x = cdr(x)) 
    {
      if (!is_rational_via_method(sc, car(x)))
	return(wrong_type_argument_n_with_type(sc, sc->LCM, position_of(x, args), car(x), A_RATIONAL));
      if (!rats)
	rats = (!is_integer_via_method(sc, car(x)));
    }
  
   if (is_null(cdr(args)))    /* (lcm -2305843009213693951/4611686018427387903) */
      return(big_abs(sc, args)); 
    
   if (!rats)
    {
      mpz_t n;
      mpz_init(n);
      mpz_set_ui(n, 1);
      for (x = args; is_not_null(x); x = cdr(x)) 
	{
	  if (!s7_is_number(car(x)))
	    {
	      lst = cons(sc, mpz_to_big_integer(sc, n), x);
	      mpz_clear(n);
	      check_method(sc, car(x), sc->LCM, lst);
	      return(wrong_type_argument_n(sc, sc->LCM, position_of(x, args), car(x), T_INTEGER));  
	    }
	  mpz_lcm(n, n, big_integer(promote_number(sc, T_BIG_INTEGER, car(x))));
	  if (mpz_cmp_ui(n, 0) == 0)
	    {
	      mpz_clear(n);
	      return(small_int(0));
	    }
	}
      x = mpz_to_big_integer(sc, n);
      mpz_clear(n);
      return(x);
    }

  {
    s7_pointer rat;
    mpq_t q;
    mpz_t n, d;
    
    if (!s7_is_number(car(args)))
      check_method(sc, car(args), sc->LCM, args);

    rat = promote_number(sc, T_BIG_RATIO, car(args));
    mpz_init_set(n, mpq_numref(big_ratio(rat)));
    if (mpz_cmp_ui(n, 0) == 0)
      {
	mpz_clear(n);
	return(small_int(0));
      }

    mpz_init_set(d, mpq_denref(big_ratio(rat)));
    for (x = cdr(args); is_not_null(x); x = cdr(x))
      {
	if (!s7_is_number(car(x)))
	  {
	    mpq_init(q);
	    mpq_set_num(q, n);
	    mpq_set_den(q, d);
	    lst = cons(sc, mpq_to_big_ratio(sc, q), x);
	    mpz_clear(n);
	    mpz_clear(d);
	    mpq_clear(q);
	    check_method(sc, car(x), sc->LCM, lst);
	    return(wrong_type_argument_n_with_type(sc, sc->GCD, position_of(x, args), car(x), A_RATIONAL));  
	  }

	rat = promote_number(sc, T_BIG_RATIO, car(x));
	mpz_lcm(n, n, mpq_numref(big_ratio(rat)));
	if (mpz_cmp_ui(n, 0) == 0)
	  {
	    mpz_clear(n);
	    mpz_clear(d);
	    return(small_int(0));
	  }
	mpz_gcd(d, d, mpq_denref(big_ratio(rat)));
      }

    if (mpz_cmp_ui(d, 1) == 0)
      {
	rat = mpz_to_big_integer(sc, n);
	mpz_clear(n);
	mpz_clear(d);
	return(rat);
      }

    mpq_init(q);
    mpq_set_num(q, n);
    mpq_set_den(q, d);
    mpz_clear(n);
    mpz_clear(d);
    x = mpq_to_big_ratio(sc, q);
    mpq_clear(q);
    return(x);
  }
}


static s7_pointer g_set_bignum_precision(s7_scheme *sc, s7_pointer args)
{
  mp_prec_t bits;
  s7_Int precision;
  s7_pointer prec;

  prec = cadr(args);
  if (!s7_is_integer(prec))
    return(simple_wrong_type_argument(sc, sc->BIGNUM_PRECISION, prec, T_INTEGER));

  precision = s7_integer(prec);
  if (precision <= 1)                   /* (set! *bignum-precision* 1) causes mpfr to segfault! (also 0 and -1) */
    return(s7_out_of_range_error(sc, "set! *bignum-precision*", 0, prec, "has to be greater than 1"));    

  bits = (mp_prec_t)precision;
  mpfr_set_default_prec(bits);
  mpc_set_default_precision(bits);
  s7_symbol_set_value(sc, sc->PI, big_pi(sc));

  return(prec);
}


typedef struct {
  gmp_randstate_t state;
} s7_big_rng_t;


static char *print_big_rng(s7_scheme *sc, void *val)
{
  char *buf;
  s7_big_rng_t *r = (s7_big_rng_t *)val;
  buf = (char *)malloc(64 * sizeof(char));
  snprintf(buf, 64, "#<big-rng %p>", r);
  return(buf);
}


static void free_big_rng(void *val)
{
  s7_big_rng_t *r = (s7_big_rng_t *)val;
  gmp_randclear(r->state);
  free(r);
}


static bool equal_big_rng(void *val1, void *val2)
{
  return(val1 == val2);
  /* I don't think the state is accessible, so we can't mimic the normal rng_t case */
}

  
static s7_pointer make_big_random_state(s7_scheme *sc, s7_pointer args)
{
#ifndef H_make_random_state
  #define H_make_random_state "(make-random-state seed) returns a new random number state initialized with 'seed'. \
Pass this as the second argument to 'random' to get a repeatable random number sequence:\n\
    (let ((seed (make-random-state 1234))) (random 1.0 seed))"
#endif

  s7_big_rng_t *r;
  s7_pointer seed;

  seed = car(args);
  if (!s7_is_integer(seed))
    {
      check_method(sc, seed, sc->MAKE_RANDOM_STATE, args);
      return(simple_wrong_type_argument(sc, sc->MAKE_RANDOM_STATE, seed, T_INTEGER));
    }
  if (is_big_number(seed))
    {
      r = (s7_big_rng_t *)calloc(1, sizeof(s7_big_rng_t));
      gmp_randinit_default(r->state);
      gmp_randseed(r->state, big_integer(seed));
      return(s7_make_object(sc, sc->big_rng_tag, (void *)r));
    }
  return(s7_make_random_state(sc, args));
}


static s7_pointer big_random(s7_scheme *sc, s7_pointer args)
{
  s7_pointer num, state, x;
  state = sc->NIL;

  num = car(args); 
  if (!s7_is_number(num))
    {
      check_method(sc, num, sc->RANDOM, args);
      return(simple_wrong_type_argument_with_type(sc, sc->RANDOM, num, A_NUMBER));
    }
  if (is_not_null(cdr(args)))
    {
      state = cadr(args);
      if ((!is_c_object(state)) ||
	  ((c_object_type(state) != sc->big_rng_tag) &&
	   (c_object_type(state) != sc->rng_tag)))
	return(s7_wrong_type_arg_error(sc, "random state", 2, state, "a random-state object"));
    }

  if (s7_is_zero(num))
    return(num);

  if ((is_big_number(num)) ||
      ((is_c_object(state)) &&
       (c_object_type(state) == sc->big_rng_tag)))
    {
      /* bignum case -- provide a state if none was passed,
       *   promote num if bignum state was passed but num is not a bignum
       *   if num==0, just return 0 (above) since gmp otherwise throws an arithmetic exception
       */
      s7_big_rng_t *r = NULL;

      if (is_null(state))
	{
	  /* no state passed, so make one */

	  if (!sc->default_big_rng)
	    {
	      mpz_t seed;
	      r = (s7_big_rng_t *)calloc(1, sizeof(s7_big_rng_t));
	      mpz_init_set_ui(seed, (unsigned int)time(NULL));
	      gmp_randinit_default(r->state);
	      gmp_randseed(r->state, seed);
	      mpz_clear(seed);
	      sc->default_big_rng = (void *)r;
	    }
	  else r = (s7_big_rng_t *)(sc->default_big_rng);
	}
      else
	{
	  /* state was passed, check its type */
	  if (c_object_type(state) == sc->rng_tag)
	    {
	      /* here "num" is a bignum, the state was passed, but it is intended for non-bignums */
	      switch (type(num))
		{
		case T_BIG_REAL: 
		  num = make_real(sc, (s7_Double)mpfr_get_d(big_real(num), GMP_RNDN));
		  break;

		case T_BIG_INTEGER:
		  num = make_integer(sc, big_integer_to_s7_Int(big_integer(num)));
		  break;

		case T_BIG_RATIO:
		  num = s7_make_ratio(sc, 
				      big_integer_to_s7_Int(mpq_numref(big_ratio(num))), 
				      big_integer_to_s7_Int(mpq_denref(big_ratio(num))));
		  break;

		case T_BIG_COMPLEX:
		  num = s7_make_complex(sc, 
					(s7_Double)mpfr_get_d(mpc_realref(big_complex(num)), GMP_RNDN),
					(s7_Double)mpfr_get_d(mpc_imagref(big_complex(num)), GMP_RNDN));
		  break;
		}
	      return(g_random(sc, list_2(sc, num, state)));
	    }
	  r = (s7_big_rng_t *)s7_object_value(state);
	}

      if (!is_big_number(num))
	{
	  switch (type(num))
	    {
	    case T_INTEGER:
	      num = promote_number(sc, T_BIG_INTEGER, num);
	      break;

	    case T_RATIO:
	      num = promote_number(sc, T_BIG_RATIO, num);
	      break;

	    case T_REAL:
	      num = promote_number(sc, T_BIG_REAL, num);
	      break;

	    default:
	      num = promote_number(sc, T_BIG_COMPLEX, num);
	      break;
	    }
	}
      /* finally both the state and the number are big */
	    
      switch (type(num))
	{
	case T_BIG_INTEGER:
	  {
	    mpz_t n;
	    mpz_init(n);
	    mpz_urandomm(n, r->state, big_integer(num));
	    
	    /* this does not work if num is a negative number -- you get positive results.
	     *   so check num for sign, and negate result if necessary.
	     */
	    if (mpz_cmp_ui(big_integer(num), 0) < 0)
	      mpz_neg(n, n);
	    
	    x = mpz_to_big_integer(sc, n);
	    mpz_clear(n);
	    return(x);
	  }
      
	case T_BIG_RATIO:
	  {
	    mpfr_t n, e;
	    mpfr_t rat;
	    
	    mpfr_init_set_ui(n, 1, GMP_RNDN);
	    mpfr_urandomb(n, r->state);
	    mpfr_init_set_q(rat, big_ratio(num), GMP_RNDN);
	    mpfr_mul(n, n, rat, GMP_RNDN);
	    
	    mpfr_init_set_str(e, "0.0000001", 10, GMP_RNDN);
	    mpfr_mul(e, e, rat, GMP_RNDN);
	    mpfr_clear(rat);
	    /* as in g_random, small ratios are a problem because the error term (sc->default_rationalize_error = 1e-12 here)
	     *   clobbers everything to 0.
	     */
	    x = big_rationalize(sc, list_2(sc, mpfr_to_big_real(sc, n), mpfr_to_big_real(sc, e)));
	    mpfr_clear(n);
	    mpfr_clear(e);
	    return(x);
	  }
      
	case T_BIG_REAL:
	  {
	    mpfr_t n;
	    mpfr_init_set_ui(n, 1, GMP_RNDN);
	    mpfr_urandomb(n, r->state);
	    mpfr_mul(n, n, big_real(num), GMP_RNDN);
	    x = mpfr_to_big_real(sc, n);
	    mpfr_clear(n);
	    return(x);
	  }
	  
	case T_BIG_COMPLEX:
	  {
	    mpc_t n;
	    mpc_init(n);
	    mpc_urandom(n, r->state);
	    mpfr_mul(mpc_realref(n), mpc_realref(n), mpc_realref(big_complex(num)), GMP_RNDN);
	    mpfr_mul(mpc_imagref(n), mpc_imagref(n), mpc_imagref(big_complex(num)), GMP_RNDN);
	    x = mpc_to_big_complex(sc, n);
	    mpc_clear(n);
	    return(x);
	  }
	}
    }
  return(g_random(sc, args));
}


static void s7_gmp_init(s7_scheme *sc)
{
  /* if these are defined as "safe", the optimizer needs to be smarter about things like (/ 1 ...)
   */
  sc->ADD =              s7_define_function(sc, "+",                   big_add,              0, 0, true,  H_add);
  sc->MINUS =            s7_define_function(sc, "-",                   big_subtract,         1, 0, true,  H_subtract);
  sc->MULTIPLY =         s7_define_function(sc, "*",                   big_multiply,         0, 0, true,  H_multiply);
  sc->DIVIDE =           s7_define_function(sc, "/",                   big_divide,           1, 0, true,  H_divide);

  sc->MAX =              s7_define_function(sc, "max",                 big_max,              1, 0, true,  H_max);
  sc->MIN =              s7_define_function(sc, "min",                 big_min,              1, 0, true,  H_min);
  sc->LT =               s7_define_function(sc, "<",                   big_less,             2, 0, true,  H_less);
  sc->LEQ =              s7_define_function(sc, "<=",                  big_less_or_equal,    2, 0, true,  H_less_or_equal);
  sc->GT =               s7_define_function(sc, ">",                   big_greater,          2, 0, true,  H_greater);
  sc->GEQ =              s7_define_function(sc, ">=",                  big_greater_or_equal, 2, 0, true,  H_greater_or_equal);
  sc->EQ =               s7_define_function(sc, "=",                   big_equal,            2, 0, true,  H_equal);

  sc->RATIONALIZE =      s7_define_function(sc, "rationalize",         big_rationalize,      1, 1, false, H_rationalize);
  sc->EXACT_TO_INEXACT = s7_define_function(sc, "exact->inexact",      big_exact_to_inexact, 1, 0, false, H_exact_to_inexact);
  sc->INEXACT_TO_EXACT = s7_define_function(sc, "inexact->exact",      big_inexact_to_exact, 1, 0, false, H_inexact_to_exact);
  sc->FLOOR =            s7_define_function(sc, "floor",               big_floor,            1, 0, false, H_floor);
  sc->CEILING =          s7_define_function(sc, "ceiling",             big_ceiling,          1, 0, false, H_ceiling);
  sc->TRUNCATE =         s7_define_function(sc, "truncate",            big_truncate,         1, 0, false, H_truncate);
  sc->ROUND =            s7_define_function(sc, "round",               big_round,            1, 0, false, H_round);
  sc->QUOTIENT =         s7_define_function(sc, "quotient",            big_quotient,         2, 0, false, H_quotient);
  sc->REMAINDER =        s7_define_function(sc, "remainder",           big_remainder,        2, 0, false, H_remainder);
  sc->MODULO =           s7_define_function(sc, "modulo",              big_modulo,           2, 0, false, H_modulo);
  sc->GCD =              s7_define_function(sc, "gcd",                 big_gcd,              0, 0, true,  H_gcd);
  sc->LCM =              s7_define_function(sc, "lcm",                 big_lcm,              0, 0, true,  H_lcm);

#if WITH_MAKE_COMPLEX
  sc->MAKE_RECTANGULAR = s7_define_function(sc, "make-complex",        big_make_rectangular, 2, 0, false, H_make_rectangular);
#else
  sc->MAKE_RECTANGULAR = s7_define_function(sc, "make-rectangular",    big_make_rectangular, 2, 0, false, H_make_rectangular);
  sc->MAKE_POLAR =       s7_define_function(sc, "make-polar",          big_make_polar,       2, 0, false, H_make_polar);
  sc->MAGNITUDE =        s7_define_function(sc, "magnitude",           big_magnitude,        1, 0, false, H_magnitude);
#endif
  sc->ANGLE =            s7_define_function(sc, "angle",               big_angle,            1, 0, false, H_angle);
  sc->ABS =              s7_define_function(sc, "abs",                 big_abs,              1, 0, false, H_abs);

  sc->LOGNOT =           s7_define_function(sc, "lognot",              big_lognot,           1, 0, false, H_lognot);
  sc->LOGIOR =           s7_define_function(sc, "logior",              big_logior,           0, 0, true,  H_logior);
  sc->LOGXOR =           s7_define_function(sc, "logxor",              big_logxor,           0, 0, true,  H_logxor);
  sc->LOGAND =           s7_define_function(sc, "logand",              big_logand,           0, 0, true,  H_logand);
  sc->ASH =              s7_define_function(sc, "ash",                 big_ash,              2, 0, false, H_ash);
  sc->INTEGER_LENGTH =   s7_define_function(sc, "integer-length",      big_integer_length,   1, 0, false, H_integer_length);
  
  sc->EXP =              s7_define_function(sc, "exp",                 big_exp,              1, 0, false, H_exp);
  sc->EXPT =             s7_define_function(sc, "expt",                big_expt,             2, 0, false, H_expt);
  sc->LOG =              s7_define_function(sc, "log",                 big_log,              1, 1, false, H_log);
  sc->SQRT =             s7_define_function(sc, "sqrt",                big_sqrt,             1, 0, false, H_sqrt);
  sc->SIN =              s7_define_function(sc, "sin",                 big_sin,              1, 0, false, H_sin);
  sc->COS =              s7_define_function(sc, "cos",                 big_cos,              1, 0, false, H_cos);
  sc->TAN =              s7_define_function(sc, "tan",                 big_tan,              1, 0, false, H_tan);
  sc->ASIN =             s7_define_function(sc, "asin",                big_asin,             1, 0, false, H_asin);
  sc->ACOS =             s7_define_function(sc, "acos",                big_acos,             1, 0, false, H_acos);
  sc->ATAN =             s7_define_function(sc, "atan",                big_atan,             1, 1, false, H_atan);
  sc->SINH =             s7_define_function(sc, "sinh",                big_sinh,             1, 0, false, H_sinh);
  sc->COSH =             s7_define_function(sc, "cosh",                big_cosh,             1, 0, false, H_cosh);
  sc->TANH =             s7_define_function(sc, "tanh",                big_tanh,             1, 0, false, H_tanh);
  sc->ASINH =            s7_define_function(sc, "asinh",               big_asinh,            1, 0, false, H_asinh);
  sc->ACOSH =            s7_define_function(sc, "acosh",               big_acosh,            1, 0, false, H_acosh);
  sc->ATANH =            s7_define_function(sc, "atanh",               big_atanh,            1, 0, false, H_atanh);

  sc->big_rng_tag = s7_new_type("<big-random-number-generator>", print_big_rng, free_big_rng, equal_big_rng, NULL, NULL, NULL);
  sc->RANDOM =           s7_define_function(sc, "random",              big_random,           1, 1, false, H_random);
  sc->MAKE_RANDOM_STATE = s7_define_function(sc,"make-random-state",  make_big_random_state,1, 1, false, H_make_random_state);

  sc->BIGNUM =          s7_define_function(sc, "bignum",              g_bignum,             1, 1, false, H_bignum);
  s7_define_function(sc,                       "bignum?",             g_is_bignum,          1, 0, false, H_is_bignum);

  sc->BIGNUM_PRECISION = s7_define_variable(sc, "*bignum-precision*", small_int(DEFAULT_BIGNUM_PRECISION));
  s7_symbol_set_access(sc, sc->BIGNUM_PRECISION,
		       s7_make_function(sc, "(set *bignum-precision*)", g_set_bignum_precision, 2, 0, false, "*bignum-precision* accessor")); 

  mpfr_set_default_prec((mp_prec_t)DEFAULT_BIGNUM_PRECISION); 
  mpc_set_default_precision((mp_prec_t)DEFAULT_BIGNUM_PRECISION);

  s7_symbol_set_value(sc, sc->PI, big_pi(sc));

  /* if these fixnum limits were read as strings, they'd be bignums in the gmp case, 
   *   so for consistency make the symbolic versions bignums as well.
   */
  s7_symbol_set_value(sc, make_symbol(sc, "most-positive-fixnum"), s7_Int_to_big_integer(sc, s7_integer(s7_name_to_value(sc, "most-positive-fixnum"))));
  s7_symbol_set_value(sc, make_symbol(sc, "most-negative-fixnum"), s7_Int_to_big_integer(sc, s7_integer(s7_name_to_value(sc, "most-negative-fixnum"))));

  s7_provide(sc, "gmp");
}

#endif
/* WITH_GMP */



/* -------------------------------- *s7* environment -------------------------------- */

static void init_s7_env(s7_scheme *sc)
{
  sc->stack_top_symbol =                     s7_make_symbol(sc, "stack-top");
  sc->stack_size_symbol =                    s7_make_symbol(sc, "stack-size");
  sc->symbol_table_is_locked_symbol =        s7_make_symbol(sc, "symbol-table-locked?");
  sc->heap_size_symbol =                     s7_make_symbol(sc, "heap-size");
  sc->free_heap_size_symbol =                s7_make_symbol(sc, "free-heap-size");
  sc->gc_freed_symbol =                      s7_make_symbol(sc, "gc-freed");
  sc->gc_protected_objects_symbol =          s7_make_symbol(sc, "gc-protected-objects");
  sc->input_ports_symbol =                   s7_make_symbol(sc, "input-ports");
  sc->output_ports_symbol =                  s7_make_symbol(sc, "output-ports");
  sc->strings_symbol =                       s7_make_symbol(sc, "strings");
  sc->gensyms_symbol =                       s7_make_symbol(sc, "gensyms");
  sc->vectors_symbol =                       s7_make_symbol(sc, "vectors");
  sc->hash_tables_symbol =                   s7_make_symbol(sc, "hash-tables");
  sc->continuations_symbol =                 s7_make_symbol(sc, "continuations");
  sc->c_objects_symbol =                     s7_make_symbol(sc, "c-objects");
  sc->file_names_symbol =                    s7_make_symbol(sc, "file-names");
  sc->symbol_table_symbol =                  s7_make_symbol(sc, "symbol-table");
  sc->rootlet_size_symbol =                  s7_make_symbol(sc, "rootlet-size");	
  sc->c_types_symbol =                       s7_make_symbol(sc, "c-types");
  sc->safety_symbol =                        s7_make_symbol(sc, "safety");
  sc->undefined_identifier_warnings_symbol = s7_make_symbol(sc, "undefined-identifier-warnings");
  sc->gc_stats_symbol =                      s7_make_symbol(sc, "gc-stats");
  sc->maximum_stack_size_symbol =            s7_make_symbol(sc, "maximum-stack-size");
  sc->cpu_time_symbol =                      s7_make_symbol(sc, "cpu-time");
  sc->catches_symbol =                       s7_make_symbol(sc, "catches");
  sc->exits_symbol =                         s7_make_symbol(sc, "exits");
  sc->stack_symbol =                         s7_make_symbol(sc, "stack");
  sc->max_string_length_symbol =             s7_make_symbol(sc, "max-string-length");
  sc->max_list_length_symbol =               s7_make_symbol(sc, "max-list-length");
  sc->max_vector_length_symbol =             s7_make_symbol(sc, "max-vector-length");
  sc->max_vector_dimensions_symbol =         s7_make_symbol(sc, "max-vector-dimensions");
  sc->default_hash_table_length_symbol =     s7_make_symbol(sc, "default-hash-table-length");
  sc->initial_string_port_length_symbol =    s7_make_symbol(sc, "initial-string-port-length");
  sc->default_rationalize_error_symbol =     s7_make_symbol(sc, "default-rationalize-error");
  sc->default_random_state_symbol =          s7_make_symbol(sc, "default-random-state");
  sc->morally_equal_float_epsilon_symbol =   s7_make_symbol(sc, "morally-equal-float-epsilon");
  sc->hash_table_float_epsilon_symbol =      s7_make_symbol(sc, "hash-table-float-epsilon");
}

static s7_pointer g_s7_let_ref_fallback(s7_scheme *sc, s7_pointer args)
{
  s7_pointer sym;
  sym = cadr(args);

  if (sym == sc->stack_top_symbol)                                       /* stack-top = #frames active (4 stack entries per frame) */
    return(s7_make_integer(sc, (sc->stack_end - sc->stack_start) / 4));
  if (sym == sc->stack_size_symbol)                                      /* stack-size (max so far) */
    return(s7_make_integer(sc, sc->stack_size));
  if (sym == sc->maximum_stack_size_symbol)                              /* maximum-stack-size */
    return(s7_make_integer(sc, sc->maximum_stack_size));

  if (sym == sc->symbol_table_is_locked_symbol)                          /* symbol-table-locked? */
    return(make_boolean(sc, sc->symbol_table_is_locked));
  if (sym == sc->symbol_table_symbol)                                    /* symbol-table (the raw vector) */
    return(sc->symbol_table);
  if (sym == sc->rootlet_size_symbol)                                    /* rootlet-size */
    return(s7_make_integer(sc, sc->rootlet_entries));
  if (sym == sc->safety_symbol)                                          /* safety */
    return(s7_make_integer(sc, sc->safety));
  if (sym == sc->undefined_identifier_warnings_symbol)                   /* undefined-identifier-warnings */
    return(s7_make_boolean(sc, sc->undefined_identifier_warnings));
  if (sym == sc->cpu_time_symbol)                                        /* cpu-time */
    return(s7_make_real(sc, (double)clock() / (double)CLOCKS_PER_SEC));
  if (sym == sc->catches_symbol)                                         /* catches */
    return(active_catches(sc));
  if (sym == sc->exits_symbol)                                           /* exits */
    return(active_exits(sc));
  if (sym == sc->stack_symbol)                                           /* stack */
    return(stack_entries(sc));

  if (sym == sc->heap_size_symbol)                                       /* heap-size */
    return(s7_make_integer(sc, sc->heap_size));
  if (sym == sc->free_heap_size_symbol)                                  /* free-heap-size (number of unused cells in the heap) */
    return(s7_make_integer(sc, sc->free_heap_top - sc->free_heap));
  if (sym == sc->gc_freed_symbol)                                        /* gc-freed = # cells freed during last GC sweep */
    return(s7_make_integer(sc, sc->gc_freed));
  if (sym == sc->gc_protected_objects_symbol)                            /* gc-protected-objects */
    return(sc->protected_objects);
  if (sym == sc->gc_stats_symbol)                                        /* gc-stats */
    return(make_boolean(sc, sc->gc_stats));

  if (sym == sc->default_rationalize_error_symbol)                       /* default-rationalize-error */
    return(make_real(sc, sc->default_rationalize_error));
  if (sym == sc->default_random_state_symbol)                            /* default-random-state */
    return(s7_make_random_state(sc, s7_random_state_to_list(sc, sc->NIL)));

  if (sym == sc->max_list_length_symbol)                                 /* max-list-length (as arg to make-list) */
    return(s7_make_integer(sc, MAX_LIST_LENGTH));
  if (sym == sc->max_vector_length_symbol)                               /* max-vector-length (as arg to make-vector and make-hash-table) */
    return(s7_make_integer(sc, MAX_VECTOR_LENGTH));
  if (sym == sc->max_vector_dimensions_symbol)                           /* max-vector-dimensions (make-vector) */
    return(s7_make_integer(sc, MAX_VECTOR_DIMENSIONS));
  if (sym == sc->max_string_length_symbol)                               /* max-string-length (as arg to make-string and read-string) */
    return(s7_make_integer(sc, MAX_STRING_LENGTH));
  if (sym == sc->default_hash_table_length_symbol)                       /* default size for make-hash-table */
    return(s7_make_integer(sc, sc->default_hash_table_length));
  if (sym == sc->morally_equal_float_epsilon_symbol)                     /* morally-equal-float-epsilon */
    return(s7_make_real(sc, sc->morally_equal_float_epsilon));
  if (sym == sc->hash_table_float_epsilon_symbol)                        /* hash-table-float-epsilon */
    return(s7_make_real(sc, sc->hash_table_float_epsilon));
  if (sym == sc->initial_string_port_length_symbol)                      /* initial-string-port-length */
    return(s7_make_integer(sc, sc->initial_string_port_length));

  if (sym == sc->input_ports_symbol)                                     /* input-ports */
    return(make_vector_wrapper(sc, sc->input_ports_loc, sc->input_ports));
  if (sym == sc->output_ports_symbol)                                    /* output-ports */
    return(make_vector_wrapper(sc, sc->output_ports_loc, sc->output_ports));
  if (sym == sc->strings_symbol)                                         /* strings */
    return(make_vector_wrapper(sc, sc->strings_loc, sc->strings));
  if (sym == sc->gensyms_symbol)                                         /* gensyms */
    return(make_vector_wrapper(sc, sc->gensyms_loc, sc->gensyms));
  if (sym == sc->vectors_symbol)                                         /* vectors */
    return(make_vector_wrapper(sc, sc->vectors_loc, sc->vectors));
  if (sym == sc->hash_tables_symbol)                                     /* hash-tables */
    return(make_vector_wrapper(sc, sc->hash_tables_loc, sc->hash_tables));
  if (sym == sc->continuations_symbol)                                   /* continuations */
    return(make_vector_wrapper(sc, sc->continuations_loc, sc->continuations));
  if (sym == sc->c_objects_symbol)                                       /* c-objects */
    return(make_vector_wrapper(sc, sc->c_objects_loc, sc->c_objects));

  if (sym == sc->file_names_symbol)                                      /* file-names (loaded files) */
    return(make_vector_wrapper(sc, sc->file_names_top, sc->file_names));
  if (sym == sc->c_types_symbol)                                         /* c-types */
    {
      s7_pointer res;
      int i;
      sc->w = sc->NIL;
      for (i = 0; i < num_types; i++)
	sc->w = cons(sc, make_string_wrapper(sc, object_types[i]->name), sc->w);
      res = sc->w;
      sc->w = sc->NIL;
      return(res);
    }

  /* sc->unlet is a scheme vector of slots -- not very useful at the scheme level */
  return(sc->UNDEFINED);
}

static s7_pointer g_s7_let_set_fallback(s7_scheme *sc, s7_pointer args)
{
  s7_pointer sym, val;

  sym = cadr(args);
  val = caddr(args);

  if (sym == sc->gc_stats_symbol)
    {
      if (s7_is_boolean(val)) {sc->gc_stats = (val == sc->T); return(val);}
      return(simple_wrong_type_argument(sc, sc->LET_SET, val, T_BOOLEAN));
    }

  if (sym == sc->symbol_table_is_locked_symbol) {sc->symbol_table_is_locked = (val != sc->F); return(val);}

  if (sym == sc->maximum_stack_size_symbol)
    {
      if (s7_is_integer(val))
	{
	  s7_Int size;
	  size = s7_integer(val);
	  if (size >= INITIAL_STACK_SIZE)
	    {
	      sc->maximum_stack_size = (unsigned int)size;
	      return(val);
	    }
	  return(simple_out_of_range(sc, sc->LET_SET, val, make_string_wrapper(sc, "should be greater than the initial stack size (512)")));
	}
      return(simple_wrong_type_argument(sc, sc->LET_SET, val, T_INTEGER));
    }

  if (sym == sc->safety_symbol)
    {
      if (s7_is_integer(val)) {sc->safety = s7_integer(val); return(val);}
      return(simple_wrong_type_argument(sc, sc->LET_SET, val, T_INTEGER));
    }

  if (sym == sc->undefined_identifier_warnings_symbol)
    {
      if (s7_is_boolean(val)) {sc->undefined_identifier_warnings = s7_boolean(sc, val); return(val);}
      return(simple_wrong_type_argument(sc, sc->LET_SET, val, T_BOOLEAN));
    }
  
  if (sym == sc->default_hash_table_length_symbol)
    {
      if (s7_is_integer(val)) {sc->default_hash_table_length = s7_integer(val); return(val);}
      return(simple_wrong_type_argument(sc, sc->LET_SET, val, T_INTEGER));
    }

  if (sym == sc->initial_string_port_length_symbol)
    {
      if (s7_is_integer(val)) {sc->initial_string_port_length = s7_integer(val); return(val);}
      return(simple_wrong_type_argument(sc, sc->LET_SET, val, T_INTEGER));
    }

  if (sym == sc->morally_equal_float_epsilon_symbol)
    {
      if (s7_is_real(val)) {sc->morally_equal_float_epsilon = s7_real(val); return(val);}
      return(simple_wrong_type_argument(sc, sc->LET_SET, val, T_REAL));
    }

  if (sym == sc->hash_table_float_epsilon_symbol)
    {
      if (s7_is_real(val)) {sc->hash_table_float_epsilon = s7_real(val); return(val);}
      return(simple_wrong_type_argument(sc, sc->LET_SET, val, T_REAL));
    }

  if (sym == sc->default_rationalize_error_symbol)
    {
      if (s7_is_real(val)) {sc->default_rationalize_error = real_to_double(sc, val, "set! default-rationalize-error"); return(val);}
      return(simple_wrong_type_argument(sc, sc->LET_SET, val, T_REAL));
    }

  if (sym == sc->default_random_state_symbol)
    {
      if ((is_c_object(val)) &&
	  (c_object_type(val) == sc->rng_tag))
	{
	  s7_rng_t *r, *dr;	  
	  r = (s7_rng_t *)s7_object_value(val);
	  dr = sc->default_rng;
	  dr->ran_seed = r->ran_seed;
	  dr->ran_carry = r->ran_carry;
	  return(val);
	}
      return(wrong_type_argument_with_type(sc, sc->LET_SET, small_int(1), val, make_string_wrapper(sc, "a random state object")));
    }
  return(sc->UNDEFINED);
}

/* one cost of *motif* -- functions that were previously global and therefore removed from the heap,
 *   are now local to *motif* so the GC mark costs rise significantly.
 */


/* -------------------------------- initialization -------------------------------- */

static s7_pointer make_unique_object(const char* name, unsigned int typ)
{
  s7_pointer p;
  p = alloc_pointer();
  set_type(p, typ | T_GC_MARK | T_IMMUTABLE);
  unique_name_length(p) = safe_strlen(name);
  unique_name(p) = copy_string_with_length(name, unique_name_length(p));
  heap_location(p) = NOT_IN_HEAP;
  return(p);
}


s7_scheme *s7_init(void) 
{
  int i;
  s7_scheme *sc;
  s7_pointer sym;
  static bool already_inited = false;

  if (!already_inited)
    {
      init_types();
      init_ctables();
      init_mark_functions();
      init_pows();
#if (!WITH_PURE_S7)
      init_uppers();
#endif
      all_x_function_init();
      init_catchers();
      /* sizeof(__float128) == sizeof(long double) so how to distinguish them for printf (L vs Q)? */
      /* if (sizeof(s7_Double) >= 16) float_format_g = "%.*Qg"; */      /* __float128 */
      if (sizeof(s7_Double) > 8)
	float_format_g = "%.*Lg";   /* long double (80-bit precision?) */
      else float_format_g = "%.*g"; /* float and double */
    }

  sc = (s7_scheme *)calloc(1, sizeof(s7_scheme)); /* malloc is not recommended here */
  
  sc->gc_off = true;                         /* sc->args and so on are not set yet, so a gc during init -> segfault */
  sc->gc_stats = false;
  init_gc_caches(sc);

  sc->longjmp_ok = false;
  sc->symbol_table_is_locked = false;

  sc->strbuf_size = INITIAL_STRBUF_SIZE;
  sc->strbuf = (char *)calloc(sc->strbuf_size, sizeof(char));
  sc->print_width = MAX_STRING_LENGTH;

  sc->initial_string_port_length = 128;
  sc->format_depth = -1;
  sc->slash_str_size = 0;
  sc->slash_str = NULL;
  
  sc->singletons = (s7_pointer *)calloc(256, sizeof(s7_pointer));
  sc->read_line_buf = NULL;
  sc->read_line_buf_size = 0;

  sc->NIL =         make_unique_object("()",             T_NIL);
  sc->GC_NIL =      make_unique_object("gc-nil",         T_UNIQUE);
  sc->T =           make_unique_object("#t",             T_BOOLEAN);
  sc->F =           make_unique_object("#f",             T_BOOLEAN);
  sc->EOF_OBJECT =  make_unique_object("#<eof>",         T_UNIQUE);
  sc->UNDEFINED =   make_unique_object("#<undefined>",   T_UNIQUE);
  sc->ELSE =        make_unique_object("else",           T_UNIQUE);
  /* "else" is added to the rootlet below -- can't do it here because the symbol table and environment don't exist yet. */
  sc->UNSPECIFIED = make_unique_object("#<unspecified>", T_UNSPECIFIED);
  sc->NO_VALUE =    make_unique_object("#<unspecified>", T_UNSPECIFIED);

  car(sc->NIL) = cdr(sc->NIL) = sc->UNSPECIFIED;
  /* this is mixing two different s7_cell structs, cons and envr, but luckily
   *    envr has two initial s7_pointer fields, equivalent to car and cdr, so
   *    let_id which is the same as ecdr is unaffected.  To get the names
   *    built-in, I'll append unique_name and unique_name_length fields to
   *    the envr struct.
   */
  let_id(sc->NIL) = -1;
  cdr(sc->UNSPECIFIED) = sc->UNSPECIFIED; 

  cdr(sc->UNDEFINED) = sc->UNDEFINED;
  /* this way find_symbol of an undefined symbol returns #<undefined> not #<unspecified>
   */

  sc->temp_cell_1 = permanent_cons(sc->NIL, sc->NIL, T_PAIR | T_GC_MARK | T_IMMUTABLE);
  sc->temp_cell = permanent_cons(sc->temp_cell_1, sc->NIL, T_PAIR | T_GC_MARK | T_IMMUTABLE);
  sc->temp_cell_2 = permanent_cons(sc->NIL, sc->NIL, T_PAIR | T_GC_MARK | T_IMMUTABLE);
  sc->temp_cell_3 = permanent_cons(sc->NIL, sc->NIL, T_PAIR | T_GC_MARK | T_IMMUTABLE);
  sc->temp_cell_4 = permanent_cons(sc->NIL, sc->NIL, T_PAIR | T_GC_MARK | T_IMMUTABLE);

  sc->T1_1 = permanent_cons(sc->NIL, sc->NIL, T_PAIR | T_GC_MARK | T_IMMUTABLE);

  sc->T2_2 = permanent_cons(sc->NIL, sc->NIL, T_PAIR | T_GC_MARK | T_IMMUTABLE);
  sc->T2_1 = permanent_cons(sc->NIL, sc->T2_2, T_PAIR | T_GC_MARK | T_IMMUTABLE);
  sc->Z2_2 = permanent_cons(sc->NIL, sc->NIL, T_PAIR | T_GC_MARK | T_IMMUTABLE);
  sc->Z2_1 = permanent_cons(sc->NIL, sc->Z2_2, T_PAIR | T_GC_MARK | T_IMMUTABLE);

  sc->T3_3 = permanent_cons(sc->NIL, sc->NIL, T_PAIR | T_GC_MARK | T_IMMUTABLE);
  sc->T3_2 = permanent_cons(sc->NIL, sc->T3_3, T_PAIR | T_GC_MARK | T_IMMUTABLE);
  sc->T3_1 = permanent_cons(sc->NIL, sc->T3_2, T_PAIR | T_GC_MARK | T_IMMUTABLE);

  sc->A4_4 = permanent_cons(sc->NIL, sc->NIL, T_PAIR | T_GC_MARK | T_IMMUTABLE);
  sc->A4_3 = permanent_cons(sc->NIL, sc->A4_4, T_PAIR | T_GC_MARK | T_IMMUTABLE);
  sc->A4_2 = permanent_cons(sc->NIL, sc->A4_3, T_PAIR | T_GC_MARK | T_IMMUTABLE);
  sc->A4_1 = permanent_cons(sc->NIL, sc->A4_2, T_PAIR | T_GC_MARK | T_IMMUTABLE);

  sc->A1_1 = sc->A4_4;
  sc->A2_1 = sc->A4_3;
  sc->A2_2 = sc->A4_4;
  sc->A3_1 = sc->A4_2;
  sc->A3_2 = sc->A4_3;
  sc->A3_3 = sc->A4_4;

  sc->safe_lists = (s7_pointer *)calloc(NUM_SAFE_LISTS, sizeof(s7_pointer));
  for (i = 1; i < NUM_SAFE_LISTS; i++)
    {
      int j;
      s7_pointer p;
      p = sc->NIL;
      for (j = 0; j < i; j++)
	p = permanent_cons(sc->NIL, p, T_PAIR);
      sc->safe_lists[i] = p;
    }

  sc->input_port_stack = sc->NIL;
  sc->code = sc->NIL;
  sc->cur_code = sc->F;
  sc->args = sc->NIL;
  sc->value = sc->NIL;
  sc->v = sc->NIL;
  sc->w = sc->NIL;
  sc->x = sc->NIL;
  sc->z = sc->NIL;

  sc->temp1 = sc->NIL;
  sc->temp2 = sc->NIL;
  sc->temp3 = sc->NIL;

  sc->begin_hook = NULL;
  sc->default_rng = NULL;
  sc->ex_fallback = NULL;

  sc->autoload_table = sc->NIL;
  sc->autoload_names = NULL;
  sc->autoload_names_sizes = NULL;
  sc->autoloaded_already = NULL;
  sc->autoload_names_loc = 0;

  sc->port_heap = NULL;
  sc->permanent_objects = NULL;
    
  sc->heap_size = INITIAL_HEAP_SIZE;
  if ((sc->heap_size % 32) != 0)
    sc->heap_size = 32 * (int)ceil((double)(sc->heap_size) / 32.0);
  sc->heap = (s7_pointer *)malloc(sc->heap_size * sizeof(s7_pointer));
  
  sc->free_heap = (s7_cell **)malloc(sc->heap_size * sizeof(s7_cell *));
  sc->free_heap_top = (s7_cell **)(sc->free_heap + INITIAL_HEAP_SIZE);
  sc->free_heap_trigger = (s7_cell **)(sc->free_heap + GC_TRIGGER_SIZE);
  sc->previous_free_heap_top = sc->free_heap_top;

  {
    s7_cell *cells;
    cells = (s7_cell *)calloc(INITIAL_HEAP_SIZE, sizeof(s7_cell));
    for (i = 0; i < INITIAL_HEAP_SIZE; i++)
      {
	sc->heap[i] = &cells[i];
 	sc->free_heap[i] = sc->heap[i];
 	heap_location(sc->heap[i]) = i;
      }
  }

  /* this has to precede s7_make_* allocations */
  sc->protected_objects_size = INITIAL_PROTECTED_OBJECTS_SIZE;
  sc->protected_objects_loc = 0;
  sc->protected_objects = s7_make_vector(sc, INITIAL_PROTECTED_OBJECTS_SIZE);
  for (i = 0; i < INITIAL_PROTECTED_OBJECTS_SIZE; i++)
    vector_element(sc->protected_objects, i) = sc->GC_NIL;
  set_immutable(sc->protected_objects);

  sc->stack = s7_make_vector(sc, INITIAL_STACK_SIZE);
  sc->stack_start = vector_elements(sc->stack);
  sc->stack_end = sc->stack_start;
  sc->stack_size = INITIAL_STACK_SIZE;
  sc->stack_resize_trigger = (s7_pointer *)(sc->stack_start + sc->stack_size / 2);
  set_type(sc->stack, T_STACK);
  sc->maximum_stack_size = (1 << 30);

  initialize_op_stack(sc);
  
  /* keep the symbol table out of the heap */
  sc->symbol_table = (s7_pointer)calloc(1, sizeof(s7_cell));
  set_type(sc->symbol_table, T_VECTOR);
  vector_length(sc->symbol_table) = SYMBOL_TABLE_SIZE;
  vector_elements(sc->symbol_table) = (s7_pointer *)malloc(SYMBOL_TABLE_SIZE * sizeof(s7_pointer));
  vector_getter(sc->symbol_table) = default_vector_getter;
  vector_setter(sc->symbol_table) = default_vector_setter;
  s7_vector_fill(sc, sc->symbol_table, sc->NIL);
  heap_location(sc->symbol_table) = NOT_IN_HEAP;
  
  sc->typnam = NULL;
  sc->typnam_len = 0;
  sc->op_names_saved = NULL;
  sc->tmp_str = NULL;
  sc->tmp_str_size = -1;
  sc->tmp_str_chars = NULL;
  sc->help_arglist = NULL;
  sc->default_rationalize_error = 1.0e-12;
  sc->hash_table_float_epsilon = 1.0e-12;
  sc->morally_equal_float_epsilon = 1.0e-15;
  sc->default_hash_table_length = 511;
  sc->gensym_counter = 0;
  sc->capture_env_counter = 0;
  sc->f_class = 0;
  sc->add_class = 0;
  sc->equal_class = 0;
  sc->let_number = 0;
  sc->format_column = 0;
  sc->file_names = NULL;
  sc->file_names_size = 0;
  sc->file_names_top = -1;
  sc->no_values = 0;
  sc->s7_call_line = 0;
  sc->s7_call_file = NULL;
  sc->s7_call_name = NULL;
  sc->safety = 0;
  sc->baffle_ctr = 0;
  sc->syms_tag = 0;
  sc->CLASS_NAME = make_symbol(sc, "class-name");
  sc->circle_info = NULL;
  sc->fdats = (format_data **)calloc(8, sizeof(format_data *));
  sc->num_fdats = 8;
  sc->direct_str = s7_make_permanent_string(NULL);
  sc->undefined_identifier_warnings = false;
  sc->wrap_only = make_wrap_only(sc);

  sc->rootlet = s7_make_vector(sc, 512);
  set_type(sc->rootlet, T_ENVIRONMENT);
  sc->rootlet_entries = 0;
  for (i = 0; i < 512; i++)
    vector_element(sc->rootlet, i) = sc->NIL;
  sc->envir = sc->NIL;
  sc->shadow_rootlet = sc->NIL;
  
  if (!already_inited)
    {
      /* keep the small_ints out of the heap */
      small_ints = (s7_pointer *)malloc((NUM_SMALL_INTS + 1) * sizeof(s7_pointer));
      {
	s7_cell *cells;
	cells = (s7_cell *)calloc((NUM_SMALL_INTS + 1), sizeof(s7_cell));
	for (i = 0; i <= NUM_SMALL_INTS; i++) 
	  {
	    s7_pointer p;
	    small_ints[i] = &cells[i];
	    p = small_ints[i];
	    typeflag(p) = T_IMMUTABLE | T_INTEGER;
	    heap_location(p) = NOT_IN_HEAP;
	    integer(p) = i;
	  }
      }
      
      real_zero = make_permanent_real(0.0);
      real_one = make_permanent_real(1.0);
      real_NaN = make_permanent_real(NAN);
      real_infinity = make_permanent_real(INFINITY);
      real_minus_infinity = make_permanent_real(-INFINITY);
      real_pi = make_permanent_real(3.1415926535897932384626433832795029L); /* M_PI is not good enough for s7_Double = long double */
      arity_not_set = make_permanent_integer(CLOSURE_ARITY_NOT_SET);
      max_arity = make_permanent_integer(MAX_ARITY);
      /* prebuilt null string is tricky mainly because it overlaps #u8() */

      /* keep the characters out of the heap */
      chars = (s7_pointer *)malloc((NUM_CHARS + 1) * sizeof(s7_pointer));
      chars[0] = sc->EOF_OBJECT;
      chars++;                    /* now chars[EOF] == chars[-1] == sc->EOF_OBJECT */
      {
	s7_cell *cells;
	cells = (s7_cell *)calloc(NUM_CHARS, sizeof(s7_cell));
	for (i = 0; i < NUM_CHARS; i++) 
	  {
	    s7_pointer cp;
	    unsigned char c;
	    
	    c = (unsigned char)i;
	    cp = &cells[i];
	    typeflag(cp) = T_IMMUTABLE | T_CHARACTER;
	    heap_location(cp) = NOT_IN_HEAP;
	    character(cp) = c;
	    upper_character(cp) = (unsigned char)toupper(i);
	    lower_character(cp) = (unsigned char)tolower(i);
	    is_char_alphabetic(cp) = (bool)isalpha(i);
	    is_char_numeric(cp) = (bool)isdigit(i);
	    is_char_whitespace(cp) = white_space[i];
	    is_char_uppercase(cp) = (bool)isupper(i);
	    is_char_lowercase(cp) = (bool)islower(i);
	    chars[i] = cp;
	    
	    switch (c) 
	      {
	      case ' ':	       character_name(cp) = copy_string("#\\space");     break;
	      case '\n':       character_name(cp) = copy_string("#\\newline");   break;
	      case '\r':       character_name(cp) = copy_string("#\\return");    break;
	      case '\t':       character_name(cp) = copy_string("#\\tab");       break;
	      case '\0':       character_name(cp) = copy_string("#\\null");      break;
	      case (char)0x1b: character_name(cp) = copy_string("#\\escape");    break;
	      case (char)0x7f: character_name(cp) = copy_string("#\\delete");    break;
	      case (char)7:    character_name(cp) = copy_string("#\\alarm");     break;
	      case (char)8:    character_name(cp) = copy_string("#\\backspace"); break;
	      default:
		{
                  #define P_SIZE 16
		  char *p;
		  p = (char *)malloc(P_SIZE * sizeof(char));
		  if ((c < 32) || (c >= 127))
		    snprintf(p, P_SIZE, "#\\x%x", c);
		  else snprintf(p, P_SIZE, "#\\%c", c); 
		  character_name(cp) = p;
		  break;
		}
	      }
	    character_name_length(cp) = safe_strlen(character_name(cp));
	  }
      }
    }

#if WITH_COUNTS
  init_hashes(sc); 
#endif

  make_standard_ports(sc);

  assign_syntax(sc, "quote",             OP_QUOTE);
  assign_syntax(sc, "if",                OP_IF);
  assign_syntax(sc, "when",              OP_WHEN);
  assign_syntax(sc, "unless",            OP_UNLESS);
  assign_syntax(sc, "begin",             OP_BEGIN);
  assign_syntax(sc, "set!",              OP_SET);
  assign_syntax(sc, "let",               OP_LET);
  assign_syntax(sc, "let*",              OP_LET_STAR);
  assign_syntax(sc, "letrec",            OP_LETREC);
  assign_syntax(sc, "letrec*",           OP_LETREC_STAR);
  assign_syntax(sc, "cond",              OP_COND);
  assign_syntax(sc, "and",               OP_AND);
  assign_syntax(sc, "or",                OP_OR);
  assign_syntax(sc, "case",              OP_CASE);
  assign_syntax(sc, "do",                OP_DO);
  assign_syntax(sc, "lambda",            OP_LAMBDA);
  assign_syntax(sc, "lambda*",           OP_LAMBDA_STAR);      /* optional, key, rest args */
  assign_syntax(sc, "define",            OP_DEFINE);
  assign_syntax(sc, "define*",           OP_DEFINE_STAR);
  assign_syntax(sc, "define-constant",   OP_DEFINE_CONSTANT);  /* unsetabble and unrebindable */
  assign_syntax(sc, "define-macro",      OP_DEFINE_MACRO); 
  assign_syntax(sc, "define-macro*",     OP_DEFINE_MACRO_STAR); 
  assign_syntax(sc, "define-expansion",  OP_DEFINE_EXPANSION); /* read-time (immediate) macro expansion */
  assign_syntax(sc, "define-bacro",      OP_DEFINE_BACRO);     /* macro expansion in calling environment */
  assign_syntax(sc, "define-bacro*",     OP_DEFINE_BACRO_STAR);
  assign_syntax(sc, "with-baffle",       OP_WITH_BAFFLE);

#if (!DISABLE_DEPRECATED)
  set_immutable(assign_syntax(sc, "with-environment", OP_WITH_LET));
#endif
  set_immutable(assign_syntax(sc, "with-let", OP_WITH_LET));


  sc->QUOTE_UNCHECKED =       assign_internal_syntax(sc, "quote",       OP_QUOTE_UNCHECKED);  
  sc->BEGIN_UNCHECKED =       assign_internal_syntax(sc, "begin",       OP_BEGIN_UNCHECKED);  
  sc->WITH_BAFFLE_UNCHECKED = assign_internal_syntax(sc, "with-baffle", OP_WITH_BAFFLE_UNCHECKED);  
  sc->LET_UNCHECKED =         assign_internal_syntax(sc, "let",         OP_LET_UNCHECKED);  
  sc->LET_STAR_UNCHECKED =    assign_internal_syntax(sc, "let*",        OP_LET_STAR_UNCHECKED);  
  sc->LETREC_UNCHECKED =      assign_internal_syntax(sc, "letrec",      OP_LETREC_UNCHECKED);  
  sc->LETREC_STAR_UNCHECKED = assign_internal_syntax(sc, "letrec*",     OP_LETREC_STAR_UNCHECKED);  
  sc->LET_NO_VARS =           assign_internal_syntax(sc, "let",         OP_LET_NO_VARS);  
  sc->LET_C =                 assign_internal_syntax(sc, "let",         OP_LET_C);  
  sc->LET_S =                 assign_internal_syntax(sc, "let",         OP_LET_S);  
  sc->LET_Q =                 assign_internal_syntax(sc, "let",         OP_LET_Q);  
  sc->LET_ALL_C =             assign_internal_syntax(sc, "let",         OP_LET_ALL_C);  
  sc->LET_ALL_S =             assign_internal_syntax(sc, "let",         OP_LET_ALL_S);  
  sc->LET_ALL_X =             assign_internal_syntax(sc, "let",         OP_LET_ALL_X);  
  sc->LET_STAR_ALL_X =        assign_internal_syntax(sc, "let*",        OP_LET_STAR_ALL_X);  
  sc->LET_opCq =              assign_internal_syntax(sc, "let",         OP_LET_opCq);  
  sc->LET_opSSq =             assign_internal_syntax(sc, "let",         OP_LET_opSSq);  
  sc->NAMED_LET_NO_VARS =     assign_internal_syntax(sc, "let",         OP_NAMED_LET_NO_VARS); 
  sc->NAMED_LET =             assign_internal_syntax(sc, "let",         OP_NAMED_LET);  
  sc->NAMED_LET_STAR =        assign_internal_syntax(sc, "let*",        OP_NAMED_LET_STAR);  
  sc->LET_STAR2 =             assign_internal_syntax(sc, "let*",        OP_LET_STAR2);  
  sc->WITH_LET_UNCHECKED =    assign_internal_syntax(sc, "with-let",    OP_WITH_LET_UNCHECKED);   
  sc->WITH_LET_S =            assign_internal_syntax(sc, "with-let",    OP_WITH_LET_S);   
  sc->SET_UNCHECKED =         assign_internal_syntax(sc, "set!",        OP_SET_UNCHECKED);  
  sc->SET_SYMBOL_C =          assign_internal_syntax(sc, "set!",        OP_SET_SYMBOL_C);  
  sc->SET_SYMBOL_S =          assign_internal_syntax(sc, "set!",        OP_SET_SYMBOL_S);  
  sc->SET_SYMBOL_Q =          assign_internal_syntax(sc, "set!",        OP_SET_SYMBOL_Q);  
  sc->SET_SYMBOL_SAFE_S =     assign_internal_syntax(sc, "set!",        OP_SET_SYMBOL_SAFE_S);  
  sc->SET_SYMBOL_SAFE_SS =    assign_internal_syntax(sc, "set!",        OP_SET_SYMBOL_SAFE_SS);  
  sc->SET_SYMBOL_SAFE_SSS =   assign_internal_syntax(sc, "set!",        OP_SET_SYMBOL_SAFE_SSS);  
  sc->SET_SYMBOL_SAFE_C =     assign_internal_syntax(sc, "set!",        OP_SET_SYMBOL_SAFE_C);  
  sc->SET_SYMBOL_P =          assign_internal_syntax(sc, "set!",        OP_SET_SYMBOL_P);  
  sc->SET_SYMBOL_Z =          assign_internal_syntax(sc, "set!",        OP_SET_SYMBOL_Z);  
  sc->SET_SYMBOL_UNKNOWN_G =  assign_internal_syntax(sc, "set!",        OP_SET_SYMBOL_UNKNOWN_G);  
  sc->SET_SYMBOL_A =          assign_internal_syntax(sc, "set!",        OP_SET_SYMBOL_A);  
  sc->SET_FV_SCALED =         assign_internal_syntax(sc, "set!",        OP_SET_FV_SCALED);  
  sc->CASE_UNCHECKED =        assign_internal_syntax(sc, "case",        OP_CASE_UNCHECKED);  
  sc->CASE_SIMPLE =           assign_internal_syntax(sc, "case",        OP_CASE_SIMPLE);  
  sc->CASE_SIMPLER =          assign_internal_syntax(sc, "case",        OP_CASE_SIMPLER);  
  sc->CASE_SIMPLER_1 =        assign_internal_syntax(sc, "case",        OP_CASE_SIMPLER_1);  
  sc->CASE_SIMPLER_SS =       assign_internal_syntax(sc, "case",        OP_CASE_SIMPLER_SS);  
  sc->CASE_SIMPLEST =         assign_internal_syntax(sc, "case",        OP_CASE_SIMPLEST);  
  sc->CASE_SIMPLEST_SS =      assign_internal_syntax(sc, "case",        OP_CASE_SIMPLEST_SS);  
  sc->CASE_SIMPLEST_ELSE =    assign_internal_syntax(sc, "case",        OP_CASE_SIMPLEST_ELSE);  
  sc->CASE_SIMPLEST_ELSE_C =  assign_internal_syntax(sc, "case",        OP_CASE_SIMPLEST_ELSE_C);  
  sc->COND_UNCHECKED =        assign_internal_syntax(sc, "cond",        OP_COND_UNCHECKED);  
  sc->COND_SIMPLE =           assign_internal_syntax(sc, "cond",        OP_COND_SIMPLE);  
  sc->DO_UNCHECKED =          assign_internal_syntax(sc, "do",          OP_DO_UNCHECKED);  
  sc->LAMBDA_UNCHECKED =      assign_internal_syntax(sc, "lambda",      OP_LAMBDA_UNCHECKED);  
  sc->LAMBDA_STAR_UNCHECKED = assign_internal_syntax(sc, "lambda*",     OP_LAMBDA_STAR_UNCHECKED);  
  sc->DEFINE_UNCHECKED =      assign_internal_syntax(sc, "define",      OP_DEFINE_UNCHECKED);  
  sc->DEFINE_FUNCHECKED =     assign_internal_syntax(sc, "define",      OP_DEFINE_FUNCHECKED);  
  sc->DEFINE_STAR_UNCHECKED = assign_internal_syntax(sc, "define*",     OP_DEFINE_STAR_UNCHECKED);  
  sc->SET_NORMAL =            assign_internal_syntax(sc, "set!",        OP_SET_NORMAL);
  sc->SET_PWS =               assign_internal_syntax(sc, "set!",        OP_SET_PWS);
  sc->SET_PAIR =              assign_internal_syntax(sc, "set!",        OP_SET_PAIR);
  sc->SET_PAIR_P =            assign_internal_syntax(sc, "set!",        OP_SET_PAIR_P);
  sc->SET_PAIR_Z =            assign_internal_syntax(sc, "set!",        OP_SET_PAIR_Z);
  sc->SET_PAIR_UNKNOWN_G =    assign_internal_syntax(sc, "set!",        OP_SET_PAIR_UNKNOWN_G);
  sc->SET_PAIR_A =            assign_internal_syntax(sc, "set!",        OP_SET_PAIR_A);
  sc->SET_PAIR_ZA =           assign_internal_syntax(sc, "set!",        OP_SET_PAIR_ZA);
  sc->SET_ENV_S =             assign_internal_syntax(sc, "set!",        OP_SET_ENV_S);
  sc->SET_ENV_ALL_X =         assign_internal_syntax(sc, "set!",        OP_SET_ENV_ALL_X);
  sc->SET_PAIR_C =            assign_internal_syntax(sc, "set!",        OP_SET_PAIR_C);
  sc->SET_PAIR_C_P =          assign_internal_syntax(sc, "set!",        OP_SET_PAIR_C_P);
  sc->AND_UNCHECKED =         assign_internal_syntax(sc, "and",         OP_AND_UNCHECKED);
  sc->AND_P =                 assign_internal_syntax(sc, "and",         OP_AND_P);
  sc->OR_UNCHECKED =          assign_internal_syntax(sc, "or",          OP_OR_UNCHECKED);
  sc->OR_P =                  assign_internal_syntax(sc, "or",          OP_OR_P);
  sc->WHEN_UNCHECKED =        assign_internal_syntax(sc, "when",        OP_WHEN_UNCHECKED);
  sc->UNLESS_UNCHECKED =      assign_internal_syntax(sc, "unless",      OP_UNLESS_UNCHECKED);
  sc->IF_UNCHECKED =          assign_internal_syntax(sc, "if",          OP_IF_UNCHECKED);
  sc->IF_P_P_P =              assign_internal_syntax(sc, "if",          OP_IF_P_P_P);
  sc->IF_P_P =                assign_internal_syntax(sc, "if",          OP_IF_P_P);
  sc->IF_ANDP_P =             assign_internal_syntax(sc, "if",          OP_IF_ANDP_P);
  sc->IF_ANDP_P_P =           assign_internal_syntax(sc, "if",          OP_IF_ANDP_P_P);
  sc->IF_ORP_P =              assign_internal_syntax(sc, "if",          OP_IF_ORP_P);
  sc->IF_ORP_P_P =            assign_internal_syntax(sc, "if",          OP_IF_ORP_P_P);
  sc->IF_B_P =                assign_internal_syntax(sc, "if",          OP_IF_B_P);
  sc->IF_P_P_X =              assign_internal_syntax(sc, "if",          OP_IF_P_P_X);
  sc->IF_A_P_X =              assign_internal_syntax(sc, "if",          OP_IF_A_P_X);
  sc->IF_P_X_P =              assign_internal_syntax(sc, "if",          OP_IF_P_X_P);
  sc->IF_S_P_P =              assign_internal_syntax(sc, "if",          OP_IF_S_P_P);
  sc->IF_S_P =                assign_internal_syntax(sc, "if",          OP_IF_S_P);
  sc->IF_S_P_X =              assign_internal_syntax(sc, "if",          OP_IF_S_P_X);
  sc->IF_S_X_P =              assign_internal_syntax(sc, "if",          OP_IF_S_X_P);
  sc->IF_P_FEED =             assign_internal_syntax(sc, "cond",        OP_IF_P_FEED);
  sc->COND_ALL_X =            assign_internal_syntax(sc, "cond",        OP_COND_ALL_X);  
  sc->COND_S =                assign_internal_syntax(sc, "cond",        OP_COND_S);  
  sc->SAFE_IF_Z_Z =           assign_internal_syntax(sc, "if",          OP_SAFE_IF_Z_Z);  
  sc->SAFE_IF_A_Z =           assign_internal_syntax(sc, "if",          OP_SAFE_IF_A_Z);  
  sc->IF_Z_P_P =              assign_internal_syntax(sc, "if",          OP_IF_Z_P_P);
  sc->IF_Z_P =                assign_internal_syntax(sc, "if",          OP_IF_Z_P);
  sc->IF_A_P_P =              assign_internal_syntax(sc, "if",          OP_IF_A_P_P);
  sc->IF_A_P =                assign_internal_syntax(sc, "if",          OP_IF_A_P);
  sc->SAFE_IF_CC_X_P =        assign_internal_syntax(sc, "if",          OP_SAFE_IF_CC_X_P);  
  sc->SAFE_IF_CC_P =          assign_internal_syntax(sc, "if",          OP_SAFE_IF_CC_P);  
  sc->SAFE_IF_CC_P_P =        assign_internal_syntax(sc, "if",          OP_SAFE_IF_CC_P_P);  
  sc->SAFE_IF_CS_P =          assign_internal_syntax(sc, "if",          OP_SAFE_IF_CS_P);  
  sc->SAFE_IF_CS_P_P =        assign_internal_syntax(sc, "if",          OP_SAFE_IF_CS_P_P);  
  sc->SAFE_IF_CS_P_X =        assign_internal_syntax(sc, "if",          OP_SAFE_IF_CS_P_X);  
  sc->SAFE_IF_CS_X_P =        assign_internal_syntax(sc, "if",          OP_SAFE_IF_CS_X_P);
  sc->SAFE_IF_CSS_X_P =       assign_internal_syntax(sc, "if",          OP_SAFE_IF_CSS_X_P);
  sc->SAFE_IF_CSC_X_O_A =     assign_internal_syntax(sc, "if",          OP_SAFE_IF_CSC_X_O_A);
  sc->SAFE_IF_CSC_X_P =       assign_internal_syntax(sc, "if",          OP_SAFE_IF_CSC_X_P);
  sc->SAFE_IF_CSQ_P =         assign_internal_syntax(sc, "if",          OP_SAFE_IF_CSQ_P);    
  sc->SAFE_IF_CSQ_P_P =       assign_internal_syntax(sc, "if",          OP_SAFE_IF_CSQ_P_P);    
  sc->SAFE_IF_CSS_P =         assign_internal_syntax(sc, "if",          OP_SAFE_IF_CSS_P);    
  sc->SAFE_IF_CSS_P_P =       assign_internal_syntax(sc, "if",          OP_SAFE_IF_CSS_P_P);    
  sc->SAFE_IF_CSC_P =         assign_internal_syntax(sc, "if",          OP_SAFE_IF_CSC_P);    
  sc->SAFE_IF_CSC_P_P =       assign_internal_syntax(sc, "if",          OP_SAFE_IF_CSC_P_P);    
  sc->SAFE_IF_C_SS_P =        assign_internal_syntax(sc, "if",          OP_SAFE_IF_C_SS_P);    
  sc->SAFE_IF_IS_NULL_Q_P =   assign_internal_syntax(sc, "if",          OP_SAFE_IF_IS_NULL_Q_P);  
  sc->SAFE_IF_IS_NULL_S_P =   assign_internal_syntax(sc, "if",          OP_SAFE_IF_IS_NULL_S_P);  
  sc->SAFE_IF_IS_PAIR_P =     assign_internal_syntax(sc, "if",          OP_SAFE_IF_IS_PAIR_P);  
  sc->SAFE_IF_IS_PAIR_P_X =   assign_internal_syntax(sc, "if",          OP_SAFE_IF_IS_PAIR_P_X);  
  sc->SAFE_IF_IS_PAIR_P_P =   assign_internal_syntax(sc, "if",          OP_SAFE_IF_IS_PAIR_P_P);  
  sc->SAFE_IF_IS_SYMBOL_P =   assign_internal_syntax(sc, "if",          OP_SAFE_IF_IS_SYMBOL_P);  
  sc->SAFE_IF_IS_SYMBOL_P_P = assign_internal_syntax(sc, "if",          OP_SAFE_IF_IS_SYMBOL_P_P);  
  sc->SAFE_IF_NOT_S_P =       assign_internal_syntax(sc, "if",          OP_SAFE_IF_NOT_S_P);  
  sc->WHEN_S =                assign_internal_syntax(sc, "when",        OP_WHEN_S);
  sc->UNLESS_S =              assign_internal_syntax(sc, "unless",      OP_UNLESS_S);
  sc->LET_R =                 assign_internal_syntax(sc, "let",         OP_LET_R);  
  sc->LET_R_P =               assign_internal_syntax(sc, "let",         OP_LET_R_P);  
  sc->LET_CAR_P =             assign_internal_syntax(sc, "let",         OP_LET_CAR_P);  
  sc->LET_ONE =               assign_internal_syntax(sc, "let",         OP_LET_ONE);  
  sc->LET_Z =                 assign_internal_syntax(sc, "let",         OP_LET_Z);  
  sc->LET_ALL_R =             assign_internal_syntax(sc, "let",         OP_LET_ALL_R);  
  sc->LET_C_D =               assign_internal_syntax(sc, "let",         OP_LET_C_D);  
  sc->INCREMENT_1 =           assign_internal_syntax(sc, "set!",        OP_INCREMENT_1);  
  sc->INCREMENT_SS =          assign_internal_syntax(sc, "set!",        OP_INCREMENT_SS);  
  sc->INCREMENT_SSS =         assign_internal_syntax(sc, "set!",        OP_INCREMENT_SSS);  
  sc->INCREMENT_SZ =          assign_internal_syntax(sc, "set!",        OP_INCREMENT_SZ);  
  sc->INCREMENT_SA =          assign_internal_syntax(sc, "set!",        OP_INCREMENT_SA);  
  sc->INCREMENT_SAA =         assign_internal_syntax(sc, "set!",        OP_INCREMENT_SAA);  
  sc->INCREMENT_C_TEMP =      assign_internal_syntax(sc, "set!",        OP_INCREMENT_C_TEMP);  
  sc->DECREMENT_1 =           assign_internal_syntax(sc, "set!",        OP_DECREMENT_1);  
  sc->SET_CONS =              assign_internal_syntax(sc, "set!",        OP_SET_CONS);
  sc->DOTIMES_P =             assign_internal_syntax(sc, "do",          OP_DOTIMES_P);
  sc->SIMPLE_DO =             assign_internal_syntax(sc, "do",          OP_SIMPLE_DO);
  sc->SIMPLE_DO_P =           assign_internal_syntax(sc, "do",          OP_SIMPLE_DO_P);
  sc->SIMPLE_DO_A =           assign_internal_syntax(sc, "do",          OP_SIMPLE_DO_A);
  sc->SIMPLE_DO_FOREVER =     assign_internal_syntax(sc, "do",          OP_SIMPLE_DO_FOREVER);
  sc->SAFE_DOTIMES =          assign_internal_syntax(sc, "do",          OP_SAFE_DOTIMES);
  sc->SAFE_DOTIMES_C_C =      assign_internal_syntax(sc, "do",          OP_SAFE_DOTIMES_C_C);
  sc->SAFE_DOTIMES_C_A =      assign_internal_syntax(sc, "do",          OP_SAFE_DOTIMES_C_A);
  sc->SIMPLE_SAFE_DOTIMES =   assign_internal_syntax(sc, "do",          OP_SIMPLE_SAFE_DOTIMES);
  sc->SAFE_DO =               assign_internal_syntax(sc, "do",          OP_SAFE_DO);
  sc->DOX =                   assign_internal_syntax(sc, "do",          OP_DOX);
  sc->DO_ALL_X =              assign_internal_syntax(sc, "do",          OP_DO_ALL_X);

  sc->LAMBDA =                make_symbol(sc, "lambda");
  sc->LAMBDA_STAR =           make_symbol(sc, "lambda*");
  sc->QUOTE =                 make_symbol(sc, "quote");
#if (!DISABLE_DEPRECATED)
  sc->WITH_ENVIRONMENT =      make_symbol(sc, "with-environment");
#endif
  sc->WITH_LET =              make_symbol(sc, "with-let");

#if WITH_IMMUTABLE_UNQUOTE
  /* this code solves the various unquote redefinition troubles
   * if "," -> "(unquote...)" in the reader, (let (, (lambda (x) (+ x 1))) ,,,,1) -> 5
   *   in s7, this requires a quote: (let (, (lambda (x) (+ x 1))) ,,,,'1)
   */
  sc->UNQUOTE =              make_symbol(sc, ","); 
  set_immutable(sc->UNQUOTE);
#else
  sc->UNQUOTE =              make_symbol(sc, "unquote");
#endif

  sc->MACROEXPAND =          make_symbol(sc, "macroexpand");
  sc->DEFINE_EXPANSION =     make_symbol(sc, "define-expansion");
  sc->FEED_TO =              make_symbol(sc, "=>");
  sc->SET =                  make_symbol(sc, "set!");
  sc->BAFFLE =               make_symbol(sc, "(baffle)");
  sc->BODY =                 make_symbol(sc, "body");
  sc->ERROR =                make_symbol(sc, "error");
  sc->READ_ERROR =           make_symbol(sc, "read-error");
  sc->SYNTAX_ERROR =         make_symbol(sc, "syntax-error");
  sc->WRONG_TYPE_ARG =       make_symbol(sc, "wrong-type-arg");
  sc->WRONG_NUMBER_OF_ARGS = make_symbol(sc, "wrong-number-of-args");
  sc->FORMAT_ERROR =         make_symbol(sc, "format-error");
  sc->OUT_OF_RANGE =         make_symbol(sc, "out-of-range");
  sc->NO_CATCH =             make_symbol(sc, "no-catch");
  sc->IO_ERROR =             make_symbol(sc, "io-error");
  sc->INVALID_ESCAPE_FUNCTION = make_symbol(sc, "invalid-escape-function");

  sc->KEY_KEY =              s7_make_keyword(sc, "key");
  sc->KEY_OPTIONAL =         s7_make_keyword(sc, "optional");
  sc->KEY_ALLOW_OTHER_KEYS = s7_make_keyword(sc, "allow-other-keys");
  sc->KEY_REST =             s7_make_keyword(sc, "rest");
  sc->KEY_READABLE =         s7_make_keyword(sc, "readable");

  sc->__FUNC__ = make_symbol(sc, "__func__");
  s7_make_slot(sc, sc->NIL, make_symbol(sc, "else"), sc->ELSE);

  sc->owlet = init_owlet(sc);

  sc->WRONG_TYPE_ARG_INFO = sc->NIL;
  for (i = 0; i < 6; i++)
    {
      sc->WRONG_TYPE_ARG_INFO = permanent_cons(sc->F, sc->WRONG_TYPE_ARG_INFO, T_PAIR);
      switch (5 - i)
	{
	case 1: sc->WTA1 = sc->WRONG_TYPE_ARG_INFO; break;
	case 2: sc->WTA2 = sc->WRONG_TYPE_ARG_INFO; break;
	case 3: sc->WTA3 = sc->WRONG_TYPE_ARG_INFO; break;
	case 4: sc->WTA4 = sc->WRONG_TYPE_ARG_INFO; break;
	case 5: sc->WTA5 = sc->WRONG_TYPE_ARG_INFO; break;
	}
    }
  car(sc->WRONG_TYPE_ARG_INFO) = s7_make_permanent_string("~A argument ~D, ~S, is ~A but should be ~A");

  sc->SIMPLE_WRONG_TYPE_ARG_INFO = sc->NIL;
  for (i = 0; i < 5; i++)
    sc->SIMPLE_WRONG_TYPE_ARG_INFO = permanent_cons(sc->F, sc->SIMPLE_WRONG_TYPE_ARG_INFO, T_PAIR);
  car(sc->SIMPLE_WRONG_TYPE_ARG_INFO) = s7_make_permanent_string("~A argument, ~S, is ~A but should be ~A");

  sc->OUT_OF_RANGE_INFO = sc->NIL;
  for (i = 0; i < 5; i++)
    sc->OUT_OF_RANGE_INFO = permanent_cons(sc->F, sc->OUT_OF_RANGE_INFO, T_PAIR);
  car(sc->OUT_OF_RANGE_INFO) = s7_make_permanent_string("~A argument ~D, ~S, is out of range (~A)");

  sc->SIMPLE_OUT_OF_RANGE_INFO = sc->NIL;
  for (i = 0; i < 4; i++)
    sc->SIMPLE_OUT_OF_RANGE_INFO = permanent_cons(sc->F, sc->SIMPLE_OUT_OF_RANGE_INFO, T_PAIR);
  car(sc->SIMPLE_OUT_OF_RANGE_INFO) = s7_make_permanent_string("~A argument, ~S, is out of range (~A)");

  sc->TOO_MANY_ARGUMENTS = s7_make_permanent_string("~A: too many arguments: ~A");
  sc->NOT_ENOUGH_ARGUMENTS = s7_make_permanent_string("~A: not enough arguments: ~A");
  sc->DIVISION_BY_ZERO_ERROR = s7_make_permanent_string("~A: division by zero, ~S");
  sc->DIVISION_BY_ZERO = make_symbol(sc, "division-by-zero");

  if (!already_inited)
    init_car_a_list();

  for (i = 0; i < NUM_TYPES; i++)
    {
      const char *str;
      str = type_name_from_type(sc, i, INDEFINITE_ARTICLE);
      if (str)
	prepackaged_type_names[i] = s7_make_permanent_string(str);
      else prepackaged_type_names[i] = sc->F;
    }
  /* unset built-ins: T_STACK (can't happen), T_C_OBJECT (want actual name), T_INPUT|OUTPUT_PORT (want string|file|etc included) */

  sc->gc_off = false;

  sc->GENSYM =                s7_define_safe_function(sc, "gensym",                  g_gensym,                 0, 1, false, H_gensym);
  sc->IS_GENSYM =             s7_define_safe_function(sc, "gensym?",                 g_is_gensym,              1, 0, false, H_is_gensym);
                              s7_define_safe_function(sc, "symbol-table",            g_symbol_table,           0, 0, false, H_symbol_table);
  sc->IS_SYMBOL =             s7_define_safe_function(sc, "symbol?",                 g_is_symbol,              1, 0, false, H_is_symbol);
  sc->SYMBOL_TO_STRING =      s7_define_safe_function(sc, "symbol->string",          g_symbol_to_string,       1, 0, false, H_symbol_to_string);
  sc->STRING_TO_SYMBOL =      s7_define_safe_function(sc, "string->symbol",          g_string_to_symbol,       1, 0, false, H_string_to_symbol);
  sc->SYMBOL =                s7_define_safe_function(sc, "symbol",                  g_symbol,                 1, 0, false, H_symbol);
  sc->SYMBOL_TO_VALUE =       s7_define_safe_function(sc, "symbol->value",           g_symbol_to_value,        1, 1, false, H_symbol_to_value);
  sc->SYMBOL_TO_DYNAMIC_VALUE = s7_define_safe_function(sc, "symbol->dynamic-value", g_symbol_to_dynamic_value,1, 0, false, H_symbol_to_dynamic_value);
  s7_dilambda(sc, "symbol-access", g_symbol_access, 1, 1, g_symbol_set_access, 2, 1, H_symbol_access);
  sc->SYMBOL_ACCESS = make_symbol(sc, "symbol-access");
  
  sc->OUTLET =                s7_define_safe_function(sc, "outlet",                  g_outlet,                 1, 0, false, H_outlet);
                              s7_define_safe_function(sc, "rootlet",                 g_rootlet,                0, 0, false, H_rootlet);
                              s7_define_safe_function(sc, "curlet",                  g_curlet,                 0, 0, false, H_curlet);
                              s7_define_constant_function(sc, "unlet",               g_unlet,                  0, 0, false, H_unlet);
  sc->SUBLET =                s7_define_safe_function(sc, "sublet",                  g_sublet,                 1, 0, true,  H_sublet);
  sc->VARLET =                s7_define_function(sc,      "varlet",                  g_varlet,                 1, 0, true,  H_varlet);
  sc->CUTLET =                s7_define_function(sc,      "cutlet",                  g_cutlet,                 1, 0, true,  H_cutlet);
  sc->INLET =                 s7_define_safe_function(sc, "inlet",                   s7_inlet,                 0, 0, true,  H_inlet);
  sc->IS_LET =                s7_define_safe_function(sc, "let?",                    g_is_let,                 1, 0, false, H_is_let);
  sc->LET_TO_LIST =           s7_define_safe_function(sc, "let->list",               g_let_to_list,            1, 0, false, H_let_to_list);
                              s7_define_safe_function(sc, "owlet",                   g_owlet,                  0, 0, false, H_owlet);
  sc->COVERLET =              s7_define_safe_function(sc, "coverlet",                g_coverlet,               1, 0, false, H_coverlet);
  sc->OPENLET =               s7_define_safe_function(sc, "openlet",                 g_openlet,                1, 0, false, H_openlet);
  sc->IS_OPENLET =            s7_define_safe_function(sc, "openlet?",                g_is_openlet,             1, 0, false, H_is_openlet);
  sc->LET_REF =               s7_define_safe_function(sc, "let-ref",                 g_let_ref,                1, 1, false, H_let_ref);
  sc->LET_SET =               s7_define_safe_function(sc, "let-set!",                g_let_set,                3, 0, false, H_let_set);
  /* the 2 and 3 arg choice here might not be optimal:
   *   (define e1 (openlet (inlet 'x 32 'let-ref (lambda (obj) (obj 'x)) 'let-set! (lambda (obj val) (set! (obj 'x) val)))))
   *   (e1) -> environment as applicable object takes one argument: ()
   *   (e1 2) -> (e1 2): too many arguments: (e1 2)
   * and similarly for the set case.  We can add an ignored 2nd arg, but it's not pretty.
   */
  sc->LET_REF_FALLBACK = make_symbol(sc, "let-ref-fallback");
  sc->LET_SET_FALLBACK = make_symbol(sc, "let-set!-fallback");

  sc->IS_PROVIDED =           s7_define_safe_function(sc, "provided?",               g_is_provided,            1, 0, false, H_is_provided);
  sc->PROVIDE =               s7_define_safe_function(sc, "provide",                 g_provide,                1, 0, false, H_provide);
  sc->IS_DEFINED =            s7_define_safe_function(sc, "defined?",                g_is_defined,             1, 2, false, H_is_defined);
  sc->IS_CONSTANT =           s7_define_safe_function(sc, "constant?",               g_is_constant,            1, 0, false, H_is_constant);
  sc->IS_MACRO =              s7_define_safe_function(sc, "macro?",                  g_is_macro,               1, 0, false, H_is_macro);

  sc->IS_KEYWORD =            s7_define_safe_function(sc, "keyword?",                g_is_keyword,             1, 0, false, H_is_keyword);
  sc->MAKE_KEYWORD =          s7_define_safe_function(sc, "make-keyword",            g_make_keyword,           1, 0, false, H_make_keyword);
  sc->SYMBOL_TO_KEYWORD =     s7_define_safe_function(sc, "symbol->keyword",         g_symbol_to_keyword,      1, 0, false, H_symbol_to_keyword);
  sc->KEYWORD_TO_SYMBOL =     s7_define_safe_function(sc, "keyword->symbol",         g_keyword_to_symbol,      1, 0, false, H_keyword_to_symbol);

  sc->IS_C_POINTER =          s7_define_safe_function(sc, "c-pointer?",              g_is_c_pointer,           1, 0, false, H_is_c_pointer);
  sc->C_POINTER =             s7_define_safe_function(sc, "c-pointer",               g_c_pointer,              1, 0, false, H_c_pointer);  

  sc->PORT_LINE_NUMBER =      s7_define_safe_function(sc, "port-line-number",        g_port_line_number,       0, 1, false, H_port_line_number);
  sc->PORT_FILENAME =         s7_define_safe_function(sc, "port-filename",           g_port_filename,          0, 1, false, H_port_filename);
  sc->PAIR_LINE_NUMBER =      s7_define_safe_function(sc, "pair-line-number",        g_pair_line_number,       1, 0, false, H_pair_line_number);
  
  sc->IS_INPUT_PORT =         s7_define_safe_function(sc, "input-port?",             g_is_input_port,          1, 0, false, H_is_input_port);
  sc->IS_OUTPUT_PORT =        s7_define_safe_function(sc, "output-port?",            g_is_output_port,         1, 0, false, H_is_output_port);
  sc->IS_PORT_CLOSED =        s7_define_safe_function(sc, "port-closed?",            g_is_port_closed,         1, 0, false, H_is_port_closed);
#if (!WITH_PURE_S7)
  sc->IS_CHAR_READY =         s7_define_safe_function(sc, "char-ready?",             g_is_char_ready,          0, 1, false, H_is_char_ready);
#endif
  sc->IS_EOF_OBJECT =         s7_define_safe_function(sc, "eof-object?",             g_is_eof_object,          1, 0, false, H_is_eof_object);
  /* this should be named eof? (what isn't an object?) */
  
                              s7_define_safe_function(sc, "current-input-port",      g_current_input_port,     0, 0, false, H_current_input_port);
                              s7_define_safe_function(sc, "current-output-port",     g_current_output_port,    0, 0, false, H_current_output_port);
                              s7_define_safe_function(sc, "current-error-port",      g_current_error_port,     0, 0, false, H_current_error_port);
                              s7_define_safe_function(sc, "set-current-input-port",  g_set_current_input_port, 1, 0, false, H_set_current_input_port);
                              s7_define_safe_function(sc, "set-current-output-port", g_set_current_output_port,1, 0, false, H_set_current_output_port);
                              s7_define_safe_function(sc, "set-current-error-port",  g_set_current_error_port, 1, 0, false, H_set_current_error_port);

  sc->CLOSE_INPUT_PORT =      s7_define_safe_function(sc, "close-input-port",        g_close_input_port,       1, 0, false, H_close_input_port);
  sc->CLOSE_OUTPUT_PORT =     s7_define_safe_function(sc, "close-output-port",       g_close_output_port,      1, 0, false, H_close_output_port);
  sc->FLUSH_OUTPUT_PORT =     s7_define_safe_function(sc, "flush-output-port",       g_flush_output_port,      0, 1, false, H_flush_output_port);
  sc->OPEN_INPUT_FILE =       s7_define_safe_function(sc, "open-input-file",         g_open_input_file,        1, 1, false, H_open_input_file);
  sc->OPEN_OUTPUT_FILE =      s7_define_safe_function(sc, "open-output-file",        g_open_output_file,       1, 1, false, H_open_output_file);
  sc->OPEN_INPUT_STRING =     s7_define_safe_function(sc, "open-input-string",       g_open_input_string,      1, 0, false, H_open_input_string);
                              s7_define_safe_function(sc, "open-output-string",      g_open_output_string,     0, 0, false, H_open_output_string);
  sc->GET_OUTPUT_STRING =     s7_define_safe_function(sc, "get-output-string",       g_get_output_string,      1, 0, false, H_get_output_string);
  
  sc->NEWLINE =               s7_define_safe_function(sc, "newline",                 g_newline,                0, 1, false, H_newline);
  sc->WRITE =                 s7_define_safe_function(sc, "write",                   g_write,                  1, 1, false, H_write);
  sc->DISPLAY =               s7_define_safe_function(sc, "display",                 g_display,                1, 1, false, H_display);
  sc->READ_CHAR =             s7_define_safe_function(sc, "read-char",               g_read_char,              0, 1, false, H_read_char);
  sc->PEEK_CHAR =             s7_define_safe_function(sc, "peek-char",               g_peek_char,              0, 1, false, H_peek_char);
  sc->WRITE_CHAR =            s7_define_safe_function(sc, "write-char",              g_write_char,             1, 1, false, H_write_char);
  sc->WRITE_STRING =          s7_define_safe_function(sc, "write-string",            g_write_string,           1, 3, false, H_write_string);
  sc->READ_BYTE =             s7_define_safe_function(sc, "read-byte",               g_read_byte,              0, 1, false, H_read_byte);
  sc->WRITE_BYTE =            s7_define_safe_function(sc, "write-byte",              g_write_byte,             1, 1, false, H_write_byte);
  sc->READ_LINE =             s7_define_safe_function(sc, "read-line",               g_read_line,              0, 2, false, H_read_line);
  sc->READ_STRING =           s7_define_safe_function(sc, "read-string",             g_read_string,            1, 1, false, H_read_string);
  sc->READ =                  s7_define_function(sc,      "read",                    g_read,                   0, 1, false, H_read);
  /* read can't be safe because it messes with the stack, expecting to be all by itself in the call sequence (not embedded in OP_SAFE_C_opSq for example)
   */
  
  sc->CALL_WITH_INPUT_STRING = s7_define_function(sc,     "call-with-input-string",  g_call_with_input_string, 2, 0, false, H_call_with_input_string);
  sc->CALL_WITH_INPUT_FILE =  s7_define_function(sc,      "call-with-input-file",    g_call_with_input_file,   2, 0, false, H_call_with_input_file);
  sc->WITH_INPUT_FROM_STRING = s7_define_function(sc,     "with-input-from-string",  g_with_input_from_string, 2, 0, false, H_with_input_from_string);
  sc->WITH_INPUT_FROM_FILE =  s7_define_function(sc,      "with-input-from-file",    g_with_input_from_file,   2, 0, false, H_with_input_from_file);
  
  sc->CALL_WITH_OUTPUT_STRING = s7_define_function(sc,    "call-with-output-string", g_call_with_output_string,1, 0, false, H_call_with_output_string);
  sc->CALL_WITH_OUTPUT_FILE = s7_define_function(sc,      "call-with-output-file",   g_call_with_output_file,  2, 0, false, H_call_with_output_file);
  sc->WITH_OUTPUT_TO_STRING = s7_define_function(sc,      "with-output-to-string",   g_with_output_to_string,  1, 0, false, H_with_output_to_string);
  sc->WITH_OUTPUT_TO_FILE =   s7_define_function(sc,      "with-output-to-file",     g_with_output_to_file,    2, 0, false, H_with_output_to_file);
  
#if WITH_SYSTEM_EXTRAS
  sc->IS_DIRECTORY =          s7_define_safe_function(sc, "directory?",              g_is_directory,           1, 0, false, H_is_directory);
  sc->FILE_EXISTS =           s7_define_safe_function(sc, "file-exists?",            g_file_exists,            1, 0, false, H_file_exists);
  sc->DELETE_FILE =           s7_define_safe_function(sc, "delete-file",             g_delete_file,            1, 0, false, H_delete_file);
  sc->GETENV =                s7_define_safe_function(sc, "getenv",                  g_getenv_1,               1, 0, false, H_getenv);
  sc->SYSTEM =                s7_define_safe_function(sc, "system",                  g_system,                 1, 1, false, H_system);
#ifndef _MSC_VER
  sc->DIRECTORY_TO_LIST =     s7_define_safe_function(sc, "directory->list",         g_directory_to_list,      1, 0, false, H_directory_to_list);
  sc->FILE_MTIME =            s7_define_safe_function(sc, "file-mtime",              g_file_mtime,             1, 0, false, H_file_mtime);
#endif
#endif
  
  sc->REAL_PART =             s7_define_safe_function(sc, "real-part",               g_real_part,              1, 0, false, H_real_part);
  sc->IMAG_PART =             s7_define_safe_function(sc, "imag-part",               g_imag_part,              1, 0, false, H_imag_part);
  sc->NUMERATOR =             s7_define_safe_function(sc, "numerator",               g_numerator,              1, 0, false, H_numerator);
  sc->DENOMINATOR =           s7_define_safe_function(sc, "denominator",             g_denominator,            1, 0, false, H_denominator);
  sc->IS_EVEN =               s7_define_safe_function(sc, "even?",                   g_is_even,                1, 0, false, H_is_even);
  sc->IS_ODD =                s7_define_safe_function(sc, "odd?",                    g_is_odd,                 1, 0, false, H_is_odd);
  sc->IS_ZERO =               s7_define_safe_function(sc, "zero?",                   g_is_zero,                1, 0, false, H_is_zero);
  sc->IS_POSITIVE =           s7_define_safe_function(sc, "positive?",               g_is_positive,            1, 0, false, H_is_positive);
  sc->IS_NEGATIVE =           s7_define_safe_function(sc, "negative?",               g_is_negative,            1, 0, false, H_is_negative);
  sc->IS_INFINITE =           s7_define_safe_function(sc, "infinite?",               g_is_infinite,            1, 0, false, H_is_infinite);
  sc->IS_NAN =                s7_define_safe_function(sc, "nan?",                    g_is_nan,                 1, 0, false, H_is_nan);

#if (!WITH_GMP)
#if WITH_MAKE_COMPLEX
  sc->MAKE_RECTANGULAR =      s7_define_safe_function(sc, "make-complex",            g_make_rectangular,       2, 0, false, H_make_rectangular);
#else
  sc->MAKE_POLAR =            s7_define_safe_function(sc, "make-polar",              g_make_polar,             2, 0, false, H_make_polar);
  sc->MAKE_RECTANGULAR =      s7_define_safe_function(sc, "make-rectangular",        g_make_rectangular,       2, 0, false, H_make_rectangular);
  sc->MAGNITUDE =             s7_define_safe_function(sc, "magnitude",               g_magnitude,              1, 0, false, H_magnitude);
#endif
  sc->ANGLE =                 s7_define_safe_function(sc, "angle",                   g_angle,                  1, 0, false, H_angle);
  sc->RATIONALIZE =           s7_define_safe_function(sc, "rationalize",             g_rationalize,            1, 1, false, H_rationalize);
  sc->ABS =                   s7_define_safe_function(sc, "abs",                     g_abs,                    1, 0, false, H_abs);
  sc->EXP =                   s7_define_safe_function(sc, "exp",                     g_exp,                    1, 0, false, H_exp);
  sc->LOG =                   s7_define_safe_function(sc, "log",                     g_log,                    1, 1, false, H_log);
  sc->SIN =                   s7_define_safe_function(sc, "sin",                     g_sin,                    1, 0, false, H_sin);
  sc->COS =                   s7_define_safe_function(sc, "cos",                     g_cos,                    1, 0, false, H_cos);
  sc->TAN =                   s7_define_safe_function(sc, "tan",                     g_tan,                    1, 0, false, H_tan);
  sc->ASIN =                  s7_define_safe_function(sc, "asin",                    g_asin,                   1, 0, false, H_asin);
  sc->ACOS =                  s7_define_safe_function(sc, "acos",                    g_acos,                   1, 0, false, H_acos);
  sc->ATAN =                  s7_define_safe_function(sc, "atan",                    g_atan,                   1, 1, false, H_atan);
  sc->SINH =                  s7_define_safe_function(sc, "sinh",                    g_sinh,                   1, 0, false, H_sinh);
  sc->COSH =                  s7_define_safe_function(sc, "cosh",                    g_cosh,                   1, 0, false, H_cosh);
  sc->TANH =                  s7_define_safe_function(sc, "tanh",                    g_tanh,                   1, 0, false, H_tanh);
  sc->ASINH =                 s7_define_safe_function(sc, "asinh",                   g_asinh,                  1, 0, false, H_asinh);
  sc->ACOSH =                 s7_define_safe_function(sc, "acosh",                   g_acosh,                  1, 0, false, H_acosh);
  sc->ATANH =                 s7_define_safe_function(sc, "atanh",                   g_atanh,                  1, 0, false, H_atanh);
  sc->SQRT =                  s7_define_safe_function(sc, "sqrt",                    g_sqrt,                   1, 0, false, H_sqrt);
  sc->EXPT =                  s7_define_safe_function(sc, "expt",                    g_expt,                   2, 0, false, H_expt);
  sc->FLOOR =                 s7_define_safe_function(sc, "floor",                   g_floor,                  1, 0, false, H_floor);
  sc->CEILING =               s7_define_safe_function(sc, "ceiling",                 g_ceiling,                1, 0, false, H_ceiling);
  sc->TRUNCATE =              s7_define_safe_function(sc, "truncate",                g_truncate,               1, 0, false, H_truncate);
  sc->ROUND =                 s7_define_safe_function(sc, "round",                   g_round,                  1, 0, false, H_round);
  sc->LCM =                   s7_define_safe_function(sc, "lcm",                     g_lcm,                    0, 0, true,  H_lcm);
  sc->GCD =                   s7_define_safe_function(sc, "gcd",                     g_gcd,                    0, 0, true,  H_gcd);
  sc->ADD =                   s7_define_safe_function(sc, "+",                       g_add,                    0, 0, true,  H_add);
  sc->MINUS =                 s7_define_safe_function(sc, "-",                       g_subtract,               1, 0, true,  H_subtract);
  sc->MULTIPLY =              s7_define_safe_function(sc, "*",                       g_multiply,               0, 0, true,  H_multiply);
  sc->DIVIDE =                s7_define_safe_function(sc, "/",                       g_divide,                 1, 0, true,  H_divide);
  sc->MAX =                   s7_define_safe_function(sc, "max",                     g_max,                    1, 0, true,  H_max);
  sc->MIN =                   s7_define_safe_function(sc, "min",                     g_min,                    1, 0, true,  H_min);
  sc->QUOTIENT =              s7_define_safe_function(sc, "quotient",                g_quotient,               2, 0, false, H_quotient);
  sc->REMAINDER =             s7_define_safe_function(sc, "remainder",               g_remainder,              2, 0, false, H_remainder);
  sc->MODULO =                s7_define_safe_function(sc, "modulo",                  g_modulo,                 2, 0, false, H_modulo);
  sc->EQ =                    s7_define_safe_function(sc, "=",                       g_equal,                  2, 0, true,  H_equal);
  sc->LT =                    s7_define_safe_function(sc, "<",                       g_less,                   2, 0, true,  H_less);
  sc->GT =                    s7_define_safe_function(sc, ">",                       g_greater,                2, 0, true,  H_greater);
  sc->LEQ =                   s7_define_safe_function(sc, "<=",                      g_less_or_equal,          2, 0, true,  H_less_or_equal);
  sc->GEQ =                   s7_define_safe_function(sc, ">=",                      g_greater_or_equal,       2, 0, true,  H_greater_or_equal);

  sc->INEXACT_TO_EXACT =      s7_define_safe_function(sc, "inexact->exact",          g_inexact_to_exact,       1, 0, false, H_inexact_to_exact);
  sc->EXACT_TO_INEXACT =      s7_define_safe_function(sc, "exact->inexact",          g_exact_to_inexact,       1, 0, false, H_exact_to_inexact);

  sc->INTEGER_LENGTH =        s7_define_safe_function(sc, "integer-length",          g_integer_length,         1, 0, false, H_integer_length);
  sc->LOGIOR =                s7_define_safe_function(sc, "logior",                  g_logior,                 0, 0, true,  H_logior);
  sc->LOGXOR =                s7_define_safe_function(sc, "logxor",                  g_logxor,                 0, 0, true,  H_logxor);
  sc->LOGAND =                s7_define_safe_function(sc, "logand",                  g_logand,                 0, 0, true,  H_logand);
  sc->LOGNOT =                s7_define_safe_function(sc, "lognot",                  g_lognot,                 1, 0, false, H_lognot);
  sc->ASH =                   s7_define_safe_function(sc, "ash",                     g_ash,                    2, 0, false, H_ash);
#endif
  sc->LOGBIT =                s7_define_safe_function(sc, "logbit?",                 g_logbit,                 2, 0, false, H_logbit);
  sc->INTEGER_DECODE_FLOAT =  s7_define_safe_function(sc, "integer-decode-float",    g_integer_decode_float,   1, 0, false, H_integer_decode_float);

  sc->rng_tag = s7_new_type_x("<random-number-generator>", print_rng, free_rng, equal_rng, NULL, NULL, NULL, NULL, copy_random_state, NULL, NULL);
  s7_set_object_print_readably(sc->rng_tag, print_rng_readably);
  sc->IS_RANDOM_STATE =       s7_define_safe_function(sc, "random-state?",           g_is_random_state,        1, 0, false, H_is_random_state);
  sc->RANDOM_STATE_TO_LIST =  s7_define_safe_function(sc, "random-state->list",      s7_random_state_to_list,  0, 1, false, H_random_state_to_list);
  sc->MAKE_RANDOM_STATE =     s7_define_safe_function(sc, "make-random-state",       s7_make_random_state,     1, 1, false, H_make_random_state);
  sc->RANDOM =                s7_define_safe_function(sc, "random",                  g_random,                 1, 1, false, H_random);

  sc->IS_NUMBER =             s7_define_safe_function(sc, "number?",                 g_is_number,              1, 0, false, H_is_number);
  sc->IS_INTEGER =            s7_define_safe_function(sc, "integer?",                g_is_integer,             1, 0, false, H_is_integer);
  sc->IS_REAL =               s7_define_safe_function(sc, "real?",                   g_is_real,                1, 0, false, H_is_real);
  sc->IS_COMPLEX =            s7_define_safe_function(sc, "complex?",                g_is_complex,             1, 0, false, H_is_complex);
  sc->IS_RATIONAL =           s7_define_safe_function(sc, "rational?",               g_is_rational,            1, 0, false, H_is_rational);
  sc->IS_EXACT =              s7_define_safe_function(sc, "exact?",                  g_is_exact,               1, 0, false, H_is_exact);
  sc->IS_INEXACT =            s7_define_safe_function(sc, "inexact?",                g_is_inexact,             1, 0, false, H_is_inexact);

  sc->NUMBER_TO_STRING =      s7_define_safe_function(sc, "number->string",          g_number_to_string,       1, 1, false, H_number_to_string);
  sc->STRING_TO_NUMBER =      s7_define_safe_function(sc, "string->number",          g_string_to_number,       1, 1, false, H_string_to_number);
  
  sc->CHAR_UPCASE =           s7_define_safe_function(sc, "char-upcase",             g_char_upcase,            1, 0, false, H_char_upcase);
  sc->CHAR_DOWNCASE =         s7_define_safe_function(sc, "char-downcase",           g_char_downcase,          1, 0, false, H_char_downcase);
  sc->CHAR_TO_INTEGER =       s7_define_safe_function(sc, "char->integer",           g_char_to_integer,        1, 0, false, H_char_to_integer);
  sc->INTEGER_TO_CHAR =       s7_define_safe_function(sc, "integer->char",           g_integer_to_char,        1, 0, false, H_integer_to_char);
  
  sc->IS_CHAR_UPPER_CASE =    s7_define_safe_function(sc, "char-upper-case?",        g_is_char_upper_case,     1, 0, false, H_is_char_upper_case);
  sc->IS_CHAR_LOWER_CASE =    s7_define_safe_function(sc, "char-lower-case?",        g_is_char_lower_case,     1, 0, false, H_is_char_lower_case);
  sc->IS_CHAR_ALPHABETIC =    s7_define_safe_function(sc, "char-alphabetic?",        g_is_char_alphabetic,     1, 0, false, H_is_char_alphabetic);
  sc->IS_CHAR_NUMERIC =       s7_define_safe_function(sc, "char-numeric?",           g_is_char_numeric,        1, 0, false, H_is_char_numeric);
  sc->IS_CHAR_WHITESPACE =    s7_define_safe_function(sc, "char-whitespace?",        g_is_char_whitespace,     1, 0, false, H_is_char_whitespace);
  sc->IS_CHAR =               s7_define_safe_function(sc, "char?",                   g_is_char,                1, 0, false, H_is_char);
  
  sc->CHAR_EQ =               s7_define_safe_function(sc, "char=?",                  g_chars_are_equal,        2, 0, true,  H_chars_are_equal);
  sc->CHAR_LT =               s7_define_safe_function(sc, "char<?",                  g_chars_are_less,         2, 0, true,  H_chars_are_less);
  sc->CHAR_GT =               s7_define_safe_function(sc, "char>?",                  g_chars_are_greater,      2, 0, true,  H_chars_are_greater);
  sc->CHAR_LEQ =              s7_define_safe_function(sc, "char<=?",                 g_chars_are_leq,          2, 0, true,  H_chars_are_leq);
  sc->CHAR_GEQ =              s7_define_safe_function(sc, "char>=?",                 g_chars_are_geq,          2, 0, true,  H_chars_are_geq);
#if (!WITH_PURE_S7)
  sc->CHAR_CI_EQ =            s7_define_safe_function(sc, "char-ci=?",               g_chars_are_ci_equal,     2, 0, true,  H_chars_are_ci_equal);
  sc->CHAR_CI_LT =            s7_define_safe_function(sc, "char-ci<?",               g_chars_are_ci_less,      2, 0, true,  H_chars_are_ci_less);
  sc->CHAR_CI_GT =            s7_define_safe_function(sc, "char-ci>?",               g_chars_are_ci_greater,   2, 0, true,  H_chars_are_ci_greater);
  sc->CHAR_CI_LEQ =           s7_define_safe_function(sc, "char-ci<=?",              g_chars_are_ci_leq,       2, 0, true,  H_chars_are_ci_leq);
  sc->CHAR_CI_GEQ =           s7_define_safe_function(sc, "char-ci>=?",              g_chars_are_ci_geq,       2, 0, true,  H_chars_are_ci_geq);
#endif  
  sc->CHAR_POSITION =         s7_define_safe_function(sc, "char-position",           g_char_position,          2, 1, false, H_char_position);
  sc->STRING_POSITION =       s7_define_safe_function(sc, "string-position",         g_string_position,        2, 1, false, H_string_position);
  
  sc->IS_STRING =             s7_define_safe_function(sc, "string?",                 g_is_string,              1, 0, false, H_is_string);
  sc->MAKE_STRING =           s7_define_safe_function(sc, "make-string",             g_make_string,            1, 1, false, H_make_string);
  sc->STRING_LENGTH =         s7_define_safe_function(sc, "string-length",           g_string_length,          1, 0, false, H_string_length);
  sc->STRING_REF =            s7_define_safe_function(sc, "string-ref",              g_string_ref,             2, 0, false, H_string_ref);
  sc->STRING_SET =            s7_define_safe_function(sc, "string-set!",             g_string_set,             3, 0, false, H_string_set);
  sc->STRING_EQ =             s7_define_safe_function(sc, "string=?",                g_strings_are_equal,      2, 0, true,  H_strings_are_equal);
  sc->STRING_LT =             s7_define_safe_function(sc, "string<?",                g_strings_are_less,       2, 0, true,  H_strings_are_less);
  sc->STRING_GT =             s7_define_safe_function(sc, "string>?",                g_strings_are_greater,    2, 0, true,  H_strings_are_greater);
  sc->STRING_LEQ =            s7_define_safe_function(sc, "string<=?",               g_strings_are_leq,        2, 0, true,  H_strings_are_leq);
  sc->STRING_GEQ =            s7_define_safe_function(sc, "string>=?",               g_strings_are_geq,        2, 0, true,  H_strings_are_geq);
#if (!WITH_PURE_S7)
  sc->STRING_CI_EQ =          s7_define_safe_function(sc, "string-ci=?",             g_strings_are_ci_equal,   2, 0, true,  H_strings_are_ci_equal);
  sc->STRING_CI_LT =          s7_define_safe_function(sc, "string-ci<?",             g_strings_are_ci_less,    2, 0, true,  H_strings_are_ci_less);
  sc->STRING_CI_GT =          s7_define_safe_function(sc, "string-ci>?",             g_strings_are_ci_greater, 2, 0, true,  H_strings_are_ci_greater);
  sc->STRING_CI_LEQ =         s7_define_safe_function(sc, "string-ci<=?",            g_strings_are_ci_leq,     2, 0, true,  H_strings_are_ci_leq);
  sc->STRING_CI_GEQ =         s7_define_safe_function(sc, "string-ci>=?",            g_strings_are_ci_geq,     2, 0, true,  H_strings_are_ci_geq);
#endif  
  sc->STRING_DOWNCASE =       s7_define_safe_function(sc, "string-downcase",         g_string_downcase,        1, 0, false, H_string_downcase);
  sc->STRING_UPCASE =         s7_define_safe_function(sc, "string-upcase",           g_string_upcase,          1, 0, false, H_string_upcase);
  sc->STRING_APPEND =         s7_define_safe_function(sc, "string-append",           g_string_append,          0, 0, true,  H_string_append);
  sc->STRING_FILL =           s7_define_safe_function(sc, "string-fill!",            g_string_fill,            2, 2, false, H_string_fill);
  sc->STRING_COPY =           s7_define_safe_function(sc, "string-copy",             g_string_copy,            1, 0, false, H_string_copy);
  sc->SUBSTRING =             s7_define_safe_function(sc, "substring",               g_substring,              2, 1, false, H_substring);
  sc->STRING =                s7_define_safe_function(sc, "string",                  g_string,                 0, 0, true,  H_string);
  sc->LIST_TO_STRING =        s7_define_safe_function(sc, "list->string",            g_list_to_string,         1, 0, false, H_list_to_string);
  sc->STRING_TO_LIST =        s7_define_safe_function(sc, "string->list",            g_string_to_list,         1, 2, false, H_string_to_list);
  sc->OBJECT_TO_STRING =      s7_define_safe_function(sc, "object->string",          g_object_to_string,       1, 1, false, H_object_to_string);
  sc->FORMAT =                s7_define_function(sc,      "format",                  g_format,                 1, 0, true,  H_format);

  /* as format runs through the saved args, "~A" can call object->string; it can call format, and 
   *    sc->temp_cell_2 can be stepped on in the arg evaluation of the recursive format call,
   *    so format isn't safe if we've seen open_env + object->string.  This isn't called
   *    millions of times, normally, so I think I'll just leave it "unsafe" (rather than
   *    clearing that flag in openlet).  Any other function that keeps sc->args
   *    in play long enough for s7_call should also be unsafe.
   * Would it fix this to save/restore the temp cells across the object->string method application? -- no.
   */

  sc->IS_NULL =               s7_define_safe_function(sc, "null?",                   g_is_null,                1, 0, false, H_is_null);
  sc->IS_LIST =               s7_define_safe_function(sc, "list?",                   g_is_list,                1, 0, false, H_is_list);
  sc->IS_PAIR =               s7_define_safe_function(sc, "pair?",                   g_is_pair,                1, 0, false, H_is_pair);
  sc->CONS =                  s7_define_safe_function(sc, "cons",                    g_cons,                   2, 0, false, H_cons);

  sc->CAR =                   s7_define_safe_function(sc, "car",                     g_car,                    1, 0, false, H_car);
  sc->CDR =                   s7_define_safe_function(sc, "cdr",                     g_cdr,                    1, 0, false, H_cdr);
  sc->SET_CAR =               s7_define_safe_function(sc, "set-car!",                g_set_car,                2, 0, false, H_set_car); /* ?: list-set! is safe */
  sc->SET_CDR =               s7_define_function(sc,      "set-cdr!",                g_set_cdr,                2, 0, false, H_set_cdr);
  sc->CAAR =                  s7_define_safe_function(sc, "caar",                    g_caar,                   1, 0, false, H_caar);
  sc->CADR =                  s7_define_safe_function(sc, "cadr",                    g_cadr,                   1, 0, false, H_cadr);
  sc->CDAR =                  s7_define_safe_function(sc, "cdar",                    g_cdar,                   1, 0, false, H_cdar);
  sc->CDDR =                  s7_define_safe_function(sc, "cddr",                    g_cddr,                   1, 0, false, H_cddr);
  sc->CAAAR =                 s7_define_safe_function(sc, "caaar",                   g_caaar,                  1, 0, false, H_caaar);
  sc->CAADR =                 s7_define_safe_function(sc, "caadr",                   g_caadr,                  1, 0, false, H_caadr);
  sc->CADAR =                 s7_define_safe_function(sc, "cadar",                   g_cadar,                  1, 0, false, H_cadar);
  sc->CDAAR =                 s7_define_safe_function(sc, "cdaar",                   g_cdaar,                  1, 0, false, H_cdaar);
  sc->CADDR =                 s7_define_safe_function(sc, "caddr",                   g_caddr,                  1, 0, false, H_caddr);
  sc->CDDDR =                 s7_define_safe_function(sc, "cdddr",                   g_cdddr,                  1, 0, false, H_cdddr);
  sc->CDADR =                 s7_define_safe_function(sc, "cdadr",                   g_cdadr,                  1, 0, false, H_cdadr);
  sc->CDDAR =                 s7_define_safe_function(sc, "cddar",                   g_cddar,                  1, 0, false, H_cddar);
  sc->CAAAAR =                s7_define_safe_function(sc, "caaaar",                  g_caaaar,                 1, 0, false, H_caaaar);
  sc->CAAADR =                s7_define_safe_function(sc, "caaadr",                  g_caaadr,                 1, 0, false, H_caaadr);
  sc->CAADAR =                s7_define_safe_function(sc, "caadar",                  g_caadar,                 1, 0, false, H_caadar);
  sc->CADAAR =                s7_define_safe_function(sc, "cadaar",                  g_cadaar,                 1, 0, false, H_cadaar);
  sc->CAADDR =                s7_define_safe_function(sc, "caaddr",                  g_caaddr,                 1, 0, false, H_caaddr);
  sc->CADDDR =                s7_define_safe_function(sc, "cadddr",                  g_cadddr,                 1, 0, false, H_cadddr);
  sc->CADADR =                s7_define_safe_function(sc, "cadadr",                  g_cadadr,                 1, 0, false, H_cadadr);
  sc->CADDAR =                s7_define_safe_function(sc, "caddar",                  g_caddar,                 1, 0, false, H_caddar);
  sc->CDAAAR =                s7_define_safe_function(sc, "cdaaar",                  g_cdaaar,                 1, 0, false, H_cdaaar);
  sc->CDAADR =                s7_define_safe_function(sc, "cdaadr",                  g_cdaadr,                 1, 0, false, H_cdaadr);
  sc->CDADAR =                s7_define_safe_function(sc, "cdadar",                  g_cdadar,                 1, 0, false, H_cdadar);
  sc->CDDAAR =                s7_define_safe_function(sc, "cddaar",                  g_cddaar,                 1, 0, false, H_cddaar);
  sc->CDADDR =                s7_define_safe_function(sc, "cdaddr",                  g_cdaddr,                 1, 0, false, H_cdaddr);
  sc->CDDDDR =                s7_define_safe_function(sc, "cddddr",                  g_cddddr,                 1, 0, false, H_cddddr);
  sc->CDDADR =                s7_define_safe_function(sc, "cddadr",                  g_cddadr,                 1, 0, false, H_cddadr);
  sc->CDDDAR =                s7_define_safe_function(sc, "cdddar",                  g_cdddar,                 1, 0, false, H_cdddar);

  sc->ASSQ =                  s7_define_safe_function(sc, "assq",                    g_assq,                   2, 0, false, H_assq);
  sc->ASSV =                  s7_define_safe_function(sc, "assv",                    g_assv,                   2, 0, false, H_assv);
  sc->ASSOC =                 s7_define_function(sc,      "assoc",                   g_assoc,                  2, 1, false, H_assoc);
  sc->MEMQ =                  s7_define_safe_function(sc, "memq",                    g_memq,                   2, 0, false, H_memq);
  sc->MEMV =                  s7_define_safe_function(sc, "memv",                    g_memv,                   2, 0, false, H_memv);
  sc->MEMBER =                s7_define_function(sc,      "member",                  g_member,                 2, 1, false, H_member);

  sc->APPEND =                s7_define_safe_function(sc, "append",                  g_append,                 0, 0, true,  H_append);
  sc->LIST =                  s7_define_safe_function(sc, "list",                    g_list,                   0, 0, true,  H_list);
  sc->LIST_REF =              s7_define_safe_function(sc, "list-ref",                g_list_ref,               2, 0, true,  H_list_ref);
  sc->LIST_SET =              s7_define_safe_function(sc, "list-set!",               g_list_set,               3, 0, true,  H_list_set);
  sc->LIST_TAIL =             s7_define_safe_function(sc, "list-tail",               g_list_tail,              2, 0, false, H_list_tail);
  sc->MAKE_LIST =             s7_define_safe_function(sc, "make-list",               g_make_list,              1, 1, false, H_make_list);

  sc->LENGTH =                s7_define_safe_function(sc, "length",                  g_length,                 1, 0, false, H_length);
  sc->COPY =                  s7_define_safe_function(sc, "copy",                    g_copy,                   1, 3, false, H_copy);
  sc->FILL =                  s7_define_safe_function(sc, "fill!",                   g_fill,                   2, 2, false, H_fill);  /* was unsafe until 12-Feb-14 */
  sc->REVERSE =               s7_define_safe_function(sc, "reverse",                 g_reverse,                1, 0, false, H_reverse);
  sc->REVERSEB =              s7_define_safe_function(sc, "reverse!",                g_reverse_in_place,       1, 0, false, H_reverse_in_place); /* same 8-9-14 */
  sc->SORT =                  s7_define_function(sc,      "sort!",                   g_sort,                   2, 0, false, H_sort);

  sc->LIST_TO_VECTOR =        s7_define_safe_function(sc, "list->vector",            g_list_to_vector,         1, 0, false, H_list_to_vector);
  sc->VECTOR_TO_LIST =        s7_define_safe_function(sc, "vector->list",            g_vector_to_list,         1, 2, false, H_vector_to_list);
  sc->IS_VECTOR =             s7_define_safe_function(sc, "vector?",                 g_is_vector,              1, 0, false, H_is_vector);
  sc->VECTOR_APPEND =         s7_define_safe_function(sc, "vector-append",           g_vector_append,          0, 0, true,  H_vector_append);
  sc->VECTOR_FILL =           s7_define_safe_function(sc, "vector-fill!",            g_vector_fill,            2, 2, false, H_vector_fill);
  sc->VECTOR_LENGTH =         s7_define_safe_function(sc, "vector-length",           g_vector_length,          1, 0, false, H_vector_length);
  sc->VECTOR_REF =            s7_define_safe_function(sc, "vector-ref",              g_vector_ref,             2, 0, true,  H_vector_ref);
  sc->VECTOR_SET =            s7_define_safe_function(sc, "vector-set!",             g_vector_set,             3, 0, true,  H_vector_set);
  sc->VECTOR_DIMENSIONS =     s7_define_safe_function(sc, "vector-dimensions",       g_vector_dimensions,      1, 0, false, H_vector_dimensions);
  sc->MAKE_VECTOR =           s7_define_safe_function(sc, "make-vector",             g_make_vector,            1, 2, false, H_make_vector);
  sc->MAKE_SHARED_VECTOR =    s7_define_safe_function(sc, "make-shared-vector",      g_make_shared_vector,     2, 1, false, H_make_shared_vector);
  sc->VECTOR =                s7_define_safe_function(sc, "vector",                  g_vector,                 0, 0, true,  H_vector);
  set_setter(sc->VECTOR); /* ?? */
  sc->Vector = s7_symbol_value(sc, sc->VECTOR);
  set_setter(sc->Vector);

  sc->IS_FLOAT_VECTOR =       s7_define_safe_function(sc, "float-vector?",           g_is_float_vector,        1, 0, false, H_is_float_vector);
  sc->FLOAT_VECTOR =          s7_define_safe_function(sc, "float-vector",            g_float_vector,           0, 0, true,  H_float_vector);
  sc->MAKE_FLOAT_VECTOR =     s7_define_safe_function(sc, "make-float-vector",       g_make_float_vector,      1, 1, false, H_make_float_vector);
  sc->FLOAT_VECTOR_SET =      s7_define_safe_function(sc, "float-vector-set!",       g_float_vector_set,       3, 0, true,  H_float_vector_set);
  sc->FLOAT_VECTOR_REF =      s7_define_safe_function(sc, "float-vector-ref",        g_float_vector_ref,       2, 0, true,  H_float_vector_ref);
  set_returns_temp(s7_symbol_value(sc, sc->FLOAT_VECTOR_REF));

  sc->IS_BYTEVECTOR =         s7_define_safe_function(sc, "bytevector?",             g_is_bytevector,          1, 0, false, H_is_bytevector);
  sc->TO_BYTEVECTOR =         s7_define_safe_function(sc, "->bytevector",            g_to_bytevector,          1, 0, false, H_to_bytevector);
  sc->BYTEVECTOR =            s7_define_safe_function(sc, "bytevector",              g_bytevector,             0, 0, true,  H_bytevector);
  sc->MAKE_BYTEVECTOR =       s7_define_safe_function(sc, "make-bytevector",         g_make_bytevector,        1, 1, false, H_make_bytevector);

  sc->HASH_TABLE =            s7_define_safe_function(sc, "hash-table",              g_hash_table,             0, 0, true,  H_hash_table);
  sc->HASH_TABLE_STAR =       s7_define_safe_function(sc, "hash-table*",             g_hash_table_star,        0, 0, true,  H_hash_table_star);
  sc->IS_HASH_TABLE =         s7_define_safe_function(sc, "hash-table?",             g_is_hash_table,          1, 0, false, H_is_hash_table);
  sc->MAKE_HASH_TABLE =       s7_define_safe_function(sc, "make-hash-table",         g_make_hash_table,        0, 2, false, H_make_hash_table);
  sc->HASH_TABLE_REF =        s7_define_safe_function(sc, "hash-table-ref",          g_hash_table_ref,         2, 0, true,  H_hash_table_ref);
  sc->HASH_TABLE_SET =        s7_define_safe_function(sc, "hash-table-set!",         g_hash_table_set,         3, 0, false, H_hash_table_set);
  sc->HASH_TABLE_SIZE =       s7_define_safe_function(sc, "hash-table-size",         g_hash_table_size,        1, 0, false, H_hash_table_size);
  sc->HASH_TABLE_ENTRIES =    s7_define_safe_function(sc, "hash-table-entries",      g_hash_table_entries,     1, 0, false, H_hash_table_entries);
                              s7_define_safe_function(sc, "hash-table-index",        g_hash_table_index,       1, 0, false, "an experiment");

  sc->ht_iter_tag = s7_new_type_x("<hash-table-iterator>", print_ht_iter, ht_iter_free, equal_ht_iter, mark_ht_iter, ref_ht_iter, NULL, NULL, copy_ht_iter, NULL, NULL);
  sc->MAKE_HASH_TABLE_ITERATOR = s7_define_safe_function(sc, "make-hash-table-iterator", g_make_hash_table_iterator, 1, 0, false, H_make_hash_table_iterator);
  sc->IS_HASH_TABLE_ITERATOR =   s7_define_safe_function(sc, "hash-table-iterator?",     g_is_hash_table_iterator,   1, 0, false, H_is_hash_table_iterator);
  s7_set_object_print_readably(sc->ht_iter_tag, write_ht_iter_readably);

                              s7_define_function(sc,      "cyclic-sequences",        g_cyclic_sequences,       1, 0, false, H_cyclic_sequences);
  sc->CALL_CC =               s7_define_function(sc,      "call/cc",                 g_call_cc,                1, 0, false, H_call_cc);
  sc->CALL_WITH_CURRENT_CONTINUATION = s7_define_function(sc, "call-with-current-continuation", g_call_cc,     1, 0, false, H_call_cc);
  sc->IS_CONTINUATION =       s7_define_safe_function(sc, "continuation?",           g_is_continuation,        1, 0, false, H_is_continuation);
  sc->CALL_WITH_EXIT =        s7_define_function(sc,      "call-with-exit",          g_call_with_exit,         1, 0, false, H_call_with_exit);

  sc->LOAD =                  s7_define_function(sc,      "load",                    g_load,                   1, 1, false, H_load);
  sc->AUTOLOAD =              s7_define_function(sc,      "autoload",                g_autoload,               2, 0, false, H_autoload);
  sc->EVAL =                  s7_define_function(sc,      "eval",                    g_eval,                   1, 1, false, H_eval);
  sc->EVAL_STRING =           s7_define_function(sc,      "eval-string",             g_eval_string,            1, 1, false, H_eval_string);
  sc->APPLY =                 s7_define_function(sc,      "apply",                   g_apply,                  1, 0, true,  H_apply);
  sc->Apply = s7_symbol_value(sc, sc->APPLY);
  set_type(sc->Apply, type(sc->Apply) | T_COPY_ARGS);
  /* (let ((x '((1 2) 3 4))) (catch #t (lambda () (apply apply apply x)) (lambda args 'error)) x) should not mess up x! */

  sc->FOR_EACH =              s7_define_function(sc,      "for-each",                g_for_each,               2, 0, true,  H_for_each);
  sc->MAP =                   s7_define_function(sc,      "map",                     g_map,                    2, 0, true,  H_map);
  sc->VALUES =                s7_define_function(sc,      "values",                  g_values,                 0, 0, true,  H_values);
  sc->DYNAMIC_WIND =          s7_define_function(sc,      "dynamic-wind",            g_dynamic_wind,           3, 0, false, H_dynamic_wind);
  sc->CATCH =                 s7_define_function(sc,      "catch",                   g_catch,                  3, 0, false, H_catch);
  sc->THROW =                 s7_define_function(sc,      "throw",                   g_throw,                  1, 0, true,  H_throw);
  sc->ERROR =                 s7_define_function(sc,      "error",                   g_error,                  0, 0, true,  H_error);

  /* these are internal for quasiquote's use -- they are values, not symbols */
  sc->QQ_Apply_Values =       s7_define_constant_function(sc, "{apply_values}",      g_apply_values,           0, 0, true,  H_apply_values);
  sc->QQ_Append =             s7_define_constant_function(sc, "{append}",            g_append,                 0, 0, true,  H_append);
  sc->Multivector =           s7_define_constant_function(sc, "{multivector}",       g_qq_multivector,         1, 0, true,  H_qq_multivector);
  sc->QQ_List =               s7_define_constant_function(sc, "{list}",              g_qq_list,                0, 0, true,  H_qq_list);
  set_type(sc->QQ_List, (T_C_RST_ARGS_FUNCTION | T_PROCEDURE | T_COPY_ARGS));

  sc->IS_PROCEDURE =          s7_define_safe_function(sc, "procedure?",              g_is_procedure,           1, 0, false, H_is_procedure);
  sc->PROCEDURE_DOCUMENTATION = s7_define_safe_function(sc, "procedure-documentation", g_procedure_documentation, 1, 0, false, H_procedure_documentation);
  sc->HELP =                  s7_define_safe_function(sc, "help",                    g_help,                   1, 0, false, H_help);
  sc->PROCEDURE_ARITY =       s7_define_safe_function(sc, "procedure-arity",         g_procedure_arity,        1, 0, false, H_procedure_arity);
  sc->PROCEDURE_SOURCE =      s7_define_safe_function(sc, "procedure-source",        g_procedure_source,       1, 0, false, H_procedure_source);
  sc->FUNCLET =               s7_define_safe_function(sc, "funclet",                 g_funclet,                1, 0, false, H_funclet);
  sc->PROCEDURE_NAME =        s7_define_safe_function(sc, "procedure-name",          g_procedure_name,         1, 0, false, H_procedure_name);
  s7_dilambda(sc, "procedure-setter", g_procedure_setter, 1, 0, g_procedure_set_setter, 2, 0, H_procedure_setter);
  sc->IS_DILAMBDA =           s7_define_safe_function(sc, "dilambda?",               g_is_dilambda,            1, 0, false, H_is_dilambda);

  sc->ARITY =                 s7_define_safe_function(sc, "arity",                   g_arity,                  1, 0, false, H_arity);
  sc->IS_ARITABLE =           s7_define_safe_function(sc, "aritable?",               g_is_aritable,            2, 0, false, H_is_aritable);
  
  sc->NOT =                   s7_define_safe_function(sc, "not",                     g_not,                    1, 0, false, H_not);
  sc->IS_BOOLEAN =            s7_define_safe_function(sc, "boolean?",                g_is_boolean,             1, 0, false, H_is_boolean);
  sc->IS_EQ =                 s7_define_safe_function(sc, "eq?",                     g_is_eq,                  2, 0, false, H_is_eq);
  sc->IS_EQV =                s7_define_safe_function(sc, "eqv?",                    g_is_eqv,                 2, 0, false, H_is_eqv);
  sc->IS_EQUAL =              s7_define_safe_function(sc, "equal?",                  g_is_equal,               2, 0, false, H_is_equal);
  sc->IS_MORALLY_EQUAL =      s7_define_safe_function(sc, "morally-equal?",          g_is_morally_equal,       2, 0, false, H_is_morally_equal);
  
  sc->GC =                    s7_define_safe_function(sc, "gc",                      g_gc,                     0, 1, false, H_gc);
                              s7_define_safe_function(sc, "s7-version",              g_s7_version,             0, 0, false, H_s7_version);
                              s7_define_safe_function(sc, "emergency-exit",          g_emergency_exit,         0, 1, false, H_emergency_exit);
                              s7_define_safe_function(sc, "exit",                    g_exit,                   0, 1, false, H_exit);
#if DEBUGGING
                              s7_define_function(sc,      "abort",                   g_abort,                  0, 0, false, "drop into gdb I hope");
#endif

  sym = s7_define_function(sc, "(c-object set)", g_internal_object_set, 1, 0, true, "internal object setter redirection");
  sc->Object_Set = s7_symbol_value(sc, sym);


  /* -------- *features* -------- */
  sc->S7_FEATURES = s7_define_variable(sc, "*features*", sc->NIL);
  s7_symbol_set_access(sc, sc->S7_FEATURES, s7_make_function(sc, "(set *features*)", g_features_set, 2, 0, false, "*features* accessor"));

  /* -------- *vector-print-length* -------- */
  sym = s7_define_variable(sc, "*vector-print-length*", small_int(8));
  sc->vector_print_length = global_slot(sym);
  s7_symbol_set_access(sc, sym, s7_make_function(sc, "(set *vector-print-length*)", g_vector_print_length_set, 2, 0, false, "*vector-print-length* accessor"));

  /* -------- *load-path* -------- */
  sc->LOAD_PATH = s7_define_variable_with_documentation(sc, "*load-path*", sc->NIL, "*load-path* is a list of directories (strings) that the load function searches if it is passed an incomplete file name");
  s7_symbol_set_access(sc, sc->LOAD_PATH, s7_make_function(sc, "(set *load-path*)", g_load_path_set, 2, 0, false, "*load-path* accessor"));


  /* -------- *autoload* -------- 
   * this pretends to be a hash-table or environment, but it's actually a function
   */
  sc->AUTOLOADER = s7_define_function(sc, "*autoload*", g_autoloader, 1, 0, false, H_autoloader);
  sym = s7_define_variable(sc, "*libraries*", sc->NIL);
  sc->libraries = global_slot(sym);

  s7_autoload(sc, make_symbol(sc, "cload.scm"),    s7_make_permanent_string("cload.scm"));
  s7_autoload(sc, make_symbol(sc, "lint.scm"),     s7_make_permanent_string("lint.scm"));
  s7_autoload(sc, make_symbol(sc, "stuff.scm"),    s7_make_permanent_string("stuff.scm"));
  s7_autoload(sc, make_symbol(sc, "mockery.scm"),  s7_make_permanent_string("mockery.scm"));
  s7_autoload(sc, make_symbol(sc, "write.scm"),    s7_make_permanent_string("write.scm"));
  s7_autoload(sc, make_symbol(sc, "r7rs.scm"),     s7_make_permanent_string("r7rs.scm"));

  s7_autoload(sc, make_symbol(sc, "libc.scm"),     s7_make_permanent_string("libc.scm"));
  s7_autoload(sc, make_symbol(sc, "libm.scm"),     s7_make_permanent_string("libm.scm"));
  s7_autoload(sc, make_symbol(sc, "libdl.scm"),    s7_make_permanent_string("libdl.scm"));
  s7_autoload(sc, make_symbol(sc, "libgsl.scm"),   s7_make_permanent_string("libgsl.scm"));
  s7_autoload(sc, make_symbol(sc, "libgdbm.scm"),  s7_make_permanent_string("libgdbm.scm"));

  sc->REQUIRE = s7_define_macro(sc, "require", g_require, 0, 0, true, H_require);  


  /* -------- *#readers* -------- */
  sym = s7_define_variable(sc, "*#readers*", sc->NIL);
  sc->sharp_readers = global_slot(sym);
  s7_symbol_set_access(sc, sym, s7_make_function(sc, "(set *#readers*)", g_sharp_readers_set, 2, 0, false, "*#readers* accessor"));

  sc->STACKTRACE = s7_define_safe_function(sc, "stacktrace", g_stacktrace, 0, 5, false, H_stacktrace);
  sc->stacktrace_env = s7_inlet(sc, s7_list(sc, 5, 
			 cons(sc, s7_make_symbol(sc, "max-frames"), small_int(3)),
			 cons(sc, s7_make_symbol(sc, "code-cols"), small_int(45)),
			 cons(sc, s7_make_symbol(sc, "total-cols"), small_int(80)),
			 cons(sc, s7_make_symbol(sc, "notes-start-col"), small_int(45)),
			 cons(sc, s7_make_symbol(sc, "as-comment"), sc->T)));
  s7_define_constant(sc, "*stacktrace*", sc->stacktrace_env);

  /* sigh... I don't like these! */
  s7_define_constant(sc, "nan.0", real_NaN);
  s7_define_constant(sc, "-nan.0", real_NaN);
  s7_define_constant(sc, "inf.0", real_infinity);
  s7_define_constant(sc, "-inf.0", real_minus_infinity);
				
  /* *features* */
  s7_provide(sc, "s7");
  s7_provide(sc, "s7-" S7_VERSION);
  s7_provide(sc, "ratio");

#if WITH_PURE_S7
  s7_provide(sc, "pure-s7");
#endif
#if WITH_EXTRA_EXPONENT_MARKERS
  s7_provide(sc, "dfls-exponents");
#endif
#if WITH_AT_SIGN_AS_EXPONENT
  s7_provide(sc, "@-exponent");
#endif
#if WITH_SYSTEM_EXTRAS
  s7_provide(sc, "system-extras");
#endif
#if WITH_IMMUTABLE_UNQUOTE
  s7_provide(sc, "immutable-unquote");
#endif
#if DEBUGGING
  s7_provide(sc, "debugging");
#endif
#if HAVE_COMPLEX_NUMBERS
  s7_provide(sc, "complex-numbers");
#endif
#if WITH_C_LOADER
  s7_provide(sc, "dlopen");
#endif
#if WITH_MAKE_COMPLEX
  s7_provide(sc, "make-complex");
#endif


#ifdef __APPLE__
  s7_provide(sc, "osx");
#endif
#ifdef __linux__
  s7_provide(sc, "linux");
#endif
#ifdef __OpenBSD__
  s7_provide(sc, "openbsd");
#endif
#ifdef __NetBSD__
  s7_provide(sc, "netbsd");
#endif
#ifdef __FreeBSD__
  s7_provide(sc, "freebsd");
#endif
#if MS_WINDOWS
  s7_provide(sc, "windows");
#endif  
#ifdef __bfin__
  s7_provide(sc, "blackfin");
#endif
#ifdef __ANDROID__
  s7_provide(sc, "android");
#endif
#ifdef __CYGWIN__
  s7_provide(sc, "cygwin");
#endif
#ifdef __hpux
  s7_provide(sc, "hpux");
#endif
#if defined(__sun) && defined(__SVR4)
  s7_provide(sc, "solaris");
#endif


  sc->Vector_Set = s7_symbol_value(sc, sc->VECTOR_SET);
  set_setter(sc->Vector_Set);
  set_setter(sc->VECTOR_SET);
  /* not float-vector-set! here */
  
  sc->List_Set = s7_symbol_value(sc, sc->LIST_SET);
  set_setter(sc->List_Set);
  set_setter(sc->LIST_SET);
  
  sc->Hash_Table_Set = s7_symbol_value(sc, sc->HASH_TABLE_SET);
  set_setter(sc->Hash_Table_Set);
  set_setter(sc->HASH_TABLE_SET);
  
  sc->Let_Set = s7_symbol_value(sc, sc->LET_SET);
  set_setter(sc->Let_Set);
  set_setter(sc->LET_SET);
  
  set_setter(sc->CONS); /* (this blocks an over-eager do loop optimization -- see do-test-15 in s7test) */
  
  sc->String_Set = s7_symbol_value(sc, sc->STRING_SET);
  set_setter(sc->String_Set);
  set_setter(sc->STRING_SET);

  set_setter(sc->SET_CAR);
  set_setter(s7_symbol_value(sc, sc->SET_CAR));
  set_setter(sc->SET_CDR);
  set_setter(s7_symbol_value(sc, sc->SET_CDR));

  set_setter(s7_make_symbol(sc, "set-current-input-port"));
  set_setter(s7_make_symbol(sc, "set-current-output-port"));
  set_setter(s7_make_symbol(sc, "set-current-error-port"));
  s7_function_set_setter(sc, "current-input-port",  "set-current-input-port");
  s7_function_set_setter(sc, "current-output-port", "set-current-output-port");
  s7_function_set_setter(sc, "current-error-port",  "set-current-error-port");

  s7_function_set_setter(sc, "car",                 "set-car!");
  s7_function_set_setter(sc, "cdr",                 "set-cdr!");
  s7_function_set_setter(sc, "hash-table-ref",      "hash-table-set!");
  s7_function_set_setter(sc, "vector-ref",          "vector-set!");
  s7_function_set_setter(sc, "float-vector-ref",    "float-vector-set!");
  s7_function_set_setter(sc, "list-ref",            "list-set!");
  s7_function_set_setter(sc, "string-ref",          "string-set!");
  c_function_setter(s7_symbol_value(sc, sc->OUTLET)) = s7_make_function(sc, "(set! outlet)", g_set_outlet, 2, 0, false, "outlet setter"); 

  {
    int i, top;
#if WITH_GMP
    #define S7_LOG_LLONG_MAX 36.736800
    #define S7_LOG_LONG_MAX  16.6355322
#else
    /* actually not safe = (log (- (expt 2 63) 1)) and (log (- (expt 2 31) 1)) 
     *   (using 63 and 31 bits)
     */
    #define S7_LOG_LLONG_MAX 43.668274
    #define S7_LOG_LONG_MAX  21.487562
#endif

    top = sizeof(s7_Int);
    s7_int_max = (top == 8) ? S7_LONG_MAX : S7_SHORT_MAX;
    s7_int_min = (top == 8) ? S7_LONG_MIN : S7_SHORT_MIN;
    s7_int_bits = (top == 8) ? 63 : 31;
    s7_int_digits = (top == 8) ? 18 : 8;

    s7_Int_max = (top == 8) ? S7_LLONG_MAX : S7_LONG_MAX;
    s7_Int_min = (top == 8) ? S7_LLONG_MIN : S7_LONG_MIN;

    s7_int_digits_by_radix[0] = 0;
    s7_int_digits_by_radix[1] = 0;

    for (i = 2; i < 17; i++)
      s7_int_digits_by_radix[i] = (int)(floor(((top == 8) ? S7_LOG_LLONG_MAX : S7_LOG_LONG_MAX) / log((double)i)));

    s7_define_constant(sc, "most-positive-fixnum", make_permanent_integer((top == 8) ? S7_LLONG_MAX : ((top == 4) ? S7_LONG_MAX : S7_SHORT_MAX)));
    s7_define_constant(sc, "most-negative-fixnum", make_permanent_integer((top == 8) ? S7_LLONG_MIN : ((top == 4) ? S7_LONG_MIN : S7_SHORT_MIN)));
    
    if (top == 4) sc->default_rationalize_error = 1.0e-6;
    s7_define_constant(sc, "pi", real_pi);
    sc->PI = s7_make_symbol(sc, "pi");

    sc->default_rng = (s7_rng_t *)calloc(1, sizeof(s7_rng_t));
    sc->default_rng->ran_seed = (unsigned int)time(NULL);
    sc->default_rng->ran_carry = 1675393560;

    /* for s7_Double, float gives about 9 digits, double 18, long Double claims 28 but I don't see more than about 22? */

    for (i = 0; i < 10; i++) sc->singletons[(unsigned char)'0' + i] = small_int(i);
    sc->singletons[(unsigned char)'+'] = sc->ADD;
    sc->singletons[(unsigned char)'-'] = sc->MINUS;
    sc->singletons[(unsigned char)'*'] = sc->MULTIPLY;
    sc->singletons[(unsigned char)'/'] = sc->DIVIDE;
    sc->singletons[(unsigned char)'<'] = sc->LT;
    sc->singletons[(unsigned char)'>'] = sc->GT;
    sc->singletons[(unsigned char)'='] = sc->EQ;
  }

#if WITH_GMP
  s7_gmp_init(sc);
#endif

#if WITH_DUMP_HEAP  
  s7_define_function(sc, "dump-heap", g_dump_heap, 0, 0, false, "write heap contents to heap.data"); 
#endif

  init_choosers(sc);

  /* macroexpand 
   *   needs to be a bacro so locally defined macros are expanded:
   *   (let () (define-macro (hi a) `(+ ,a 1)) (macroexpand (hi 2)))
   *   we could use a gensym in place of __mac__ (see s7.html for an example), but then symbol mark in the gc sweep is less optimized.
   */
  s7_eval_c_string(sc, "(define-bacro (macroexpand __mac__) `(,(procedure-source (car __mac__)) ',__mac__))");
  
  /* quasiquote
   */
  s7_define_macro(sc, "quasiquote", g_quasiquote, 1, 0, false, H_quasiquote);

  /* CL-style macros */
  s7_eval_c_string(sc, "(define-macro (defmacro name args . body) `(define-macro ,(cons name args) ,@body))");
  s7_eval_c_string(sc, "(define-macro (defmacro* name args . body) `(define-macro* ,(cons name args) ,@body))");

  /* call-with-values is almost a no-op; r7rs's fault.
   *   this really should check that the producer and consumer make sense (not a circular list for example).
   */
  s7_eval_c_string(sc, "(define-macro (call-with-values producer consumer) `(,consumer (,producer)))"); 
  /* (call-with-values (lambda () (values 1 2 3)) +) */

  s7_eval_c_string(sc, "(define-macro (multiple-value-bind vars expression . body)                            \n\
                           (if (or (symbol? vars) (negative? (length vars)))                                  \n\
                               `((lambda ,vars ,@body) ,expression)                                           \n\
                               `((lambda* (,@vars . ,(gensym)) ,@body) ,expression)))");
  /* (multiple-value-bind (a b) (values 1 2) (+ a b)) */
  /*   named "receive" in srfi-8 which strikes me as perverse */

  s7_eval_c_string(sc, "(define-macro (multiple-value-set! vars expr . body)                                  \n\
                          (let ((local-vars (map (lambda (n) (gensym)) vars)))                                \n\
                            `((lambda* (,@local-vars . ,(gensym))                                             \n\
                                ,@(map (lambda (n ln) `(set! ,n ,ln)) vars local-vars)                        \n\
                                ,@body)                                                                       \n\
                              ,expr)))");


  /* ---------------- dilambda ---------------- */

  s7_eval_c_string(sc, "(define (dilambda g s)                                                                \n\
                          (if (or (not (procedure? g)) (not (procedure? s)))                                  \n\
                              (error 'wrong-type-arg \"dilambda takes 2 procedures: ~A ~A\" g s)              \n\
                              (set! (procedure-setter g) s))                                                  \n\
                          g)");


  /* ---------------- cond-expand ---------------- */

  /* cond-expand (uses *features*) 
   */
  s7_eval_c_string(sc, "(define-macro (cond-expand . clauses)                                                 \n\
                          (letrec ((traverse (lambda (tree)                                                   \n\
		                               (if (pair? tree)                                               \n\
			                            (cons (traverse (car tree))                               \n\
				                          (if (null? (cdr tree)) () (traverse (cdr tree))))   \n\
			                            (if (memq tree '(and or not else)) tree                   \n\
			                                (and (symbol? tree) (provided? tree)))))))            \n\
                            `(cond ,@(map (lambda (clause)                                                    \n\
		                             (cons (traverse (car clause))                                    \n\
			                           (if (null? (cdr clause)) '(#f) (cdr clause))))             \n\
		                          clauses))))");


  s7_eval_c_string(sc, "(define-expansion (reader-cond . clauses)                                             \n\
                          (call-with-exit                                                                     \n\
                            (lambda (return)                                                                  \n\
                              (for-each                                                                       \n\
                                (lambda (clause)                                                              \n\
	                          (if (eval (car clause))                                                     \n\
	                              (if (null? (cddr clause))                                               \n\
                                          (return (cadr clause))                                              \n\
                                          (return `(values ,@(cdr clause))))))                                \n\
                                clauses)                                                                      \n\
                              (values))))");


  /* ---------------- hooks ---------------- */

  s7_eval_c_string(sc, "(define (make-hook . args)                                                            \n\
                          (let ((init ())                                                                     \n\
	                        (body ())                                                                     \n\
	                        (end ()))                                                                     \n\
                            (apply lambda* args                                                               \n\
                              '(let ((result #<unspecified>))                                                 \n\
                                 (let ((e (curlet)))                                                          \n\
                                   (dynamic-wind                                                              \n\
	                             (lambda () (for-each (lambda (f) (f e)) init))                           \n\
	                             (lambda () (for-each (lambda (f) (f e)) body) result)                    \n\
	                             (lambda () (for-each (lambda (f) (f e)) end)))))                         \n\
                              ())))");

  s7_eval_c_string(sc, "(define hook-functions                                                                \n\
                          (dilambda                                                                           \n\
                            (lambda (hook)                                                                    \n\
                              ((funclet hook) 'body))                                                         \n\
                            (lambda (hook lst)                                                                \n\
                              (if (or (null? lst)                                                             \n\
                                      (and (pair? lst)                                                        \n\
                                           (apply and (map (lambda (f)                                        \n\
                                                             (and (procedure? f)                              \n\
                                                                  (aritable? f 1)))                           \n\
                                                           lst))))                                            \n\
                                  (set! ((funclet hook) 'body) lst)                                           \n\
                                  (error 'wrong-type-arg \"hook-functions must be a list of functions, each accepting one argument: ~S\" lst)))))");

  /* if the error checks are removed, s7test will be very unhappy 
   */
  
  /* -------- *unbound-variable-hook* -------- */
  sc->unbound_variable_hook = s7_eval_c_string(sc, "(make-hook 'variable)");
  s7_define_constant_with_documentation(sc, "*unbound-variable-hook*", sc->unbound_variable_hook, "*unbound-variable-hook* functions are called when an unbound variable is encountered, passed (hook 'variable).");
  sc->ubh_cur_code_loc = -1; /* this is the saved context in the unbound variable hook caller */
  sc->ubh_value_loc = -1;
  sc->ubh_args_loc = -1;
  sc->ubh_code_loc = -1;
  sc->ubh_hook_loc = -1;
  sc->ubh_x = -1;
  sc->ubh_z = -1;

  
  /* -------- *load-hook* -------- */
  sc->load_hook = s7_eval_c_string(sc, "(make-hook 'name)");
  s7_define_constant_with_documentation(sc, "*load-hook*", sc->load_hook, "*load-hook* functions are invoked by load, passing the to-be-loaded filename as (hook 'name)");

  /* -------- *error-hook* -------- */
  sc->error_hook = s7_eval_c_string(sc, "(make-hook 'type 'data)");
  s7_define_constant_with_documentation(sc, "*error-hook*", sc->error_hook, "*error-hook* functions are called in the error handler, passed (hook 'type) and (hook 'data).");

  s7_define_constant(sc, "*s7*", 
    s7_openlet(s7_inlet(sc, 
       s7_list(sc, 2, 
	       s7_cons(sc, s7_make_symbol(sc, "let-ref-fallback"), s7_make_function(sc, "s7-let-ref", g_s7_let_ref_fallback, 2, 0, false, "*s7* reader")),
	       s7_cons(sc, s7_make_symbol(sc, "let-set!-fallback"), s7_make_function(sc, "s7-let-set", g_s7_let_set_fallback, 3, 0, false, "*s7* writer"))))));


#if (!DISABLE_DEPRECATED)
  s7_eval_c_string(sc, "(define environment?                 let?)     \n\
                        (define global-environment           rootlet)  \n\
                        (define-constant initial-environment unlet)    \n\
                        (define outer-environment            outlet)   \n\
                        (define augment-environment          sublet)   \n\
                        (define augment-environment!         varlet)   \n\
                        (define current-environment          curlet)   \n\
                        (define error-environment            owlet)    \n\
                        (define procedure-environment        funclet)  \n\
                        (define environment->list            let->list)\n\
                        (define open-environment             openlet)  \n\
                        (define open-environment?            openlet?) \n\
                        (define close-environment            coverlet) \n\
                        (define environment-ref              let-ref)  \n\
                        (define environment-set!             let-set!) \n\
                        (define environment                  inlet)    \n\
                        (define environment*                 inlet)    \n\
                        (define make-procedure-with-setter   dilambda) \n\
                        (define procedure-with-setter?       dilambda?)");
#endif

  /* fprintf(stderr, "size: %d, max op: %d, opt: %d\n", (int)sizeof(s7_cell), OP_MAX_DEFINED, OPT_MAX_DEFINED); */
  /* 64 bit machine: size: 48 [size 72 if gmp], op: 324, opt: 430 */

#if DEBUGGING
  if (strcmp(op_names[OP_DEFINE_BACRO_STAR], "define-bacro*") != 0)
    fprintf(stderr, "define-bacro* op: %s\n", op_names[OP_DEFINE_BACRO_STAR]);
  if (strcmp(real_op_names[OP_DEFINE_BACRO_STAR], "define_bacro_star") != 0)
    fprintf(stderr, "define-bacro* real op: %s\n", real_op_names[OP_DEFINE_BACRO_STAR]);

  if (strcmp(op_names[OP_SAFE_C_opSq_P_1], "c_opsq_p_1") != 0)
    fprintf(stderr, "safe_c_opsq_p_1 op_names: %s\n", op_names[OP_SAFE_C_opSq_P_1]);
  if (strcmp(real_op_names[OP_SAFE_C_opSq_P_1], "safe_c_opsq_p_1") != 0)
    fprintf(stderr, "safe_c_opsq_p_1 real_op_names: %s\n", real_op_names[OP_SAFE_C_opSq_P_1]);

  if (strcmp(opt_names[OPT_MAX_DEFINED], "opt_max_defined") != 0)
    fprintf(stderr, "opt_max_defined opt_names: %s\n", opt_names[OPT_MAX_DEFINED]);
#else
  if ((!opt_names[0]) || (!real_op_names[0])) fprintf(stderr, "right"); /* this is just to make a compiler warning go away */
#endif
  if (sizeof(void *) > sizeof(s7_Int))
    fprintf(stderr, "s7_Int is too small: it has %d bytes, but void* has %d\n", (int)sizeof(s7_Int), (int)sizeof(void *));

  save_unlet(sc);
#if WITH_COUNTS
  clear_counts();
#endif
  init_s7_env(sc);          /* set up *s7* */
  already_inited = true;
  return(sc);
}


/* -------------------------------- repl -------------------------------- */

#if WITH_MAIN
/* this code extracted from xen.c in sndlib
 */
#include <readline/readline.h>
#include <curses.h> 

static s7_scheme *s7;

typedef struct {int loc, size, len; const char *text; const char **matches;} completions;

static bool find_match(const char *symbol_name, void *udata)
{
  completions *data = (completions *)udata;
  if (strncmp(data->text, symbol_name, data->len) == 0)
    {
      if (data->matches == NULL)
	{
	  data->matches = (const char **)calloc(8, sizeof(char *));
	  data->size = 8;
	  data->loc = 0;
	}
      else
	{
	  if (data->loc >= data->size)
	    {
	      int i;
	      data->size *= 2;
	      data->matches = (const char **)realloc(data->matches, data->size * sizeof(char *));
	      for (i = data->loc; i < data->size; i++) data->matches[i] = NULL;
	    }
	}
      data->matches[data->loc] = symbol_name;
      data->loc++;
    }
  return(false);
}

static int complete_loc = 0;
static completions *complete_data = NULL;
static char *copy_match(const char *text, int state)
{
  if (complete_loc < complete_data->loc)
    {
      char *result;
      int len;
      len = strlen(complete_data->matches[complete_loc]);
      result = calloc(len + 1, sizeof(char));
      memcpy((void *)result, (void *)complete_data->matches[complete_loc], len);
      complete_loc++;
      return(result);
    }
  return(NULL);
}

static char **completion(const char *text, int start, int end)
{
  completions *data; 
  char **matches = NULL;
  /* start and end are indices into the original line buffer, we just want text here */

  if (!text) return(NULL);

  data = (completions *)calloc(1, sizeof(completions));
  data->len = strlen(text);
  data->text = text;
		
  s7_for_each_symbol_name(s7, find_match, (void *)data);
  
  /* now to make readline happy, we apparently have to give it copies of everything! */
  if (data->matches)
    {
      complete_data = data;
      complete_loc = 0;
      matches = rl_completion_matches(text, copy_match);
      free(data->matches);
    }

  free(data);
  complete_data = NULL;
  return(matches);
}


int main(int argc, char **argv)
{
  s7 = s7_init();  

  rl_readline_name = "s7";
  rl_attempted_completion_function = completion;
  /* rl_initialize(); */

  if (argc == 2)
    {
      fprintf(stderr, "load %s\n", argv[1]);
      s7_load(s7, argv[1]);
    }
  else
    {
      while (true)
	{
	  char *line_read = NULL;
	  line_read = readline("s7> ");
	  if (line_read)
	    {
	      char *str;
	      str = line_read;
	      while (isspace((int)str[0])) str++;
	      if (*str)
		{
		  s7_pointer result;
		  add_history(str);
		  result = s7_eval_c_string(s7, str);
		  s7_write(s7, result, s7_current_output_port(s7));   /* use write, not display so that strings are in double quotes */
		  s7_newline(s7, s7_current_output_port(s7));
		}
	      free(line_read);
	    }
	}
    }
}

/* in Linux:    gcc s7.c -o repl -DWITH_MAIN -I. -g3 -lreadline -lncurses -lrt -ldl -lm -Wl,-export-dynamic
 * in *BSD:     gcc s7.c -o repl -DWITH_MAIN -I. -g3 -lreadline -lncurses -lm -Wl,-export-dynamic
 * in OSX:      gcc s7.c -o repl -DWITH_MAIN -I. -g3 -lreadline -lncurses -lm
 *   (clang also needs LDFLAGS="-Wl,-export-dynamic" in Linux)
 */
#endif


/* --------------------------------------------------
 *
 *           12.x | 13.0 | 14.2 | 15.0 15.1 15.2 15.3
 * s7test    1721 | 1358 |  995 | 1194 1185 1144
 * index    44300 | 3291 | 1725 | 1276 1243 1173
 * bench    42736 | 8752 | 4220 | 3506 3506 3104
 * lg             |      |      | 6547 6497 6494
 * t455|6     265 |   89 |  9   |       8.4  8.0
 * t502        90 |   43 | 14.5 | 12.7 12.7 12.6
 * t816           |   71 | 70.6 | 38.0 31.8 28.2
 * calls      359 |  275 | 54   | 34.7 34.7 35.2
 *
 * --------------------------------------------------
 *
 * mockery.scm needs documentation (and stuff.scm: doc cyclic-seq+stuff under circular lists)
 *   also needs a complete morally-equal? method that cooperates with the built-in version
 *   perhaps an optional trailing arg = cyclic|shared-sequences + numbers? (useful in object->string too)
 *
 * need to check new openGL for API changes (GL_VERSION?)
 *   test/Mesa-10.3.1/include/GL/glext.h|gl.h (current version appears to be 7.6)
 *   how much of glext.h is needed?  None, I think.  No gl.h diffs that matter.
 *   GLU primarily for unproject (HAVE_GLU snd-chn) -- where is this in the new GL? -- in a separate package libglu
 *     also gldata has GLU ints
 *   GL: snd-[0]|1|x0.h=#includes with glx.h, 
 *       [snd-axis], [snd], snd-chn, [snd-draw], [snd-data], snd-motif, [snd-print], [snd-xen, snd-help]
 *     [snd-xen|help GLXContext and version -- motif-specific?] (also snd-1.h)
 *   check out the GL support in gtk 3.16 -- this looks straightforward, snd-chn.c
 *     gtk+-3.15.1/gtk/gtkglarea.c, gtk-demo/glarea.c, tests/gtkgears.c + testglarea.c, docs/reference/gtk/html/GtkGLArea.html
 *     GdkGLContext -- needs expoxy/gl.h?
 *     snd-axis: gdk_gl_font_use_pango... -- this is not in the gtk/cl code (it's ancient gtkglext stuff)
 *   need color-dialog use-gl button in gtk callbacks for labels
 * snd-genv needs a lot of gtk3 work
 *
 * cyclic-seq in rest of full-* and cyclic-sequences is minimally tested in s7test (also c_object env)
 * why not snd-g* -> snd-gtk?
 * perhaps if let/env is large, display contents more carefully (for pretty-print?)
 * g_load of .so file should try "./fname" and others unchanged?
 * C-G in Snd listener can cause a segfault!
 * if possible clear hops in all unhop Z and A cases making a_is_ok unnecessary (if hop==0 don't look for A cases) (only safe_c* has z cases)
 * add --with-s7webserver
 */
