about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--002test.cc1
-rw-r--r--003trace.cc2
-rw-r--r--012transform.cc2
-rw-r--r--020run.cc24
-rw-r--r--021arithmetic.cc227
-rw-r--r--022boolean.cc6
-rw-r--r--023jump.cc6
-rw-r--r--024compare.cc30
-rw-r--r--027debug.cc2
-rw-r--r--030container.cc10
-rw-r--r--031address.cc2
-rw-r--r--032array.cc21
-rw-r--r--033length.cc4
-rw-r--r--034exclusive_container.cc7
-rw-r--r--036call_ingredient.cc4
-rw-r--r--038scheduler.cc4
-rw-r--r--039wait.cc12
-rw-r--r--040brace.cc31
-rw-r--r--041name.cc8
-rw-r--r--042new.cc11
-rw-r--r--043space.cc8
-rw-r--r--044space_surround.cc4
-rw-r--r--047jump_label.cc2
-rw-r--r--050scenario.cc20
-rw-r--r--060string.mu16
-rw-r--r--070display.cc6
-rw-r--r--071print.mu2
-rw-r--r--072scenario_screen.cc31
-rw-r--r--075scenario_keyboard.cc6
29 files changed, 316 insertions, 193 deletions
diff --git a/002test.cc b/002test.cc
index 0b445526..9a6765d1 100644
--- a/002test.cc
+++ b/002test.cc
@@ -54,6 +54,7 @@ if (Run_tests) {
   time_t t; time(&t);
   cerr << "C tests: " << ctime(&t);
   for (index_t i=0; i < sizeof(Tests)/sizeof(Tests[0]); ++i) {
+//?     cerr << "===\n"; //? 1
     run_test(i);
   }
   // End Tests
diff --git a/003trace.cc b/003trace.cc
index 6fb6dfa7..85779092 100644
--- a/003trace.cc
+++ b/003trace.cc
@@ -205,7 +205,7 @@ struct lease_tracer {
 #define START_TRACING_UNTIL_END_OF_SCOPE  lease_tracer leased_tracer;
 :(before "End Test Setup")
 START_TRACING_UNTIL_END_OF_SCOPE
-//? Trace_stream->dump_layer = "all"; //? 1
+//? Trace_stream->dump_layer = "all"; //? 2
 
 :(before "End Tracing")
 void trace_all(const string& label, const list<string>& in) {
diff --git a/012transform.cc b/012transform.cc
index 7b2b58e8..1ec8d6e4 100644
--- a/012transform.cc
+++ b/012transform.cc
@@ -48,5 +48,5 @@ void parse_int_reagents() {
 void populate_value(reagent& r) {
   if (r.initialized) return;
   if (!is_number(r.name)) return;
-  r.set_value(to_number(r.name));
+  r.set_value(mu_integer(to_number(r.name)));
 }
diff --git a/020run.cc b/020run.cc
index 3f3237e8..ff23200e 100644
--- a/020run.cc
+++ b/020run.cc
@@ -78,7 +78,11 @@ void run_current_routine()
     switch (current_instruction().operation) {
       // Primitive Recipe Implementations
       case COPY: {
-        copy(ingredients.begin(), ingredients.end(), inserter(products, products.begin()));
+        products.resize(ingredients.size());
+        for (index_t i = 0; i < ingredients.size(); ++i) {
+          copy(ingredients.at(i).begin(), ingredients.at(i).end(), inserter(products.at(i), products.at(i).begin()));
+        }
+//?         copy(ingredients.begin(), ingredients.end(), inserter(products, products.begin()));
         break;
       }
       // End Primitive Recipe Implementations
@@ -121,6 +125,7 @@ inline bool routine::completed() const {
 }
 
 :(before "End Commandline Parsing")
+// Loading Commandline Files
 if (argc > 1) {
   for (int i = 1; i < argc; ++i) {
     load_permanently(argv[i]);
@@ -183,10 +188,14 @@ vector<long long int> read_memory(reagent x) {
     return result;
   }
   index_t base = x.value;
+//?   cerr << "AAA " << base << '\n'; //? 1
+  assert(!is_negative(base));
+  base = value(base);
   size_t size = size_of(x);
   for (index_t offset = 0; offset < size; ++offset) {
     long long int val = Memory[base+offset];
-    trace("mem") << "location " << base+offset << " is " << val;
+//?     cerr << "AAA2 " << val << '\n'; //? 1
+    trace("mem") << "location " << base+offset << " is " << value(val);
     result.push_back(val);
   }
   return result;
@@ -194,11 +203,20 @@ vector<long long int> read_memory(reagent x) {
 
 void write_memory(reagent x, vector<long long int> data) {
   if (is_dummy(x)) return;
+  // Preprocess x.
+  // End Preprocess x.
   index_t base = x.value;
+  assert(!is_negative(base));
+  base = value(base);
   if (size_of(x) != data.size())
     raise << "size mismatch in storing to " << x.to_string() << '\n';
+//?   cerr << "BBB " << base << '\n'; //? 1
   for (index_t offset = 0; offset < data.size(); ++offset) {
-    trace("mem") << "storing " << data.at(offset) << " in location " << base+offset;
+    trace("mem") << "storing " << value(data.at(offset)) << " in location " << base+offset;
+//?     if (base+offset > 99999) { //? 1
+//?       raise << "AAAAAAAAAAAAAAAAAAAAA\n"; //? 1
+//?       Trace_stream->newline(), exit(0); //? 1
+//?     } //? 1
     Memory[base+offset] = data.at(offset);
   }
 }
diff --git a/021arithmetic.cc b/021arithmetic.cc
index d36b15f3..ed852b9e 100644
--- a/021arithmetic.cc
+++ b/021arithmetic.cc
@@ -6,13 +6,13 @@ ADD,
 Recipe_number["add"] = ADD;
 :(before "End Primitive Recipe Implementations")
 case ADD: {
-  long long int result = 0;
+  double result = 0;
   for (index_t i = 0; i < ingredients.size(); ++i) {
     assert(ingredients.at(i).size() == 1);  // scalar
-    result += ingredients.at(i).at(0);
+    result += value(ingredients.at(i).at(0));
   }
   products.resize(1);
-  products.at(0).push_back(result);
+  products.at(0).push_back(mu_noninteger(result));
   break;
 }
 
@@ -53,13 +53,13 @@ Recipe_number["subtract"] = SUBTRACT;
 :(before "End Primitive Recipe Implementations")
 case SUBTRACT: {
   assert(ingredients.at(0).size() == 1);  // scalar
-  long long int result = ingredients.at(0).at(0);
+  double result = value(ingredients.at(0).at(0));
   for (index_t i = 1; i < ingredients.size(); ++i) {
     assert(ingredients.at(i).size() == 1);  // scalar
-    result -= ingredients.at(i).at(0);
+    result -= value(ingredients.at(i).at(0));
   }
   products.resize(1);
-  products.at(0).push_back(result);
+  products.at(0).push_back(mu_noninteger(result));
   break;
 }
 
@@ -99,13 +99,13 @@ MULTIPLY,
 Recipe_number["multiply"] = MULTIPLY;
 :(before "End Primitive Recipe Implementations")
 case MULTIPLY: {
-  long long int result = 1;
+  double result = 1;
   for (index_t i = 0; i < ingredients.size(); ++i) {
     assert(ingredients.at(i).size() == 1);  // scalar
-    result *= ingredients.at(i).at(0);
+    result *= value(ingredients.at(i).at(0));
   }
   products.resize(1);
-  products.at(0).push_back(result);
+  products.at(0).push_back(mu_noninteger(result));
   break;
 }
 
@@ -146,13 +146,13 @@ Recipe_number["divide"] = DIVIDE;
 :(before "End Primitive Recipe Implementations")
 case DIVIDE: {
   assert(ingredients.at(0).size() == 1);  // scalar
-  long long int result = ingredients.at(0).at(0);
+  double result = value(ingredients.at(0).at(0));
   for (index_t i = 1; i < ingredients.size(); ++i) {
     assert(ingredients.at(i).size() == 1);  // scalar
-    result /= ingredients.at(i).at(0);
+    result /= value(ingredients.at(i).at(0));
   }
   products.resize(1);
-  products.at(0).push_back(result);
+  products.at(0).push_back(mu_noninteger(result));
   break;
 }
 
@@ -192,11 +192,15 @@ DIVIDE_WITH_REMAINDER,
 Recipe_number["divide-with-remainder"] = DIVIDE_WITH_REMAINDER;
 :(before "End Primitive Recipe Implementations")
 case DIVIDE_WITH_REMAINDER: {
-  long long int quotient = ingredients.at(0).at(0) / ingredients.at(1).at(0);
-  long long int remainder = ingredients.at(0).at(0) % ingredients.at(1).at(0);
+  assert(ingredients.at(0).size() == 1);  // scalar
+  long long int a = value(ingredients.at(0).at(0));
+  assert(ingredients.at(1).size() == 1);  // scalar
+  long long int b = value(ingredients.at(1).at(0));
+  long long int quotient = a / b;
+  long long int remainder = a % b;
   products.resize(2);
-  products.at(0).push_back(quotient);
-  products.at(1).push_back(remainder);
+  products.at(0).push_back(mu_integer(quotient));
+  products.at(1).push_back(mu_integer(remainder));
   break;
 }
 
@@ -230,6 +234,13 @@ recipe main [
 
 //:: Support for non-integer numbers.
 
+:(scenario divide_with_decimal_point)
+recipe main [
+  # todo: literal floats?
+  1:integer <- divide 5:literal, 2:literal
+]
++mem: storing 2.5 in location 1
+
 //: Supporting non-integers is hopefully the only place where we need to think
 //: about the size of each location of memory.
 :(after "int main")
@@ -240,129 +251,181 @@ assert(sizeof(double) == 8);
 //: Conventional hardware uses the most significant bit to represent the sign
 //: in both (2's complement) integers, and so-called floating-point numbers
 //: (with sign, exponent and fraction regions: https://en.wikipedia.org/wiki/Double-precision_floating-point_format)
-static const long long int HOST_SET_NEGATIVE = 0x8000000000000000LL;
-//: Floating-point numbers store the sign of the exponent in the second-most
-//: significant bit.
-static const long long int HOST_SET_FLOAT_NEGATIVE_EXPONENT = 0x4000000000000000LL;
+
+//: Watch out: perform bitwise operations only on unsigned values to avoid
+//: undefined behavior.
+//: For similar reasons, don't coerce between signed and unsigned, instead
+//: manually interpret the bit-pattern
+
+const unsigned long long int HOST_SET_NEGATIVE = (0x1ULL << 63ULL);
 
 //: As an experiment, we'd like to not have to distinguish between the two in
 //: mu. So we'll use the most-significant bit to represent whether a number is
 //: an integer (MSB 0) or a float (MSB 1). This will halve the set of numbers
 //: we can represent, whether integers or non-integers, but that price seems
 //: reasonable right now.
-const long long int MU_NUMBER_TYPE_MASK = 0x8000000000000000LL;
+const unsigned long long int MU_NUMBER_NONINTEGER_MASK = (0x1ULL << 63ULL);
 //: As a result, the sign bit is now pushed to the second-most significant
 //: bit..
-const long long int MU_NUMBER_SIGN_MASK = 0x4000000000000000LL;
-//: ..and the sign of the exponent for floating-point is pushed to the
-//: third-most significant bit.
-const long long int MU_FLOAT_EXPONENT_SIGN_MASK = 0x2000000000000000LL;
-
-//: One nice side-effect of this mergek
+const unsigned long long int MU_NUMBER_SIGN_MASK = (0x1ULL << 62ULL);
 
 :(after "int main")
-assert(MU_NUMBER_TYPE_MASK == HOST_SET_NEGATIVE);
-assert(MU_NUMBER_SIGN_MASK == HOST_SET_FLOAT_NEGATIVE_EXPONENT);
+assert(MU_NUMBER_NONINTEGER_MASK == HOST_SET_NEGATIVE);
 
 :(code)
 inline bool is_float(long long int number) {
-  return number & MU_NUMBER_TYPE_MASK;
+  unsigned long long int tmp = *reinterpret_cast<unsigned long long int*>(&number);
+  return tmp & MU_NUMBER_NONINTEGER_MASK;
 }
 
 inline bool is_integer(long long int number) {
   return !is_float(number);
 }
 
-// both floats and integers use the most significant bit for the sign
 inline bool is_negative(long long int number) {
-  return number & MU_NUMBER_SIGN_MASK;
+  unsigned long long int tmp = *reinterpret_cast<unsigned long long int*>(&number);
+  return tmp & MU_NUMBER_SIGN_MASK;
 }
 
-inline bool float_has_negative_exponent(long long int number) {
-  return number & MU_FLOAT_EXPONENT_SIGN_MASK;
+inline double value(long long int number) {
+  return is_integer(number) ? to_int(number) : to_float(number);
 }
 
-long long int to_integer(long long int number) {
+// convert a mu integer to host representation
+long long int to_int(long long int number) {
   assert(is_integer(number));
-  if (is_negative(number)) {
-    // slide the sign over by one bit
-    number = number | HOST_SET_NEGATIVE;
-    // clear the old sign bit
-    number = number & (~MU_NUMBER_SIGN_MASK);
-  }
-  return number;
+  if (!is_negative(number)) return number;
+  // negative number
+  unsigned long long int tmp = *reinterpret_cast<unsigned long long int*>(&number);
+  // slide the sign over by one bit
+  tmp = tmp | HOST_SET_NEGATIVE;
+//?   // clear the old sign bit
+//?   // so the range of numbers we can represent shrinks by half
+//?   tmp = tmp & (~MU_NUMBER_SIGN_MASK);
+  // reinterpret back as signed
+  long long int result = *reinterpret_cast<long long int*>(&tmp);
+  return result;
+}
+
+// convert an integer from host representation to mu representation
+long long int mu_integer(long long int n) {
+  unsigned long long int tmp = *reinterpret_cast<unsigned long long int*>(&n);
+  // slide the sign to the right
+  if (tmp & HOST_SET_NEGATIVE) tmp = tmp | MU_NUMBER_SIGN_MASK;
+  // clear the old sign bit
+  tmp = tmp & (~HOST_SET_NEGATIVE);
+  // reinterpret back as signed
+  long long int result = *reinterpret_cast<long long int*>(&tmp);
+  assert(is_integer(result));
+//?   printf("%llx\n", result); //? 1
+  return result;
 }
 
+// convert a mu non-integer to host representation
 double to_float(long long int number) {
   assert(is_float(number));
-//?   cerr << "0: " << std::hex << number << std::dec << ' ' << *reinterpret_cast<double*>(&number) << '\n'; //? 1
-  if (is_negative(number)) {
-    // slide the sign over by one bit
-    number = number | HOST_SET_NEGATIVE;
-//?     cerr << "1: " << std::hex << number << std::dec << ' ' << *reinterpret_cast<double*>(&number) << '\n'; //? 1
-    // clear the old sign bit
-    number = number & (~MU_NUMBER_SIGN_MASK);
-//?     cerr << "2: " << std::hex << number << std::dec << ' ' << *reinterpret_cast<double*>(&number) << '\n'; //? 1
-  }
-  if (float_has_negative_exponent(number)) {
-    // slide the sign of the exponent over by one bit
-    number = number | HOST_SET_FLOAT_NEGATIVE_EXPONENT;
-//?     cerr << "3: " << std::hex << number << std::dec << ' ' << *reinterpret_cast<double*>(&number) << '\n'; //? 1
-//?     // clear the old exponent sign bit
-//?     number = number & (~MU_FLOAT_EXPONENT_SIGN_MASK);
-//?     cerr << "4: " << std::hex << number << std::dec << ' ' << *reinterpret_cast<double*>(&number) << '\n';
-  }
-  double result = *reinterpret_cast<double*>(&number);
+  unsigned long long int tmp = *reinterpret_cast<unsigned long long int*>(&number);
+  // slide the entire number over the most significant bit
+  // so the precision of numbers we can represent shrinks by 1 bit
+  tmp = (tmp << 1);
+  double result = *reinterpret_cast<double*>(&tmp);
+  return result;
+}
+
+// convert a float from host representation to mu representation
+long long int mu_noninteger(double n) {
+  unsigned long long int tmp = *reinterpret_cast<unsigned long long int*>(&n);
+  tmp = (tmp >> 1);
+  tmp = (tmp | MU_NUMBER_NONINTEGER_MASK);
+  long long int result = *reinterpret_cast<long long int*>(&tmp);
+  assert(is_float(result));
   return result;
 }
 
+// Spot-check some bit-patterns and make sure they convert back to themselves.
 void test_integer_representation() {
   // Assuming long long int is 8 bytes:
-  static const long long int nbits = 64;
+  static const int nbits = 64;
 //?   cerr << '\n'; //? 1
 //?   cerr << nbits << " iterations\n"; //? 1
 //?   cerr << std::hex; //? 1
-//?   cerr << "type mask: " << MU_NUMBER_TYPE_MASK << '\n'; //? 1
+//?   cerr << "type mask: " << MU_NUMBER_NONINTEGER_MASK << '\n'; //? 1
 //?   cerr << "sign mask: " << MU_NUMBER_SIGN_MASK << '\n'; //? 1
 //?   cerr << std::dec; //? 1
   // until the last 2 bits all integers retain their value
   for (int i = 0; i < nbits-2; ++i) {
-    long long int n = (0x1LL << i);
-//?     cerr << i << ": " << "0x" << std::hex << n << std::dec << ' ' << n << " => " << to_integer(n) << '\n'; //? 1
+    unsigned long long int x = (0x1ULL << i);
+    long long int n = *reinterpret_cast<long long int*>(&x);
+//?     cerr << i << ": " << "0x" << std::hex << n << std::dec << ' ' << n << " => " << to_int(n) << '\n'; //? 2
     CHECK(is_integer(n));
-    CHECK(n == to_integer(n));
+    CHECK_EQ(n, to_int(n));
+//?     printf("0x%llx\n", mu_integer(to_int(n))); //? 1
+    CHECK_EQ(n, mu_integer(to_int(n)));
   }
-  long long int n = (0x1LL << (nbits-2));
+  // second-last bit
+  unsigned long long int x = (0x1ULL << (nbits-2));
+  long long int n = *reinterpret_cast<long long int*>(&x);
   CHECK(is_integer(n));
-//?   cerr << nbits-2 << ": " << "0x" << std::hex << n << std::dec << ' ' << n << " => " << to_integer(n) << '\n'; //? 1
+//?   cerr << nbits-2 << ": " << "0x" << std::hex << n << std::dec << ' ' << n << " => " << to_int(n) << '\n'; //? 1
   CHECK(is_negative(n));
+  CHECK_EQ(n, mu_integer(to_int(n)));
+  // most significant bit is for non-integers below
+}
+
+// Now go the other way; spot-check a few mu integers and make sure they
+// convert back to themselves.
+void test_small_integers() {
+  for (long long int n = -1000; n < 1000; ++n) {
+//?     printf("0x%llx vs 0x%llx\n", n, to_int(mu_integer(n))); //? 1
+    CHECK_EQ(n, to_int(mu_integer(n)));
+  }
 }
 
+// Spot-check some bit-patterns and make sure they convert back to themselves.
 void test_noninteger_representation() {
   // Assuming long long int is 8 bytes:
-  static const long long int nbits = 64;
-  static const long long int FLOAT_MASK = (0x1LL << (nbits-1));
-  double f = -2.0;
-  printf("0x%llx\n", *(long long int*)&f);
-  cout << '\n';
+  static const int nbits = 64;
+  static const long long int FLOAT_MASK = (0x1ULL << (nbits-1));
+//?   double f = -2.0; //? 1
+//?   printf("0x%llx\n", *(long long int*)&f); //? 1
+//?   printf("\n"); //? 1
   for (int fraction = 0; fraction < 52; ++fraction) {
     for (int exponent = 52; exponent < 63; ++exponent) {
-      long long int n = (0x1LL << fraction) | (0x1LL << exponent) | FLOAT_MASK;
+      long long int n = (0x1ULL << fraction) | (0x1ULL << exponent) | FLOAT_MASK;
       CHECK(is_float(n));
-      double result_on_host = *reinterpret_cast<double*>(&n);
       double result = to_float(n);
-      printf("%02d %d: 0x%llx %.30e\n", fraction, exponent, n, result_on_host);
-      printf("=>                        %.30e\n", result);
+//?       double result_on_host = *reinterpret_cast<double*>(&n); //? 1
+//?       printf("%02d %d: 0x%llx %.30e\n", fraction, exponent, n, result_on_host); //? 1
+//?       printf("=>                        %.30e\n", result); //? 1
+//?       printf("=>     0x%llx\n", mu_noninteger(result)); //? 1
+      CHECK_EQ(n, mu_noninteger(result));
     }
+    int exponent = 63;
+    long long int n = ((0x1ULL << 62) | (0x1ULL << exponent) | FLOAT_MASK);
+    CHECK(is_float(n));
+    double result = to_float(n);
+//?     double result_on_host = *reinterpret_cast<double*>(&n); //? 1
+//?     printf("%02d %d: 0x%llx %.30e\n", fraction, nbits-1, n, result_on_host); //? 1
+//?     printf("=>                        %.30e\n", result); //? 1
+//?     printf("=>     0x%llx\n", mu_noninteger(result)); //? 1
+    CHECK_EQ(n, mu_noninteger(result));
   }
-//?   long long int n = ((0x1LL << (nbits-2)) | FLOAT_MASK);
-//?   CHECK(is_float(n));
-//?   double result_on_host = *reinterpret_cast<double*>(&n);
-//?   double result = to_float(n);
-//?   cout << nbits-2 << ": " << "0x" << std::hex << n << std::dec << ' ' << result_on_host
-//?        << " => " << result << '\n';
 }
 
+:(code)
+// Now go the other way; spot-check a few mu non-integers and make sure they
+// convert back to themselves.
+void test_small_nonintegers() {
+  for (double n = -1000.0; n < 1000.0; n += 0.001) {
+//?     printf("%.30e vs %.30e\n", n, to_float(mu_noninteger(n))); //? 1
+    CHECK(fabs(n - to_float(mu_noninteger(n))) < epsilon);
+  }
+}
+
+:(before "End Globals")
+const double epsilon = 1e-13;
+
 :(before "End Includes")
 #include<iomanip>
 #include<limits>
+#include<math.h>
diff --git a/022boolean.cc b/022boolean.cc
index e91dc209..bca425b0 100644
--- a/022boolean.cc
+++ b/022boolean.cc
@@ -12,7 +12,7 @@ case AND: {
     result = result && ingredients.at(i).at(0);
   }
   products.resize(1);
-  products.at(0).push_back(result);
+  products.at(0).push_back(result);  // boolean must be a positive integer
   break;
 }
 
@@ -60,7 +60,7 @@ case OR: {
     result = result || ingredients.at(i).at(0);
   }
   products.resize(1);
-  products.at(0).push_back(result);
+  products.at(0).push_back(result);  // boolean must be a positive integer
   break;
 }
 
@@ -105,7 +105,7 @@ case NOT: {
   products.resize(ingredients.size());
   for (index_t i = 0; i < ingredients.size(); ++i) {
     assert(ingredients.at(i).size() == 1);  // must be a scalar
-    products.at(i).push_back(!ingredients.at(i).at(0));
+    products.at(i).push_back(!ingredients.at(i).at(0));  // boolean must be a positive integer
   }
   break;
 }
diff --git a/023jump.cc b/023jump.cc
index 807113f3..4252b36e 100644
--- a/023jump.cc
+++ b/023jump.cc
@@ -9,7 +9,7 @@ case JUMP: {
   assert(current_instruction().ingredients.at(0).initialized);
   assert(ingredients.size() == 1);
   assert(ingredients.at(0).size() == 1);  // scalar
-  instruction_counter += ingredients.at(0).at(0);
+  instruction_counter += value(ingredients.at(0).at(0));
   trace("run") << "jumping to instruction " << instruction_counter+1;
   break;
 }
@@ -49,7 +49,7 @@ case JUMP_IF: {
     break;
   }
   assert(ingredients.at(1).size() == 1);  // scalar
-  instruction_counter += ingredients.at(1).at(0);
+  instruction_counter += value(ingredients.at(1).at(0));
   trace("run") << "jumping to instruction " << instruction_counter+1;
   break;
 }
@@ -89,7 +89,7 @@ case JUMP_UNLESS: {
     break;
   }
   assert(ingredients.at(1).size() == 1);  // scalar
-  instruction_counter += ingredients.at(1).at(0);
+  instruction_counter += value(ingredients.at(1).at(0));
   trace("run") << "jumping to instruction " << instruction_counter+1;
   break;
 }
diff --git a/024compare.cc b/024compare.cc
index 92d87e5c..c9d73170 100644
--- a/024compare.cc
+++ b/024compare.cc
@@ -9,13 +9,23 @@ case EQUAL: {
   vector<long long int>& exemplar = ingredients.at(0);
   bool result = true;
   for (index_t i = 1; i < ingredients.size(); ++i) {
-    if (!equal(ingredients.at(i).begin(), ingredients.at(i).end(), exemplar.begin())) {
+//?     cerr << ingredients.at(i).at(0) << " <=> " << exemplar.at(0) << '\n'; //? 1
+//?     cerr << value(ingredients.at(i).at(0)) << " <=> " << value(exemplar.at(0)) << '\n'; //? 1
+    if (ingredients.at(i).size() != exemplar.size()) {
       result = false;
       break;
     }
+//?     if (!equal(ingredients.at(i).begin(), ingredients.at(i).end(), exemplar.begin()))
+    for (index_t j = 0; j < exemplar.size(); ++j) {
+      if (value(ingredients.at(i).at(j)) != value(exemplar.at(j))) {
+        result = false;
+        goto finish;
+      }
+    }
   }
+  finish:
   products.resize(1);
-  products.at(0).push_back(result);
+  products.at(0).push_back(result);  // boolean must be a positive integer
   break;
 }
 
@@ -70,12 +80,12 @@ case GREATER_THAN: {
     assert(ingredients.at(i).size() == 1);  // scalar
   }
   for (index_t i = /**/1; i < ingredients.size(); ++i) {
-    if (ingredients.at(i-1).at(0) <= ingredients.at(i).at(0)) {
+    if (value(ingredients.at(i-1).at(0)) <= value(ingredients.at(i).at(0))) {
       result = false;
     }
   }
   products.resize(1);
-  products.at(0).push_back(result);
+  products.at(0).push_back(result);  // boolean must be a positive integer
   break;
 }
 
@@ -130,12 +140,12 @@ case LESSER_THAN: {
     assert(ingredients.at(i).size() == 1);  // scalar
   }
   for (index_t i = /**/1; i < ingredients.size(); ++i) {
-    if (ingredients.at(i-1).at(0) >= ingredients.at(i).at(0)) {
+    if (value(ingredients.at(i-1).at(0)) >= value(ingredients.at(i).at(0))) {
       result = false;
     }
   }
   products.resize(1);
-  products.at(0).push_back(result);
+  products.at(0).push_back(result);  // boolean must be a positive integer
   break;
 }
 
@@ -190,12 +200,12 @@ case GREATER_OR_EQUAL: {
     assert(ingredients.at(i).size() == 1);  // scalar
   }
   for (index_t i = /**/1; i < ingredients.size(); ++i) {
-    if (ingredients.at(i-1).at(0) < ingredients.at(i).at(0)) {
+    if (value(ingredients.at(i-1).at(0)) < value(ingredients.at(i).at(0))) {
       result = false;
     }
   }
   products.resize(1);
-  products.at(0).push_back(result);
+  products.at(0).push_back(result);  // boolean must be a positive integer
   break;
 }
 
@@ -264,12 +274,12 @@ case LESSER_OR_EQUAL: {
     assert(ingredients.at(i).size() == 1);  // scalar
   }
   for (index_t i = /**/1; i < ingredients.size(); ++i) {
-    if (ingredients.at(i-1).at(0) > ingredients.at(i).at(0)) {
+    if (value(ingredients.at(i-1).at(0)) > value(ingredients.at(i).at(0))) {
       result = false;
     }
   }
   products.resize(1);
-  products.at(0).push_back(result);
+  products.at(0).push_back(result);  // boolean must be a positive integer
   break;
 }
 
diff --git a/027debug.cc b/027debug.cc
index bbce5d6e..004dd68d 100644
--- a/027debug.cc
+++ b/027debug.cc
@@ -15,7 +15,7 @@ case _PRINT: {
       for (index_t j = 0; j < ingredients.at(i).size(); ++j) {
         trace("run") << "$print: " << ingredients.at(i).at(j);
         if (j > 0) cout << " ";
-        cout << ingredients.at(i).at(j);
+        cout << value(ingredients.at(i).at(j));
       }
     }
   }
diff --git a/030container.cc b/030container.cc
index edde3395..042c0b5a 100644
--- a/030container.cc
+++ b/030container.cc
@@ -110,7 +110,8 @@ Recipe_number["get"] = GET;
 :(before "End Primitive Recipe Implementations")
 case GET: {
   reagent base = current_instruction().ingredients.at(0);
-  index_t base_address = base.value;
+  assert(!is_negative(base.value));
+  index_t base_address = value(base.value);
   type_number base_type = base.types.at(0);
   assert(Type[base_type].kind == container);
   assert(isa_literal(current_instruction().ingredients.at(1)));
@@ -126,7 +127,7 @@ case GET: {
   type_number src_type = Type[base_type].elements.at(offset).at(0);
   trace("run") << "its type is " << src_type;
   reagent tmp;
-  tmp.set_value(src);
+  tmp.set_value(mu_integer(src));
   tmp.types.push_back(src_type);
   products.push_back(read_memory(tmp));
   break;
@@ -174,7 +175,8 @@ Recipe_number["get-address"] = GET_ADDRESS;
 :(before "End Primitive Recipe Implementations")
 case GET_ADDRESS: {
   reagent base = current_instruction().ingredients.at(0);
-  index_t base_address = base.value;
+  assert(!is_negative(base.value));
+  index_t base_address = value(base.value);
   type_number base_type = base.types.at(0);
   assert(Type[base_type].kind == container);
   assert(isa_literal(current_instruction().ingredients.at(1)));
@@ -186,6 +188,6 @@ case GET_ADDRESS: {
   }
   trace("run") << "address to copy is " << result;
   products.resize(1);
-  products.at(0).push_back(result);
+  products.at(0).push_back(mu_integer(result));  // address must be a positive integer
   break;
 }
diff --git a/031address.cc b/031address.cc
index 58d93466..259ea976 100644
--- a/031address.cc
+++ b/031address.cc
@@ -48,7 +48,7 @@ reagent deref(reagent x) {
   assert(x.types.at(0) == ADDRESS);
 
   // compute value
-  result.set_value(Memory[x.value]);
+  result.set_value(mu_integer(value(Memory[x.value])));  // address must be a positive integer
   trace("mem") << "location " << x.value << " is " << result.value;
 
   // populate types
diff --git a/032array.cc b/032array.cc
index 67b03293..19a4ac50 100644
--- a/032array.cc
+++ b/032array.cc
@@ -53,7 +53,7 @@ if (x.types.at(0) != Type_number["array"] && size_of(x) != data.size())
   if (r.types.at(0) == Type_number["array"]) {
     assert(r.types.size() > 1);
     // skip the 'array' type to get at the element type
-    return 1 + Memory[r.value]*size_of(array_element(r.types));
+    return 1 + value(Memory[r.value])*size_of(array_element(r.types));
   }
 
 //:: To access elements of an array, use 'index'
@@ -98,7 +98,8 @@ case INDEX: {
 //?   if (Trace_stream) Trace_stream->dump_layer = "run"; //? 1
   reagent base = canonize(current_instruction().ingredients.at(0));
 //?   trace("run") << "ingredient 0 after canonize: " << base.to_string(); //? 1
-  index_t base_address = base.value;
+  assert(!is_negative(base.value));
+  index_t base_address = value(base.value);
   assert(base.types.at(0) == Type_number["array"]);
   reagent offset = canonize(current_instruction().ingredients.at(1));
 //?   trace("run") << "ingredient 1 after canonize: " << offset.to_string(); //? 1
@@ -106,11 +107,12 @@ case INDEX: {
   vector<type_number> element_type = array_element(base.types);
 //?   trace("run") << "offset: " << offset_val.at(0); //? 1
 //?   trace("run") << "size of elements: " << size_of(element_type); //? 1
-  index_t src = base_address + 1 + offset_val.at(0)*size_of(element_type);
+  assert(offset_val.size() == 1);  // scalar
+  index_t src = base_address + 1 + value(offset_val.at(0))*size_of(element_type);
   trace("run") << "address to copy is " << src;
   trace("run") << "its type is " << element_type.at(0);
   reagent tmp;
-  tmp.set_value(src);
+  tmp.set_value(mu_integer(src));
   copy(element_type.begin(), element_type.end(), inserter(tmp.types, tmp.types.begin()));
   products.push_back(read_memory(tmp));
   break;
@@ -153,13 +155,14 @@ Recipe_number["index-address"] = INDEX_ADDRESS;
 :(before "End Primitive Recipe Implementations")
 case INDEX_ADDRESS: {
   reagent base = canonize(current_instruction().ingredients.at(0));
-  index_t base_address = base.value;
+  assert(!is_negative(base.value));
+  index_t base_address = value(base.value);
   assert(base.types.at(0) == Type_number["array"]);
-  reagent offset = canonize(current_instruction().ingredients.at(1));
-  vector<long long int> offset_val(read_memory(offset));
+  vector<long long int>& offset_val = ingredients.at(1);
+  assert(offset_val.size() == 1);
   vector<type_number> element_type = array_element(base.types);
-  index_t result = base_address + 1 + offset_val.at(0)*size_of(element_type);
+  index_t result = base_address + 1 + value(offset_val.at(0))*size_of(element_type);
   products.resize(1);
-  products.at(0).push_back(result);
+  products.at(0).push_back(mu_integer(result));  // address must be a positive integer
   break;
 }
diff --git a/033length.cc b/033length.cc
index 2268611a..8f18180e 100644
--- a/033length.cc
+++ b/033length.cc
@@ -23,6 +23,8 @@ case LENGTH: {
     break;
   }
   products.resize(1);
-  products.at(0).push_back(Memory[x.value]);
+  assert(is_integer(Memory[x.value]));
+  assert(!is_negative(Memory[x.value]));
+  products.at(0).push_back(Memory[x.value]);  // length must be a positive integer
   break;
 }
diff --git a/034exclusive_container.cc b/034exclusive_container.cc
index b2855f79..5b849916 100644
--- a/034exclusive_container.cc
+++ b/034exclusive_container.cc
@@ -89,19 +89,20 @@ Recipe_number["maybe-convert"] = MAYBE_CONVERT;
 :(before "End Primitive Recipe Implementations")
 case MAYBE_CONVERT: {
   reagent base = canonize(current_instruction().ingredients.at(0));
-  index_t base_address = base.value;
+  assert(!is_negative(base.value));
+  index_t base_address = value(base.value);
   type_number base_type = base.types.at(0);
   assert(Type[base_type].kind == exclusive_container);
   assert(isa_literal(current_instruction().ingredients.at(1)));
   index_t tag = current_instruction().ingredients.at(1).value;
   long long int result;
-  if (tag == static_cast<index_t>(Memory[base_address])) {
+  if (tag == static_cast<index_t>(value(Memory[base_address]))) {
     result = base_address+1;
   }
   else {
     result = 0;
   }
   products.resize(1);
-  products.at(0).push_back(result);
+  products.at(0).push_back(mu_integer(result));  // address must be a positive integer
   break;
 }
diff --git a/036call_ingredient.cc b/036call_ingredient.cc
index 5c321ef9..f5964e4b 100644
--- a/036call_ingredient.cc
+++ b/036call_ingredient.cc
@@ -105,14 +105,14 @@ case INGREDIENT: {
     products.push_back(
         Current_routine->calls.top().ingredient_atoms.at(Current_routine->calls.top().next_ingredient_to_process));
     assert(products.size() == 1);  products.resize(2);  // push a new vector
-    products.at(1).push_back(1);
+    products.at(1).push_back(1);  // non-negative integer
     ++Current_routine->calls.top().next_ingredient_to_process;
   }
   else {
     if (current_instruction().products.size() > 1) {
       products.resize(2);
       products.at(0).push_back(0);  // todo: will fail noisily if we try to read a compound value
-      products.at(1).push_back(0);
+      products.at(1).push_back(0);  // non-negative integer
     }
   }
   break;
diff --git a/038scheduler.cc b/038scheduler.cc
index 99ced920..64889f31 100644
--- a/038scheduler.cc
+++ b/038scheduler.cc
@@ -135,7 +135,7 @@ case START_RUNNING: {
     new_routine->calls.top().ingredient_atoms.push_back(ingredients.at(i));
   Routines.push_back(new_routine);
   products.resize(1);
-  products.at(0).push_back(new_routine->id);
+  products.at(0).push_back(new_routine->id);  // routine ids are positive integers
   break;
 }
 
@@ -301,7 +301,7 @@ case ROUTINE_STATE: {
     }
   }
   products.resize(1);
-  products.at(0).push_back(result);
+  products.at(0).push_back(result);  // routine states are positive integers
   break;
 }
 
diff --git a/039wait.cc b/039wait.cc
index 82354a53..a674ad25 100644
--- a/039wait.cc
+++ b/039wait.cc
@@ -24,7 +24,7 @@ WAITING,
 :(before "End routine Fields")
 // only if state == WAITING
 index_t waiting_on_location;
-int old_value_of_waiting_location;
+double old_value_of_waiting_location;
 :(before "End routine Constructor")
 waiting_on_location = old_value_of_waiting_location = 0;
 
@@ -39,9 +39,9 @@ case WAIT_FOR_LOCATION: {
   reagent loc = canonize(current_instruction().ingredients.at(0));
   Current_routine->state = WAITING;
   Current_routine->waiting_on_location = loc.value;
-  Current_routine->old_value_of_waiting_location = Memory[loc.value];
-  trace("run") << "waiting for location " << loc.value << " to change from " << Memory[loc.value];
-//?   trace("schedule") << Current_routine->id << ": waiting for location " << loc.value << " to change from " << Memory[loc.value]; //? 2
+  Current_routine->old_value_of_waiting_location = value(Memory[loc.value]);
+  trace("run") << "waiting for location " << loc.value << " to change from " << value(Memory[loc.value]);
+//?   trace("schedule") << Current_routine->id << ": waiting for location " << loc.value << " to change from " << value(Memory[loc.value]); //? 2
   break;
 }
 
@@ -54,9 +54,9 @@ for (index_t i = 0; i < Routines.size(); ++i) {
 //?   trace("schedule") << "waiting on location: " << Routines.at(i)->waiting_on_location; //? 1
 //?   if (Routines.at(i)->waiting_on_location) //? 2
 //?     trace("schedule") << "checking routine " << Routines.at(i)->id << " waiting on location " //? 2
-//?       << Routines.at(i)->waiting_on_location << ": " << Memory[Routines.at(i)->waiting_on_location] << " vs " << Routines.at(i)->old_value_of_waiting_location; //? 2
+//?       << Routines.at(i)->waiting_on_location << ": " << value(Memory[Routines.at(i)->waiting_on_location]) << " vs " << Routines.at(i)->old_value_of_waiting_location; //? 2
   if (Routines.at(i)->waiting_on_location &&
-      Memory[Routines.at(i)->waiting_on_location] != Routines.at(i)->old_value_of_waiting_location) {
+      value(Memory[Routines.at(i)->waiting_on_location]) != Routines.at(i)->old_value_of_waiting_location) {
     trace("schedule") << "waking up routine\n";
     Routines.at(i)->state = RUNNING;
     Routines.at(i)->waiting_on_location = Routines.at(i)->old_value_of_waiting_location = 0;
diff --git a/040brace.cc b/040brace.cc
index 224dbf96..f4c493c9 100644
--- a/040brace.cc
+++ b/040brace.cc
@@ -69,10 +69,10 @@ void transform_braces(const recipe_number r) {
       }
       else {
         reagent ing;
-        ing.set_value(open_braces.top()-index);
+        ing.set_value(mu_integer(open_braces.top()-index));
         ing.types.push_back(Type_number["offset"]);
         inst.ingredients.push_back(ing);
-        trace("after-brace") << "jump " << ing.value << ":offset";
+        trace("after-brace") << "jump " << to_int(ing.value) << ":offset";
         trace("after-brace") << index << ": " << ing.to_string();
         trace("after-brace") << index << ": " << Recipe[r].steps.at(index).ingredients.at(0).to_string();
       }
@@ -85,10 +85,10 @@ void transform_braces(const recipe_number r) {
       }
       else {
         reagent ing;
-        ing.set_value(matching_brace(open_braces.top(), braces) - index - 1);
+        ing.set_value(mu_integer(matching_brace(open_braces.top(), braces) - index - 1));
         ing.types.push_back(Type_number["offset"]);
         inst.ingredients.push_back(ing);
-        trace("after-brace") << "jump " << ing.value << ":offset";
+        trace("after-brace") << "jump " << to_int(ing.value) << ":offset";
       }
     }
     else if (inst.operation == Recipe_number["loop-if"]) {
@@ -99,10 +99,10 @@ void transform_braces(const recipe_number r) {
       }
       else {
         reagent ing;
-        ing.set_value(open_braces.top()-index);
+        ing.set_value(mu_integer(open_braces.top()-index));
         ing.types.push_back(Type_number["offset"]);
         inst.ingredients.push_back(ing);
-        trace("after-brace") << "jump-if " << inst.ingredients.at(0).name << ", " << ing.value << ":offset";
+        trace("after-brace") << "jump-if " << inst.ingredients.at(0).name << ", " << to_int(ing.value) << ":offset";
       }
     }
     else if (inst.operation == Recipe_number["break-if"]) {
@@ -113,10 +113,10 @@ void transform_braces(const recipe_number r) {
       }
       else {
         reagent ing;
-        ing.set_value(matching_brace(open_braces.top(), braces) - index - 1);
+        ing.set_value(mu_integer(matching_brace(open_braces.top(), braces) - index - 1));
         ing.types.push_back(Type_number["offset"]);
         inst.ingredients.push_back(ing);
-        trace("after-brace") << "jump-if " << inst.ingredients.at(0).name << ", " << ing.value << ":offset";
+        trace("after-brace") << "jump-if " << inst.ingredients.at(0).name << ", " << to_int(ing.value) << ":offset";
       }
     }
     else if (inst.operation == Recipe_number["loop-unless"]) {
@@ -127,10 +127,10 @@ void transform_braces(const recipe_number r) {
       }
       else {
         reagent ing;
-        ing.set_value(open_braces.top()-index);
+        ing.set_value(mu_integer(open_braces.top()-index));
         ing.types.push_back(Type_number["offset"]);
         inst.ingredients.push_back(ing);
-        trace("after-brace") << "jump-unless " << inst.ingredients.at(0).name << ", " << ing.value << ":offset";
+        trace("after-brace") << "jump-unless " << inst.ingredients.at(0).name << ", " << to_int(ing.value) << ":offset";
       }
     }
     else if (inst.operation == Recipe_number["break-unless"]) {
@@ -142,10 +142,10 @@ void transform_braces(const recipe_number r) {
       }
       else {
         reagent ing;
-        ing.set_value(matching_brace(open_braces.top(), braces) - index - 1);
+        ing.set_value(mu_integer(matching_brace(open_braces.top(), braces) - index - 1));
         ing.types.push_back(Type_number["offset"]);
         inst.ingredients.push_back(ing);
-        trace("after-brace") << "jump-unless " << inst.ingredients.at(0).name << ", " << ing.value << ":offset";
+        trace("after-brace") << "jump-unless " << inst.ingredients.at(0).name << ", " << to_int(ing.value) << ":offset";
       }
     }
     else {
@@ -380,13 +380,14 @@ recipe test-factorial [
   1:integer <- copy 5:literal
   2:integer <- copy 1:literal
   {
-    3:boolean <- equal 1:integer 1:literal
+    3:boolean <- equal 1:integer, 1:literal
     break-if 3:boolean
+#?     $print 1:integer, [ ], 2:integer, [
+#? ]
 #    $print 1:integer
     2:integer <- multiply 2:integer, 1:integer
     1:integer <- subtract 1:integer, 1:literal
     loop
   }
-  4:integer <- copy 2:integer  # trigger a read
 ]
-+mem: location 2 is 120
++mem: storing 120 in location 2
diff --git a/041name.cc b/041name.cc
index e5e9449b..ff2b811c 100644
--- a/041name.cc
+++ b/041name.cc
@@ -46,7 +46,7 @@ void transform_names(const recipe_number r) {
       if (!already_transformed(inst.ingredients.at(in), names)) {
         raise << "use before set: " << inst.ingredients.at(in).name << " in " << Recipe[r].name << '\n';
       }
-      inst.ingredients.at(in).set_value(lookup_name(inst.ingredients.at(in), r));
+      inst.ingredients.at(in).set_value(mu_integer(lookup_name(inst.ingredients.at(in), r)));  // must be a positive integer
     }
     for (index_t out = 0; out < inst.products.size(); ++out) {
       if (is_numeric_location(inst.products.at(out))) numeric_locations_used = true;
@@ -57,7 +57,7 @@ void transform_names(const recipe_number r) {
         names[inst.products.at(out).name] = curr_idx;
         curr_idx += size_of(inst.products.at(out));
       }
-      inst.products.at(out).set_value(lookup_name(inst.products.at(out), r));
+      inst.products.at(out).set_value(mu_integer(lookup_name(inst.products.at(out), r)));  // must be a positive integer
     }
   }
   if (names_used && numeric_locations_used)
@@ -220,7 +220,7 @@ if (inst.operation == Recipe_number["get"]
   if (inst.ingredients.at(1).name.find_first_not_of("0123456789") == string::npos) continue;
   // since first non-address in base type must be a container, we don't have to canonize
   type_number base_type = skip_addresses(inst.ingredients.at(0).types);
-  inst.ingredients.at(1).set_value(find_element_name(base_type, inst.ingredients.at(1).name));
+  inst.ingredients.at(1).set_value(mu_integer(find_element_name(base_type, inst.ingredients.at(1).name)));  // must be a positive integer
   trace("name") << "element " << inst.ingredients.at(1).name << " of type " << Type[base_type].name << " is at offset " << inst.ingredients.at(1).value;
 }
 
@@ -256,6 +256,6 @@ if (inst.operation == Recipe_number["maybe-convert"]) {
   if (inst.ingredients.at(1).name.find_first_not_of("0123456789") == string::npos) continue;
   // since first non-address in base type must be an exclusive container, we don't have to canonize
   type_number base_type = skip_addresses(inst.ingredients.at(0).types);
-  inst.ingredients.at(1).set_value(find_element_name(base_type, inst.ingredients.at(1).name));
+  inst.ingredients.at(1).set_value(mu_integer(find_element_name(base_type, inst.ingredients.at(1).name)));  // must be a positive integer
   trace("name") << "variant " << inst.ingredients.at(1).name << " of type " << Type[base_type].name << " has tag " << inst.ingredients.at(1).value;
 }
diff --git a/042new.cc b/042new.cc
index 754091c8..c960210b 100644
--- a/042new.cc
+++ b/042new.cc
@@ -37,7 +37,7 @@ if (inst.operation == Recipe_number["new"]) {
 //?   cout << inst.ingredients.at(0).to_string() << '\n'; //? 1
   assert(isa_literal(inst.ingredients.at(0)));
   if (inst.ingredients.at(0).properties.at(0).second.at(0) == "type") {
-    inst.ingredients.at(0).set_value(Type_number[inst.ingredients.at(0).name]);
+    inst.ingredients.at(0).set_value(mu_integer(Type_number[inst.ingredients.at(0).name]));  // type numbers must be positive integers
   }
   trace("new") << inst.ingredients.at(0).name << " -> " << inst.ingredients.at(0).value;
 }
@@ -51,15 +51,16 @@ Recipe_number["new"] = NEW;
 :(before "End Primitive Recipe Implementations")
 case NEW: {
   // compute the space we need
+//?   cerr << current_instruction().to_string() << '\n'; //? 1
   size_t size = 0;
   size_t array_length = 0;
   {
     vector<type_number> type;
     assert(isa_literal(current_instruction().ingredients.at(0)));
-    type.push_back(current_instruction().ingredients.at(0).value);
+    type.push_back(value(current_instruction().ingredients.at(0).value));  // type numbers must be positive integers
     if (current_instruction().ingredients.size() > 1) {
       // array
-      array_length = ingredients.at(1).at(0);
+      array_length = value(ingredients.at(1).at(0));
       trace("mem") << "array size is " << array_length;
       size = array_length*size_of(type) + /*space for length*/1;
     }
@@ -78,7 +79,7 @@ case NEW: {
   products.at(0).push_back(result);
   // initialize array if necessary
   if (current_instruction().ingredients.size() > 1) {
-    Memory[result] = array_length;
+    Memory[result] = mu_integer(array_length);  // array lengths must be positive integers
   }
   // bump
   Current_routine->alloc += size;
@@ -89,6 +90,7 @@ case NEW: {
 
 :(code)
 void ensure_space(size_t size) {
+//?   cerr << size << '\n'; //? 1
   assert(size <= Initial_memory_per_routine);
 //?   cout << Current_routine->alloc << " " << Current_routine->alloc_max << " " << size << '\n'; //? 1
   if (Current_routine->alloc + size > Current_routine->alloc_max) {
@@ -158,6 +160,7 @@ if (isa_literal(current_instruction().ingredients.at(0))
   // allocate an array just large enough for it
   size_t string_length = current_instruction().ingredients.at(0).name.size();
 //?   cout << "string_length is " << string_length << '\n'; //? 1
+//?   cerr << "AAA\n"; //? 1
   ensure_space(string_length+1);  // don't forget the extra location for array size
   products.resize(1);
   products.at(0).push_back(Current_routine->alloc);
diff --git a/043space.cc b/043space.cc
index b0ee7f85..24d63505 100644
--- a/043space.cc
+++ b/043space.cc
@@ -42,7 +42,7 @@ reagent absolutize(reagent x) {
 //?   cout << "not raw: " << x.to_string() << '\n'; //? 1
   assert(x.initialized);
   reagent r = x;
-  r.set_value(address(r.value, space_base(r)));
+  r.set_value(mu_integer(address(r.value, space_base(r))));  // must be a positive integer
 //?   cout << "after absolutize: " << r.value << '\n'; //? 1
   r.properties.push_back(pair<string, vector<string> >("raw", vector<string>()));
   assert(is_raw(r));
@@ -100,11 +100,11 @@ index_t space_base(const reagent& x) {
 index_t address(index_t offset, index_t base) {
   if (base == 0) return offset;  // raw
 //?   cout << base << '\n'; //? 2
-  if (offset >= static_cast<index_t>(Memory[base])) {
+  if (value(offset) >= value(Memory[base])) {
     // todo: test
-    raise << "location " << offset << " is out of bounds " << Memory[base] << '\n';
+    raise << "location " << value(offset) << " is out of bounds " << value(Memory[base]) << '\n';
   }
-  return base+1 + offset;
+  return value(base)+1 + value(offset);
 }
 
 :(after "void write_memory(reagent x, vector<long long int> data)")
diff --git a/044space_surround.cc b/044space_surround.cc
index b1e4c5e7..30d4c3f2 100644
--- a/044space_surround.cc
+++ b/044space_surround.cc
@@ -36,8 +36,8 @@ index_t space_base(const reagent& x, index_t space_index, index_t base) {
 //?     trace("foo") << "base of space " << space_index << " is " << base << '\n'; //? 1
     return base;
   }
-//?   trace("foo") << "base of space " << space_index << " is " << Memory[base+1] << '\n'; //? 1
-  index_t result = space_base(x, space_index-1, Memory[base+1]);
+//?   trace("foo") << "base of space " << space_index << " is " << value(Memory[base+1]) << '\n'; //? 1
+  index_t result = space_base(x, space_index-1, value(Memory[base+1]));
   return result;
 }
 
diff --git a/047jump_label.cc b/047jump_label.cc
index c9dcb4f5..b03d8b73 100644
--- a/047jump_label.cc
+++ b/047jump_label.cc
@@ -52,7 +52,7 @@ void replace_offset(reagent& x, /*const*/ map<string, index_t>& offset, const in
 //?   cerr << "DDD " << x.to_string() << '\n'; //? 1
   if (offset.find(x.name) == offset.end())
     raise << "can't find label " << x.name << " in routine " << Recipe[r].name << '\n';
-  x.set_value(offset[x.name]-current_offset);
+  x.set_value(mu_integer(offset[x.name]-current_offset));
 }
 
 :(scenario break_to_label)
diff --git a/050scenario.cc b/050scenario.cc
index c5ff2765..3aa34089 100644
--- a/050scenario.cc
+++ b/050scenario.cc
@@ -88,7 +88,7 @@ time_t mu_time; time(&mu_time);
 cerr << "\nMu tests: " << ctime(&mu_time);
 for (index_t i = 0; i < Scenarios.size(); ++i) {
 //?   cerr << Passed << '\n'; //? 1
-//?   cerr << i << ": " << Scenarios.at(i).name << '\n'; //? 2
+//?   cerr << i << ": " << Scenarios.at(i).name << '\n'; //? 3
   run_mu_scenario(Scenarios.at(i));
   if (Passed) cerr << ".";
 }
@@ -215,15 +215,15 @@ void check_memory(const string& s) {
     skip_whitespace_and_comments(in);
     string _assign;  in >> _assign;  assert(_assign == "<-");
     skip_whitespace_and_comments(in);
-    int value = 0;  in >> value;
+    int expected_value = 0;  in >> expected_value;
     if (locations_checked.find(address) != locations_checked.end())
       raise << "duplicate expectation for location " << address << '\n';
     trace("run") << "checking location " << address;
-    if (Memory[address] != value) {
+    if (value(Memory[address]) != expected_value) {
       if (Current_scenario)
-        raise << "\nF - " << Current_scenario->name << ": expected location " << address << " to contain " << value << " but saw " << Memory[address] << '\n';
+        raise << "\nF - " << Current_scenario->name << ": expected location " << address << " to contain " << expected_value << " but saw " << value(Memory[address]) << '\n';
       else
-        raise << "expected location " << address << " to contain " << value << " but saw " << Memory[address] << '\n';
+        raise << "expected location " << address << " to contain " << expected_value << " but saw " << value(Memory[address]) << '\n';
       Passed = false;
       return;
     }
@@ -234,7 +234,7 @@ void check_memory(const string& s) {
 void check_type(const string& lhs, istream& in) {
   reagent x(lhs);
   if (x.properties.at(0).second.at(0) == "string") {
-    x.set_value(to_number(x.name));
+    x.set_value(mu_integer(to_number(x.name)));  // address must be a positive integer
     skip_whitespace_and_comments(in);
     string _assign = next_word(in);
     assert(_assign == "<-");
@@ -252,13 +252,13 @@ void check_type(const string& lhs, istream& in) {
 
 void check_string(index_t address, const string& literal) {
   trace("run") << "checking string length at " << address;
-  if (Memory[address] != static_cast<signed>(literal.size()))
-    raise << "expected location " << address << " to contain length " << literal.size() << " of string [" << literal << "] but saw " << Memory[address] << '\n';
+  if (value(Memory[address]) != static_cast<signed>(literal.size()))
+    raise << "expected location " << address << " to contain length " << literal.size() << " of string [" << literal << "] but saw " << value(Memory[address]) << '\n';
   ++address;  // now skip length
   for (index_t i = 0; i < literal.size(); ++i) {
     trace("run") << "checking location " << address+i;
-    if (Memory[address+i] != literal.at(i))
-      raise << "expected location " << (address+i) << " to contain " << literal.at(i) << " but saw " << Memory[address+i] << '\n';
+    if (value(Memory[address+i]) != literal.at(i))
+      raise << "expected location " << (address+i) << " to contain " << literal.at(i) << " but saw " << value(Memory[address+i]) << '\n';
   }
 }
 
diff --git a/060string.mu b/060string.mu
index 6b38803b..c2b4e356 100644
--- a/060string.mu
+++ b/060string.mu
@@ -156,6 +156,8 @@ recipe buffer-append [
   default-space:address:array:location <- new location:type, 30:literal
   in:address:buffer <- next-ingredient
   c:character <- next-ingredient
+#?   $print c:character, [  #? 1
+#? ] #? 1
   {
     # grow buffer if necessary
     full?:boolean <- buffer-full? in:address:buffer
@@ -175,8 +177,12 @@ scenario buffer-append-works [
     default-space:address:array:location <- new location:type, 30:literal
     x:address:buffer <- init-buffer 3:literal
     s1:address:array:character <- get x:address:buffer/deref, data:offset
+#?     $start-tracing #? 1
     x:address:buffer <- buffer-append x:address:buffer, 97:literal  # 'a'
+#?     $exit #? 1
+#?     $start-tracing #? 1
     x:address:buffer <- buffer-append x:address:buffer, 98:literal  # 'b'
+#?     $exit #? 1
     x:address:buffer <- buffer-append x:address:buffer, 99:literal  # 'c'
     s2:address:array:character <- get x:address:buffer/deref, data:offset
     1:boolean/raw <- equal s1:address:array:character, s2:address:array:character
@@ -194,7 +200,9 @@ scenario buffer-append-works [
 #? ]
 #?     $print 1065:integer/raw, [
 #? ]
+#?     $start-tracing #? 1
     2:array:character/raw <- copy s2:address:array:character/deref
+#?     $exit #? 1
     +buffer-filled
     x:address:buffer <- buffer-append x:address:buffer, 100:literal  # 'd'
     s3:address:array:character <- get x:address:buffer/deref, data:offset
@@ -261,6 +269,8 @@ recipe integer-to-decimal-string [
   buf:address:array:character <- get tmp:address:buffer/deref, data:offset
   result:address:array:character <- new character:type, len:integer
   i:integer <- subtract len:integer, 1:literal
+#?   $print len:integer, [ - 1 = ], i:integer, [  #? 1
+#? ] #? 1
   j:integer <- copy 0:literal
   {
     # while i >= 0
@@ -271,7 +281,11 @@ recipe integer-to-decimal-string [
     dest:address:character <- index-address result:address:array:character/deref, j:integer
     dest:address:character/deref <- copy src:character
     # ++i
+#?     $print [aaa: ], i:integer, [  #? 1
+#? ] #? 1
     i:integer <- subtract i:integer, 1:literal
+#?     $print [bbb: ], i:integer, [  #? 1
+#? ] #? 1
     # --j
     j:integer <- add j:integer, 1:literal
     loop
@@ -290,6 +304,7 @@ scenario integer-to-decimal-digit-zero [
 ]
 
 scenario integer-to-decimal-digit-positive [
+#?   $start-tracing #? 1
   run [
     1:address:array:character/raw <- integer-to-decimal-string 234:literal
     2:array:character/raw <- copy 1:address:array:character/deref/raw
@@ -300,6 +315,7 @@ scenario integer-to-decimal-digit-positive [
 ]
 
 scenario integer-to-decimal-digit-negative [
+#?   $start-tracing #? 1
   run [
     1:address:array:character/raw <- integer-to-decimal-string -1:literal
     2:array:character/raw <- copy 1:address:array:character/deref/raw
diff --git a/070display.cc b/070display.cc
index 8a782f87..7c850ef8 100644
--- a/070display.cc
+++ b/070display.cc
@@ -70,7 +70,7 @@ case PRINT_CHARACTER_TO_DISPLAY: {
   size_t height = (h >= 0) ? h : 0;
   size_t width = (w >= 0) ? w : 0;
   assert(ingredients.at(0).size() == 1);  // scalar
-  long long int c = ingredients.at(0).at(0);
+  long long int c = value(ingredients.at(0).at(0));  // unicode code-point will probably always be a positive integer
   if (c == '\n' || c == '\r') {
     if (Display_row < height-1) {
       Display_column = 0;
@@ -117,9 +117,9 @@ Recipe_number["move-cursor-on-display"] = MOVE_CURSOR_ON_DISPLAY;
 :(before "End Primitive Recipe Implementations")
 case MOVE_CURSOR_ON_DISPLAY: {
   assert(ingredients.at(0).size() == 1);  // scalar
-  Display_row = ingredients.at(0).at(0);
+  Display_row = ingredients.at(0).at(0);  // screen coordinate will always be a non-negative integer
   assert(ingredients.at(1).size() == 1);  // scalar
-  Display_column = ingredients.at(1).at(0);
+  Display_column = ingredients.at(1).at(0);  // screen coordinate will always be a non-negative integer
   tb_set_cursor(Display_column, Display_row);
   tb_present();
   break;
diff --git a/071print.mu b/071print.mu
index d6c72b32..487608a2 100644
--- a/071print.mu
+++ b/071print.mu
@@ -41,7 +41,7 @@ recipe clear-screen [
       done?:boolean <- greater-or-equal i:integer, max:integer
       break-if done?:boolean
       c:address:character <- index-address buf:address:array:character/deref, i:integer
-      c:address:character/deref <- copy [ ]
+      c:address:character/deref <- copy [ ]  # hack: can't copy any literal string except a single space
       i:integer <- add i:integer, 1:literal
       loop
     }
diff --git a/072scenario_screen.cc b/072scenario_screen.cc
index 2e563b82..5d57c199 100644
--- a/072scenario_screen.cc
+++ b/072scenario_screen.cc
@@ -6,7 +6,7 @@
 :(scenarios run_mu_scenario)
 :(scenario screen_in_scenario)
 scenario screen-in-scenario [
-#?   $start-tracing
+#?   $start-tracing #? 3
   assume-screen 5:literal/width, 3:literal/height
   run [
     screen:address <- print-character screen:address, 97:literal  # 'a'
@@ -35,6 +35,7 @@ scenario screen-in-scenario-error [
     .     .
   ]
 ]
+# manual test: modifying the trace line below should cause a failure
 +warn: expected screen location (0, 0) to contain 'b' instead of 'a'
 
 :(before "End Globals")
@@ -66,7 +67,7 @@ if (curr.name == "assume-screen") {
   curr.operation = Recipe_number["init-fake-screen"];
   assert(curr.products.empty());
   curr.products.push_back(reagent("screen:address"));
-  curr.products.at(0).set_value(SCREEN);
+  curr.products.at(0).set_value(mu_integer(SCREEN));  // address must be a positive integer
 //? cout << "after: " << curr.to_string() << '\n'; //? 1
 //? cout << "AAA " << Recipe_number["init-fake-screen"] << '\n'; //? 1
 }
@@ -86,15 +87,15 @@ case SCREEN_SHOULD_CONTAIN: {
 :(code)
 void check_screen(const string& contents) {
   assert(!Current_routine->calls.top().default_space);  // not supported
-  index_t screen_location = Memory[SCREEN];
+  index_t screen_location = value(Memory[SCREEN]);
   int data_offset = find_element_name(Type_number["screen"], "data");
   assert(data_offset >= 0);
   index_t screen_data_location = screen_location+data_offset;  // type: address:array:character
-  index_t screen_data_start = Memory[screen_data_location];  // type: array:character
+  index_t screen_data_start = value(Memory[screen_data_location]);  // type: array:character
   int width_offset = find_element_name(Type_number["screen"], "num-columns");
-  size_t screen_width = Memory[screen_location+width_offset];
+  size_t screen_width = value(Memory[screen_location+width_offset]);
   int height_offset = find_element_name(Type_number["screen"], "num-rows");
-  size_t screen_height = Memory[screen_location+height_offset];
+  size_t screen_height = value(Memory[screen_location+height_offset]);
   string expected_contents;
   istringstream in(contents);
   in >> std::noskipws;
@@ -112,19 +113,21 @@ void check_screen(const string& contents) {
 //?   assert(in.get() == ']');
   trace("run") << "checking screen size at " << screen_data_start;
 //?   cout << expected_contents.size() << '\n'; //? 1
-  if (Memory[screen_data_start] > static_cast<signed>(expected_contents.size()))
-    raise << "expected contents are larger than screen size " << Memory[screen_data_start] << '\n';
+  if (value(Memory[screen_data_start]) > static_cast<signed>(expected_contents.size()))
+    raise << "expected contents are larger than screen size " << value(Memory[screen_data_start]) << '\n';
   ++screen_data_start;  // now skip length
   for (index_t i = 0; i < expected_contents.size(); ++i) {
     trace("run") << "checking location " << screen_data_start+i;
-//?     cerr << "comparing " << i/screen_width << ", " << i%screen_width << ": " << Memory[screen_data_start+i] << " vs " << (int)expected_contents.at(i) << '\n'; //? 1
-    if ((!Memory[screen_data_start+i] && !isspace(expected_contents.at(i)))  // uninitialized memory => spaces
-        || (Memory[screen_data_start+i] && Memory[screen_data_start+i] != expected_contents.at(i))) {
+//?     cerr << "comparing " << i/screen_width << ", " << i%screen_width << ": " << value(Memory[screen_data_start+i]) << " vs " << (int)expected_contents.at(i) << '\n'; //? 1
+    long long int curr = value(Memory[screen_data_start+i]);
+//?     cerr << curr << " <= " << expected_contents.at(i) << '\n'; //? 1
+    if ((!curr && !isspace(expected_contents.at(i)))  // uninitialized memory => spaces
+        || (curr && value(Memory[screen_data_start+i]) != expected_contents.at(i))) {
 //?       cerr << "CCC " << Trace_stream << " " << Hide_warnings << '\n'; //? 1
-      if (Current_scenario)
-        raise << "\nF - " << Current_scenario->name << ": expected screen location (" << i/screen_width << ", " << i%screen_width << ") to contain '" << expected_contents.at(i) << "' instead of '" << static_cast<char>(Memory[screen_data_start+i]) << "'\n";
+      if (Current_scenario && !Hide_warnings)  // Hide_warnings indicates we're checking for the warning at the C++ level rather than raising a test failure at the mu level
+        raise << "\nF - " << Current_scenario->name << ": expected screen location (" << i/screen_width << ", " << i%screen_width << ") to contain '" << expected_contents.at(i) << "' instead of '" << static_cast<char>(value(Memory[screen_data_start+i])) << "'\n";
       else
-        raise << "expected screen location (" << i/screen_width << ", " << i%screen_width << ") to contain '" << expected_contents.at(i) << "' instead of '" << static_cast<char>(Memory[screen_data_start+i]) << "'\n";
+        raise << "expected screen location (" << i/screen_width << ", " << i%screen_width << ") to contain '" << expected_contents.at(i) << "' instead of '" << static_cast<char>(value(Memory[screen_data_start+i])) << "'\n";
       Passed = false;
       return;
     }
diff --git a/075scenario_keyboard.cc b/075scenario_keyboard.cc
index 0de6a2eb..3d816c2a 100644
--- a/075scenario_keyboard.cc
+++ b/075scenario_keyboard.cc
@@ -41,15 +41,15 @@ if (curr.name == "assume-keyboard") {
   curr.operation = Recipe_number["new"];
   assert(curr.products.empty());
   curr.products.push_back(reagent("keyboard:address"));
-  curr.products.at(0).set_value(KEYBOARD);
+  curr.products.at(0).set_value(mu_integer(KEYBOARD));  // address must be a positive integer
   result.steps.push_back(curr);  // hacky that "Rewrite Instruction" is converting to multiple instructions
   // leave second instruction in curr
   curr.clear();
   curr.operation = Recipe_number["init-fake-keyboard"];
   assert(curr.ingredients.empty());
   curr.ingredients.push_back(reagent("keyboard:address"));
-  curr.ingredients.at(0).set_value(KEYBOARD);
+  curr.ingredients.at(0).set_value(mu_integer(KEYBOARD));  // address must be a positive integer
   assert(curr.products.empty());
   curr.products.push_back(reagent("keyboard:address"));
-  curr.products.at(0).set_value(KEYBOARD);
+  curr.products.at(0).set_value(mu_integer(KEYBOARD));  // address must be a positive integer
 }