~ chicken-core (chicken-5) db5d28646c2802be55f604c1566ee3d212e3512a


commit db5d28646c2802be55f604c1566ee3d212e3512a
Author:     Peter Bex <peter@more-magic.net>
AuthorDate: Thu Mar 19 20:38:36 2015 +0100
Commit:     Peter Bex <peter@more-magic.net>
CommitDate: Sun May 31 14:55:23 2015 +0200

    Make generic dyadic + and - inlineable!

diff --git a/chicken.h b/chicken.h
index 2970689e..d8fcabbb 100644
--- a/chicken.h
+++ b/chicken.h
@@ -1964,10 +1964,8 @@ C_fctexport void C_ccall C_2_basic_times(C_word c, C_word self, C_word k, C_word
 C_fctexport void C_ccall C_u_2_integer_times(C_word c, C_word self, C_word k, C_word x, C_word y) C_noret;
 /* XXX TODO OBSOLETE: This can be removed after recompiling c-platform.scm */
 C_fctexport void C_ccall C_plus(C_word c, C_word closure, C_word k, ...) C_noret;
-C_fctexport void C_ccall C_2_basic_plus(C_word c, C_word self, C_word k, C_word x, C_word y) C_noret;
 /* XXX TODO OBSOLETE: This can be removed after recompiling c-platform.scm */
 C_fctexport void C_ccall C_minus(C_word c, C_word closure, C_word k, C_word n1, ...) C_noret;
-C_fctexport void C_ccall C_2_basic_minus(C_word c, C_word self, C_word k, C_word x, C_word y) C_noret;
 /* XXX TODO OBSOLETE: This can be removed after recompiling c-platform.scm */
 C_fctexport void C_ccall C_divide(C_word c, C_word closure, C_word k, C_word n1, ...) C_noret;
 C_fctexport void C_ccall C_basic_quotient(C_word c, C_word self, C_word k, C_word x, C_word y) C_noret;
@@ -2188,8 +2186,10 @@ C_fctexport C_word C_fcall C_i_file_exists_p(C_word name, C_word file, C_word di
 
 C_fctexport C_word C_fcall C_s_a_i_abs(C_word **ptr, C_word n, C_word x) C_regparm;
 C_fctexport C_word C_fcall C_s_a_i_negate(C_word **ptr, C_word n, C_word x) C_regparm;
+C_fctexport C_word C_fcall C_s_a_i_minus(C_word **ptr, C_word n, C_word x, C_word y) C_regparm;
 C_fctexport C_word C_fcall C_s_a_u_i_integer_negate(C_word **ptr, C_word n, C_word x) C_regparm;
 C_fctexport C_word C_fcall C_s_a_u_i_integer_minus(C_word **ptr, C_word n, C_word x, C_word y) C_regparm;
+C_fctexport C_word C_fcall C_s_a_i_plus(C_word **ptr, C_word n, C_word x, C_word y) C_regparm;
 C_fctexport C_word C_fcall C_s_a_u_i_integer_plus(C_word **ptr, C_word n, C_word x, C_word y) C_regparm;
 C_fctexport C_word C_fcall C_s_a_u_i_integer_gcd(C_word **ptr, C_word n, C_word x, C_word y) C_regparm;
 
diff --git a/library.scm b/library.scm
index e9b0190c..e086b79b 100644
--- a/library.scm
+++ b/library.scm
@@ -1060,33 +1060,6 @@ EOF
 	       #:type-error 'numerator
 	       "bad argument type - not a rational number" n))))
 
-;; Knuth, 4.5.1
-(define (rat+/- loc op x y)
-  (let ((a (%ratnum-numerator x)) (b (%ratnum-denominator x))
-        (c (%ratnum-numerator y)) (d (%ratnum-denominator y)))
-    (let ((g1 (##sys#integer-gcd b d)))
-      (cond
-       ((eq? g1 1) (%make-ratnum (op (##sys#integer-times a d)
-				     (##sys#integer-times b c))
-				 (##sys#integer-times b d)))
-       ;; Save a quotient and multiplication if the gcd is equal
-       ;; to one of the denominators since quotient of b or d and g1 = 1
-       ((##sys#=-2 g1 b)
-	(let* ((t (op (##sys#integer-times a (##sys#integer-quotient d g1)) c))
-	       (g2 (##sys#integer-gcd t g1)))
-	  (ratnum (##sys#integer-quotient t g2) (##sys#integer-quotient d g2))))
-       ((##sys#=-2 g1 d)
-	(let* ((t (op a (##sys#integer-times c (##sys#integer-quotient b g1))))
-	       (g2 (##sys#integer-gcd t g1)))
-	  (ratnum (##sys#integer-quotient t g2) (##sys#integer-quotient b g2))))
-       (else (let* ((b/g1 (##sys#integer-quotient b g1))
-                    (t (op (##sys#integer-times a (##sys#integer-quotient d g1))
-			   (##sys#integer-times c b/g1)))
-                    (g2 (##sys#integer-gcd t g1)))
-               (%make-ratnum (##sys#integer-quotient t g2)
-                             (##sys#integer-times
-			      b/g1 (##sys#integer-quotient d g2)))))))))
-
 (define (##sys#extended-signum x)
   (cond
    ((ratnum? x) (##core#inline "C_u_i_integer_signum" (%ratnum-numerator x)))
@@ -1157,36 +1130,13 @@ EOF
                   (loop (##sys#slot args 1)
 			(##sys#+-2 x (##sys#slot args 0))) ) )  ) ) ) )
 
-(define ##sys#+-2 (##core#primitive "C_2_basic_plus"))
+;; OBSOLETE: Remove this (or change to define-inline)
+(define (##sys#+-2 x y)
+  (##core#inline_allocate ("C_s_a_i_plus" 36) x y))
 ;; OBSOLETE: Remove this (or change to define-inline)
 (define (##sys#integer-plus x y)
   (##core#inline_allocate ("C_s_a_u_i_integer_plus" 6) x y))
 
-(define (##sys#extended-plus x y)
-  (cond ((or (cplxnum? x) (cplxnum? y))
-         ;; Just add real and imag parts together
-         (let ((r (##sys#+-2 (real-part x) (real-part y)))
-               (i (##sys#+-2 (imag-part x) (imag-part y))) )
-           (make-complex r i) ))
-        ((ratnum? x)
-         (if (ratnum? y)
-             (rat+/- '+ ##sys#integer-plus x y)
-             ;; a/b + c/d = (a*d + b*c)/(b*d)  [with d = 1]
-             (let* ((b (%ratnum-denominator x))
-                    (numerator (##sys#+-2 (%ratnum-numerator x)
-					  (##sys#*-2 b y))))
-               (if (##core#inline "C_i_flonump" numerator)
-                   (##sys#/-2 numerator b)
-                   (%make-ratnum numerator b)))))
-        ((ratnum? y)
-         ;; a/b + c/d = (a*d + b*c)/(b*d)  [with b = 1]
-         (let* ((d (%ratnum-denominator y))
-                (numerator (##sys#+-2 (##sys#*-2 x d) (%ratnum-numerator y))))
-           (if (##core#inline "C_i_flonump" numerator)
-               (##sys#/-2 numerator d)
-               (%make-ratnum numerator d))))
-        (else (##sys#error-bad-number y '+)) ) )
-
 ;; OBSOLETE: Remove this (or change to define-inline)
 (define (##sys#negate x) (##core#inline_allocate ("C_s_a_i_negate" 36) x))
 ;; OBSOLETE: Remove this (or change to define-inline)
@@ -1203,35 +1153,13 @@ EOF
 	    (loop (##sys#slot args 1)
 		  (##sys#--2 x (##sys#slot args 0))) ) ) ) )
 
-(define ##sys#--2 (##core#primitive "C_2_basic_minus"))
+;; OBSOLETE: Remove this (or change to define-inline)
+(define (##sys#--2 x y)
+  (##core#inline_allocate ("C_s_a_i_minus" 36) x y))
 ;; OBSOLETE: Remove this (or change to define-inline)
 (define (##sys#integer-minus x y)
   (##core#inline_allocate ("C_s_a_u_i_integer_minus" 6) x y))
 
-(define (##sys#extended-minus x y)
-  (cond ((or (cplxnum? x) (cplxnum? y))
-         ;; Just subtract real and imag parts from eachother
-         (let ((r (##sys#--2 (real-part x) (real-part y)))
-               (i (##sys#--2 (imag-part x) (imag-part y))))
-           (make-complex r i) ))
-        ((ratnum? x)
-         (if (ratnum? y)
-             (rat+/- '- ##sys#integer-minus x y)
-             ;; a/b - c/d = (a*d - b*c)/(b*d)  [with d = 1]
-             (let* ((b (%ratnum-denominator x))
-                    (numerator (##sys#--2 (%ratnum-numerator x) (##sys#*-2 b y))))
-               (if (##core#inline "C_i_flonump" numerator)
-                   (##sys#/-2 numerator b)
-                   (%make-ratnum numerator b)))))
-        ((ratnum? y)
-         ;; a/b - c/d = (a*d - b*c)/(b*d)  [with b = 1]
-         (let* ((d (%ratnum-denominator y))
-                (numerator (##sys#--2 (##sys#*-2 x d) (%ratnum-numerator y))))
-           (if (##core#inline "C_i_flonump" numerator)
-               (##sys#/-2 numerator d)
-               (%make-ratnum numerator d))))
-        (else (##sys#error-bad-number y '-)) ) )
-
 (define ##sys#*-2 (##core#primitive "C_2_basic_times"))
 (define ##sys#integer-times (##core#primitive "C_u_2_integer_times"))
 
diff --git a/runtime.c b/runtime.c
index b6db3da1..a476184f 100644
--- a/runtime.c
+++ b/runtime.c
@@ -281,6 +281,8 @@ extern void _C_do_apply_hack(void *proc, C_word *args, int count) C_noret;
 
 /* Type definitions: */
 
+typedef C_regparm C_word C_fcall (*integer_plusmin_op) (C_word **ptr, C_word n, C_word x, C_word y);
+
 typedef void (*TOPLEVEL)(C_word c, C_word self, C_word k) C_noret;
 typedef void (C_fcall *TRAMPOLINE)(void *proc) C_regparm C_noret;
 
@@ -519,6 +521,9 @@ static void bignum_times_bignum_unsigned(C_word k, C_word x, C_word y, C_word ne
 static void bignum_times_bignum_unsigned_2(C_word c, C_word self, C_word result) C_noret;
 static void integer_times_2(C_word c, C_word self, C_word new_big) C_noret;
 static C_word bignum_plus_unsigned(C_word **ptr, C_word x, C_word y, C_word negp);
+static C_word rat_plusmin_integer(C_word **ptr, C_word rat, C_word i, integer_plusmin_op plusmin_op);
+static C_word integer_minus_rat(C_word **ptr, C_word i, C_word rat);
+static C_word rat_plusmin_rat(C_word **ptr, C_word x, C_word y, integer_plusmin_op plusmin_op);
 static C_word bignum_minus_unsigned(C_word **ptr, C_word x, C_word y);
 static C_regparm void basic_divrem(C_word c, C_word self, C_word k, C_word x, C_word y, C_word return_r, C_word return_q) C_noret;
 static C_regparm void integer_divrem(C_word c, C_word self, C_word k, C_word x, C_word y, C_word return_q, C_word return_r) C_noret;
@@ -844,7 +849,7 @@ static C_PTABLE_ENTRY *create_initial_ptable()
 {
   /* IMPORTANT: hardcoded table size -
      this must match the number of C_pte calls + 1 (NULL terminator)! */
-  C_PTABLE_ENTRY *pt = (C_PTABLE_ENTRY *)C_malloc(sizeof(C_PTABLE_ENTRY) * 74);
+  C_PTABLE_ENTRY *pt = (C_PTABLE_ENTRY *)C_malloc(sizeof(C_PTABLE_ENTRY) * 73);
   int i = 0;
 
   if(pt == NULL)
@@ -915,8 +920,6 @@ static C_PTABLE_ENTRY *create_initial_ptable()
   C_pte(C_flonum_to_string);
   /* IMPORTANT: have you read the comments at the start and the end of this function? */
   C_pte(C_signum);
-  C_pte(C_2_basic_plus);
-  C_pte(C_2_basic_minus);
   C_pte(C_2_basic_times);
   C_pte(C_basic_quotient);
   C_pte(C_basic_remainder);
@@ -7596,56 +7599,317 @@ static C_word bignum_plus_unsigned(C_word **ptr, C_word x, C_word y, C_word negp
   return C_bignum_simplify(result);
 }
 
-void C_ccall
-C_2_basic_plus(C_word c, C_word self, C_word k, C_word x, C_word y)
+/* Unfortunately we can't use karatsuba multiplication here, either... */
+static C_word rat_plusmin_integer(C_word **ptr, C_word rat, C_word i, integer_plusmin_op plusmin_op)
+{
+  C_word ab[C_SIZEOF_FIX_BIGNUM*2], *a = ab,
+         num, denom, tmp, res, size, negp;
+
+  if (i == C_fix(0)) return rat;
+
+  /* Make i and denom bignums to keep things simple */
+  if (i & C_FIXNUM_BIT) i = C_a_u_i_fix_to_big(&a, i);
+  num = C_block_item(rat, 1);
+  denom = C_block_item(rat, 2);
+  if (denom & C_FIXNUM_BIT) denom = C_a_u_i_fix_to_big(&a, denom);
+
+  /* a/b [+-] c/d = (a*d [+-] b*c)/(b*d) | d = 1: (num + denom * i) / denom */
+  size = C_fix(C_bignum_size(denom) + C_bignum_size(i));
+  negp = C_mk_bool(C_bignum_negativep(denom) != C_bignum_negativep(i));
+  tmp = allocate_tmp_bignum(C_fix(size), negp, C_SCHEME_TRUE);
+  bignum_digits_multiply(i, denom, tmp); /*  tmp = denom * i  */
+
+  res = plusmin_op(ptr, 2, num, C_bignum_simplify(tmp));
+  free_tmp_bignum(tmp);
+  return C_ratnum(ptr, res, C_block_item(rat, 2));
+}
+
+/* This is needed only for minus: plus is commutative but minus isn't. */
+static C_word integer_minus_rat(C_word **ptr, C_word i, C_word rat)
+{
+  C_word ab[C_SIZEOF_FIX_BIGNUM*2], *a = ab,
+         num, denom, tmp, res, size, negp;
+
+  num = C_block_item(rat, 1);
+  denom = C_block_item(rat, 2);
+
+  if (i == C_fix(0))
+    return C_ratnum(ptr, C_s_a_u_i_integer_negate(ptr, 1, num), denom);
+
+  /* Make i and denom bignums to keep things simple */
+  if (i & C_FIXNUM_BIT) i = C_a_u_i_fix_to_big(&a, i);
+  if (denom & C_FIXNUM_BIT) denom = C_a_u_i_fix_to_big(&a, denom);
+
+  /* a/b - c/d = (a*d - b*c)/(b*d) | b = 1: (denom * i - num) / denom */
+  size = C_fix(C_bignum_size(denom) + C_bignum_size(i));
+  negp = C_mk_bool(C_bignum_negativep(denom) != C_bignum_negativep(i));
+  tmp = allocate_tmp_bignum(C_fix(size), negp, C_SCHEME_TRUE);
+  bignum_digits_multiply(i, denom, tmp); /*  tmp = denom * i  */
+
+  res = C_s_a_u_i_integer_minus(ptr, 2, C_bignum_simplify(tmp), num);
+  free_tmp_bignum(tmp);
+  return C_ratnum(ptr, res, C_block_item(rat, 2));
+}
+
+/* This is completely braindead and slow */
+static C_word rat_plusmin_rat(C_word **ptr, C_word x, C_word y, integer_plusmin_op plusmin_op)
+{
+  C_word ab[C_SIZEOF_FIX_BIGNUM * 10], *a = ab,
+         xnum = C_block_item(x, 1), ynum = C_block_item(y, 1),
+         xdenom = C_block_item(x, 2), ydenom = C_block_item(y, 2),
+         xnorm, ynorm, tmp_r, g1, ydenom_g1, xdenom_g1, norm_sum, g2, len,
+         res_num, res_tmp_denom, res_denom;
+
+  /* Knuth, 4.5.1.  Start with g1 = gcd(xdenom, ydenom) */
+  g1 = C_s_a_u_i_integer_gcd(&a, 2, xdenom, ydenom);
+  if (g1 & C_FIXNUM_BIT) g1 = C_a_u_i_fix_to_big(&a, g1);
+
+  if (xnum & C_FIXNUM_BIT) xnum = C_a_u_i_fix_to_big(&a, xnum);
+  if (xdenom & C_FIXNUM_BIT) xdenom = C_a_u_i_fix_to_big(&a, xdenom);
+  if (ynum & C_FIXNUM_BIT) ynum = C_a_u_i_fix_to_big(&a, ynum);
+  if (ydenom & C_FIXNUM_BIT) ydenom = C_a_u_i_fix_to_big(&a, ydenom);
+
+  /* ydenom/g1: No need to compare first because |ydenom| >= |g1| */
+  len = C_bignum_size(ydenom) + 1 - C_bignum_size(g1);
+  ydenom_g1 = allocate_tmp_bignum(C_fix(len), C_SCHEME_FALSE, C_SCHEME_FALSE);
+  len = C_bignum_size(ydenom) + 1;
+  tmp_r = allocate_tmp_bignum(C_fix(len), C_SCHEME_FALSE, C_SCHEME_FALSE);
+  bignum_destructive_divide_full(ydenom, g1, ydenom_g1, tmp_r, C_SCHEME_FALSE);
+  free_tmp_bignum(tmp_r);
+  C_bignum_simplify(ydenom_g1); /* mutate in-place; ignore fixnum results */
+
+  /* xnorm = xnum * (ydenom/g1) */
+  len = C_bignum_size(xnum) + C_bignum_size(ydenom_g1);
+  xnorm = allocate_tmp_bignum(
+          C_fix(len), C_mk_bool(C_bignum_negativep(xnum)), C_SCHEME_TRUE);
+  bignum_digits_multiply(xnum, ydenom_g1, xnorm);
+  free_tmp_bignum(ydenom_g1); /* Not needed anymore */
+  C_bignum_simplify(xnorm); /* mutate in-place; ignore fixnum results */
+
+  /* xdenom/g1: No need to compare first because |xdenom| >= |g1| */
+  len = C_bignum_size(xdenom) + 1 - C_bignum_size(g1);
+  xdenom_g1 = allocate_tmp_bignum(C_fix(len), C_SCHEME_FALSE, C_SCHEME_FALSE);
+  len = C_bignum_size(xdenom) + 1;
+  tmp_r = allocate_tmp_bignum(C_fix(len), C_SCHEME_FALSE, C_SCHEME_FALSE);
+  bignum_destructive_divide_full(xdenom, g1, xdenom_g1, tmp_r, C_SCHEME_FALSE);
+  free_tmp_bignum(tmp_r);
+  C_bignum_simplify(xdenom_g1); /* mutate in-place; ignore fixnum results */
+
+  /* ynorm = ynum * (xdenom/g1) */
+  len = C_bignum_size(ynum) + C_bignum_size(xdenom_g1);
+  ynorm = allocate_tmp_bignum(
+          C_fix(len), C_mk_bool(C_bignum_negativep(ynum)), C_SCHEME_TRUE);
+  bignum_digits_multiply(ynum, xdenom_g1, ynorm);
+  C_bignum_simplify(ynorm); /* mutate in-place; ignore fixnum results */
+
+  /* norm_sum = xnorm [+-] ynorm */
+  norm_sum = plusmin_op(&a, 2, xnorm, ynorm); /* Not tmp, scratch */
+  free_tmp_bignum(xnorm);
+  free_tmp_bignum(ynorm);
+
+  /* g2 = gcd(norm_sum, g1) */
+  g2 = C_s_a_u_i_integer_gcd(&a, 2, norm_sum, C_bignum_simplify(g1));
+  if (g2 & C_FIXNUM_BIT) g2 = C_a_u_i_fix_to_big(&a, g2);
+  if (norm_sum & C_FIXNUM_BIT) norm_sum = C_a_u_i_fix_to_big(&a, norm_sum);
+
+  /* res_num = norm_sum / g2 */
+  switch(bignum_cmp_unsigned(norm_sum, g2)) {
+  case 0:
+   res_num = C_bignum_negativep(norm_sum) ? C_fix(-1) : C_fix(1);
+   break;
+
+  case -1:
+   free_tmp_bignum(xdenom_g1);
+   clear_buffer_object(ab, g1);
+   clear_buffer_object(ab, g2);
+   clear_buffer_object(ab, norm_sum);
+   return C_fix(0); /* Done: abort */
+   break;
+
+  case 1:
+  default:
+    len = C_bignum_size(norm_sum) + 1 - C_bignum_size(g2);
+    res_num = C_allocate_scratch_bignum(
+            ptr, C_fix(len),
+            C_mk_bool(C_bignum_negativep(norm_sum)), C_SCHEME_FALSE);
+    len = C_bignum_size(norm_sum) + 1;
+    tmp_r = allocate_tmp_bignum(C_fix(len), C_SCHEME_FALSE, C_SCHEME_FALSE);
+    bignum_destructive_divide_full(norm_sum, g2, res_num, tmp_r, C_SCHEME_FALSE);
+    free_tmp_bignum(tmp_r);
+    res_num = C_bignum_simplify(res_num);
+  }
+
+  /* res_denom = xdenom_g1 * (ydenom / g2).  We know |ydenom| >= |g2| */
+  if (bignum_cmp_unsigned(ydenom, g2) == 0) {
+    /* We must copy because xdenom is a tmp bignum.  TODO: Make it scratch? */
+    res_denom = C_allocate_scratch_bignum(ptr, C_fix(C_bignum_size(xdenom_g1)),
+                                          C_SCHEME_FALSE, C_SCHEME_FALSE);
+    bignum_digits_destructive_copy(res_denom, xdenom_g1);
+    res_denom = C_bignum_simplify(res_denom);
+  } else {
+    /* res_tmp_denom = ydenom / g2 */
+    len = C_bignum_size(ydenom) + 1 - C_bignum_size(g2);
+    res_tmp_denom = allocate_tmp_bignum(C_fix(len),
+                                        C_SCHEME_FALSE, C_SCHEME_FALSE);
+    len = C_bignum_size(ydenom) + 1;
+    tmp_r = allocate_tmp_bignum(C_fix(len), C_SCHEME_FALSE, C_SCHEME_FALSE);
+    bignum_destructive_divide_full(ydenom, g2,
+                                   res_tmp_denom, tmp_r, C_SCHEME_FALSE);
+    free_tmp_bignum(tmp_r);
+
+    /* res_denom = xdenom_g1 * res_tmp_denom */
+    len = C_bignum_size(xdenom_g1) + C_bignum_size(res_tmp_denom);
+    res_denom = C_allocate_scratch_bignum(ptr, C_fix(len),
+                                          C_SCHEME_FALSE, C_SCHEME_TRUE);
+    bignum_digits_multiply(xdenom_g1, res_tmp_denom, res_denom);
+    free_tmp_bignum(res_tmp_denom);
+    res_denom = C_bignum_simplify(res_denom);
+  }
+  free_tmp_bignum(xdenom_g1);
+
+  /* Ensure they're allocated in the correct place */
+  res_num = move_buffer_object(ptr, ab, res_num);
+  res_denom = move_buffer_object(ptr, ab, res_denom);
+
+  clear_buffer_object(ab, g1);
+  clear_buffer_object(ab, g2);
+  clear_buffer_object(ab, norm_sum);
+
+  switch (res_denom) {
+  case C_fix(0): return C_fix(0);
+  case C_fix(1): return res_num;
+  default: return C_ratnum(ptr, res_num, res_denom);
+  }
+}
+
+/* The maximum size this needs is that required to store a complex
+ * number result, where both real and imag parts consist of ratnums.
+ * The maximum size of those ratnums is if they consist of two "fix
+ * bignums", so we're looking at C_SIZEOF_STRUCT(3) * 3 +
+ * C_SIZEOF_FIX_BIGNUM * 4 = 36 words!
+ */
+C_regparm C_word C_fcall
+C_s_a_i_plus(C_word **ptr, C_word n, C_word x, C_word y)
 {
   if (x & C_FIXNUM_BIT) {
     if (y & C_FIXNUM_BIT) {
-      C_word *a = C_alloc(C_SIZEOF_FIX_BIGNUM);
-      C_kontinue(k, C_a_i_fixnum_plus(&a, 2, x, y));
+      return C_a_i_fixnum_plus(ptr, 2, x, y);
     } else if (C_immediatep(y)) {
       barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "+", y);
     } else if (C_block_header(y) == C_FLONUM_TAG) {
-      C_word *a = C_alloc(C_SIZEOF_FLONUM);
-      C_kontinue(k, C_flonum(&a, (double)C_unfix(x) + C_flonum_magnitude(y)));
+      return C_flonum(ptr, (double)C_unfix(x) + C_flonum_magnitude(y));
     } else if (C_truep(C_bignump(y))) {
-      C_word ab[C_SIZEOF_BIGNUM_WRAPPER], *a = ab;
-      C_kontinue(k, C_s_a_u_i_integer_plus(&a, 2, x, y));
+      return C_s_a_u_i_integer_plus(ptr, 2, x, y);
+    } else if (C_block_header(y) == C_STRUCTURE3_TAG) {
+      if (C_block_item(y, 0) == C_ratnum_type_tag) {
+        return rat_plusmin_integer(ptr, y, x, C_s_a_u_i_integer_plus);
+      } else if (C_block_item(y, 0) == C_cplxnum_type_tag) {
+        C_word real_sum = C_s_a_i_plus(ptr, 2, x, C_block_item(y, 1)),
+               imag = C_block_item(y, 2);
+        if (C_truep(C_u_i_inexactp(real_sum)))
+          imag = C_a_i_exact_to_inexact(ptr, 1, imag);
+        return C_cplxnum(ptr, real_sum, imag);
+      } else {
+        barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "+", y);
+      }
     } else {
-      try_extended_number("\003sysextended-plus", 3, k, x, y);
+      barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "+", y);
     }
   } else if (C_immediatep(x)) {
     barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "+", x);
   } else if (C_block_header(x) == C_FLONUM_TAG) {
-    C_word *a = C_alloc(C_SIZEOF_FLONUM);
     if (y & C_FIXNUM_BIT) {
-      C_kontinue(k, C_flonum(&a, C_flonum_magnitude(x) + (double)C_unfix(y)));
+      return C_flonum(ptr, C_flonum_magnitude(x) + (double)C_unfix(y));
     } else if (C_immediatep(y)) {
       barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "+", y);
     } else if (C_block_header(y) == C_FLONUM_TAG) {
-      C_kontinue(k, C_a_i_flonum_plus(&a, 2, x, y));
+      return C_a_i_flonum_plus(ptr, 2, x, y);
     } else if (C_truep(C_bignump(y))) {
-      C_kontinue(k, C_flonum(&a, C_flonum_magnitude(x)+C_bignum_to_double(y)));
+      return C_flonum(ptr, C_flonum_magnitude(x)+C_bignum_to_double(y));
+    } else if (C_block_header(y) == C_STRUCTURE3_TAG) {
+      if (C_block_item(y, 0) == C_ratnum_type_tag) {
+        return C_s_a_i_plus(ptr, 2, x, C_a_i_exact_to_inexact(ptr, 1, y));
+      } else if (C_block_item(y, 0) == C_cplxnum_type_tag) {
+        C_word real_sum = C_s_a_i_plus(ptr, 2, x, C_block_item(y, 1)),
+               imag = C_block_item(y, 2);
+        if (C_truep(C_u_i_inexactp(real_sum)))
+          imag = C_a_i_exact_to_inexact(ptr, 1, imag);
+        return C_cplxnum(ptr, real_sum, imag);
+      } else {
+        barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "+", y);
+      }
     } else {
-      try_extended_number("\003sysextended-plus", 3, k, x, y);
+      barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "+", y);
     }
   } else if (C_truep(C_bignump(x))) {
     if (y & C_FIXNUM_BIT) {
-      C_word ab[C_SIZEOF_BIGNUM_WRAPPER], *a = ab;
-      C_kontinue(k, C_s_a_u_i_integer_plus(&a, 2, x, y));
+      return C_s_a_u_i_integer_plus(ptr, 2, x, y);
     } else if (C_immediatep(y)) {
       barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "+", y);
     } else if (C_block_header(y) == C_FLONUM_TAG) {
-      C_word *a = C_alloc(C_SIZEOF_FLONUM);
-      C_kontinue(k, C_flonum(&a, C_bignum_to_double(x)+C_flonum_magnitude(y)));
+      return C_flonum(ptr, C_bignum_to_double(x)+C_flonum_magnitude(y));
     } else if (C_truep(C_bignump(y))) {
-      C_word ab[C_SIZEOF_BIGNUM_WRAPPER], *a = ab;
-      C_kontinue(k, C_s_a_u_i_integer_plus(&a, 2, x, y));
+      return C_s_a_u_i_integer_plus(ptr, 2, x, y);
+    } else if (C_block_header(y) == C_STRUCTURE3_TAG) {
+      if (C_block_item(y, 0) == C_ratnum_type_tag) {
+        return rat_plusmin_integer(ptr, y, x, C_s_a_u_i_integer_plus);
+      } else if (C_block_item(y, 0) == C_cplxnum_type_tag) {
+        C_word real_sum = C_s_a_i_plus(ptr, 2, x, C_block_item(y, 1)),
+               imag = C_block_item(y, 2);
+        if (C_truep(C_u_i_inexactp(real_sum)))
+          imag = C_a_i_exact_to_inexact(ptr, 1, imag);
+        return C_cplxnum(ptr, real_sum, imag);
+      } else {
+        barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "+", y);
+      }
+    } else {
+      barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "+", y);
+    }
+  } else if (C_block_header(x) == C_STRUCTURE3_TAG) {
+    if (C_block_item(x, 0) == C_ratnum_type_tag) {
+      if (y & C_FIXNUM_BIT) {
+        return rat_plusmin_integer(ptr, x, y, C_s_a_u_i_integer_plus);
+      } else if (C_immediatep(y)) {
+        barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "+", y);
+      } else if (C_block_header(y) == C_FLONUM_TAG) {
+        return C_s_a_i_plus(ptr, 2, C_a_i_exact_to_inexact(ptr, 1, x), y);
+      } else if (C_truep(C_bignump(y))) {
+        return rat_plusmin_integer(ptr, x, y, C_s_a_u_i_integer_plus);
+      } else if (C_block_header(y) == C_STRUCTURE3_TAG) {
+        if (C_block_item(y, 0) == C_ratnum_type_tag) {
+          return rat_plusmin_rat(ptr, x, y, C_s_a_u_i_integer_plus);
+        } else if (C_block_item(y, 0) == C_cplxnum_type_tag) {
+          C_word real_sum = C_s_a_i_plus(ptr, 2, x, C_block_item(y, 1)),
+                 imag = C_block_item(y, 2);
+          if (C_truep(C_u_i_inexactp(real_sum)))
+            imag = C_a_i_exact_to_inexact(ptr, 1, imag);
+          return C_cplxnum(ptr, real_sum, imag);
+        } else {
+          barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "+", y);
+        }
+      } else {
+        barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "+", y);
+      }
+    } else if (C_block_item(x, 0) == C_cplxnum_type_tag) {
+      if (!C_immediatep(y) && C_block_header(y) == C_STRUCTURE3_TAG &&
+          C_block_item(y, 0) == C_cplxnum_type_tag) {
+        C_word real_sum, imag_sum;
+        real_sum = C_s_a_i_plus(ptr, 2, C_block_item(x, 1), C_block_item(y, 1));
+        imag_sum = C_s_a_i_plus(ptr, 2, C_block_item(x, 2), C_block_item(y, 2));
+        if (C_truep(C_u_i_zerop(imag_sum))) return real_sum;
+        else return C_cplxnum(ptr, real_sum, imag_sum);
+      } else {
+        C_word real_sum = C_s_a_i_plus(ptr, 2, C_block_item(x, 1), y),
+               imag = C_block_item(x, 2);
+        if (C_truep(C_u_i_inexactp(real_sum)))
+          imag = C_a_i_exact_to_inexact(ptr, 1, imag);
+        return C_cplxnum(ptr, real_sum, imag);
+      }
     } else {
-      try_extended_number("\003sysextended-plus", 3, k, x, y);
+      barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "+", x);
     }
   } else {
-    try_extended_number("\003sysextended-plus", 3, k, x, y);
+    barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "+", x);
   }
 }
 
@@ -7810,56 +8074,129 @@ static C_word bignum_minus_unsigned(C_word **ptr, C_word x, C_word y)
   return C_bignum_simplify(res);
 }
 
-void C_ccall
-C_2_basic_minus(C_word c, C_word self, C_word k, C_word x, C_word y)
+/* Like C_s_a_i_plus, this needs at most 36 words */
+C_regparm C_word C_fcall
+C_s_a_i_minus(C_word **ptr, C_word n, C_word x, C_word y)
 {
   if (x & C_FIXNUM_BIT) {
     if (y & C_FIXNUM_BIT) {
-      C_word *a = C_alloc(C_SIZEOF_FIX_BIGNUM);
-      C_kontinue(k, C_a_i_fixnum_difference(&a, 2, x, y));
+      return C_a_i_fixnum_difference(ptr, 2, x, y);
     } else if (C_immediatep(y)) {
       barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "-", y);
     } else if (C_block_header(y) == C_FLONUM_TAG) {
-      C_word *a = C_alloc(C_SIZEOF_FLONUM);
-      C_kontinue(k, C_flonum(&a, (double)C_unfix(x) - C_flonum_magnitude(y)));
+      return C_flonum(ptr, (double)C_unfix(x) - C_flonum_magnitude(y));
     } else if (C_truep(C_bignump(y))) {
-      C_word ab[C_SIZEOF_BIGNUM_WRAPPER], *a = ab;
-      C_kontinue(k, C_s_a_u_i_integer_minus(&a, 2, x, y));
+      return C_s_a_u_i_integer_minus(ptr, 2, x, y);
+    } else if (C_block_header(y) == C_STRUCTURE3_TAG) {
+      if (C_block_item(y, 0) == C_ratnum_type_tag) {
+        return integer_minus_rat(ptr, x, y);
+      } else if (C_block_item(y, 0) == C_cplxnum_type_tag) {
+        C_word real_diff = C_s_a_i_minus(ptr, 2, x, C_block_item(y, 1)),
+               imag = C_s_a_i_negate(ptr, 1, C_block_item(y, 2));
+        if (C_truep(C_u_i_inexactp(real_diff)))
+          imag = C_a_i_exact_to_inexact(ptr, 1, imag);
+        return C_cplxnum(ptr, real_diff, imag);
+      } else {
+        barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "-", y);
+      }
     } else {
-      try_extended_number("\003sysextended-minus", 3, k, x, y);
+      barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "-", y);
     }
   } else if (C_immediatep(x)) {
     barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "-", x);
   } else if (C_block_header(x) == C_FLONUM_TAG) {
-    C_word *a = C_alloc(C_SIZEOF_FLONUM);
     if (y & C_FIXNUM_BIT) {
-      C_kontinue(k, C_flonum(&a, C_flonum_magnitude(x) - (double)C_unfix(y)));
+      return C_flonum(ptr, C_flonum_magnitude(x) - (double)C_unfix(y));
     } else if (C_immediatep(y)) {
       barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "-", y);
     } else if (C_block_header(y) == C_FLONUM_TAG) {
-      C_kontinue(k, C_a_i_flonum_difference(&a, 2, x, y)); /* XXX NAMING! */
+      return C_a_i_flonum_difference(ptr, 2, x, y);
     } else if (C_truep(C_bignump(y))) {
-      C_kontinue(k, C_flonum(&a, C_flonum_magnitude(x)-C_bignum_to_double(y)));
+      return C_flonum(ptr, C_flonum_magnitude(x)-C_bignum_to_double(y));
+    } else if (C_block_header(y) == C_STRUCTURE3_TAG) {
+      if (C_block_item(y, 0) == C_ratnum_type_tag) {
+        return C_s_a_i_minus(ptr, 2, x, C_a_i_exact_to_inexact(ptr, 1, y));
+      } else if (C_block_item(y, 0) == C_cplxnum_type_tag) {
+        C_word real_diff = C_s_a_i_minus(ptr, 2, x, C_block_item(y, 1)),
+               imag = C_s_a_i_negate(ptr, 1, C_block_item(y, 2));
+        if (C_truep(C_u_i_inexactp(real_diff)))
+          imag = C_a_i_exact_to_inexact(ptr, 1, imag);
+        return C_cplxnum(ptr, real_diff, imag);
+      } else {
+        barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "-", y);
+      }
     } else {
-      try_extended_number("\003sysextended-minus", 3, k, x, y);
+      barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "-", y);
     }
   } else if (C_truep(C_bignump(x))) {
     if (y & C_FIXNUM_BIT) {
-      C_word ab[C_SIZEOF_BIGNUM_WRAPPER], *a = ab;
-      C_kontinue(k, C_s_a_u_i_integer_minus(&a, 2, x, y));
+      return C_s_a_u_i_integer_minus(ptr, 2, x, y);
     } else if (C_immediatep(y)) {
       barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "-", y);
     } else if (C_block_header(y) == C_FLONUM_TAG) {
-      C_word *a = C_alloc(C_SIZEOF_FLONUM);
-      C_kontinue(k, C_flonum(&a, C_bignum_to_double(x)-C_flonum_magnitude(y)));
+      return C_flonum(ptr, C_bignum_to_double(x)-C_flonum_magnitude(y));
     } else if (C_truep(C_bignump(y))) {
-      C_word ab[C_SIZEOF_BIGNUM_WRAPPER], *a = ab;
-      C_kontinue(k, C_s_a_u_i_integer_minus(&a, 2, x, y));
+      return C_s_a_u_i_integer_minus(ptr, 2, x, y);
+    } else if (C_block_header(y) == C_STRUCTURE3_TAG) {
+      if (C_block_item(y, 0) == C_ratnum_type_tag) {
+        return integer_minus_rat(ptr, x, y);
+      } else if (C_block_item(y, 0) == C_cplxnum_type_tag) {
+        C_word real_diff = C_s_a_i_minus(ptr, 2, x, C_block_item(y, 1)),
+               imag = C_s_a_i_negate(ptr, 1, C_block_item(y, 2));
+        if (C_truep(C_u_i_inexactp(real_diff)))
+          imag = C_a_i_exact_to_inexact(ptr, 1, imag);
+        return C_cplxnum(ptr, real_diff, imag);
+      } else {
+        barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "-", y);
+      }
+    } else {
+      barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "-", y);
+    }
+  } else if (C_block_header(x) == C_STRUCTURE3_TAG) {
+    if (C_block_item(x, 0) == C_ratnum_type_tag) {
+      if (y & C_FIXNUM_BIT) {
+        return rat_plusmin_integer(ptr, x, y, C_s_a_u_i_integer_minus);
+      } else if (C_immediatep(y)) {
+        barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "-", y);
+      } else if (C_block_header(y) == C_FLONUM_TAG) {
+        return C_s_a_i_minus(ptr, 2, C_a_i_exact_to_inexact(ptr, 1, x), y);
+      } else if (C_truep(C_bignump(y))) {
+        return rat_plusmin_integer(ptr, x, y, C_s_a_u_i_integer_minus);
+      } else if (C_block_header(y) == C_STRUCTURE3_TAG) {
+        if (C_block_item(y, 0) == C_ratnum_type_tag) {
+          return rat_plusmin_rat(ptr, x, y, C_s_a_u_i_integer_minus);
+        } else if (C_block_item(y, 0) == C_cplxnum_type_tag) {
+          C_word real_diff = C_s_a_i_minus(ptr, 2, x, C_block_item(y, 1)),
+                 imag = C_s_a_i_negate(ptr, 1, C_block_item(y, 2));
+          if (C_truep(C_u_i_inexactp(real_diff)))
+            imag = C_a_i_exact_to_inexact(ptr, 1, imag);
+          return C_cplxnum(ptr, real_diff, imag);
+        } else {
+          barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "-", y);
+        }
+      } else {
+        barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "-", y);
+      }
+    } else if (C_block_item(x, 0) == C_cplxnum_type_tag) {
+      if (!C_immediatep(y) && C_block_header(y) == C_STRUCTURE3_TAG &&
+          C_block_item(y, 0) == C_cplxnum_type_tag) {
+        C_word real_diff, imag_diff;
+        real_diff = C_s_a_i_minus(ptr,2,C_block_item(x, 1),C_block_item(y, 1));
+        imag_diff = C_s_a_i_minus(ptr,2,C_block_item(x, 2),C_block_item(y, 2));
+        if (C_truep(C_u_i_zerop(imag_diff))) return real_diff;
+        else return C_cplxnum(ptr, real_diff, imag_diff);
+      } else {
+        C_word real_diff = C_s_a_i_minus(ptr, 2, C_block_item(x, 1), y),
+               imag = C_block_item(x, 2);
+        if (C_truep(C_u_i_inexactp(real_diff)))
+          imag = C_a_i_exact_to_inexact(ptr, 1, imag);
+        return C_cplxnum(ptr, real_diff, imag);
+      }
     } else {
-      try_extended_number("\003sysextended-minus", 3, k, x, y);
+      barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "-", x);
     }
   } else {
-    try_extended_number("\003sysextended-minus", 3, k, x, y);
+    barf(C_BAD_ARGUMENT_TYPE_NO_NUMBER_ERROR, "-", x);
   }
 }
 
@@ -9418,7 +9755,14 @@ C_regparm C_word C_fcall C_bignum_simplify(C_word big)
   while (scan >= start && *scan == 0)
     scan--;
   length = scan - start + 1;
-  
+
+  /* Always trim the bignum, even before returning a fixnum.  This
+   * benefits some code that uses bignums everywhere for simplicity.
+   * NOTE: This only works for stack-allocated or tmp bignums, not
+   * for scratchspace bignums!
+   */
+  if (scan < last_digit && length > 0) /* Keep a minimum of 1 word */
+    C_bignum_mutate_size(big, length);
   switch(length) {
   case 0:
     if (C_in_scratchspacep(C_internal_bignum_vector(big)))
@@ -9435,7 +9779,6 @@ C_regparm C_word C_fcall C_bignum_simplify(C_word big)
     }
     /* FALLTHROUGH */
   default:
-    if (scan < last_digit) C_bignum_mutate_size(big, length);
     return big;
   }
 }
diff --git a/types.db b/types.db
index 58e938bd..b8f410a5 100644
--- a/types.db
+++ b/types.db
@@ -321,7 +321,7 @@
    ((integer integer) (integer)
     (##core#inline_allocate ("C_s_a_u_i_integer_plus" 6) #(1) #(2)))
    ((* *) (number)
-    (##sys#+-2 #(1) #(2))))
+    (##core#inline_allocate ("C_s_a_i_plus" 36) #(1) #(2))))
 
 (- (#(procedure #:clean #:enforce #:foldable) - (number #!rest number) number)
    ((fixnum) (integer) (##core#inline_allocate ("C_a_i_fixnum_negate" 6) #(1)))
@@ -346,7 +346,7 @@
    ((integer integer) (integer)
     (##core#inline_allocate ("C_s_a_u_i_integer_minus" 6) #(1) #(2)))
    ((* *) (number)
-    (##sys#--2 #(1) #(2))))
+    (##core#inline_allocate ("C_s_a_i_minus" 36) #(1) #(2))))
 
 (* (#(procedure #:clean #:enforce #:foldable) * (#!rest number) number)
    (() (fixnum) '1)
@@ -846,9 +846,14 @@
 (##sys#abort (procedure abort (*) noreturn))
 
 (add1 (#(procedure #:clean #:enforce #:foldable) add1 (number) number)
-      ((integer) (integer) (##sys#integer-plus #(1) '1))
+      ((fixnum) (integer)
+       (##core#inline_allocate ("C_a_i_fixnum_plus" 6) #(1) '1))
+      ((integer) (integer)
+       (##core#inline_allocate ("C_s_a_u_i_integer_plus" 6) #(1) '1))
       ((float) (float) 
-       (##core#inline_allocate ("C_a_i_flonum_plus" 4) #(1) '1.0)))
+       (##core#inline_allocate ("C_a_i_flonum_plus" 4) #(1) '1.0))
+      ((*) (number)
+       (##core#inline_allocate ("C_s_a_i_plus" 36) #(1) '1)))
 
 (argc+argv (#(procedure #:clean) argc+argv () fixnum (list-of string) fixnum))
 (argv (#(procedure #:clean) argv () (list-of string)))
@@ -1264,9 +1269,14 @@
 (strip-syntax (#(procedure #:clean) strip-syntax (*) *))
 
 (sub1 (#(procedure #:clean #:enforce #:foldable) sub1 (number) number)
-      ((integer) (integer) (##sys#integer-minus #(1) '1))
-      ((float) (float)
-       (##core#inline_allocate ("C_a_i_flonum_difference" 4) #(1) '1.0)))
+      ((fixnum) (integer)
+       (##core#inline_allocate ("C_a_i_fixnum_difference" 6) #(1) '1))
+      ((integer) (integer)
+       (##core#inline_allocate ("C_s_a_u_i_integer_minus" 6) #(1) '1))
+      ((float) (float) 
+       (##core#inline_allocate ("C_a_i_flonum_difference" 4) #(1) '1.0))
+      ((*) (number)
+       (##core#inline_allocate ("C_s_a_i_minus" 36) #(1) '1)))
 
 (subvector (forall (a) (#(procedure #:clean #:enforce) subvector ((vector-of a) fixnum #!optional fixnum) (vector-of a))))
 (symbol-escape (#(procedure #:clean) symbol-escape (#!optional *) *))
Trap