/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/**
 * A number of the tests in this file depend on the setting of
 * HOTLOOP.  Define some constants up front, so they're easy to grep
 * for.
 */
// The HOTLOOP constant we depend on; only readable from our stats
// object in debug builds.
const haveTracemonkey = !!(this.tracemonkey)
const HOTLOOP = haveTracemonkey ? tracemonkey.HOTLOOP : 2;
// The loop count at which we trace
const RECORDLOOP = HOTLOOP;
// The loop count at which we run the trace
const RUNLOOP = HOTLOOP + 1;

var gDoMandelbrotTest = true;
if ("gSkipSlowTests" in this && gSkipSlowTests) {
    print("** Skipping slow tests");
    gDoMandelbrotTest = false;
}

if (!('gSrcdir' in this))
    gSrcdir = '.';

if (!('gReportSummary' in this))
    gReportSummary = true;

var testName = null;
if ("arguments" in this && arguments.length > 0)
  testName = arguments[0];
var fails = [], passes=[];

function jitstatHandler(f)
{
    if (!haveTracemonkey)
        return;

    // XXXbz this is a nasty hack, but I can't figure out a way to
    // just use jitstats.tbl here
    f("recorderStarted");
    f("recorderAborted");
    f("traceCompleted");
    f("sideExitIntoInterpreter");
    f("timeoutIntoInterpreter");
    f("typeMapMismatchAtEntry");
    f("returnToDifferentLoopHeader");
    f("traceTriggered");
    f("globalShapeMismatchAtEntry");
    f("treesTrashed");
    f("slotPromoted");
    f("unstableLoopVariable");
    f("breakLoopExits");
    f("returnLoopExits");
    f("mergedLoopExits")
    f("noCompatInnerTrees");
}

var jitProps = {};
jitstatHandler(function(prop) {
                 jitProps[prop] = true;
               });
var hadJITstats = false;
for (var p in jitProps)
  hadJITstats = true;

function test(f)
{
  // Clear out any accumulated confounding state in the oracle / JIT cache.
  gc();

  if (!testName || testName == f.name) {
    var expectedJITstats = f.jitstats;
    if (hadJITstats && expectedJITstats)
    {
      var expectedProps = {};
      jitstatHandler(function(prop) {
                       if (prop in expectedJITstats)
                         expectedProps[prop] = true;
                     });
      for (var p in expectedJITstats)
      {
        if (!(p in expectedProps))
          throw "Bad property in " + f.name + ".jitstats: " + p;
      }
    }

    // Collect our jit stats
    var localJITstats = {};
    jitstatHandler(function(prop) {
                     localJITstats[prop] = tracemonkey[prop];
                   });
    check(f.name, f(), f.expected, localJITstats, expectedJITstats);
  }
}

function check(desc, actual, expected, oldJITstats, expectedJITstats)
{
  if (expected == actual) {
    var pass = true;
    jitstatHandler(function(prop) {
                     if (expectedJITstats && prop in expectedJITstats &&
                         expectedJITstats[prop] !=
                           tracemonkey[prop] - oldJITstats[prop]) {
                       pass = false;
                     }
                   });
    if (pass) {
      passes.push(desc);
      return print("TEST-PASS | trace-test.js |", desc);
    }
  }
  fails.push(desc);
  var expectedStats = "";
  if (expectedJITstats) {
      jitstatHandler(function(prop) {
                       if (prop in expectedJITstats) {
                         if (expectedStats)
                           expectedStats += " ";
                         expectedStats +=
                           prop + ": " + expectedJITstats[prop];
                       }
                     });
  }
  var actualStats = "";
  if (expectedJITstats) {
      jitstatHandler(function(prop) {
                       if (prop in expectedJITstats) {
                         if (actualStats)
                           actualStats += " ";
                         actualStats += prop + ": " + (tracemonkey[prop]-oldJITstats[prop]);
                       }
                     });
  }
  print("TEST-UNEXPECTED-FAIL | trace-test.js |", desc, ": expected", typeof(expected),
        "(", uneval(expected), ")",
        (expectedStats ? " [" + expectedStats + "] " : ""),
        "!= actual",
        typeof(actual), "(", uneval(actual), ")",
        (actualStats ? " [" + actualStats + "] " : ""));
}

function ifInsideLoop()
{
  var cond = true, intCond = 5, count = 0;
  for (var i = 0; i < 100; i++) {
    if (cond)
      count++;
    if (intCond)
      count++;
  }
  return count;
}
ifInsideLoop.expected = 200;
test(ifInsideLoop);

function bitwiseAnd_inner(bitwiseAndValue) {
  for (var i = 0; i < 60000; i++)
    bitwiseAndValue = bitwiseAndValue & i;
  return bitwiseAndValue;
}
function bitwiseAnd()
{
  return bitwiseAnd_inner(12341234);
}
bitwiseAnd.expected = 0;
test(bitwiseAnd);

if (!testName || testName == "bitwiseGlobal") {
  bitwiseAndValue = Math.pow(2,32);
  for (var i = 0; i < 60000; i++)
    bitwiseAndValue = bitwiseAndValue & i;
  check("bitwiseGlobal", bitwiseAndValue, 0);
}


function equalInt()
{
  var i1 = 55, one = 1, zero = 0, undef;
  var o1 = { }, o2 = { };
  var s = "5";
  var hits = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
  for (var i = 0; i < 5000; i++) {
    if (i1 == 55) hits[0]++;
    if (i1 != 56) hits[1]++;
    if (i1 < 56)  hits[2]++;
    if (i1 > 50)  hits[3]++;
    if (i1 <= 60) hits[4]++;
    if (i1 >= 30) hits[5]++;
    if (i1 == 7)  hits[6]++;
    if (i1 != 55) hits[7]++;
    if (i1 < 30)  hits[8]++;
    if (i1 > 90)  hits[9]++;
    if (i1 <= 40) hits[10]++;
    if (i1 >= 70) hits[11]++;
    if (o1 == o2) hits[12]++;
    if (o2 != null) hits[13]++;
    if (s < 10) hits[14]++;
    if (true < zero) hits[15]++;
    if (undef > one) hits[16]++;
    if (undef < zero) hits[17]++;
  }
  return hits.toString();
}
equalInt.expected = "5000,5000,5000,5000,5000,5000,0,0,0,0,0,0,0,5000,5000,0,0,0";
test(equalInt);

var a;
function setelem()
{
  a = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
  a = a.concat(a, a, a);
  var l = a.length;
  for (var i = 0; i < l; i++) {
    a[i] = i;
  }
  return a.toString();
}
setelem.expected = "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83";
test(setelem);

function getelem_inner(a)
{
  var accum = 0;
  var l = a.length;
  for (var i = 0; i < l; i++) {
    accum += a[i];
  }
  return accum;
}
function getelem()
{
  return getelem_inner(a);
}
getelem.expected = 3486;
test(getelem);

globalName = 907;
function name()
{
  var a = 0;
  for (var i = 0; i < 100; i++)
    a = globalName;
  return a;
}
name.expected = 907;
test(name);

var globalInt = 0;
if (!testName || testName == "globalGet") {
  for (var i = 0; i < 500; i++)
    globalInt = globalName + i;
  check("globalGet", globalInt, globalName + 499);
}

if (!testName || testName == "globalSet") {
  for (var i = 0; i < 500; i++)
    globalInt = i;
  check("globalSet", globalInt, 499);
}

function arith()
{
  var accum = 0;
  for (var i = 0; i < 100; i++) {
    accum += (i * 2) - 1;
  }
  return accum;
}
arith.expected = 9800;
test(arith);

function lsh_inner(n)
{
  var r;
  for (var i = 0; i < 35; i++)
    r = 0x1 << n;
  return r;
}
function lsh()
{
  return [lsh_inner(15),lsh_inner(55),lsh_inner(1),lsh_inner(0)];
}
lsh.expected = "32768,8388608,2,1";
test(lsh);

function rsh_inner(n)
{
  var r;
  for (var i = 0; i < 35; i++)
    r = 0x11010101 >> n;
  return r;
}
function rsh()
{
  return [rsh_inner(8),rsh_inner(5),rsh_inner(35),rsh_inner(-1)];
}
rsh.expected = "1114369,8914952,35659808,0";
test(rsh);

function ursh_inner(n)
{
  var r;
  for (var i = 0; i < 35; i++)
    r = -55 >>> n;
  return r;
}
function ursh() {
  return [ursh_inner(8),ursh_inner(33),ursh_inner(0),ursh_inner(1)];
}
ursh.expected = "16777215,2147483620,4294967241,2147483620";
test(ursh);

function doMath_inner(cos)
{
    var s = 0;
    var sin = Math.sin;
    for (var i = 0; i < 200; i++)
        s = -Math.pow(sin(i) + cos(i * 0.75), 4);
    return s;
}
function doMath() {
  return doMath_inner(Math.cos);
}
doMath.expected = -0.5405549555611059;
test(doMath);

function fannkuch() {
   var count = Array(8);
   var r = 8;
   var done = 0;
   while (done < 40) {
      // write-out the first 30 permutations
      done += r;
      while (r != 1) { count[r - 1] = r; r--; }
      while (true) {
         count[r] = count[r] - 1;
         if (count[r] > 0) break;
         r++;
      }
   }
   return done;
}
fannkuch.expected = 41;
test(fannkuch);

function xprop()
{
  a = 0;
  for (var i = 0; i < 20; i++)
    a += 7;
  return a;
}
xprop.expected = 140;
test(xprop);

var a = 2;
function getprop_inner(o2)
{
  var o = {a:5};
  var t = this;
  var x = 0;
  for (var i = 0; i < 20; i++) {
    t = this;
    x += o.a + o2.a + this.a + t.a;
  }
  return x;
}
function getprop() {
  return getprop_inner({a:9});
}
getprop.expected = 360;
test(getprop);

function mod()
{
  var mods = [-1,-1,-1,-1];
  var a = 9.5, b = -5, c = 42, d = (1/0);
  for (var i = 0; i < 20; i++) {
    mods[0] = a % b;
    mods[1] = b % 1;
    mods[2] = c % d;
    mods[3] = c % a;
    mods[4] = b % 0;
  }
  return mods.toString();
}
mod.expected = "4.5,0,42,4,NaN";
test(mod);

function glob_f1() {
  return 1;
}
function glob_f2() {
  return glob_f1();
}
function call()
{
  var q1 = 0, q2 = 0, q3 = 0, q4 = 0, q5 = 0;
  var o = {};
  function f1() {
      return 1;
  }
  function f2(f) {
      return f();
  }
  o.f = f1;
  for (var i = 0; i < 100; ++i) {
      q1 += f1();
      q2 += f2(f1);
      q3 += glob_f1();
      q4 += o.f();
      q5 += glob_f2();
  }
  var ret = [q1, q2, q3, q4, q5];
  return ret;
}
call.expected =  "100,100,100,100,100";
test(call);

function setprop()
{
  var obj = { a:-1 };
  var obj2 = { b:-1, a:-1 };
  for (var i = 0; i < 20; i++) {
    obj2.b = obj.a = i;
  }
  return [obj.a, obj2.a, obj2.b].toString();
}
setprop.expected =  "19,-1,19";
test(setprop);

function testif() {
    var q = 0;
    for (var i = 0; i < 100; i++) {
        if ((i & 1) == 0)
            q++;
        else
            q--;
    }
    return q;
}
testif.expected = "0";
test(testif);

var globalinc = 0;
function testincops(n) {
  var i = 0, o = {p:0}, a = [0];
  n = 100;

  for (i = 0; i < n; i++);
  while (i-- > 0);
  for (i = 0; i < n; ++i);
  while (--i >= 0);

  for (o.p = 0; o.p < n; o.p++) globalinc++;
  while (o.p-- > 0) --globalinc;
  for (o.p = 0; o.p < n; ++o.p) ++globalinc;
  while (--o.p >= 0) globalinc--;

  ++i; // set to 0
  for (a[i] = 0; a[i] < n; a[i]++);
  while (a[i]-- > 0);
  for (a[i] = 0; a[i] < n; ++a[i]);
  while (--a[i] >= 0);

  return [++o.p, ++a[i], globalinc].toString();
}
testincops.expected = "0,0,0";
test(testincops);

function trees() {
  var i = 0, o = [0,0,0];
  for (i = 0; i < 100; ++i) {
    if ((i & 1) == 0) o[0]++;
    else if ((i & 2) == 0) o[1]++;
    else o[2]++;
  }
  return o;
}
trees.expected = "50,25,25";
test(trees);

function unboxint() {
    var q = 0;
    var o = [4];
    for (var i = 0; i < 100; ++i)
        q = o[0] << 1;
    return q;
}
unboxint.expected = "8";
test(unboxint);

function strings()
{
  var a = [], b = -1;
  var s = "abcdefghij", s2 = "a";
  var f = "f";
  var c = 0, d = 0, e = 0, g = 0;
  for (var i = 0; i < 10; i++) {
    a[i] = (s.substring(i, i+1) + s[i] + String.fromCharCode(s2.charCodeAt(0) + i)).concat(i) + i;
    if (s[i] == f)
      c++;
    if (s[i] != 'b')
      d++;
    if ("B" > s2)
      g++; // f already used
    if (s2 < "b")
      e++;
    b = s.length;
  }
  return a.toString() + b + c + d + e + g;
}
strings.expected = "aaa00,bbb11,ccc22,ddd33,eee44,fff55,ggg66,hhh77,iii88,jjj991019100";
test(strings);

function dependentStrings()
{
  var a = [];
  var t = "abcdefghijklmnopqrst";
  for (var i = 0; i < 10; i++) {
    var s = t.substring(2*i, 2*i + 2);
    a[i] = s + s.length;
  }
  return a.join("");
}
dependentStrings.expected = "ab2cd2ef2gh2ij2kl2mn2op2qr2st2";
test(dependentStrings);

function stringConvert()
{
  var a = [];
  var s1 = "F", s2 = "1.3", s3 = "5";
  for (var i = 0; i < 10; i++) {
    a[0] = 1 >> s1;
    a[1] = 10 - s2;
    a[2] = 15 * s3;
    a[3] = s3 | 32;
    a[4] = s2 + 60;
    // a[5] = 9 + s3;
    // a[6] = -s3;
    a[7] = s3 & "7";
    // a[8] = ~s3;
  }
  return a.toString();
}
stringConvert.expected = "1,8.7,75,37,1.360,,,5";
test(stringConvert);

function orTestHelper(a, b, n)
{
  var k = 0;
  for (var i = 0; i < n; i++) {
    if (a || b)
      k += i;
  }
  return k;
}

var orNaNTest1, orNaNTest2;

orNaNTest1 = new Function("return orTestHelper(NaN, NaN, 10);");
orNaNTest1.name = 'orNaNTest1';
orNaNTest1.expected = '0';
orNaNTest2 = new Function("return orTestHelper(NaN, 1, 10);");
orNaNTest2.name = 'orNaNTest2';
orNaNTest2.expected = '45';
test(orNaNTest1);
test(orNaNTest2);

function andTestHelper(a, b, n)
{
  var k = 0;
  for (var i = 0; i < n; i++) {
    if (a && b)
      k += i;
  }
  return k;
}

if (!testName || testName == "truthies") {
  (function () {
     var opsies   = ["||", "&&"];
     var falsies  = [null, undefined, false, NaN, 0, ""];
     var truthies = [{}, true, 1, 42, 1/0, -1/0, "blah"];
     var boolies  = [falsies, truthies];

     // The for each here should abort tracing, so that this test framework
     // relies only on the interpreter while the orTestHelper and andTestHelper
     //  functions get trace-JITed.
     for each (var op in opsies) {
       for (var i in boolies) {
         for (var j in boolies[i]) {
           var x = uneval(boolies[i][j]);
           for (var k in boolies) {
             for (var l in boolies[k]) {
               var y = uneval(boolies[k][l]);
               var prefix = (op == "||") ? "or" : "and";
               var f = new Function("return " + prefix + "TestHelper(" + x + "," + y + ",10)");
               f.name = prefix + "Test(" + x + "," + y + ")";
               f.expected = eval(x + op + y) ? 45 : 0;
               test(f);
             }
           }
         }
       }
     }
   })();
}

function nonEmptyStack1Helper(o, farble) {
    var a = [];
    var j = 0;
    for (var i in o)
        a[j++] = i;
    return a.join("");
}

function nonEmptyStack1() {
    return nonEmptyStack1Helper({a:1,b:2,c:3,d:4,e:5,f:6,g:7,h:8}, "hi");
}

nonEmptyStack1.expected = "abcdefgh";
test(nonEmptyStack1);

function nonEmptyStack2()
{
  var a = 0;
  for (var c in {a:1, b:2, c:3}) {
    for (var i = 0; i < 10; i++)
      a += i;
  }
  return String(a);
}
nonEmptyStack2.expected = "135";
test(nonEmptyStack2);

function arityMismatchMissingArg(arg)
{
  for (var a = 0, i = 1; i < 10000; i *= 2) {
    a += i;
  }
  return a;
}
arityMismatchMissingArg.expected = 16383;
test(arityMismatchMissingArg);

function arityMismatchExtraArg()
{
  return arityMismatchMissingArg(1, 2);
}
arityMismatchExtraArg.expected = 16383;
test(arityMismatchExtraArg);

function MyConstructor(i)
{
  this.i = i;
}
MyConstructor.prototype.toString = function() {return this.i + ""};

function newTest()
{
  var a = [];
  for (var i = 0; i < 10; i++)
    a[i] = new MyConstructor(i);
  return a.join("");
}
newTest.expected = "0123456789";
test(newTest);

// The following functions use a delay line of length 2 to change the value
// of the callee without exiting the traced loop. This is obviously tuned to
// match the current HOTLOOP setting of 2.
function shapelessArgCalleeLoop(f, g, h, a)
{
  for (var i = 0; i < 10; i++) {
    f(i, a);
    f = g;
    g = h;
  }
}

function shapelessVarCalleeLoop(f0, g, h, a)
{
  var f = f0;
  for (var i = 0; i < 10; i++) {
    f(i, a);
    f = g;
    g = h;
  }
}

function shapelessLetCalleeLoop(f0, g, h, a)
{
  for (var i = 0; i < 10; i++) {
    let f = f0;
    f(i, a);
    f = g;
    g = h;
  }
}

function shapelessUnknownCalleeLoop(n, f, g, h, a)
{
  for (var i = 0; i < 10; i++) {
    (n || f)(i, a);
    f = g;
    g = h;
  }
}

function shapelessCalleeTest()
{
  var a = [];

  var helper = function (i, a) a[i] = i;
  shapelessArgCalleeLoop(helper, helper, function (i, a) a[i] = -i, a);

  helper = function (i, a) a[10 + i] = i;
  shapelessVarCalleeLoop(helper, helper, function (i, a) a[10 + i] = -i, a);

  helper = function (i, a) a[20 + i] = i;
  shapelessLetCalleeLoop(helper, helper, function (i, a) a[20 + i] = -i, a);

  helper = function (i, a) a[30 + i] = i;
  shapelessUnknownCalleeLoop(null, helper, helper, function (i, a) a[30 + i] = -i, a);

  try {
    helper = {hack: 42};
    shapelessUnknownCalleeLoop(null, helper, helper, helper, a);
  } catch (e) {
    if (e + "" != "TypeError: f is not a function")
      print("shapelessUnknownCalleeLoop: unexpected exception " + e);
  }
  return a.join("");
}
shapelessCalleeTest.expected = "01-2-3-4-5-6-7-8-901-2-3-4-5-6-7-8-9012345678901-2-3-4-5-6-7-8-9";
test(shapelessCalleeTest);

function typeofTest()
{
  var values = ["hi", "hi", "hi", null, 5, 5.1, true, undefined, /foo/, typeofTest, [], {}], types = [];
  for (var i = 0; i < values.length; i++)
    types[i] = typeof values[i];
  return types.toString();
}
typeofTest.expected = "string,string,string,object,number,number,boolean,undefined,object,function,object,object";
test(typeofTest);

function joinTest()
{
  var s = "";
  var a = [];
  for (var i = 0; i < 8; i++)
    a[i] = [String.fromCharCode(97 + i)];
  for (i = 0; i < 8; i++) {
    for (var j = 0; j < 8; j++)
      a[i][1 + j] = j;
  }
  for (i = 0; i < 8; i++)
    s += a[i].join(",");
  return s;
}
joinTest.expected = "a,0,1,2,3,4,5,6,7b,0,1,2,3,4,5,6,7c,0,1,2,3,4,5,6,7d,0,1,2,3,4,5,6,7e,0,1,2,3,4,5,6,7f,0,1,2,3,4,5,6,7g,0,1,2,3,4,5,6,7h,0,1,2,3,4,5,6,7";
test(joinTest);

function arity1(x)
{
  return (x == undefined) ? 1 : 0;
}
function missingArgTest() {
  var q;
  for (var i = 0; i < 10; i++) {
    q = arity1();
  }
  return q;
}
missingArgTest.expected = "1"
test(missingArgTest);

JSON = function () {
    return {
        stringify: function stringify(value, whitelist) {
            switch (typeof(value)) {
              case "object":
                return value.constructor.name;
            }
        }
    };
}();

function missingArgTest2() {
  var testPairs = [
    ["{}", {}],
    ["[]", []],
    ['{"foo":"bar"}', {"foo":"bar"}],
  ]

  var a = [];
  for (var i=0; i < testPairs.length; i++) {
    var s = JSON.stringify(testPairs[i][1])
    a[i] = s;
  }
  return a.join(",");
}
missingArgTest2.expected = "Object,Array,Object";
test(missingArgTest2);

function deepForInLoop() {
  // NB: the number of props set in C is arefully tuned to match HOTLOOP = 2.
  function C(){this.p = 1, this.q = 2}
  C.prototype = {p:1, q:2, r:3, s:4, t:5};
  var o = new C;
  var j = 0;
  var a = [];
  for (var i in o)
    a[j++] = i;
  return a.join("");
}
deepForInLoop.expected = "pqrst";
test(deepForInLoop);

function nestedExit(x) {
    var q = 0;
    for (var i = 0; i < 10; ++i)
    {
        if (x)
            ++q;
    }
}
function nestedExitLoop() {
    for (var j = 0; j < 10; ++j)
        nestedExit(j < 7);
    return "ok";
}
nestedExitLoop.expected = "ok";
test(nestedExitLoop);

function bitsinbyte(b) {
    var m = 1, c = 0;
    while(m<0x100) {
        if(b & m) c++;
        m <<= 1;
    }
    return 1;
}
function TimeFunc(func) {
    var x,y;
    for(var y=0; y<256; y++) func(y);
}
function nestedExit2() {
    TimeFunc(bitsinbyte);
    return "ok";
}
nestedExit2.expected = "ok";
test(nestedExit2);

function parsingNumbers() {
    var s1 = "123";
    var s1z = "123zzz";
    var s2 = "123.456";
    var s2z = "123.456zzz";

    var e1 = 123;
    var e2 = 123.456;

    var r1, r1z, r2, r2z;

    for (var i = 0; i < 10; i++) {
        r1 = parseInt(s1);
        r1z = parseInt(s1z);
        r2 = parseFloat(s2);
        r2z = parseFloat(s2z);
    }

    if (r1 == e1 && r1z == e1 && r2 == e2 && r2z == e2)
        return "ok";
    return "fail";
}
parsingNumbers.expected = "ok";
test(parsingNumbers);

function matchInLoop() {
    var k = "hi";
    for (var i = 0; i < 10; i++) {
        var result = k.match(/hi/) != null;
    }
    return result;
}
matchInLoop.expected = true;
test(matchInLoop);

function testMatchAsCondition() {
    var a = ['0', '0', '0', '0'];
    var r = /0/;
    "x".q;
    for (var z = 0; z < 4; z++)
        a[z].match(r) ? 1 : 2;
}
test(testMatchAsCondition);

function deep1(x) {
    if (x > 90)
       return 1;
    return 2;
}
function deep2() {
    for (var i = 0; i < 100; ++i)
        deep1(i);
    return "ok";
}
deep2.expected = "ok";
test(deep2);

function heavyFn1(i) { 
    if (i == 3) {
	var x = 3;
        return [0, i].map(function (i) i + x);
    }
    return [];
}
function testHeavy() {
    for (var i = 0; i <= 3; i++)
        heavyFn1(i);
}
test(testHeavy);

function heavyFn2(i) {
    if (i < 1000)
        return heavyFn1(i);
    return function () i;
}
function testHeavy2() {
    for (var i = 0; i <= 3; i++)
        heavyFn2(i);
}
test(testHeavy2);

var merge_type_maps_x = 0, merge_type_maps_y = 0;
function merge_type_maps() {
    for (merge_type_maps_x = 0; merge_type_maps_x < 50; ++merge_type_maps_x)
        if ((merge_type_maps_x & 1) == 1)
            ++merge_type_maps_y;
    return [merge_type_maps_x,merge_type_maps_y].join(",");
}
merge_type_maps.expected = "50,25";
test(merge_type_maps)

function inner_double_outer_int() {
    function f(i) {
        for (var m = 0; m < 20; ++m)
            for (var n = 0; n < 100; n += i)
                ;
        return n;
    }
    return f(.5);
}
inner_double_outer_int.expected = "100";
test(inner_double_outer_int);

function newArrayTest()
{
  var a = [];
  for (var i = 0; i < 10; i++)
    a[i] = new Array();
  return a.map(function(x) x.length).toString();
}
newArrayTest.expected="0,0,0,0,0,0,0,0,0,0";
test(newArrayTest);

function stringSplitTest()
{
  var s = "a,b"
  var a = null;
  for (var i = 0; i < 10; ++i)
    a = s.split(",");
  return a.join();
}
stringSplitTest.expected="a,b";
test(stringSplitTest);

function stringSplitIntoArrayTest()
{
  var s = "a,b"
  var a = [];
  for (var i = 0; i < 10; ++i)
    a[i] = s.split(",");
  return a.join();
}
stringSplitIntoArrayTest.expected="a,b,a,b,a,b,a,b,a,b,a,b,a,b,a,b,a,b,a,b";
test(stringSplitIntoArrayTest);

function forVarInWith() {
    function foo() ({notk:42});
    function bar() ({p:1, q:2, r:3, s:4, t:5});
    var o = foo();
    var a = [];
    with (o) {
        for (var k in bar())
            a[a.length] = k;
    }
    return a.join("");
}
forVarInWith.expected = "pqrst";
test(forVarInWith);

function inObjectTest() {
    var o = {p: 1, q: 2, r: 3, s: 4, t: 5};
    var r = 0;
    for (var i in o) {
        if (!(i in o))
            break;
        if ((i + i) in o)
            break;
        ++r;
    }
    return r;
}
inObjectTest.expected = 5;
test(inObjectTest);

function inArrayTest() {
    var a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
    for (var i = 0; i < a.length; i++) {
        if (!(i in a))
            break;
    }
    return i;
}
inArrayTest.expected = 10;
test(inArrayTest);

function innerLoopIntOuterDouble() {
    var n = 10000, i=0, j=0, count=0, limit=0;
    for (i = 1; i <= n; ++i) {
        limit = i * 1;
        for (j = 0; j < limit; ++j) {
            ++count;
        }
    }
    return "" + count;
}
innerLoopIntOuterDouble.expected="50005000";
test(innerLoopIntOuterDouble);

function outerline(){
    var i=0;
    var j=0;

    for (i = 3; i<= 100000; i+=2)
    {
        for (j = 3; j < 1000; j+=2)
        {
            if ((i & 1) == 1)
                break;
        }
    }
    return "ok";
}
outerline.expected="ok";
test(outerline);

function addAccumulations(f) {
  var a = f();
  var b = f();
  return a() + b();
}

function loopingAccumulator() {
  var x = 0;
  return function () {
    for (var i = 0; i < 10; ++i) {
      ++x;
    }
    return x;
  }
}

function testLoopingAccumulator() {
    var x = addAccumulations(loopingAccumulator);
    return x;
}
testLoopingAccumulator.expected = 20;
test(testLoopingAccumulator);

function testBranchingLoop() {
  var x = 0;
  for (var i=0; i < 100; ++i) {
    if (i == 51) {
      x += 10;
    }
    x++;
  }
  return x;
}
testBranchingLoop.expected = 110;
test(testBranchingLoop);

function testBranchingUnstableLoop() {
  var x = 0;
  for (var i=0; i < 100; ++i) {
    if (i == 51) {
      x += 10.1;
    }
    x++;
  }
  return x;
}
testBranchingUnstableLoop.expected = 110.1;
test(testBranchingUnstableLoop);

function testBranchingUnstableLoopCounter() {
  var x = 0;
  for (var i=0; i < 100; ++i) {
    if (i == 51) {
      i += 1.1;
    }
    x++;
  }
  return x;
}
testBranchingUnstableLoopCounter.expected = 99;
test(testBranchingUnstableLoopCounter);


function testBranchingUnstableObject() {
  var x = {s: "a"};
  var t = "";
  for (var i=0; i < 100; ++i) {
      if (i == 51)
      {
        x.s = 5;
      }
      t += x.s;
  }
  return t.length;
}
testBranchingUnstableObject.expected = 100;
test(testBranchingUnstableObject);

function testArrayDensityChange() {
  var x = [];
  var count = 0;
  for (var i=0; i < 100; ++i) {
    x[i] = "asdf";
  }
  for (var i=0; i < x.length; ++i) {
      if (i == 51)
      {
        x[199] = "asdf";
      }
      if (x[i])
        count += x[i].length;
  }
  return count;
}
testArrayDensityChange.expected = 404;
test(testArrayDensityChange);

function testDoubleToStr() {
    var x = 0.0;
    var y = 5.5;
    for (var i = 0; i < 200; i++) {
       x += parseFloat(y.toString());
    }
    return x;
}
testDoubleToStr.expected = 5.5*200;
test(testDoubleToStr);

function testNumberToString() {
    var x = new Number(0);
    for (var i = 0; i < 4; i++)
        x.toString();
}
test(testNumberToString);

function testDecayingInnerLoop() {
    var i, j, k = 10;
    for (i = 0; i < 5000; ++i) {
        for (j = 0; j < k; ++j);
            --k;
    }
    return i;
}
testDecayingInnerLoop.expected = 5000;
test(testDecayingInnerLoop);

function testContinue() {
    var i;
    var total = 0;
    for (i = 0; i < 20; ++i) {
        if (i == 11)
            continue;
        total++;
    }
    return total;
}
testContinue.expected = 19;
test(testContinue);

function testContinueWithLabel() {
    var i = 0;
    var j = 20;
    checkiandj :
    while (i < 10) {
        i += 1;
        checkj :
        while (j > 10) {
            j -= 1;
            if ((j % 2) == 0)
            continue checkj;
        }
    }
    return i + j;
}
testContinueWithLabel.expected = 20;
test(testContinueWithLabel);

function testDivision() {
    var a = 32768;
    var b;
    while (b !== 1) {
        b = a / 2;
        a = b;
    }
    return a;
}
testDivision.expected = 1;
test(testDivision);

function testDivisionFloat() {
    var a = 32768.0;
    var b;
    while (b !== 1) {
        b = a / 2.0;
        a = b;
    }
    return a === 1.0;
}
testDivisionFloat.expected = true;
test(testDivisionFloat);

function testToUpperToLower() {
    var s = "Hello", s1, s2;
    for (i = 0; i < 100; ++i) {
        s1 = s.toLowerCase();
        s2 = s.toUpperCase();
    }
    return s1 + s2;
}
testToUpperToLower.expected = "helloHELLO";
test(testToUpperToLower);

function testReplace2() {
    var s = "H e l l o", s1;
    for (i = 0; i < 100; ++i)
        s1 = s.replace(" ", "");
    return s1;
}
testReplace2.expected = "He l l o";
test(testReplace2);

function testBitwise() {
    var x = 10000;
    var y = 123456;
    var z = 987234;
    for (var i = 0; i < 50; i++) {
        x = x ^ y;
        y = y | z;
        z = ~x;
    }
    return x + y + z;
}
testBitwise.expected = -1298;
test(testBitwise);

function testSwitch() {
    var x = 0;
    var ret = 0;
    for (var i = 0; i < 100; ++i) {
        switch (x) {
            case 0:
                ret += 1;
                break;
            case 1:
                ret += 2;
                break;
            case 2:
                ret += 3;
                break;
            case 3:
                ret += 4;
                break;
            default:
                x = 0;
        }
        x++;
    }
    return ret;
}
testSwitch.expected = 226;
test(testSwitch);

function testSwitchString() {
    var x = "asdf";
    var ret = 0;
    for (var i = 0; i < 100; ++i) {
        switch (x) {
        case "asdf":
            x = "asd";
            ret += 1;
            break;
        case "asd":
            x = "as";
            ret += 2;
            break;
        case "as":
            x = "a";
            ret += 3;
            break;
        case "a":
            x = "foo";
            ret += 4;
            break;
        default:
            x = "asdf";
        }
    }
    return ret;
}
testSwitchString.expected = 200;
test(testSwitchString);

function testNegZero1Helper(z) {
    for (let j = 0; j < 5; ++j) { z = -z; }
    return Math.atan2(0, -0) == Math.atan2(0, z);
}

var testNegZero1 = function() { return testNegZero1Helper(0); }
testNegZero1.expected = true;
testNegZero1.name = 'testNegZero1';
testNegZero1Helper(1);
test(testNegZero1);

// No test case, just make sure this doesn't assert.
function testNegZero2() {
    var z = 0;
    for (let j = 0; j < 5; ++j) { ({p: (-z)}); }
}
testNegZero2();

function testConstSwitch() {
    var x;
    for (var j=0;j<5;++j) { switch(1.1) { case NaN: case 2: } x = 2; }
    return x;
}
testConstSwitch.expected = 2;
test(testConstSwitch);

function testConstSwitch2() {
    var x;
    for (var j = 0; j < 4; ++j) { switch(0/0) { } }
    return "ok";
}
testConstSwitch2.expected = "ok";
test(testConstSwitch2);

function testConstIf() {
    var x;
    for (var j=0;j<5;++j) { if (1.1 || 5) { } x = 2;}
    return x;
}
testConstIf.expected = 2;
test(testConstIf);

function testTypeofHole() {
  var a = new Array(6);
  a[5] = 3;
  for (var i = 0; i < 6; ++i)
    a[i] = typeof a[i];
  return a.join(",");
}
testTypeofHole.expected = "undefined,undefined,undefined,undefined,undefined,number"
test(testTypeofHole);

function testNativeLog() {
  var a = new Array(5);
  for (var i = 0; i < 5; i++) {
    a[i] = Math.log(Math.pow(Math.E, 10));
  }
  return a.join(",");
}
testNativeLog.expected = "10,10,10,10,10";
test(testNativeLog);

function test_JSOP_ARGSUB() {
    function f0() { return arguments[0]; }
    function f1() { return arguments[1]; }
    function f2() { return arguments[2]; }
    function f3() { return arguments[3]; }
    function f4() { return arguments[4]; }
    function f5() { return arguments[5]; }
    function f6() { return arguments[6]; }
    function f7() { return arguments[7]; }
    function f8() { return arguments[8]; }
    function f9() { return arguments[9]; }
    var a = [];
    for (var i = 0; i < 10; i++) {
        a[0] = f0('a');
        a[1] = f1('a','b');
        a[2] = f2('a','b','c');
        a[3] = f3('a','b','c','d');
        a[4] = f4('a','b','c','d','e');
        a[5] = f5('a','b','c','d','e','f');
        a[6] = f6('a','b','c','d','e','f','g');
        a[7] = f7('a','b','c','d','e','f','g','h');
        a[8] = f8('a','b','c','d','e','f','g','h','i');
        a[9] = f9('a','b','c','d','e','f','g','h','i','j');
    }
    return a.join("");
}
test_JSOP_ARGSUB.expected = "abcdefghij";
test(test_JSOP_ARGSUB);

function test_JSOP_ARGCNT() {
    function f0() { return arguments.length; }
    function f1() { return arguments.length; }
    function f2() { return arguments.length; }
    function f3() { return arguments.length; }
    function f4() { return arguments.length; }
    function f5() { return arguments.length; }
    function f6() { return arguments.length; }
    function f7() { return arguments.length; }
    function f8() { return arguments.length; }
    function f9() { return arguments.length; }
    var a = [];
    for (var i = 0; i < 10; i++) {
        a[0] = f0('a');
        a[1] = f1('a','b');
        a[2] = f2('a','b','c');
        a[3] = f3('a','b','c','d');
        a[4] = f4('a','b','c','d','e');
        a[5] = f5('a','b','c','d','e','f');
        a[6] = f6('a','b','c','d','e','f','g');
        a[7] = f7('a','b','c','d','e','f','g','h');
        a[8] = f8('a','b','c','d','e','f','g','h','i');
        a[9] = f9('a','b','c','d','e','f','g','h','i','j');
    }
    return a.join(",");
}
test_JSOP_ARGCNT.expected = "1,2,3,4,5,6,7,8,9,10";
test(test_JSOP_ARGCNT);

function testNativeMax() {
    var out = [], k;
    for (var i = 0; i < 5; ++i) {
        k = Math.max(k, i);
    }
    out.push(k);

    k = 0;
    for (var i = 0; i < 5; ++i) {
        k = Math.max(k, i);
    }
    out.push(k);

    for (var i = 0; i < 5; ++i) {
        k = Math.max(0, -0);
    }
    out.push((1 / k) < 0);
    return out.join(",");
}
testNativeMax.expected = "NaN,4,false";
test(testNativeMax);

function testFloatArrayIndex() {
    var a = [];
    for (var i = 0; i < 10; ++i) {
        a[3] = 5;
        a[3.5] = 7;
    }
    return a[3] + "," + a[3.5];
}
testFloatArrayIndex.expected = "5,7";
test(testFloatArrayIndex);

function testStrict() {
    var n = 10, a = [];
    for (var i = 0; i < 10; ++i) {
        a[0] = (n === 10);
        a[1] = (n !== 10);
        a[2] = (n === null);
        a[3] = (n == null);
    }
    return a.join(",");
}
testStrict.expected = "true,false,false,false";
test(testStrict);

function testSetPropNeitherMissNorHit() {
    for (var j = 0; j < 5; ++j) { if (({}).__proto__ = 1) { } }
    return "ok";
}
testSetPropNeitherMissNorHit.expected = "ok";
test(testSetPropNeitherMissNorHit);

function testPrimitiveConstructorPrototype() {
    var f = function(){};
    f.prototype = false;
    for (let j=0;j<5;++j) { new f; }
    return "ok";
}
testPrimitiveConstructorPrototype.expected = "ok";
test(testPrimitiveConstructorPrototype);

function testSideExitInConstructor() {
    var FCKConfig = {};
    FCKConfig.CoreStyles =
    {
        'Bold': { },
        'Italic': { },
        'FontFace': { },
        'Size' :
        {
        Overrides: [ ]
        },

        'Color' :
        {
        Element: '',
        Styles: {  },
        Overrides: [  ]
        },
        'BackColor': {
        Element : '',
        Styles : { 'background-color' : '' }
        },

    };
    var FCKStyle = function(A) {
        A.Element;
    };

    var pass = true;
    for (var s in FCKConfig.CoreStyles) {
        var x = new FCKStyle(FCKConfig.CoreStyles[s]);
        if (!x)
            pass = false;
    }
    return pass;
}
testSideExitInConstructor.expected = true;
test(testSideExitInConstructor);

function testNot() {
    var a = new Object(), b = null, c = "foo", d = "", e = 5, f = 0, g = 5.5, h = -0, i = true, j = false, k = undefined;
    var r;
    for (var i = 0; i < 10; ++i)
        r = [!a, !b, !c, !d, !e, !f, !g, !h, !i, !j, !k];
    return r.join(",");
}
testNot.expected = "false,true,false,true,false,true,false,true,false,true,true";
test(testNot);

function doTestDifferingArgc(a, b)
{
    var k = 0;
    for (var i = 0; i < 10; i++)
    {
        k += i;
    }
    return k;
}
function testDifferingArgc()
{
    var x = 0;
    x += doTestDifferingArgc(1, 2);
    x += doTestDifferingArgc(1);
    x += doTestDifferingArgc(1, 2, 3);
    return x;
}
testDifferingArgc.expected = 45*3;
test(testDifferingArgc);

function doTestMoreArgcThanNargs()
{
    var x = 0;
    for (var i = 0; i < 10; i++)
    {
        x = x + arguments[3];
    }
    return x;
}
function testMoreArgcThanNargs()
{
    return doTestMoreArgcThanNargs(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
}
testMoreArgcThanNargs.expected = 4*10;
test(testMoreArgcThanNargs);

// Test stack reconstruction after a nested exit
function testNestedExitStackInner(j, counter) {
  ++counter;
  var b = 0;
  for (var i = 1; i <= RUNLOOP; i++) {
    ++b;
    var a;
    // Make sure that once everything has been traced we suddenly switch to
    // a different control flow the first time we run the outermost tree,
    // triggering a side exit.
    if (j < RUNLOOP)
      a = 1;
    else
      a = 0;
    ++b;
    b += a;
  }
  return counter + b;
}
function testNestedExitStackOuter() {
  var counter = 0;
  for (var j = 1; j <= RUNLOOP; ++j) {
    for (var k = 1; k <= RUNLOOP; ++k) {
      counter = testNestedExitStackInner(j, counter);
    }
  }
  return counter;
}
testNestedExitStackOuter.expected = 81;
test(testNestedExitStackOuter);

function testHOTLOOPSize() {
    return HOTLOOP > 1;
}
testHOTLOOPSize.expected = true;
test(testHOTLOOPSize);

function testMatchStringObject() {
    var a = new String("foo");
    var b;
    for (i = 0; i < 300; i++)
        b = a.match(/bar/);
    return b;
}
testMatchStringObject.expected = null;
test(testMatchStringObject);

function innerSwitch(k)
{
    var m = 0;

    switch (k)
    {
    case 0:
        m = 1;
        break;
    }

    return m;
}
function testInnerSwitchBreak()
{
    var r = new Array(5);
    for (var i = 0; i < 5; i++)
    {
        r[i] = innerSwitch(0);
    }

    return r.join(",");
}
testInnerSwitchBreak.expected = "1,1,1,1,1";
test(testInnerSwitchBreak);

function testArrayNaNIndex()
{
    for (var j = 0; j < 4; ++j) { [this[NaN]]; }
    for (var j = 0; j < 5; ++j) { if([1][-0]) { } }
    return "ok";
}
testArrayNaNIndex.expected = "ok";
test(testArrayNaNIndex);

function innerTestInnerMissingArgs(a,b,c,d)
{
        if (a) {
        } else {
        }
}
function doTestInnerMissingArgs(k)
{
    for (i = 0; i < 10; i++) {
        innerTestInnerMissingArgs(k);
    }
}
function testInnerMissingArgs()
{
    doTestInnerMissingArgs(1);
    doTestInnerMissingArgs(0);
    return 1;
}
testInnerMissingArgs.expected = 1;  //Expected: that we don't crash.
test(testInnerMissingArgs);

function regexpLastIndex()
{
    var n = 0;
    var re = /hi/g;
    var ss = " hi hi hi hi hi hi hi hi hi hi";
    for (var i = 0; i < 10; i++) {
        // re.exec(ss);
        n += (re.lastIndex > 0) ? 3 : 0;
        re.lastIndex = 0;
    }
    return n;
}
regexpLastIndex.expected = 0; // 30;
test(regexpLastIndex);

function testHOTLOOPCorrectness() {
    var b = 0;
    for (var i = 0; i < HOTLOOP; ++i)
        ++b;
    return b;
}
testHOTLOOPCorrectness.expected = HOTLOOP;
testHOTLOOPCorrectness.jitstats = {
    recorderStarted: 1,
    recorderAborted: 0,
    traceTriggered: 0
};
// Change the global shape right before doing the test
this.testHOTLOOPCorrectnessVar = 1;
test(testHOTLOOPCorrectness);

function testRUNLOOPCorrectness() {
    var b = 0;
    for (var i = 0; i < RUNLOOP; ++i) {
    ++b;
    }
    return b;
}
testRUNLOOPCorrectness.expected = RUNLOOP;
testRUNLOOPCorrectness.jitstats = {
    recorderStarted: 1,
    recorderAborted: 0,
    traceTriggered: 1
};
// Change the global shape right before doing the test
this.testRUNLOOPCorrectnessVar = 1;
test(testRUNLOOPCorrectness);

function testDateNow() {
    // Accessing global.Date for the first time will change the global shape,
    // so do it before the loop starts; otherwise we have to loop an extra time
    // to pick things up.
    var time = Date.now();
    for (var j = 0; j < RUNLOOP; ++j)
        time = Date.now();
    return "ok";
}
testDateNow.expected = "ok";
testDateNow.jitstats = {
    recorderStarted: 1,
    recorderAborted: 0,
    traceTriggered: 1
};
test(testDateNow);

function testINITELEM()
{
    var x;
    for (var i = 0; i < 10; ++i)
        x = { 0: 5, 1: 5 };
    return x[0] + x[1];
}
testINITELEM.expected = 10;
test(testINITELEM);

function testUndefinedBooleanCmp()
{
    var t = true, f = false, x = [];
    for (var i = 0; i < 10; ++i) {
        x[0] = t == undefined;
        x[1] = t != undefined;
        x[2] = t === undefined;
        x[3] = t !== undefined;
        x[4] = t < undefined;
        x[5] = t > undefined;
        x[6] = t <= undefined;
        x[7] = t >= undefined;
        x[8] = f == undefined;
        x[9] = f != undefined;
        x[10] = f === undefined;
        x[11] = f !== undefined;
        x[12] = f < undefined;
        x[13] = f > undefined;
        x[14] = f <= undefined;
        x[15] = f >= undefined;
    }
    return x.join(",");
}
testUndefinedBooleanCmp.expected = "false,true,false,true,false,false,false,false,false,true,false,true,false,false,false,false";
test(testUndefinedBooleanCmp);

function testConstantBooleanExpr()
{
    for (var j = 0; j < 3; ++j) { if(true <= true) { } }
    return "ok";
}
testConstantBooleanExpr.expected = "ok";
test(testConstantBooleanExpr);

function testNegativeGETELEMIndex()
{
    for (let i=0;i<3;++i) /x/[-4];
    return "ok";
}
testNegativeGETELEMIndex.expected = "ok";
test(testNegativeGETELEMIndex);

function doTestInvalidCharCodeAt(input)
{
    var q = "";
    for (var i = 0; i < 10; i++)
       q += input.charCodeAt(i);
    return q;
}
function testInvalidCharCodeAt()
{
    return doTestInvalidCharCodeAt("");
}
testInvalidCharCodeAt.expected = "NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN";
test(testInvalidCharCodeAt);

function FPQuadCmp()
{
    for (let j = 0; j < 3; ++j) { true == 0; }
    return "ok";
}
FPQuadCmp.expected = "ok";
test(FPQuadCmp);

function testDestructuring() {
    var t = 0;
    for (var i = 0; i < HOTLOOP + 1; ++i) {
        var [r, g, b] = [1, 1, 1];
        t += r + g + b;
    }
    return t
}
testDestructuring.expected = (HOTLOOP + 1) * 3;
test(testDestructuring);

function loopWithUndefined1(t, val) {
    var a = new Array(6);
    for (var i = 0; i < 6; i++)
        a[i] = (t > val);
    return a;
}
loopWithUndefined1(5.0, 2);     //compile version with val=int

function testLoopWithUndefined1() {
    return loopWithUndefined1(5.0).join(",");  //val=undefined
};
testLoopWithUndefined1.expected = "false,false,false,false,false,false";
test(testLoopWithUndefined1);

function loopWithUndefined2(t, dostuff, val) {
    var a = new Array(6);
    for (var i = 0; i < 6; i++) {
        if (dostuff) {
            val = 1;
            a[i] = (t > val);
        } else {
            a[i] = (val == undefined);
        }
    }
    return a;
}
function testLoopWithUndefined2() {
    var a = loopWithUndefined2(5.0, true, 2);
    var b = loopWithUndefined2(5.0, true);
    var c = loopWithUndefined2(5.0, false, 8);
    var d = loopWithUndefined2(5.0, false);
    return [a[0], b[0], c[0], d[0]].join(",");
}
testLoopWithUndefined2.expected = "true,true,false,true";
test(testLoopWithUndefined2);

//test no multitrees assert
function testBug462388() {
    var c = 0, v; for each (let x in ["",v,v,v]) { for (c=0;c<4;++c) { } }
    return true;
}
testBug462388.expected = true;
test(testBug462388);

//test no multitrees assert
function testBug462407() {
    for each (let i in [0, {}, 0, 1.5, {}, 0, 1.5, 0, 0]) { }
    return true;
}
testBug462407.expected = true;
test(testBug462407);

//test no multitrees assert
function testBug463490() {
    function f(a, b, d) {
        for (var i = 0; i < 10; i++) {
            if (d)
                b /= 2;
        }
        return a + b;
    }
    //integer stable loop
    f(2, 2, false);
    //double stable loop
    f(3, 4.5, false);
    //integer unstable branch
    f(2, 2, true);
    return true;
};
testBug463490.expected = true;
test(testBug463490);

// Test no assert (bug 464089)
function shortRecursiveLoop(b, c) {
    for (var i = 0; i < c; i++) {
        if (b)
            shortRecursiveLoop(c - 1);
    }
}
function testClosingRecursion() {
    shortRecursiveLoop(false, 1);
    shortRecursiveLoop(true, 3);
    return true;
}
testClosingRecursion.expected = true;
test(testClosingRecursion);

// Test no assert or crash from outer recorders (bug 465145)
function testBug465145() {
    this.__defineSetter__("x", function(){});
    this.watch("x", function(){});
    y = this;
    for (var z = 0; z < 2; ++z) { x = y };
    this.__defineSetter__("x", function(){});
    for (var z = 0; z < 2; ++z) { x = y };
}

function testTrueShiftTrue() {
    var a = new Array(5);
    for (var i=0;i<5;++i) a[i] = "" + (true << true);
    return a.join(",");
}
testTrueShiftTrue.expected = "2,2,2,2,2";
test(testTrueShiftTrue);

// Test no assert or crash
function testBug465261() {
    for (let z = 0; z < 2; ++z) { for each (let x in [0, true, (void 0), 0, (void
    0)]) { if(x){} } };
    return true;
}
testBug465261.expected = true;
test(testBug465261);

function testBug465272() {
    var a = new Array(5);
    for (j=0;j<5;++j) a[j] = "" + ((5) - 2);
    return a.join(",");
}
testBug465272.expected = "3,3,3,3,3"
test(testBug465272);

function testBug465483() {
    var a = new Array(4);
    var c = 0;
    for each (i in [4, 'a', 'b', (void 0)]) a[c++] = '' + (i + i);
    return a.join(',');
}
testBug465483.expected = '8,aa,bb,NaN';
test(testBug465483);

function testNullCallee() {
    try {
        function f() {
            var x = new Array(5);
            for (var i = 0; i < 5; i++)
                x[i] = a[i].toString();
            return x.join(',');
        }
        f([[1],[2],[3],[4],[5]]);
        f([null, null, null, null, null]);
    } catch (e) {
        return true;
    }
    return false;
}
testNullCallee.expected = true;
test(testNullCallee);

//test no multitrees assert
function testBug466128() {
    for (let a = 0; a < 3; ++a) {
      for each (let b in [1, 2, "three", 4, 5, 6, 7, 8]) {
      }
    }
    return true;
}
testBug466128.expected = true;
test(testBug466128);

//test no assert
function testBug465688() {
    for each (let d in [-0x80000000, -0x80000000]) - -d;
    return true;
}
testBug465688.expected = true;
test(testBug465688);

//test no assert
function testBug466262() {
    var e = 1;
    for (var d = 0; d < 3; ++d) {
      if (d == 2) {
        e = "";
      }
    }
    return true;
}
testBug466262.expected = true;
test(testBug466262);

function testNewDate()
{
    // Accessing global.Date for the first time will change the global shape,
    // so do it before the loop starts; otherwise we have to loop an extra time
    // to pick things up.
    var start = new Date();
    var time = new Date();
    for (var j = 0; j < RUNLOOP; ++j)
        time = new Date();
    return time > 0 && time >= start;
}
testNewDate.expected = true;
testNewDate.jitstats = {
    recorderStarted: 1,
    recorderAborted: 0,
    traceTriggered: 1
};
test(testNewDate);

function testArrayPushPop() {
    var a = [], sum1 = 0, sum2 = 0;
    for (var i = 0; i < 10; ++i)
        sum1 += a.push(i);
    for (var i = 0; i < 10; ++i)
        sum2 += a.pop();
    a.push(sum1);
    a.push(sum2);
    return a.join(",");
}
testArrayPushPop.expected = "55,45";
test(testArrayPushPop);

function testSlowArrayPop() {
    var a = [];
    for (var i = 0; i < RUNLOOP; i++)
        a[i] = [0];
    a[RUNLOOP-1].__defineGetter__("0", function () { return 'xyzzy'; });

    var last;
    for (var i = 0; i < RUNLOOP; i++)
        last = a[i].pop();  // reenters interpreter in getter
    return last;
}
testSlowArrayPop.expected = 'xyzzy';
test(testSlowArrayPop);

// Same thing but it needs to reconstruct multiple stack frames (so,
// multiple functions called inside the loop)
function testSlowArrayPopMultiFrame() {    
    var a = [];
    for (var i = 0; i < RUNLOOP; i++)
        a[i] = [0];
    a[RUNLOOP-1].__defineGetter__("0", function () { return 23; });

    function child(a, i) {
        return a[i].pop();  // reenters interpreter in getter
    }
    function parent(a, i) {
        return child(a, i);
    }
    function gramps(a, i) { 
        return parent(a, i);
    }

    var last;
    for (var i = 0; i < RUNLOOP; i++)
        last = gramps(a, i);
    return last;
}
testSlowArrayPopMultiFrame.expected = 23;
test(testSlowArrayPopMultiFrame);

// Same thing but nested trees, each reconstructing one or more stack frames 
// (so, several functions with loops, such that the loops end up being
// nested though they are not lexically nested)

function testSlowArrayPopNestedTrees() {    
    var a = [];
    for (var i = 0; i < RUNLOOP; i++)
        a[i] = [0];
    a[RUNLOOP-1].__defineGetter__("0", function () { return 3.14159 });

    function child(a, i, j, k) {
        var last = 2.71828;
        for (var l = 0; l < RUNLOOP; l++)
            if (i == RUNLOOP-1 && j == RUNLOOP-1 && k == RUNLOOP-1)
                last = a[l].pop();  // reenters interpreter in getter
        return last;
    }
    function parent(a, i, j) {
        var last;
        for (var k = 0; k < RUNLOOP; k++)
            last = child(a, i, j, k);
        return last;
    }
    function gramps(a, i) { 
        var last;
        for (var j = 0; j < RUNLOOP; j++)
            last = parent(a, i, j);
        return last;
    }

    var last;
    for (var i = 0; i < RUNLOOP; i++)
        last = gramps(a, i);
    return last;
}
testSlowArrayPopNestedTrees.expected = 3.14159;
test(testSlowArrayPopNestedTrees);

function testResumeOp() {
    var a = [1,"2",3,"4",5,"6",7,"8",9,"10",11,"12",13,"14",15,"16"];
    var x = "";
    while (a.length > 0)
        x += a.pop();
    return x;
}
testResumeOp.expected = "16151413121110987654321";
test(testResumeOp);

function testUndefinedCmp() {
    var a = false;
    for (var j = 0; j < 4; ++j) { if (undefined < false) { a = true; } }
    return a;
}
testUndefinedCmp.expected = false;
test(testUndefinedCmp);

function reallyDeepNestedExit(schedule)
{
    var c = 0, j = 0;
    for (var i = 0; i < 5; i++) {
        for (j = 0; j < 4; j++) {
            c += (schedule[i*4 + j] == 1) ? 1 : 2;
        }
    }
    return c;
}
function testReallyDeepNestedExit()
{
    var c = 0;
    var schedule1 = new Array(5*4);
    var schedule2 = new Array(5*4);
    for (var i = 0; i < 5*4; i++) {
        schedule1[i] = 0;
        schedule2[i] = 0;
    }
    /**
     * First innermost compile: true branch runs through.
     * Second '': false branch compiles new loop edge.
     * First outer compile: expect true branch.
     * Second '': hit false branch.
     */
    schedule1[0*4 + 3] = 1;
    var schedules = [schedule1,
                     schedule2,
                     schedule1,
                     schedule2,
                     schedule2];

    for (var i = 0; i < 5; i++) {
        c += reallyDeepNestedExit(schedules[i]);
    }
    return c;
}
testReallyDeepNestedExit.expected = 198;
test(testReallyDeepNestedExit);

function testRegExpTest() {
    var r = /abc/;
    var flag = false;
    for (var i = 0; i < 10; ++i)
        flag = r.test("abc");
    return flag;
}
testRegExpTest.expected = true;
test(testRegExpTest);

function testNumToString() {
    var r = [];
    var d = 123456789;
    for (var i = 0; i < 10; ++i) {
        r = [
             d.toString(),
             (-d).toString(),
             d.toString(10),
             (-d).toString(10),
             d.toString(16),
             (-d).toString(16),
             d.toString(36),
             (-d).toString(36)
            ];
    }
    return r.join(",");
}
testNumToString.expected = "123456789,-123456789,123456789,-123456789,75bcd15,-75bcd15,21i3v9,-21i3v9";
test(testNumToString);

function testLongNumToString() {
    var s;
    for (var i = 0; i < 5; i++)
        s = (0x08000000).toString(2);
    return s;
}
testLongNumToString.expected = '1000000000000000000000000000';
test(testLongNumToString);

function testSubstring() {
    for (var i = 0; i < 5; ++i) {
        actual = "".substring(5);
    }
    return actual;
}
testSubstring.expected = "";
test(testSubstring);

function testForInLoopChangeIteratorType() {
    for(y in [0,1,2]) y = NaN;
    (function(){
        [].__proto__.u = void 0;
        for (let y in [5,6,7,8])
            y = NaN;
        delete [].__proto__.u;
    })()
    return "ok";
}
testForInLoopChangeIteratorType.expected = "ok";
test(testForInLoopChangeIteratorType);

function testGrowDenseArray() {
    var a = new Array();
    for (var i = 0; i < 10; ++i)
        a[i] |= 5;
    return a.join(",");
}
testGrowDenseArray.expected = "5,5,5,5,5,5,5,5,5,5";
test(testGrowDenseArray);

function testCallProtoMethod() {
    function X() { this.x = 1; }
    X.prototype.getName = function () { return "X"; }

    function Y() { this.x = 2; }
    Y.prototype.getName = function() "Y";

    var a = [new X, new X, new X, new X, new Y];
    var s = '';
    for (var i = 0; i < a.length; i++)
        s += a[i].getName();
    return s;
}
testCallProtoMethod.expected = 'XXXXY';
test(testCallProtoMethod);

function testTypeUnstableForIn() {
    var a = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16];
    var x = 0;
    for (var i in a) {
        i = parseInt(i);
        x++;
    }
    return x;
}
testTypeUnstableForIn.expected = 16;
test(testTypeUnstableForIn);

function testAddUndefined() {
    for (var j = 0; j < 3; ++j)
        (0 + void 0) && 0;
}
test(testAddUndefined);

function testStringify() {
    var t = true, f = false, u = undefined, n = 5, d = 5.5, s = "x";
    var a = [];
    for (var i = 0; i < 10; ++i) {
        a[0] = "" + t;
        a[1] = t + "";
        a[2] = "" + f;
        a[3] = f + "";
        a[4] = "" + u;
        a[5] = u + "";
        a[6] = "" + n;
        a[7] = n + "";
        a[8] = "" + d;
        a[9] = d + "";
        a[10] = "" + s;
        a[11] = s + "";
    }
    return a.join(",");
}
testStringify.expected = "true,true,false,false,undefined,undefined,5,5,5.5,5.5,x,x";
test(testStringify);

function testObjectToString() {
    var o = {toString: function()"foo"};
    var s = "";
    for (var i = 0; i < 10; i++)
        s += o;
    return s;
}
testObjectToString.expected = "foofoofoofoofoofoofoofoofoofoo";
test(testObjectToString);

function testObjectToNumber() {
    var o = {valueOf: function()-3};
    var x = 0;
    for (var i = 0; i < 10; i++)
        x -= o;
    return x;
}
testObjectToNumber.expected = 30;
test(testObjectToNumber);

function my_iterator_next() {
    if (this.i == 10) {
        this.i = 0;
        throw this.StopIteration;
    }
    return this.i++;
}
function testCustomIterator() {
    var o = {
        __iterator__: function () {
            return {
                i: 0,
                next: my_iterator_next,
                StopIteration: StopIteration
            };
        }
    };
    var a=[];
    for (var k = 0; k < 100; k += 10) {
        for(var j in o) {
            a[k + (j >> 0)] = j*k;
        }
    }
    return a.join();
}
testCustomIterator.expected = "0,0,0,0,0,0,0,0,0,0,0,10,20,30,40,50,60,70,80,90,0,20,40,60,80,100,120,140,160,180,0,30,60,90,120,150,180,210,240,270,0,40,80,120,160,200,240,280,320,360,0,50,100,150,200,250,300,350,400,450,0,60,120,180,240,300,360,420,480,540,0,70,140,210,280,350,420,490,560,630,0,80,160,240,320,400,480,560,640,720,0,90,180,270,360,450,540,630,720,810";
test(testCustomIterator);

function bug464403() {
    print(8);
    var u = [print, print, function(){}]
    for each (x in u) for (u.e in [1,1,1,1]);
    return "ok";
}
bug464403.expected = "ok";
test(bug464403);

function testBoxDoubleWithDoubleSizedInt()
{
  var i = 0;
  var a = new Array(3);

  while (i < a.length)
    a[i++] = 0x5a827999;
  return a.join(",");
}
testBoxDoubleWithDoubleSizedInt.expected = "1518500249,1518500249,1518500249";
test(testBoxDoubleWithDoubleSizedInt);

function testObjectOrderedCmp()
{
  var a = new Array(5);
  for(var i=0;i<5;++i) a[i] = ({} < {});
  return a.join(",");
}
testObjectOrderedCmp.expected = "false,false,false,false,false";
test(testObjectOrderedCmp);

function testObjectOrderedCmp2()
{
  var a = new Array(5);
  for(var i=0;i<5;++i) a[i] = ("" <= null);
  return a.join(",");
}
testObjectOrderedCmp2.expected = "true,true,true,true,true";
test(testObjectOrderedCmp2);

function testLogicalNotNaN() {
    var i = 0;
    var a = new Array(5);
    while (i < a.length)
        a[i++] = !NaN;
    return a.join();
}
testLogicalNotNaN.expected = "true,true,true,true,true";
test(testLogicalNotNaN);

function testStringToInt32() {
    var s = "";
    for (let j = 0; j < 5; ++j) s += ("1e+81" ^  3);
    return s;
}
testStringToInt32.expected = "33333";
test(testStringToInt32);

function testIn() {
    var array = [3];
    var obj = { "-1": 5, "1.7": 3, "foo": 5, "1": 7 };
    var a = [];
    for (let j = 0; j < 5; ++j) {
        a.push("0" in array);
        a.push(-1 in obj);
        a.push(1.7 in obj);
        a.push("foo" in obj);
        a.push(1 in obj);
        a.push("1" in array);
        a.push(-2 in obj);
        a.push(2.7 in obj);
        a.push("bar" in obj);
        a.push(2 in obj);
    }
    return a.join(",");
}
testIn.expected = "true,true,true,true,true,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false";
test(testIn);

function testBranchCse() {
    empty = [];
    out = [];
    for (var j=0;j<10;++j) { empty[42]; out.push((1 * (1)) | ""); }
    return out.join(",");
}
testBranchCse.expected = "1,1,1,1,1,1,1,1,1,1";
test(testBranchCse);

function testMulOverflow() {
    var a = [];
    for (let j=0;j<5;++j) a.push(0 | ((0x60000009) * 0x60000009));
    return a.join(",");
}
testMulOverflow.expected = "-1073741824,-1073741824,-1073741824,-1073741824,-1073741824";
test(testMulOverflow);

function testThinLoopDemote() {
    function f()
    {
        var k = 1;
        for (var n = 0; n < 4; n++) {
            k = (k * 10);
        }
        return k;
    }
    f();
    return f();
}
testThinLoopDemote.expected = 10000;
testThinLoopDemote.jitstats = {
    recorderStarted: 2,
    recorderAborted: 0,
    traceCompleted: 2,
    traceTriggered: 2,
    unstableLoopVariable: 1
};
test(testThinLoopDemote);

var global = this;
function testWeirdDateParseOuter()
{
    var vDateParts = ["11", "17", "2008"];
    var out = [];
    for (var vI = 0; vI < vDateParts.length; vI++)
        out.push(testWeirdDateParseInner(vDateParts[vI]));
    /* Mutate the global shape so we fall off trace; this causes
     * additional oddity */
    global.x = Math.random();
    return out;
}
function testWeirdDateParseInner(pVal)
{
    var vR = 0;
    for (var vI = 0; vI < pVal.length; vI++) {
        var vC = pVal.charAt(vI);
        if ((vC >= '0') && (vC <= '9'))
            vR = (vR * 10) + parseInt(vC);
    }
    return vR;
}
function testWeirdDateParse() {
    var result = [];
    result.push(testWeirdDateParseInner("11"));
    result.push(testWeirdDateParseInner("17"));
    result.push(testWeirdDateParseInner("2008"));
    result.push(testWeirdDateParseInner("11"));
    result.push(testWeirdDateParseInner("17"));
    result.push(testWeirdDateParseInner("2008"));
    result = result.concat(testWeirdDateParseOuter());
    result = result.concat(testWeirdDateParseOuter());
    result.push(testWeirdDateParseInner("11"));
    result.push(testWeirdDateParseInner("17"));
    result.push(testWeirdDateParseInner("2008"));
    return result.join(",");
}
testWeirdDateParse.expected = "11,17,2008,11,17,2008,11,17,2008,11,17,2008,11,17,2008";
testWeirdDateParse.jitstats = {
    recorderStarted: 8,
    recorderAborted: 1,
    traceCompleted: 7,
    traceTriggered: 14,
    unstableLoopVariable: 3,
    noCompatInnerTrees: 1
};
test(testWeirdDateParse);

function testUndemotableBinaryOp() {
    var out = [];
    for (let j = 0; j < 5; ++j) { out.push(6 - ((void 0) ^ 0x80000005)); }
    return out.join(",");
}
testUndemotableBinaryOp.expected = "2147483649,2147483649,2147483649,2147483649,2147483649";
test(testUndemotableBinaryOp);

function testNullRelCmp() {
    var out = [];
    for(j=0;j<3;++j) { out.push(3 > null); out.push(3 < null); out.push(0 == null); out.push(3 == null); }
    return out.join(",");
}
testNullRelCmp.expected = "true,false,false,false,true,false,false,false,true,false,false,false";
test(testNullRelCmp);

function testEqFalseEmptyString() {
    var x = [];
    for (var i=0;i<5;++i) x.push(false == "");
    return x.join(",");
}
testEqFalseEmptyString.expected = "true,true,true,true,true";
test(testEqFalseEmptyString);

function testIncDec2(ii) {
    var x = [];
    for (let j=0;j<5;++j) {
        ii=j;
        jj=j;
        var kk=j;
        x.push(ii--);
        x.push(jj--);
        x.push(kk--);
        x.push(++ii);
        x.push(++jj);
        x.push(++kk);
    }
    return x.join(",");
}
function testIncDec() {
    return testIncDec2(0);
}
testIncDec.expected = "0,0,0,0,0,0,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4";
test(testIncDec);

function testApply() {
    var q = [];
    for (var i = 0; i < 10; ++i)
        Array.prototype.push.apply(q, [5]);
    return q.join(",");
}
testApply.expected = "5,5,5,5,5,5,5,5,5,5";
test(testApply);

function testNestedForIn() {
    var a = {x: 1, y: 2, z: 3};
    var s = '';
    for (var p1 in a)
        for (var p2 in a)
            s += p1 + p2 + ' ';
    return s;
}
testNestedForIn.expected = 'xx xy xz yx yy yz zx zy zz ';
test(testNestedForIn);

function testForEach() {
    var r;
    var a = ["zero", "one", "two", "three"];
    for (var i = 0; i < RUNLOOP; i++) {
        r = "";
        for each (var s in a)
            r += s + " ";
    }
    return r;
}
testForEach.expected = "zero one two three ";
test(testForEach);

function testThinForEach() {
    var a = ["red"];
    var n = 0;
    for (var i = 0; i < 10; i++)
        for each (var v in a)
            if (v)
                n++;
    return n;
}
testThinForEach.expected = 10;
test(testThinForEach);

function testComparisons()
{
  // All the special values from each of the types in
  // ECMA-262, 3rd ed. section 8
  var undefinedType, nullType, booleanType, stringType, numberType, objectType;

  var types = [];
  types[undefinedType = 0] = "Undefined";
  types[nullType = 1] = "Null";
  types[booleanType = 2] = "Boolean";
  types[stringType = 3] = "String";
  types[numberType = 4] = "Number";
  types[objectType = 5] = "Object";

  var JSVAL_INT_MIN = -Math.pow(2, 30);
  var JSVAL_INT_MAX = Math.pow(2, 30) - 1;

  // Values from every ES3 type, hitting all the edge-case and special values
  // that can be dreamed up
  var values =
    {
     "undefined":
       {
         value: function() { return undefined; },
         type: undefinedType
       },
     "null":
       {
         value: function() { return null; },
         type: nullType
       },
     "true":
       {
         value: function() { return true; },
         type: booleanType
       },
     "false":
       {
         value: function() { return false; },
         type: booleanType
       },
     '""':
       {
         value: function() { return ""; },
         type: stringType
       },
     '"a"':
       {
         // a > [, for string-object comparisons
         value: function() { return "a"; },
         type: stringType
       },
     '"Z"':
       {
         // Z < [, for string-object comparisons
         value: function() { return "Z"; },
         type: stringType
       },
     "0":
       {
         value: function() { return 0; },
         type: numberType
       },
     "-0":
       {
         value: function() { return -0; },
         type: numberType
       },
     "1":
       {
         value: function() { return 1; },
         type: numberType
       },
     "Math.E":
       {
         value: function() { return Math.E; },
         type: numberType
       },
     "JSVAL_INT_MIN - 1":
       {
         value: function() { return JSVAL_INT_MIN - 1; },
         type: numberType
       },
     "JSVAL_INT_MIN":
       {
         value: function() { return JSVAL_INT_MIN; },
         type: numberType
       },
     "JSVAL_INT_MIN + 1":
       {
         value: function() { return JSVAL_INT_MIN + 1; },
         type: numberType
       },
     "JSVAL_INT_MAX - 1":
       {
         value: function() { return JSVAL_INT_MAX - 1; },
         type: numberType
       },
     "JSVAL_INT_MAX":
       {
         value: function() { return JSVAL_INT_MAX; },
         type: numberType
       },
     "JSVAL_INT_MAX + 1":
       {
         value: function() { return JSVAL_INT_MAX + 1; },
         type: numberType
       },
     "Infinity":
       {
         value: function() { return Infinity; },
         type: numberType
       },
     "-Infinity":
       {
         value: function() { return -Infinity; },
         type: numberType
       },
     "NaN":
       {
         value: function() { return NaN; },
         type: numberType
       },
     "{}":
       {
         value: function() { return {}; },
         type: objectType
       },
     "{ valueOf: undefined }":
       {
         value: function() { return { valueOf: undefined }; },
         type: objectType
       },
     "[]":
       {
         value: function() { return []; },
         type: objectType
       },
     '[""]':
       {
         value: function() { return [""]; },
         type: objectType
       },
     '["a"]':
       {
         value: function() { return ["a"]; },
         type: objectType
       },
     "[0]":
       {
         value: function() { return [0]; },
         type: objectType
       }
    };

  var orderOps =
    {
     "<": function(a, b) { return a < b; },
     ">": function(a, b) { return a > b; },
     "<=": function(a, b) { return a <= b; },
     ">=": function(a, b) { return a >= b; }
    };
  var eqOps =
    {
     "==": function(a, b) { return a == b; },
     "!=": function(a, b) { return a != b; },
     "===": function(a, b) { return a === b; },
     "!==": function(a, b) { return a !== b; }
    };


  var notEqualIncomparable =
    {
      eq: { "==": false, "!=": true, "===": false, "!==": true },
      order: { "<": false, ">": false, "<=": false, ">=": false }
    };
  var notEqualLessThan =
    {
      eq: { "==": false, "!=": true, "===": false, "!==": true },
      order: { "<": true, ">": false, "<=": true, ">=": false }
    };
  var notEqualGreaterThan =
    {
      eq: { "==": false, "!=": true, "===": false, "!==": true },
      order: { "<": false, ">": true, "<=": false, ">=": true }
    };
  var notEqualNorDifferent =
    {
      eq: { "==": false, "!=": true, "===": false, "!==": true },
      order: { "<": false, ">": false, "<=": true, ">=": true }
    };
  var strictlyEqual =
    {
      eq: { "==": true, "!=": false, "===": true, "!==": false },
      order: { "<": false, ">": false, "<=": true, ">=": true }
    };
  var looselyEqual =
    {
      eq: { "==": true, "!=": false, "===": false, "!==": true },
      order: { "<": false, ">": false, "<=": true, ">=": true }
    };
  var looselyEqualNotDifferent =
    {
      eq: { "==": true, "!=": false, "===": false, "!==": true },
      order: { "<": false, ">": false, "<=": true, ">=": true }
    };
  var looselyEqualIncomparable =
    {
      eq: { "==": true, "!=": false, "===": false, "!==": true },
      order: { "<": false, ">": false, "<=": false, ">=": false }
    };
  var strictlyEqualNotDifferent =
    {
      eq: { "==": true, "!=": false, "===": true, "!==": false },
      order: { "<": false, ">": false, "<=": true, ">=": true }
    };
  var strictlyEqualIncomparable =
    {
      eq: { "==": true, "!=": false, "===": true, "!==": false },
      order: { "<": false, ">": false, "<=": false, ">=": false }
    };

  var comparingZeroToSomething =
    {
      "undefined": notEqualIncomparable,
      "null": notEqualNorDifferent,
      "true": notEqualLessThan,
      "false": looselyEqual,
      '""': looselyEqualNotDifferent,
      '"a"': notEqualIncomparable,
      '"Z"': notEqualIncomparable,
      "0": strictlyEqual,
      "-0": strictlyEqual,
      "1": notEqualLessThan,
      "Math.E": notEqualLessThan,
      "JSVAL_INT_MIN - 1": notEqualGreaterThan,
      "JSVAL_INT_MIN": notEqualGreaterThan,
      "JSVAL_INT_MIN + 1": notEqualGreaterThan,
      "JSVAL_INT_MAX - 1": notEqualLessThan,
      "JSVAL_INT_MAX": notEqualLessThan,
      "JSVAL_INT_MAX + 1": notEqualLessThan,
      "Infinity": notEqualLessThan,
      "-Infinity": notEqualGreaterThan,
      "NaN": notEqualIncomparable,
      "{}": notEqualIncomparable,
      "{ valueOf: undefined }": notEqualIncomparable,
      "[]": looselyEqual,
      '[""]': looselyEqual,
      '["a"]': notEqualIncomparable,
      "[0]": looselyEqual
    };

  var comparingObjectOrObjectWithValueUndefined =
    {
      "undefined": notEqualIncomparable,
      "null": notEqualIncomparable,
      "true": notEqualIncomparable,
      "false": notEqualIncomparable,
      '""': notEqualGreaterThan,
      '"a"': notEqualLessThan,
      '"Z"': notEqualGreaterThan,
      "0": notEqualIncomparable,
      "-0": notEqualIncomparable,
      "1": notEqualIncomparable,
      "Math.E": notEqualIncomparable,
      "JSVAL_INT_MIN - 1": notEqualIncomparable,
      "JSVAL_INT_MIN": notEqualIncomparable,
      "JSVAL_INT_MIN + 1": notEqualIncomparable,
      "JSVAL_INT_MAX - 1": notEqualIncomparable,
      "JSVAL_INT_MAX": notEqualIncomparable,
      "JSVAL_INT_MAX + 1": notEqualIncomparable,
      "Infinity": notEqualIncomparable,
      "-Infinity": notEqualIncomparable,
      "NaN": notEqualIncomparable,
      "{}": notEqualNorDifferent,
      "{ valueOf: undefined }": notEqualNorDifferent,
      "[]": notEqualGreaterThan,
      '[""]': notEqualGreaterThan,
      '["a"]': notEqualLessThan,
      "[0]": notEqualGreaterThan
    };

  // Constructed expected-value matrix
  var expected =
    {
     "undefined":
       {
         "undefined": strictlyEqualIncomparable,
         "null": looselyEqualIncomparable,
         "true": notEqualIncomparable,
         "false": notEqualIncomparable,
         '""': notEqualIncomparable,
         '"a"': notEqualIncomparable,
         '"Z"': notEqualIncomparable,
         "0": notEqualIncomparable,
         "-0": notEqualIncomparable,
         "1": notEqualIncomparable,
         "Math.E": notEqualIncomparable,
         "JSVAL_INT_MIN - 1": notEqualIncomparable,
         "JSVAL_INT_MIN": notEqualIncomparable,
         "JSVAL_INT_MIN + 1": notEqualIncomparable,
         "JSVAL_INT_MAX - 1": notEqualIncomparable,
         "JSVAL_INT_MAX": notEqualIncomparable,
         "JSVAL_INT_MAX + 1": notEqualIncomparable,
         "Infinity": notEqualIncomparable,
         "-Infinity": notEqualIncomparable,
         "NaN": notEqualIncomparable,
         "{}": notEqualIncomparable,
         "{ valueOf: undefined }": notEqualIncomparable,
         "[]": notEqualIncomparable,
         '[""]': notEqualIncomparable,
         '["a"]': notEqualIncomparable,
         "[0]": notEqualIncomparable
       },
     "null":
       {
         "undefined": looselyEqualIncomparable,
         "null": strictlyEqualNotDifferent,
         "true": notEqualLessThan,
         "false": notEqualNorDifferent,
         '""': notEqualNorDifferent,
         '"a"': notEqualIncomparable,
         '"Z"': notEqualIncomparable,
         "0": notEqualNorDifferent,
         "-0": notEqualNorDifferent,
         "1": notEqualLessThan,
         "Math.E": notEqualLessThan,
         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
         "JSVAL_INT_MIN": notEqualGreaterThan,
         "JSVAL_INT_MIN + 1": notEqualGreaterThan,
         "JSVAL_INT_MAX - 1": notEqualLessThan,
         "JSVAL_INT_MAX": notEqualLessThan,
         "JSVAL_INT_MAX + 1": notEqualLessThan,
         "Infinity": notEqualLessThan,
         "-Infinity": notEqualGreaterThan,
         "NaN": notEqualIncomparable,
         "{}": notEqualIncomparable,
         "{ valueOf: undefined }": notEqualIncomparable,
         "[]": notEqualNorDifferent,
         '[""]': notEqualNorDifferent,
         '["a"]': notEqualIncomparable,
         "[0]": notEqualNorDifferent
       },
     "true":
       {
         "undefined": notEqualIncomparable,
         "null": notEqualGreaterThan,
         "true": strictlyEqual,
         "false": notEqualGreaterThan,
         '""': notEqualGreaterThan,
         '"a"': notEqualIncomparable,
         '"Z"': notEqualIncomparable,
         "0": notEqualGreaterThan,
         "-0": notEqualGreaterThan,
         "1": looselyEqual,
         "Math.E": notEqualLessThan,
         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
         "JSVAL_INT_MIN": notEqualGreaterThan,
         "JSVAL_INT_MIN + 1": notEqualGreaterThan,
         "JSVAL_INT_MAX - 1": notEqualLessThan,
         "JSVAL_INT_MAX": notEqualLessThan,
         "JSVAL_INT_MAX + 1": notEqualLessThan,
         "Infinity": notEqualLessThan,
         "-Infinity": notEqualGreaterThan,
         "NaN": notEqualIncomparable,
         "{}": notEqualIncomparable,
         "{ valueOf: undefined }": notEqualIncomparable,
         "[]": notEqualGreaterThan,
         '[""]': notEqualGreaterThan,
         '["a"]': notEqualIncomparable,
         "[0]": notEqualGreaterThan
       },
     "false":
       {
         "undefined": notEqualIncomparable,
         "null": notEqualNorDifferent,
         "true": notEqualLessThan,
         "false": strictlyEqual,
         '""': looselyEqualNotDifferent,
         '"a"': notEqualIncomparable,
         '"Z"': notEqualIncomparable,
         "0": looselyEqual,
         "-0": looselyEqual,
         "1": notEqualLessThan,
         "Math.E": notEqualLessThan,
         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
         "JSVAL_INT_MIN": notEqualGreaterThan,
         "JSVAL_INT_MIN + 1": notEqualGreaterThan,
         "JSVAL_INT_MAX - 1": notEqualLessThan,
         "JSVAL_INT_MAX": notEqualLessThan,
         "JSVAL_INT_MAX + 1": notEqualLessThan,
         "Infinity": notEqualLessThan,
         "-Infinity": notEqualGreaterThan,
         "NaN": notEqualIncomparable,
         "{}": notEqualIncomparable,
         "{ valueOf: undefined }": notEqualIncomparable,
         "[]": looselyEqual,
         '[""]': looselyEqual,
         '["a"]': notEqualIncomparable,
         "[0]": looselyEqual
       },
     '""':
       {
         "undefined": notEqualIncomparable,
         "null": notEqualNorDifferent,
         "true": notEqualLessThan,
         "false": looselyEqual,
         '""': strictlyEqual,
         '"a"': notEqualLessThan,
         '"Z"': notEqualLessThan,
         "0": looselyEqual,
         "-0": looselyEqual,
         "1": notEqualLessThan,
         "Math.E": notEqualLessThan,
         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
         "JSVAL_INT_MIN": notEqualGreaterThan,
         "JSVAL_INT_MIN + 1": notEqualGreaterThan,
         "JSVAL_INT_MAX - 1": notEqualLessThan,
         "JSVAL_INT_MAX": notEqualLessThan,
         "JSVAL_INT_MAX + 1": notEqualLessThan,
         "Infinity": notEqualLessThan,
         "-Infinity": notEqualGreaterThan,
         "NaN": notEqualIncomparable,
         "{}": notEqualLessThan,
         "{ valueOf: undefined }": notEqualLessThan,
         "[]": looselyEqual,
         '[""]': looselyEqual,
         '["a"]': notEqualLessThan,
         "[0]": notEqualLessThan
       },
     '"a"':
       {
         "undefined": notEqualIncomparable,
         "null": notEqualIncomparable,
         "true": notEqualIncomparable,
         "false": notEqualIncomparable,
         '""': notEqualGreaterThan,
         '"a"': strictlyEqual,
         '"Z"': notEqualGreaterThan,
         "0": notEqualIncomparable,
         "-0": notEqualIncomparable,
         "1": notEqualIncomparable,
         "Math.E": notEqualIncomparable,
         "JSVAL_INT_MIN - 1": notEqualIncomparable,
         "JSVAL_INT_MIN": notEqualIncomparable,
         "JSVAL_INT_MIN + 1": notEqualIncomparable,
         "JSVAL_INT_MAX - 1": notEqualIncomparable,
         "JSVAL_INT_MAX": notEqualIncomparable,
         "JSVAL_INT_MAX + 1": notEqualIncomparable,
         "Infinity": notEqualIncomparable,
         "-Infinity": notEqualIncomparable,
         "NaN": notEqualIncomparable,
         "{}": notEqualGreaterThan,
         "{ valueOf: undefined }": notEqualGreaterThan,
         "[]": notEqualGreaterThan,
         '[""]': notEqualGreaterThan,
         '["a"]': looselyEqualNotDifferent,
         "[0]": notEqualGreaterThan
       },
     '"Z"':
       {
         "undefined": notEqualIncomparable,
         "null": notEqualIncomparable,
         "true": notEqualIncomparable,
         "false": notEqualIncomparable,
         '""': notEqualGreaterThan,
         '"a"': notEqualLessThan,
         '"Z"': strictlyEqual,
         "0": notEqualIncomparable,
         "-0": notEqualIncomparable,
         "1": notEqualIncomparable,
         "Math.E": notEqualIncomparable,
         "JSVAL_INT_MIN - 1": notEqualIncomparable,
         "JSVAL_INT_MIN": notEqualIncomparable,
         "JSVAL_INT_MIN + 1": notEqualIncomparable,
         "JSVAL_INT_MAX - 1": notEqualIncomparable,
         "JSVAL_INT_MAX": notEqualIncomparable,
         "JSVAL_INT_MAX + 1": notEqualIncomparable,
         "Infinity": notEqualIncomparable,
         "-Infinity": notEqualIncomparable,
         "NaN": notEqualIncomparable,
         "{}": notEqualLessThan,
         "{ valueOf: undefined }": notEqualLessThan,
         "[]": notEqualGreaterThan,
         '[""]': notEqualGreaterThan,
         '["a"]': notEqualLessThan,
         "[0]": notEqualGreaterThan
       },
     "0": comparingZeroToSomething,
     "-0": comparingZeroToSomething,
     "1":
       {
         "undefined": notEqualIncomparable,
         "null": notEqualGreaterThan,
         "true": looselyEqual,
         "false": notEqualGreaterThan,
         '""': notEqualGreaterThan,
         '"a"': notEqualIncomparable,
         '"Z"': notEqualIncomparable,
         "0": notEqualGreaterThan,
         "-0": notEqualGreaterThan,
         "1": strictlyEqual,
         "Math.E": notEqualLessThan,
         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
         "JSVAL_INT_MIN": notEqualGreaterThan,
         "JSVAL_INT_MIN + 1": notEqualGreaterThan,
         "JSVAL_INT_MAX - 1": notEqualLessThan,
         "JSVAL_INT_MAX": notEqualLessThan,
         "JSVAL_INT_MAX + 1": notEqualLessThan,
         "Infinity": notEqualLessThan,
         "-Infinity": notEqualGreaterThan,
         "NaN": notEqualIncomparable,
         "{}": notEqualIncomparable,
         "{ valueOf: undefined }": notEqualIncomparable,
         "[]": notEqualGreaterThan,
         '[""]': notEqualGreaterThan,
         '["a"]': notEqualIncomparable,
         "[0]": notEqualGreaterThan
       },
     "Math.E":
       {
         "undefined": notEqualIncomparable,
         "null": notEqualGreaterThan,
         "true": notEqualGreaterThan,
         "false": notEqualGreaterThan,
         '""': notEqualGreaterThan,
         '"a"': notEqualIncomparable,
         '"Z"': notEqualIncomparable,
         "0": notEqualGreaterThan,
         "-0": notEqualGreaterThan,
         "1": notEqualGreaterThan,
         "Math.E": strictlyEqual,
         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
         "JSVAL_INT_MIN": notEqualGreaterThan,
         "JSVAL_INT_MIN + 1": notEqualGreaterThan,
         "JSVAL_INT_MAX - 1": notEqualLessThan,
         "JSVAL_INT_MAX": notEqualLessThan,
         "JSVAL_INT_MAX + 1": notEqualLessThan,
         "Infinity": notEqualLessThan,
         "-Infinity": notEqualGreaterThan,
         "NaN": notEqualIncomparable,
         "{}": notEqualIncomparable,
         "{ valueOf: undefined }": notEqualIncomparable,
         "[]": notEqualGreaterThan,
         '[""]': notEqualGreaterThan,
         '["a"]': notEqualIncomparable,
         "[0]": notEqualGreaterThan
       },
     "JSVAL_INT_MIN - 1":
       {
         "undefined": notEqualIncomparable,
         "null": notEqualLessThan,
         "true": notEqualLessThan,
         "false": notEqualLessThan,
         '""': notEqualLessThan,
         '"a"': notEqualIncomparable,
         '"Z"': notEqualIncomparable,
         "0": notEqualLessThan,
         "-0": notEqualLessThan,
         "1": notEqualLessThan,
         "Math.E": notEqualLessThan,
         "JSVAL_INT_MIN - 1": strictlyEqual,
         "JSVAL_INT_MIN": notEqualLessThan,
         "JSVAL_INT_MIN + 1": notEqualLessThan,
         "JSVAL_INT_MAX - 1": notEqualLessThan,
         "JSVAL_INT_MAX": notEqualLessThan,
         "JSVAL_INT_MAX + 1": notEqualLessThan,
         "Infinity": notEqualLessThan,
         "-Infinity": notEqualGreaterThan,
         "NaN": notEqualIncomparable,
         "{}": notEqualIncomparable,
         "{ valueOf: undefined }": notEqualIncomparable,
         "[]": notEqualLessThan,
         '[""]': notEqualLessThan,
         '["a"]': notEqualIncomparable,
         "[0]": notEqualLessThan
       },
     "JSVAL_INT_MIN":
       {
         "undefined": notEqualIncomparable,
         "null": notEqualLessThan,
         "true": notEqualLessThan,
         "false": notEqualLessThan,
         '""': notEqualLessThan,
         '"a"': notEqualIncomparable,
         '"Z"': notEqualIncomparable,
         "0": notEqualLessThan,
         "-0": notEqualLessThan,
         "1": notEqualLessThan,
         "Math.E": notEqualLessThan,
         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
         "JSVAL_INT_MIN": strictlyEqual,
         "JSVAL_INT_MIN + 1": notEqualLessThan,
         "JSVAL_INT_MAX - 1": notEqualLessThan,
         "JSVAL_INT_MAX": notEqualLessThan,
         "JSVAL_INT_MAX + 1": notEqualLessThan,
         "Infinity": notEqualLessThan,
         "-Infinity": notEqualGreaterThan,
         "NaN": notEqualIncomparable,
         "{}": notEqualIncomparable,
         "{ valueOf: undefined }": notEqualIncomparable,
         "[]": notEqualLessThan,
         '[""]': notEqualLessThan,
         '["a"]': notEqualIncomparable,
         "[0]": notEqualLessThan
       },
     "JSVAL_INT_MIN + 1":
       {
         "undefined": notEqualIncomparable,
         "null": notEqualLessThan,
         "true": notEqualLessThan,
         "false": notEqualLessThan,
         '""': notEqualLessThan,
         '"a"': notEqualIncomparable,
         '"Z"': notEqualIncomparable,
         "0": notEqualLessThan,
         "-0": notEqualLessThan,
         "1": notEqualLessThan,
         "Math.E": notEqualLessThan,
         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
         "JSVAL_INT_MIN": notEqualGreaterThan,
         "JSVAL_INT_MIN + 1": strictlyEqual,
         "JSVAL_INT_MAX - 1": notEqualLessThan,
         "JSVAL_INT_MAX": notEqualLessThan,
         "JSVAL_INT_MAX + 1": notEqualLessThan,
         "Infinity": notEqualLessThan,
         "-Infinity": notEqualGreaterThan,
         "NaN": notEqualIncomparable,
         "{}": notEqualIncomparable,
         "{ valueOf: undefined }": notEqualIncomparable,
         "[]": notEqualLessThan,
         '[""]': notEqualLessThan,
         '["a"]': notEqualIncomparable,
         "[0]": notEqualLessThan
       },
     "JSVAL_INT_MAX - 1":
       {
         "undefined": notEqualIncomparable,
         "null": notEqualGreaterThan,
         "true": notEqualGreaterThan,
         "false": notEqualGreaterThan,
         '""': notEqualGreaterThan,
         '"a"': notEqualIncomparable,
         '"Z"': notEqualIncomparable,
         "0": notEqualGreaterThan,
         "-0": notEqualGreaterThan,
         "1": notEqualGreaterThan,
         "Math.E": notEqualGreaterThan,
         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
         "JSVAL_INT_MIN": notEqualGreaterThan,
         "JSVAL_INT_MIN + 1": notEqualGreaterThan,
         "JSVAL_INT_MAX - 1": strictlyEqual,
         "JSVAL_INT_MAX": notEqualLessThan,
         "JSVAL_INT_MAX + 1": notEqualLessThan,
         "Infinity": notEqualLessThan,
         "-Infinity": notEqualGreaterThan,
         "NaN": notEqualIncomparable,
         "{}": notEqualIncomparable,
         "{ valueOf: undefined }": notEqualIncomparable,
         "[]": notEqualGreaterThan,
         '[""]': notEqualGreaterThan,
         '["a"]': notEqualIncomparable,
         "[0]": notEqualGreaterThan
       },
     "JSVAL_INT_MAX":
       {
         "undefined": notEqualIncomparable,
         "null": notEqualGreaterThan,
         "true": notEqualGreaterThan,
         "false": notEqualGreaterThan,
         '""': notEqualGreaterThan,
         '"a"': notEqualIncomparable,
         '"Z"': notEqualIncomparable,
         "0": notEqualGreaterThan,
         "-0": notEqualGreaterThan,
         "1": notEqualGreaterThan,
         "Math.E": notEqualGreaterThan,
         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
         "JSVAL_INT_MIN": notEqualGreaterThan,
         "JSVAL_INT_MIN + 1": notEqualGreaterThan,
         "JSVAL_INT_MAX - 1": notEqualGreaterThan,
         "JSVAL_INT_MAX": strictlyEqual,
         "JSVAL_INT_MAX + 1": notEqualLessThan,
         "Infinity": notEqualLessThan,
         "-Infinity": notEqualGreaterThan,
         "NaN": notEqualIncomparable,
         "{}": notEqualIncomparable,
         "{ valueOf: undefined }": notEqualIncomparable,
         "[]": notEqualGreaterThan,
         '[""]': notEqualGreaterThan,
         '["a"]': notEqualIncomparable,
         "[0]": notEqualGreaterThan
       },
     "JSVAL_INT_MAX + 1":
       {
         "undefined": notEqualIncomparable,
         "null": notEqualGreaterThan,
         "true": notEqualGreaterThan,
         "false": notEqualGreaterThan,
         '""': notEqualGreaterThan,
         '"a"': notEqualIncomparable,
         '"Z"': notEqualIncomparable,
         "0": notEqualGreaterThan,
         "-0": notEqualGreaterThan,
         "1": notEqualGreaterThan,
         "Math.E": notEqualGreaterThan,
         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
         "JSVAL_INT_MIN": notEqualGreaterThan,
         "JSVAL_INT_MIN + 1": notEqualGreaterThan,
         "JSVAL_INT_MAX - 1": notEqualGreaterThan,
         "JSVAL_INT_MAX": notEqualGreaterThan,
         "JSVAL_INT_MAX + 1": strictlyEqual,
         "Infinity": notEqualLessThan,
         "-Infinity": notEqualGreaterThan,
         "NaN": notEqualIncomparable,
         "{}": notEqualIncomparable,
         "{ valueOf: undefined }": notEqualIncomparable,
         "[]": notEqualGreaterThan,
         '[""]': notEqualGreaterThan,
         '["a"]': notEqualIncomparable,
         "[0]": notEqualGreaterThan
       },
     "Infinity":
       {
         "undefined": notEqualIncomparable,
         "null": notEqualGreaterThan,
         "true": notEqualGreaterThan,
         "false": notEqualGreaterThan,
         '""': notEqualGreaterThan,
         '"a"': notEqualIncomparable,
         '"Z"': notEqualIncomparable,
         "0": notEqualGreaterThan,
         "-0": notEqualGreaterThan,
         "1": notEqualGreaterThan,
         "Math.E": notEqualGreaterThan,
         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
         "JSVAL_INT_MIN": notEqualGreaterThan,
         "JSVAL_INT_MIN + 1": notEqualGreaterThan,
         "JSVAL_INT_MAX - 1": notEqualGreaterThan,
         "JSVAL_INT_MAX": notEqualGreaterThan,
         "JSVAL_INT_MAX + 1": notEqualGreaterThan,
         "Infinity": strictlyEqual,
         "-Infinity": notEqualGreaterThan,
         "NaN": notEqualIncomparable,
         "{}": notEqualIncomparable,
         "{ valueOf: undefined }": notEqualIncomparable,
         "[]": notEqualGreaterThan,
         '[""]': notEqualGreaterThan,
         '["a"]': notEqualIncomparable,
         "[0]": notEqualGreaterThan
       },
     "-Infinity":
       {
         "undefined": notEqualIncomparable,
         "null": notEqualLessThan,
         "true": notEqualLessThan,
         "false": notEqualLessThan,
         '""': notEqualLessThan,
         '"a"': notEqualIncomparable,
         '"Z"': notEqualIncomparable,
         "0": notEqualLessThan,
         "-0": notEqualLessThan,
         "1": notEqualLessThan,
         "Math.E": notEqualLessThan,
         "JSVAL_INT_MIN - 1": notEqualLessThan,
         "JSVAL_INT_MIN": notEqualLessThan,
         "JSVAL_INT_MIN + 1": notEqualLessThan,
         "JSVAL_INT_MAX - 1": notEqualLessThan,
         "JSVAL_INT_MAX": notEqualLessThan,
         "JSVAL_INT_MAX + 1": notEqualLessThan,
         "Infinity": notEqualLessThan,
         "-Infinity": strictlyEqual,
         "NaN": notEqualIncomparable,
         "{}": notEqualIncomparable,
         "{ valueOf: undefined }": notEqualIncomparable,
         "[]": notEqualLessThan,
         '[""]': notEqualLessThan,
         '["a"]': notEqualIncomparable,
         "[0]": notEqualLessThan
       },
     "NaN":
       {
         "undefined": notEqualIncomparable,
         "null": notEqualIncomparable,
         "true": notEqualIncomparable,
         "false": notEqualIncomparable,
         '""': notEqualIncomparable,
         '"a"': notEqualIncomparable,
         '"Z"': notEqualIncomparable,
         "0": notEqualIncomparable,
         "-0": notEqualIncomparable,
         "1": notEqualIncomparable,
         "Math.E": notEqualIncomparable,
         "JSVAL_INT_MIN - 1": notEqualIncomparable,
         "JSVAL_INT_MIN": notEqualIncomparable,
         "JSVAL_INT_MIN + 1": notEqualIncomparable,
         "JSVAL_INT_MAX - 1": notEqualIncomparable,
         "JSVAL_INT_MAX": notEqualIncomparable,
         "JSVAL_INT_MAX + 1": notEqualIncomparable,
         "Infinity": notEqualIncomparable,
         "-Infinity": notEqualIncomparable,
         "NaN": notEqualIncomparable,
         "{}": notEqualIncomparable,
         "{ valueOf: undefined }": notEqualIncomparable,
         "[]": notEqualIncomparable,
         '[""]': notEqualIncomparable,
         '["a"]': notEqualIncomparable,
         "[0]": notEqualIncomparable
       },
     "{}": comparingObjectOrObjectWithValueUndefined,
     "{ valueOf: undefined }": comparingObjectOrObjectWithValueUndefined,
     "[]":
       {
         "undefined": notEqualIncomparable,
         "null": notEqualNorDifferent,
         "true": notEqualLessThan,
         "false": looselyEqual,
         '""': looselyEqual,
         '"a"': notEqualLessThan,
         '"Z"': notEqualLessThan,
         "0": looselyEqual,
         "-0": looselyEqual,
         "1": notEqualLessThan,
         "Math.E": notEqualLessThan,
         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
         "JSVAL_INT_MIN": notEqualGreaterThan,
         "JSVAL_INT_MIN + 1": notEqualGreaterThan,
         "JSVAL_INT_MAX - 1": notEqualLessThan,
         "JSVAL_INT_MAX": notEqualLessThan,
         "JSVAL_INT_MAX + 1": notEqualLessThan,
         "Infinity": notEqualLessThan,
         "-Infinity": notEqualGreaterThan,
         "NaN": notEqualIncomparable,
         "{}": notEqualLessThan,
         "{ valueOf: undefined }": notEqualLessThan,
         "[]": notEqualNorDifferent,
         '[""]': notEqualNorDifferent,
         '["a"]': notEqualLessThan,
         "[0]": notEqualLessThan
       },
     '[""]':
       {
         "undefined": notEqualIncomparable,
         "null": notEqualNorDifferent,
         "true": notEqualLessThan,
         "false": looselyEqual,
         '""': looselyEqual,
         '"a"': notEqualLessThan,
         '"Z"': notEqualLessThan,
         "0": looselyEqual,
         "-0": looselyEqual,
         "1": notEqualLessThan,
         "Math.E": notEqualLessThan,
         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
         "JSVAL_INT_MIN": notEqualGreaterThan,
         "JSVAL_INT_MIN + 1": notEqualGreaterThan,
         "JSVAL_INT_MAX - 1": notEqualLessThan,
         "JSVAL_INT_MAX": notEqualLessThan,
         "JSVAL_INT_MAX + 1": notEqualLessThan,
         "Infinity": notEqualLessThan,
         "-Infinity": notEqualGreaterThan,
         "NaN": notEqualIncomparable,
         "{}": notEqualLessThan,
         "{ valueOf: undefined }": notEqualLessThan,
         "[]": notEqualNorDifferent,
         '[""]': notEqualNorDifferent,
         '["a"]': notEqualLessThan,
         "[0]": notEqualLessThan
       },
     '["a"]':
       {
         "undefined": notEqualIncomparable,
         "null": notEqualIncomparable,
         "true": notEqualIncomparable,
         "false": notEqualIncomparable,
         '""': notEqualGreaterThan,
         '"a"': looselyEqual,
         '"Z"': notEqualGreaterThan,
         "0": notEqualIncomparable,
         "-0": notEqualIncomparable,
         "1": notEqualIncomparable,
         "Math.E": notEqualIncomparable,
         "JSVAL_INT_MIN - 1": notEqualIncomparable,
         "JSVAL_INT_MIN": notEqualIncomparable,
         "JSVAL_INT_MIN + 1": notEqualIncomparable,
         "JSVAL_INT_MAX - 1": notEqualIncomparable,
         "JSVAL_INT_MAX": notEqualIncomparable,
         "JSVAL_INT_MAX + 1": notEqualIncomparable,
         "Infinity": notEqualIncomparable,
         "-Infinity": notEqualIncomparable,
         "NaN": notEqualIncomparable,
         "{}": notEqualGreaterThan,
         "{ valueOf: undefined }": notEqualGreaterThan,
         "[]": notEqualGreaterThan,
         '[""]': notEqualGreaterThan,
         '["a"]': notEqualNorDifferent,
         "[0]": notEqualGreaterThan
       },
     "[0]":
       {
         "undefined": notEqualIncomparable,
         "null": notEqualNorDifferent,
         "true": notEqualLessThan,
         "false": looselyEqual,
         '""': notEqualGreaterThan,
         '"a"': notEqualLessThan,
         '"Z"': notEqualLessThan,
         "0": looselyEqual,
         "-0": looselyEqual,
         "1": notEqualLessThan,
         "Math.E": notEqualLessThan,
         "JSVAL_INT_MIN - 1": notEqualGreaterThan,
         "JSVAL_INT_MIN": notEqualGreaterThan,
         "JSVAL_INT_MIN + 1": notEqualGreaterThan,
         "JSVAL_INT_MAX - 1": notEqualLessThan,
         "JSVAL_INT_MAX": notEqualLessThan,
         "JSVAL_INT_MAX + 1": notEqualLessThan,
         "Infinity": notEqualLessThan,
         "-Infinity": notEqualGreaterThan,
         "NaN": notEqualIncomparable,
         "{}": notEqualLessThan,
         "{ valueOf: undefined }": notEqualLessThan,
         "[]": notEqualGreaterThan,
         '[""]': notEqualGreaterThan,
         '["a"]': notEqualLessThan,
         "[0]": notEqualNorDifferent
       }
    };



  var failures = [];
  function fail(a, ta, b, tb, ex, ac, op)
  {
    failures.push("(" + a + " " + op + " " + b + ") wrong: " +
                  "expected " + ex + ", got " + ac +
                  " (types " + types[ta] + ", " + types[tb] + ")");
  }

  var result = false;
  for (var i in values)
  {
    for (var j in values)
    {
      // Constants, so hoist to help JIT know that
      var vala = values[i], valb = values[j];
      var a = vala.value(), b = valb.value();

      for (var opname in orderOps)
      {
        var op = orderOps[opname];
        var expect = expected[i][j].order[opname];
        var failed = false;

        for (var iter = 0; iter < 5; iter++)
        {
          result = op(a, b);
          failed = failed || result !== expect;
        }

        if (failed)
          fail(i, vala.type, j, valb.type, expect, result, opname);
      }

      for (var opname in eqOps)
      {
        var op = eqOps[opname];
        var expect = expected[i][j].eq[opname];
        var failed = false;

        for (var iter = 0; iter < 5; iter++)
        {
          result = op(a, b);
          failed = failed || result !== expect;
        }

        if (failed)
          fail(i, vala.type, j, valb.type, expect, result, opname);
      }
    }
  }

  if (failures.length == 0)
    return "no failures reported!";

  return "\n" + failures.join(",\n");
}
testComparisons.expected = "no failures reported!";
test(testComparisons);

function testCaseAbort()
{
  var four = "4";
  var r = 0;
  for (var i = 0; i < 5; i++)
  {
    switch (i)
    {
      case four: r += 1; break;
      default: r += 2; break;
    }
  }

  return "" + r;
}
testCaseAbort.expected = "10";
testCaseAbort.jitstats = {
  recorderAborted: 0
};
test(testCaseAbort);

function testApplyCallHelper(f) {
    var r = [];
    for (var i = 0; i < 10; ++i) f.call();
    r.push(x);
    for (var i = 0; i < 10; ++i) f.call(this);
    r.push(x);
    for (var i = 0; i < 10; ++i) f.apply(this);
    r.push(x);
    for (var i = 0; i < 10; ++i) f.call(this,0);
    r.push(x);
    for (var i = 0; i < 10; ++i) f.apply(this,[0]);
    r.push(x);
    for (var i = 0; i < 10; ++i) f.call(this,0,1);
    r.push(x);
    for (var i = 0; i < 10; ++i) f.apply(this,[0,1]);
    r.push(x);
    for (var i = 0; i < 10; ++i) f.call(this,0,1,2);
    r.push(x);
    for (var i = 0; i < 10; ++i) f.apply(this,[0,1,2]);
    r.push(x);
    for (var i = 0; i < 10; ++i) f.call(this,0,1,2,3);
    r.push(x);
    for (var i = 0; i < 10; ++i) f.apply(this,[0,1,2,3]);
    r.push(x);
    for (var i = 0; i < 10; ++i) f.call(this,0,1,2,3,4);
    r.push(x);
    for (var i = 0; i < 10; ++i) f.apply(this,[0,1,2,3,4]);
    r.push(x);
    for (var i = 0; i < 10; ++i) f.call(this,0,1,2,3,4,5);
    r.push(x);
    for (var i = 0; i < 10; ++i) f.apply(this,[0,1,2,3,4,5])
    r.push(x);
    return(r.join(","));
}
function testApplyCall() {
    var r = testApplyCallHelper(function (a0,a1,a2,a3,a4,a5,a6,a7) { x = [a0,a1,a2,a3,a4,a5,a6,a7]; });
    r += testApplyCallHelper(function (a0,a1,a2,a3,a4,a5,a6,a7) { x = [a0,a1,a2,a3,a4,a5,a6,a7]; });
    return r;
}
testApplyCall.expected =
",,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,,0,,,,,,,,0,1,,,,,,,0,1,,,,,,,0,1,2,,,,,,0,1,2,,,,,,0,1,2,3,,,,,0,1,2,3,,,,,0,1,2,3,4,,,,0,1,2,3,4,,,,0,1,2,3,4,5,,,0,1,2,3,4,5,," +
",,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,,0,,,,,,,,0,1,,,,,,,0,1,,,,,,,0,1,2,,,,,,0,1,2,,,,,,0,1,2,3,,,,,0,1,2,3,,,,,0,1,2,3,4,,,,0,1,2,3,4,,,,0,1,2,3,4,5,,,0,1,2,3,4,5,,";
test(testApplyCall);

function testApplyUnboxHelper(f,a) {
    var q;
    for (var i = 0; i < 10; ++i)
        q = f.apply(f,a);
    return q;
}
function testApplyUnbox() {
    var f = function(x) { return x; }
    return [testApplyUnboxHelper(f,[1]), testApplyUnboxHelper(f,[true])].join(",");
}
testApplyUnbox.expected = "1,true";
test(testApplyUnbox);

function testCallPick() {
    function g(x,a) {
        x.f();
    }

    var x = [];
    x.f = function() { }

    var y = [];
    y.f = function() { }

    var z = [x,x,x,x,x,y,y,y,y,y];

    for (var i = 0; i < 10; ++i)
        g.call(this, z[i], "");
    return true;
}
testCallPick.expected = true;
test(testCallPick);

function testInvertNullAfterNegateNull()
{
  for (var i = 0; i < 5; i++) !null;
  for (var i = 0; i < 5; i++) -null;
  return "no assertion";
}
testInvertNullAfterNegateNull.expected = "no assertion";
test(testInvertNullAfterNegateNull);

function testUnaryImacros()
{
  function checkArg(x)
  {
    return 1;
  }

  var o = { valueOf: checkArg, toString: null };
  var count = 0;
  var v = 0;
  for (var i = 0; i < 5; i++)
    v += +o + -(-o);

  var results = [v === 10 ? "valueOf passed" : "valueOf failed"];

  o.valueOf = null;
  o.toString = checkArg;

  for (var i = 0; i < 5; i++)
    v += +o + -(-o);

  results.push(v === 20 ? "toString passed" : "toString failed");

  return results.join(", ");
}
testUnaryImacros.expected = "valueOf passed, toString passed";
test(testUnaryImacros);

function testAddAnyInconvertibleObject()
{
  var count = 0;
  function toString() { ++count; if (count == 95) return {}; return "" + count; }

  var threw = false;
  try
  {
    for (var i = 0; i < 100; i++)
    {
        var o = {valueOf: undefined, toString: toString};
        var q = 5 + o;
    }
  }
  catch (e)
  {
    threw = true;
    if (i !== 94)
      return "expected i === 94, got " + i;
    if (q !== "594")
      return "expected q === '594', got " + q + " (type " + typeof q + ")";
    if (count !== 95)
      return "expected count === 95, got " + count;
  }
  if (!threw)
    return "expected throw with 5 + o"; // hey, a rhyme!

  return "pass";
}
testAddAnyInconvertibleObject.expected = "pass";
testAddAnyInconvertibleObject.jitstats = {
  recorderStarted: 1,
  recorderAborted: 0,
  sideExitIntoInterpreter: 93
};
test(testAddAnyInconvertibleObject);

function testAddInconvertibleObjectAny()
{
  var count = 0;
  function toString()
  {
    ++count;
    if (count == 95)
      return {};
    return "" + count;
  }

  var threw = false;
  try
  {
    for (var i = 0; i < 100; i++)
    {
        var o = {valueOf: undefined, toString: toString};
        var q = o + 5;
    }
  }
  catch (e)
  {
    threw = true;
    if (i !== 94)
      return "expected i === 94, got " + i;
    if (q !== "945")
      return "expected q === '945', got " + q + " (type " + typeof q + ")";
    if (count !== 95)
      return "expected count === 95, got " + count;
  }
  if (!threw)
    return "expected throw with o + 5";

  return "pass";
}
testAddInconvertibleObjectAny.expected = "pass";
testAddInconvertibleObjectAny.jitstats = {
  recorderStarted: 1,
  recorderAborted: 0,
  sideExitIntoInterpreter: 93
};
test(testAddInconvertibleObjectAny);

function testAddInconvertibleObjectInconvertibleObject()
{
  var count1 = 0;
  function toString1() { ++count1; if (count1 == 95) return {}; return "" + count1; }
  var count2 = 0;
  function toString2() { ++count2; if (count2 == 95) return {}; return "" + count2; }

  var threw = false;
  try
  {
    for (var i = 0; i < 100; i++)
    {
        var o1 = {valueOf: undefined, toString: toString1};
        var o2 = {valueOf: undefined, toString: toString2};
        var q = o1 + o2;
    }
  }
  catch (e)
  {
    threw = true;
    if (i !== 94)
      return "expected i === 94, got " + i;
    if (q !== "9494")
      return "expected q === '9494', got " + q + " (type " + typeof q + ")";
    if (count1 !== 95)
      return "expected count1 === 95, got " + count1;
    if (count2 !== 94)
      return "expected count2 === 94, got " + count2;
  }
  if (!threw)
    return "expected throw with o1 + o2";

  return "pass";
}
testAddInconvertibleObjectInconvertibleObject.expected = "pass";
testAddInconvertibleObjectInconvertibleObject.jitstats = {
  recorderStarted: 1,
  recorderAborted: 0,
  sideExitIntoInterpreter: 93
};
test(testAddInconvertibleObjectInconvertibleObject);

function testBitOrAnyInconvertibleObject()
{
  var count = 0;
  function toString() { ++count; if (count == 95) return {}; return count; }

  var threw = false;
  try
  {
    for (var i = 0; i < 100; i++)
    {
        var o = {valueOf: undefined, toString: toString};
        var q = 1 | o;
    }
  }
  catch (e)
  {
    threw = true;
    if (i !== 94)
      return "expected i === 94, got " + i;
    if (q !== 95)
      return "expected q === 95, got " + q;
    if (count !== 95)
      return "expected count === 95, got " + count;
  }
  if (!threw)
    return "expected throw with 2 | o"; // hey, a rhyme!

  return "pass";
}
testBitOrAnyInconvertibleObject.expected = "pass";
testBitOrAnyInconvertibleObject.jitstats = {
  recorderStarted: 1,
  recorderAborted: 0,
  sideExitIntoInterpreter: 93
};
test(testBitOrAnyInconvertibleObject);

function testBitOrInconvertibleObjectAny()
{
  var count = 0;
  function toString() { ++count; if (count == 95) return {}; return count; }

  var threw = false;
  try
  {
    for (var i = 0; i < 100; i++)
    {
        var o = {valueOf: undefined, toString: toString};
        var q = o | 1;
    }
  }
  catch (e)
  {
    threw = true;
    if (i !== 94)
      return "expected i === 4, got " + i;
    if (q !== 95)
      return "expected q === 95, got " + q;
    if (count !== 95)
      return "expected count === 95, got " + count;
  }
  if (!threw)
    return "expected throw with o | 2";

  return "pass";
}
testBitOrInconvertibleObjectAny.expected = "pass";
testBitOrInconvertibleObjectAny.jitstats = {
  recorderStarted: 1,
  recorderAborted: 0,
  sideExitIntoInterpreter: 93
};
test(testBitOrInconvertibleObjectAny);

function testBitOrInconvertibleObjectInconvertibleObject()
{
  var count1 = 0;
  function toString1() { ++count1; if (count1 == 95) return {}; return count1; }
  var count2 = 0;
  function toString2() { ++count2; if (count2 == 95) return {}; return count2; }

  var threw = false;
  try
  {
    for (var i = 0; i < 100; i++)
    {
        var o1 = {valueOf: undefined, toString: toString1};
        var o2 = {valueOf: undefined, toString: toString2};
        var q = o1 | o2;
    }
  }
  catch (e)
  {
    threw = true;
    if (i !== 94)
      return "expected i === 94, got " + i;
    if (q !== 94)
      return "expected q === 94, got " + q;
    if (count1 !== 95)
      return "expected count1 === 95, got " + count1;
    if (count2 !== 94)
      return "expected count2 === 94, got " + count2;
  }
  if (!threw)
    return "expected throw with o1 | o2";

  return "pass";
}
testBitOrInconvertibleObjectInconvertibleObject.expected = "pass";
testBitOrInconvertibleObjectInconvertibleObject.jitstats = {
  recorderStarted: 1,
  recorderAborted: 0,
  sideExitIntoInterpreter: 93
};
test(testBitOrInconvertibleObjectInconvertibleObject);

function testCaseTypeMismatchBadness()
{
  for (var z = 0; z < 3; ++z)
  {
    switch ("")
    {
      default:
      case 9:
        break;

      case "":
      case <x/>:
        break;
    }
  }

  return "no crash";
}
testCaseTypeMismatchBadness.expected = "no crash";
testCaseTypeMismatchBadness.jitstats = {
    recorderAborted: 0
};
test(testCaseTypeMismatchBadness);

function testDoubleComparison()
{
  for (var i = 0; i < 500000; ++i)
  {
    switch (1 / 0)
    {
      case Infinity:
    }
  }

  return "finished";
}
testDoubleComparison.expected = "finished";
testDoubleComparison.jitstats = {
  sideExitIntoInterpreter: 1
};
test(testDoubleComparison);

function testLirBufOOM()
{
    var a = [
             "12345678901234",
             "123456789012",
             "1234567890123456789012345678",
             "12345678901234567890123456789012345678901234567890123456",
             "f",
             "$",
             "",
             "f()",
             "(\\*)",
             "b()",
             "()",
             "(#)",
             "ABCDEFGHIJK",
             "ABCDEFGHIJKLM",
             "ABCDEFGHIJKLMNOPQ",
             "ABCDEFGH",
             "(.)",
             "(|)",
             "()$",
             "/()",
             "(.)$"
             ];
    
    for (var j = 0; j < 200; ++j) {
        var js = "" + j;
        for (var i = 0; i < a.length; i++)
            "".match(a[i] + js)
    }
    return "ok";
}
testLirBufOOM.expected = "ok";
test(testLirBufOOM);

function testStringResolve() {
    var x = 0;
    for each (let d in [new String('q'), new String('q'), new String('q')]) {
        if (("" + (0 in d)) === "true")
            x++;
    }
    return x;
}
testStringResolve.expected = 3;
test(testStringResolve);

//test no multitrees assert
function testGlobalMultitrees1() {
    (function() { 
      for (var j = 0; j < 4; ++j) {
        for each (e in ['A', 1, 'A']) {
        }
      }
    })();
    return true;
}
testGlobalMultitrees1.expected = true;
test(testGlobalMultitrees1);

var q = [];
for each (b in [0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF]) {
  for each (let e in [{}, {}, {}, "", {}]) { 
    b = (b | 0x40000000) + 1;
    q.push(b);
  }
}
function testLetWithUnstableGlobal() {
    return q.join(",");
}
testLetWithUnstableGlobal.expected = "2147483648,-1073741823,-1073741822,-1073741821,-1073741820,2147483648,-1073741823,-1073741822,-1073741821,-1073741820,2147483648,-1073741823,-1073741822,-1073741821,-1073741820";
test(testLetWithUnstableGlobal);
delete b;
delete q;

for each (testBug474769_b in [1, 1, 1, 1.5, 1, 1]) {
    (function() { for each (let testBug474769_h in [0, 0, 1.4, ""]) {} })()
}
function testBug474769() {
    return testBug474769_b;
}
testBug474769.expected = 1;
test(testBug474769);

function testInterpreterReentry() {
    this.__defineSetter__('x', function(){})
    for (var j = 0; j < 5; ++j) { x = 3; }
    return 1;
}
testInterpreterReentry.expected = 1;
test(testInterpreterReentry);

function testInterpreterReentry2() {
    var a = false;
    var b = {};
    var c = false;
    var d = {};
    this.__defineGetter__('e', function(){});
    for (let f in this) print(f);
    [1 for each (g in this) for each (h in [])]
    return 1;
}
testInterpreterReentry2.expected = 1;
test(testInterpreterReentry2);

function testInterpreterReentry3() {
    for (let i=0;i<5;++i) this["y" + i] = function(){};
    this.__defineGetter__('e', function (x2) { yield; });
    [1 for each (a in this) for (b in {})];
    return 1;
}
testInterpreterReentry3.expected = 1;
test(testInterpreterReentry3);

function testReverseArgTypes() {
    for (var j = 0; j < 4; ++j) ''.replace('', /x/);
    return 1;
}
testReverseArgTypes.expected = 1;
test(testReverseArgTypes);

function testInterpreterReentry4() {
    var obj = {a:1, b:1, c:1, d:1, get e() 1000 };
    for (var p in obj)
        obj[p];
}
test(testInterpreterReentry4);

function testInterpreterReentry5() {
    var arr = [0, 1, 2, 3, 4];
    arr.__defineGetter__("4", function() 1000);
    for (var i = 0; i < 5; i++)
        arr[i];
    for (var p in arr)
        arr[p];
}
test(testInterpreterReentry5);

function testInterpreterReentry6() {
    var obj = {a:1, b:1, c:1, d:1, set e(x) { this._e = x; }};
    for (var p in obj)
        obj[p] = "grue";
    return obj._e;
}
testInterpreterReentry6.expected = "grue";
test(testInterpreterReentry6);

function testInterpreterReentry7() {
    var arr = [0, 1, 2, 3, 4];
    arr.__defineSetter__("4", function(x) { this._4 = x; });
    for (var i = 0; i < 5; i++)
        arr[i] = "grue";
    var tmp = arr._4;
    for (var p in arr)
        arr[p] = "bleen";
    return tmp + " " + arr._4;
}
testInterpreterReentry7.expected = "grue bleen";
test(testInterpreterReentry7);

// Bug 462027 comment 54.
function testInterpreterReentery8() {
    var e = <x><y/></x>;
    for (var j = 0; j < 4; ++j) { +[e]; }
}
test(testInterpreterReentery8);

function testHolePushing() {
    var a = ["foobar", "baz"];
    for (var i = 0; i < 5; i++)
        a = [, "overwritten", "new"];
    var s = "[";
    for (i = 0; i < a.length; i++) {
        s += (i in a) ? a[i] : "<hole>";
        if (i != a.length - 1)
            s += ",";
    }
    return s + "], " + (0 in a);
}
testHolePushing.expected = "[<hole>,overwritten,new], false";
test(testHolePushing);

function testDeepBail1() {
    var y = <z/>;
    for (var i = 0; i < RUNLOOP; i++)
        "" in y;
}
test(testDeepBail1);

/* Array comprehension tests */

function Range(start, stop) {
    this.i = start;
    this.stop = stop;
}
Range.prototype = {
    __iterator__: function() this,
    next: function() {
        if (this.i >= this.stop)
            throw StopIteration;
        return this.i++;
    }
};

function range(start, stop) {
    return new Range(start, stop);
}

function testArrayComp1() {
    return [a for (a in range(0, 10))].join('');
}
testArrayComp1.expected='0123456789';
test(testArrayComp1);

function testArrayComp2() {
    return [a + b for (a in range(0, 5)) for (b in range(0, 5))].join('');
}
testArrayComp2.expected='0123412345234563456745678';
test(testArrayComp2);

function testSwitchUndefined()
{
  var x = undefined;
  var y = 0;
  for (var i = 0; i < 5; i++)
  {
    switch (x)
    {
      default:
        y++;
    }
  }
  return y;
}
testSwitchUndefined.expected = 5;
test(testSwitchUndefined);

function testGeneratorDeepBail() {
    function g() { yield 2; }
    var iterables = [[1], [], [], [], g()];

    var total = 0;
    for (let i = 0; i < iterables.length; i++)
        for each (let j in iterables[i])
                     total += j;
    return total;
}
testGeneratorDeepBail.expected = 3;
test(testGeneratorDeepBail);

function testRegexpGet() {
    var re = /hi/;
    var a = [];
    for (let i = 0; i < 5; ++i)
        a.push(re.source);
    return a.toString();
}
testRegexpGet.expected = "hi,hi,hi,hi,hi";
test(testRegexpGet);

function testThrowingObjectEqUndefined()
{
  try
  {
    var obj = { toString: function() { throw 0; } };
    for (var i = 0; i < 5; i++)
      "" + (obj == undefined);
    return i === 5;
  }
  catch (e)
  {
    return "" + e;
  }
}
testThrowingObjectEqUndefined.expected = true;
testThrowingObjectEqUndefined.jitstats = {
  sideExitIntoInterpreter: 1
};
test(testThrowingObjectEqUndefined);

function x4(v) { return "" + v + v + v + v; }
function testConvertibleObjectEqUndefined()
{
  var compares =
    [
     false, false, false, false,
     undefined, undefined, undefined, undefined,
     false, false, false, false,
     undefined, undefined, undefined, undefined,
     false, false, false, false,
     undefined, undefined, undefined, undefined,
     false, false, false, false,
     undefined, undefined, undefined, undefined,
     false, false, false, false,
     undefined, undefined, undefined, undefined,
    ];
  var count = 0;
  var obj = { valueOf: function() { count++; return 1; } };
  var results = compares.map(function(v) { return "unwritten"; });

  for (var i = 0, sz = compares.length; i < sz; i++)
    results[i] = compares[i] == obj;

  return results.join("") + count;
}
testConvertibleObjectEqUndefined.expected =
  x4(false) + x4(false) + x4(false) + x4(false) + x4(false) + x4(false) +
  x4(false) + x4(false) + x4(false) + x4(false) + "20";
testConvertibleObjectEqUndefined.jitstats = {
  sideExitIntoInterpreter: 3
};
test(testConvertibleObjectEqUndefined);

function testUndefinedPropertyAccess() {
    var x = [1,2,3];
    var y = {};
    var a = { foo: 1 };
    y.__proto__ = x;
    var z = [x, x, x, y, y, y, y, a, a, a];
    var s = "";
    for (var i = 0; i < z.length; ++i)
        s += z[i].foo;
    return s;
}
testUndefinedPropertyAccess.expected = "undefinedundefinedundefinedundefinedundefinedundefinedundefined111";
testUndefinedPropertyAccess.jitstats = {
    traceCompleted: 3
};
test(testUndefinedPropertyAccess);

q = "";
function g() { q += "g"; }
function h() { q += "h"; }
a = [g, g, g, g, h];
for (i=0; i<5; i++) { f = a[i];  f(); }

function testRebranding() {
    return q;
}
testRebranding.expected = "ggggh";
test(testRebranding);
delete q;
delete g;
delete h;
delete a;
delete f;

function testLambdaCtor() {
    var a = [];
    for (var x = 0; x < RUNLOOP; ++x) {
        var f = function(){};
        a[a.length] = new f;
    }

    // This prints false until the upvar2 bug is fixed:
    // print(a[HOTLOOP].__proto__ !== a[HOTLOOP-1].__proto__);

    // Assert that the last f was properly constructed.
    return a[RUNLOOP-1].__proto__ === f.prototype;
}
testLambdaCtor.expected = true;
test(testLambdaCtor);

function testNonStubGetter() {
    let ([] = false) { (this.watch("x", /a/g)); };
    (function () { (eval("(function(){for each (x in [1, 2, 2]);});"))(); })();
    this.unwatch("x");
    return "ok";
}
testNonStubGetter.expected = "ok";
test(testNonStubGetter);

function testString() {
    var q;
    for (var i = 0; i <= RUNLOOP; ++i) {
        q = [];
        q.push(String(void 0));
        q.push(String(true));
        q.push(String(5));
        q.push(String(5.5));
        q.push(String("5"));
        q.push(String([5]));
    }
    return q.join(",");
}
testString.expected = "undefined,true,5,5.5,5,5";
testString.jitstats = {
    recorderStarted: 1,
    sideExitIntoInterpreter: 1
};
test(testString);

function testToStringBeforeValueOf()
{
    var o = {toString: function() { return "s"; }, valueOf: function() { return "v"; } };
    var a = [];
    for (var i = 0; i < 10; i++)
        a.push(String(o));
    return a.join(",");
}
testToStringBeforeValueOf.expected = "s,s,s,s,s,s,s,s,s,s";
testToStringBeforeValueOf.jitstats = {
    recorderStarted: 1,
    sideExitIntoInterpreter: 1
};
test(testToStringBeforeValueOf);

function testNullToString()
{
    var a = [];
    for (var i = 0; i < 10; i++)
        a.push(String(null));
    for (i = 0; i < 10; i++) {
        var t = typeof a[i];
        if (t != "string")
           a.push(t);
    }
    return a.join(",");
}
testNullToString.expected = "null,null,null,null,null,null,null,null,null,null";
testNullToString.jitstats = {
    recorderStarted: 2,
    sideExitIntoInterpreter: 2,
    recorderAborted: 0
};
test(testNullToString);

function testAddNull()
{
  var rv;
  for (var x = 0; x < HOTLOOP + 1; ++x)
    rv = null + [,,];
  return rv;
}
testAddNull.expected = "null,";
testAddNull.jitstats = {
    recorderStarted: 1,
    sideExitIntoInterpreter: 1,
    recorderAborted: 0
};
test(testAddNull);

function testClosures()
{
    function MyObject(id) {
        var thisObject = this;
        this.id = id;
        this.toString = str;

        function str() {
            return "" + this.id + thisObject.id;
        }
    }

    var a = [];
    for (var i = 0; i < 5; i++)
        a.push(new MyObject(i));
    return a.toString();
}
testClosures.expected = "00,11,22,33,44";
test(testClosures);

function testMoreClosures() {
    var f = {}, max = 3;

    var hello = function(n) {
        function howdy() { return n * n }
        f.test = howdy;
    };

    for (var i = 0; i <= max; i++)
        hello(i);

    return f.test();
}
testMoreClosures.expected = 9;
test(testMoreClosures);

function testLambdaInitedVar() {
    var jQuery = function (a, b) {
        return jQuery && jQuery.length;
    }
    return jQuery();
}

testLambdaInitedVar.expected = 2;
test(testLambdaInitedVar);

function testNestedEscapingLambdas()
{
    try {
        return (function() {
            var a = [], r = [];
            function setTimeout(f, t) {
                a.push(f);
            }

            function runTimeouts() {
                for (var i = 0; i < a.length; i++)
                    a[i]();
            }

            var $foo = "#nothiddendiv";
            setTimeout(function(){
                r.push($foo);
                setTimeout(function(){
                    r.push($foo);
                }, 100);
            }, 100);

            runTimeouts();

            return r.join("");
        })();
    } catch (e) {
        return e;
    }
}
testNestedEscapingLambdas.expected = "#nothiddendiv#nothiddendiv";
test(testNestedEscapingLambdas);

function testPropagatedFunArgs()
{
  var win = this;
  var res = [], q = [];
  function addEventListener(name, func, flag) {
    q.push(func);
  }

  var pageInfo, obs;
  addEventListener("load", handleLoad, true);

  var observer = {
    observe: function(win, topic, data) {
      // obs.removeObserver(observer, "page-info-dialog-loaded");
      handlePageInfo();
    }
  };

  function handleLoad() {
    pageInfo = { toString: function() { return "pageInfo"; } };
    obs = { addObserver: function (obs, topic, data) { obs.observe(win, topic, data); } };
    obs.addObserver(observer, "page-info-dialog-loaded", false);
  }

  function handlePageInfo() {
    res.push(pageInfo);
    function $(aId) { res.push(pageInfo); };
    var feedTab = $("feedTab");
  }

  q[0]();
  return res.join(',');
}
testPropagatedFunArgs.expected = "pageInfo,pageInfo";
test(testPropagatedFunArgs);

// Second testPropagatedFunArgs test -- this is a crash-test.
(function () {
  var escapee;

  function testPropagatedFunArgs()
  {
    const magic = 42;

    var win = this;
    var res = [], q = [];
    function addEventListener(name, func, flag) {
      q.push(func);
    }

    var pageInfo = "pageInfo", obs;
    addEventListener("load", handleLoad, true);

    var observer = {
      observe: function(win, topic, data) {
        // obs.removeObserver(observer, "page-info-dialog-loaded");
        handlePageInfo();
      }
    };

    function handleLoad() {
      //pageInfo = { toString: function() { return "pageInfo"; } };
      obs = { addObserver: function (obs, topic, data) { obs.observe(win, topic, data); } };
      obs.addObserver(observer, "page-info-dialog-loaded", false);
    }

    function handlePageInfo() {
      res.push(pageInfo);
      function $(aId) {
        function notSafe() {
          return magic;
        }
        notSafe();
        res.push(pageInfo);
      };
      var feedTab = $("feedTab");
    }

    escapee = q[0];
    return res.join(',');
  }

  testPropagatedFunArgs();

  escapee();
})();

function testStringLengthNoTinyId()
{
  var x = "unset";
  var t = new String("");
  for (var i = 0; i < 5; i++)
    x = t["-1"];

  var r = "t['-1'] is " + x;
  t["-1"] = "foo";
  r += " when unset, '" + t["-1"] + "' when set";
  return r;
}
testStringLengthNoTinyId.expected = "t['-1'] is undefined when unset, 'foo' when set";
test(testStringLengthNoTinyId);

function testLengthInString()
{
  var s = new String();
  var res = "length" in s;
  for (var i = 0; i < 5; i++)
    res = res && ("length" in s);
  res = res && s.hasOwnProperty("length");
  for (var i = 0; i < 5; i++)
    res = res && s.hasOwnProperty("length");
  return res;
}
testLengthInString.expected = true;
test(testLengthInString);

function testSlowArrayLength()
{
  var counter = 0;
  var a = [];
  a[10000000 - 1] = 0;
  for (var i = 0; i < a.length; i++)
    counter++;
  return counter;
}
testSlowArrayLength.expected = 10000000;
testSlowArrayLength.jitstats = {
  recorderStarted: 1,
  recorderAborted: 0,
  sideExitIntoInterpreter: 1
};
test(testSlowArrayLength);

function testObjectLength()
{
  var counter = 0;
  var a = {};
  a.length = 10000000;
  for (var i = 0; i < a.length; i++)
    counter++;
  return counter;
}
testObjectLength.expected = 10000000;
testObjectLength.jitstats = {
  recorderStarted: 1,
  recorderAborted: 0,
  sideExitIntoInterpreter: 1
};
test(testObjectLength);

function testChangingObjectWithLength()
{
  var obj = { length: 10 };
  var dense = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
  var slow = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; slow.slow = 5;

  /*
   * The elements of objs constitute a De Bruijn sequence repeated 4x to trace
   * and run native code for every object and transition.
   */
  var objs = [obj, obj, obj, obj,
              obj, obj, obj, obj,
              dense, dense, dense, dense,
              obj, obj, obj, obj,
              slow, slow, slow, slow,
              dense, dense, dense, dense,
              dense, dense, dense, dense,
              slow, slow, slow, slow,
              slow, slow, slow, slow,
              obj, obj, obj, obj];

  var counter = 0;

  for (var i = 0, sz = objs.length; i < sz; i++)
  {
    var o = objs[i];
    for (var j = 0; j < o.length; j++)
      counter++;
  }

  return counter;
}
testChangingObjectWithLength.expected = 400;
testChangingObjectWithLength.jitstats = {
  recorderAborted: 0,
  sideExitIntoInterpreter: 15 // empirically determined
};
test(testChangingObjectWithLength);

function testNEWINIT()
{
    var a;
    for (var i = 0; i < 10; ++i)
        a = [{}];
    return uneval(a);
}
testNEWINIT.expected = "[{}]";
test(testNEWINIT);

function testNEWINIT_DOUBLE()
{
    for (var z = 0; z < 2; ++z) { ({ 0.1: null })}
    return "ok";
}
testNEWINIT_DOUBLE.expected = "ok";
test(testNEWINIT_DOUBLE);

function testBug458838() {
    var a = 1;
    function g() {
        var b = 0
            for (var i = 0; i < 10; ++i) {
                b += a;
            }
        return b;
    }

    return g();
}
testBug458838.expected = 10;
testBug458838.jitstats = {
  recorderStarted: 1,
  recorderAborted: 0,
  traceCompleted: 1
};
test(testBug458838);

function testIntOverflow() {
    // int32_max - 7
    var ival = 2147483647 - 7;
    for (var i = 0; i < 30; i++) {
        ival += 2;
    }
    return (ival < 2147483647);
}
testIntOverflow.expected = false;
testIntOverflow.jitstats = {
    recorderStarted: 2,
    recorderAborted: 0,
    traceCompleted: 2,
    traceTriggered: 2,
};
test(testIntOverflow);

function testIntUnderflow() {
    // int32_min + 8
    var ival = -2147483648 + 8;
    for (var i = 0; i < 30; i++) {
        ival -= 2;
    }
    return (ival > -2147483648);
}
testIntUnderflow.expected = false;
testIntUnderflow.jitstats = {
    recorderStarted: 2,
    recorderAborted: 0,
    traceCompleted: 2,
    traceTriggered: 2,
};
test(testIntUnderflow);

function testCALLELEM()
{
    function f() {
        return 5;
    }

    function g() {
        return 7;
    }

    var x = [f,f,f,f,g];
    var y = 0;
    for (var i = 0; i < 5; ++i)
        y = x[i]();
    return y;
}
testCALLELEM.expected = 7;
test(testCALLELEM);

function testNewString()
{
  var o = { toString: function() { return "string"; } };
  var r = [];
  for (var i = 0; i < 5; i++)
    r.push(typeof new String(o));
  for (var i = 0; i < 5; i++)
    r.push(typeof new String(3));
  for (var i = 0; i < 5; i++)
    r.push(typeof new String(2.5));
  for (var i = 0; i < 5; i++)
    r.push(typeof new String("string"));
  for (var i = 0; i < 5; i++)
    r.push(typeof new String(null));
  for (var i = 0; i < 5; i++)
    r.push(typeof new String(true));
  for (var i = 0; i < 5; i++)
    r.push(typeof new String(undefined));
  return r.length === 35 && r.every(function(v) { return v === "object"; });
}
testNewString.expected = true;
testNewString.jitstats = {
  recorderStarted:  7,
  recorderAborted: 0,
  traceCompleted: 7,
  sideExitIntoInterpreter: 7
};
test(testNewString);

function testWhileObjectOrNull()
{
  try
  {
    for (var i = 0; i < 3; i++)
    {
      var o = { p: { p: null } };
      while (o.p)
        o = o.p;
    }
    return "pass";
  }
  catch (e)
  {
    return "threw exception: " + e;
  }
}
testWhileObjectOrNull.expected = "pass";
test(testWhileObjectOrNull);

function testDenseArrayProp()
{
    [].__proto__.x = 1;
    ({}).__proto__.x = 2;
    var a = [[],[],[],({}).__proto__];
    for (var i = 0; i < a.length; ++i)
        uneval(a[i].x);
    delete [].__proto__.x;
    delete ({}).__proto__.x;
    return "ok";
}
testDenseArrayProp.expected = "ok";
test(testDenseArrayProp);

function testNewWithNonNativeProto()
{
  function f() { }
  var a = f.prototype = [];
  for (var i = 0; i < 5; i++)
    var o = new f();
  return Object.getPrototypeOf(o) === a && o.splice === Array.prototype.splice;
}
testNewWithNonNativeProto.expected = true;
testNewWithNonNativeProto.jitstats = {
  recorderStarted: 1,
  recorderAborted: 0,
  sideExitIntoInterpreter: 1
};
test(testNewWithNonNativeProto);

function testLengthOnNonNativeProto()
{
  var o = {};
  o.__proto__ = [3];
  for (var j = 0; j < 5; j++)
    o[0];

  var o2 = {};
  o2.__proto__ = [];
  for (var j = 0; j < 5; j++)
    o2.length;

  function foo() { }
  foo.__proto__ = [];
  for (var j = 0; j < 5; j++)
    foo.length;

  return "no assertion";
}
testLengthOnNonNativeProto.expected = "no assertion";
test(testLengthOnNonNativeProto);

function testDeepPropertyShadowing()
{
    function h(node) {
        var x = 0;
        while (node) {
            x++;
            node = node.parent;
        }
        return x;
    }
    var tree = {__proto__: {__proto__: {parent: null}}};
    h(tree);
    h(tree);
    tree.parent = {};
    assertEq(h(tree), 2);
}
test(testDeepPropertyShadowing);

// Complicated whitebox test for bug 487845.
function testGlobalShapeChangeAfterDeepBail() {
    function f(name) {
        this[name] = 1;  // may change global shape
        for (var i = 0; i < 4; i++)
            ; // MonitorLoopEdge eventually triggers assertion
    }

    // When i==3, deep-bail, then change global shape enough times to exhaust
    // the array of GlobalStates.
    var arr = [[], [], [], ["bug0", "bug1", "bug2", "bug3", "bug4"]];
    for (var i = 0; i < arr.length; i++)
        arr[i].forEach(f);
}
test(testGlobalShapeChangeAfterDeepBail);
for (let i = 0; i < 5; i++)
    delete this["bug" + i];

function testFunctionIdentityChange()
{
  function a() {}
  function b() {}

  var o = { a: a, b: b };

  for (var prop in o)
  {
    for (var i = 0; i < 1000; i++)
      o[prop]();
  }

  return true;
}
testFunctionIdentityChange.expected = true;
testFunctionIdentityChange.jitstats = {
  recorderStarted: 2,
  traceCompleted: 2,
  sideExitIntoInterpreter: 3
};
test(testFunctionIdentityChange);

function testStringObjectLength() {
    var x = new String("foo"), y = 0;
    for (var i = 0; i < 10; ++i)
        y = x.length;
    return y;
}
testStringObjectLength.expected = 3;
test(testStringObjectLength);

var _quit;
function testNestedDeepBail()
{
    _quit = false;
    function loop() {
        for (var i = 0; i < 4; i++)
            ;
    }
    loop();

    function f() {
        loop();
        _quit = true;
    }
    var stk = [[1], [], [], [], []];
    while (!_quit)
        stk.pop().forEach(f);
}
test(testNestedDeepBail);
delete _quit;

function testSlowNativeCtor() {
    for (var i = 0; i < 4; i++)
	new Date().valueOf();
}
test(testSlowNativeCtor);

function testSlowNativeBail() {
    var a = ['0', '1', '2', '3', '+'];
    try {
	for (var i = 0; i < a.length; i++)
	    new RegExp(a[i]);
    } catch (exc) {
	assertEq(""+exc.stack.match(/^RegExp/), "RegExp");
    }
}
test(testSlowNativeBail);

function testStringConstructorWithExtraArg() {
    for (let i = 0; i < 5; ++i)
        new String(new String(), 2);
    return "ok";
}
testStringConstructorWithExtraArg.expected = "ok";
test(testStringConstructorWithExtraArg);

function testConstructorBail() {
    for (let i = 0; i < 5; ++i) new Number(/x/);
}
test(testConstructorBail);

function testNewArrayCount()
{
  var a = [];
  for (var i = 0; i < 5; i++)
    a = [0];
  assertEq(a.__count__, 1);
  for (var i = 0; i < 5; i++)
    a = [0, , 2];
  assertEq(a.__count__, 2);
}
test(testNewArrayCount);

function testNewArrayCount2() {
    var x = 0;
    for (var i = 0; i < 10; ++i)
        x = new Array(1,2,3).__count__;
    return x;
}
testNewArrayCount2.expected = 3;
test(testNewArrayCount2);

function testGetCallObjInlined(i) {
    if (i > 7) eval("1");
    return 1;
}

function testGetCallObj() {
    for (var i = 0; i < 10; ++i)
        testGetCallObjInlined(i);
    return "ok";
}
testGetCallObj.expected = "ok";
test(testGetCallObj);

function testGetThis() {
    for (var i = 0; i < 3; ++i) {
        (function() { return this; })();
    }
    return "ok";
}
testGetThis.expected = "ok";
test(testGetThis);

function testArrayNamedProp() {
    for (var x = 0; x < 10; ++x) { [4].sort-- }
    return "ok";
}
testArrayNamedProp.expected = "ok";
test(testArrayNamedProp);

function testUndemoteLateGlobalSlots() {
    for each (aaa in ["", "", 0/0, ""]) {
        ++aaa;
        for each (bbb in [0, "", aaa, "", 0, "", 0/0]) {
        }
    }
    delete aaa;
    delete bbb;
    return "ok";
}
testUndemoteLateGlobalSlots.expected = "ok";
test(testUndemoteLateGlobalSlots);

function testSetProtoRegeneratesObjectShape()
{
  var f = function() {};
  var g = function() {};
  g.prototype.__proto__ = {};

  function iq(obj)
  {
    for (var i = 0; i < 10; ++i)
      "" + obj.prototype;
  }

  iq(f);
  iq(f);
  iq(f);
  iq(f);
  iq(g);

  if (shapeOf(f.prototype) === shapeOf(g.prototype))
    return "object shapes same after proto of one is changed";

  return true;
}
testSetProtoRegeneratesObjectShape.expected = true;
test(testSetProtoRegeneratesObjectShape);

function testFewerGlobalsInInnerTree() {
    for each (a in [new Number(1), new Number(1), {}, {}, new Number(1)]) {
        for each (b in [2, "", 2, "", "", ""]) {
		    for each (c in [{}, {}, 3, 3, 3, 3, {}, {}]) {
                4 + a;
			}
		}
	}
    delete a;
    delete b;
    delete c;
    return "ok";
}
testFewerGlobalsInInnerTree.expected = "ok";
test(testFewerGlobalsInInnerTree);

/*****************************************************************************
 *                                                                           *
 *  _____ _   _  _____ ______ _____ _______                                  *
 * |_   _| \ | |/ ____|  ____|  __ \__   __|                                 *
 *   | | |  \| | (___ | |__  | |__) | | |                                    *
 *   | | | . ` |\___ \|  __| |  _  /  | |                                    *
 *  _| |_| |\  |____) | |____| | \ \  | |                                    *
 * |_____|_| \_|_____/|______|_|  \_\ |_|                                    *
 *                                                                           *
 *                                                                           *
 *  _______ ______  _____ _______ _____                                      *
 * |__   __|  ____|/ ____|__   __/ ____|                                     *
 *    | |  | |__  | (___    | | | (___                                       *
 *    | |  |  __|  \___ \   | |  \___ \                                      *
 *    | |  | |____ ____) |  | |  ____) |                                     *
 *    |_|  |______|_____/   |_| |_____/                                      *
 *                                                                           *
 *                                                                           *
 *  ____  ______ ______ ____  _____  ______    _    _ ______ _____  ______   *
 * |  _ \|  ____|  ____/ __ \|  __ \|  ____|  | |  | |  ____|  __ \|  ____|  *
 * | |_) | |__  | |__ | |  | | |__) | |__     | |__| | |__  | |__) | |__     *
 * |  _ <|  __| |  __|| |  | |  _  /|  __|    |  __  |  __| |  _  /|  __|    *
 * | |_) | |____| |   | |__| | | \ \| |____   | |  | | |____| | \ \| |____   *
 * |____/|______|_|    \____/|_|  \_\______|  |_|  |_|______|_|  \_\______|  *
 *                                                                           *
 *****************************************************************************/

load(gSrcdir + "/math-trace-tests.js");

// BEGIN MANDELBROT STUFF
// XXXbz I would dearly like to wrap it up into a function to avoid polluting
// the global scope, but the function ends up heavyweight, and then we lose on
// the jit.
if (gDoMandelbrotTest) {
load(gSrcdir + "/mandelbrot-results.js");
//function testMandelbrotAll() {
  // Configuration options that affect which codepaths we follow.
  var doImageData = true;
  var avoidSparseArray = true;

  // Control of iteration numbers and sizing.  We'll do
  // scaler * colorNames.length iterations or so before deciding that we
  // don't escape.
  const scaler = 5;
  const numRows = 600;
  const numCols = 600;

  const colorNames = [
    "black",
    "green",
    "blue",
    "red",
    "purple",
    "orange",
    "cyan",
    "yellow",
    "magenta",
    "brown",
    "pink",
    "chartreuse",
    "darkorange",
    "crimson",
    "gray",
    "deeppink",
    "firebrick",
    "lavender",
    "lawngreen",
    "lightsalmon",
    "lime",
    "goldenrod"
  ];
  const threshold = (colorNames.length - 1) * scaler;

  // Now set up our colors
  var colors = [];
  // 3-part for loop (iterators buggy, we will add a separate test for them)
  for (var colorNameIdx = 0; colorNameIdx < colorNames.length; ++colorNameIdx) {
  //for (var colorNameIdx in colorNames) {
    colorNameIdx = parseInt(colorNameIdx);
    colors.push([colorNameIdx, colorNameIdx, colorNameIdx, 0]);
  }

  // Storage for our point data
  var points;

  var scratch = {};
  var scratchZ = {};
  function complexMult(a, b) {
    var newr = a.r * b.r - a.i * b.i;
    var newi = a.r * b.i + a.i * b.r;
    scratch.r = newr;
    scratch.i = newi;
    return scratch;
  }
  function complexAdd(a, b) {
    scratch.r = a.r + b.r;
    scratch.i = a.i + b.i;
    return scratch;
  }
  function abs(a) {
    return Math.sqrt(a.r * a.r + a.i * a.i);
  }

  function escapeAbsDiff(normZ, absC) {
    var absZ = Math.sqrt(normZ);
    return normZ > absZ + absC;
  }

  function escapeNorm2(normZ) {
    return normZ > 4;
  }

  function fuzzyColors(i) {
    return Math.floor(i / scaler) + 1;
  }

  function moddedColors(i) {
    return (i % (colorNames.length - 1)) + 1;
  }

  function computeEscapeSpeedObjects(real, imag) {
    var c = { r: real, i: imag }
    scratchZ.r = scratchZ.i = 0;
    var absC = abs(c);
    for (var i = 0; i < threshold; ++i) {
      scratchZ = complexAdd(c, complexMult(scratchZ, scratchZ));
      if (escape(scratchZ.r * scratchZ.r + scratchZ.i * scratchZ.i,
                 absC)) {
        return colorMap(i);
      }
    }
    return 0;
  }

  function computeEscapeSpeedOneObject(real, imag) {
    // fold in the fact that we start with 0
    var r = real;
    var i = imag;
    var absC = abs({r: real, i: imag});
    for (var j = 0; j < threshold; ++j) {
      var r2 = r * r;
      var i2 = i * i;
      if (escape(r2 + i2, absC)) {
        return colorMap(j);
      }
      i = 2 * r * i + imag;
      r = r2 - i2 + real;
    }
    return 0;
  }

  function computeEscapeSpeedDoubles(real, imag) {
    // fold in the fact that we start with 0
    var r = real;
    var i = imag;
    var absC = Math.sqrt(real * real + imag * imag);
    for (var j = 0; j < threshold; ++j) {
      var r2 = r * r;
      var i2 = i * i;
      if (escape(r2 + i2, absC)) {
        return colorMap(j);
      }
      i = 2 * r * i + imag;
      r = r2 - i2 + real;
    }
    return 0;
  }

  var computeEscapeSpeed = computeEscapeSpeedDoubles;
  var escape = escapeNorm2;
  var colorMap = fuzzyColors;

  function addPointOrig(pointArray, n, i, j) {
    if (!points[n]) {
      points[n] = [];
      points[n].push([i, j, 1, 1]);
    } else {
      var point = points[n][points[n].length-1];
      if (point[0] == i && point[1] == j - point[3]) {
        ++point[3];
      } else {
        points[n].push([i, j, 1, 1]);
      }
    }
  }

  function addPointImagedata(pointArray, n, col, row) {
    var slotIdx = ((row * numCols) + col) * 4;
    pointArray[slotIdx] = colors[n][0];
    pointArray[slotIdx+1] = colors[n][1];
    pointArray[slotIdx+2] = colors[n][2];
    pointArray[slotIdx+3] = colors[n][3];
  }

  function createMandelSet() {
    var realRange = { min: -2.1, max: 1 };
    var imagRange = { min: -1.5, max: 1.5 };

    var addPoint;
    if (doImageData) {
      addPoint = addPointImagedata;
      points = new Array(4*numCols*numRows);
      if (avoidSparseArray) {
        for (var idx = 0; idx < 4*numCols*numRows; ++idx) {
          points[idx] = 0;
        }
      }
    } else {
      addPoint = addPointOrig;
      points = [];
    }
    var realStep = (realRange.max - realRange.min)/numCols;
    var imagStep = (imagRange.min - imagRange.max)/numRows;
    for (var i = 0, curReal = realRange.min;
         i < numCols;
         ++i, curReal += realStep) {
      for (var j = 0, curImag = imagRange.max;
           j < numRows;
           ++j, curImag += imagStep) {
        var n = computeEscapeSpeed(curReal, curImag);
        addPoint(points, n, i, j)
      }
    }
    var result;
    if (doImageData) {
      if (colorMap == fuzzyColors) {
        result = mandelbrotImageDataFuzzyResult;
      } else {
        result = mandelbrotImageDataModdedResult;
      }
    } else {
      result = mandelbrotNoImageDataResult;
    }
    return points.toSource() == result;
  }

  createMandelSet.expected = true;

  const escapeTests = [ escapeAbsDiff ];
  const colorMaps = [ fuzzyColors, moddedColors ];
  const escapeComputations = [ computeEscapeSpeedObjects,
                               computeEscapeSpeedOneObject,
                               computeEscapeSpeedDoubles ];
  // Test all possible escape-speed generation codepaths, using the
  // imageData + sparse array avoidance storage.
  doImageData = true;
  avoidSparseArray = true;
  for (var escapeIdx in escapeTests) {
    escape = escapeTests[escapeIdx];
    for (var colorMapIdx in colorMaps) {
      colorMap = colorMaps[colorMapIdx];
      for (var escapeComputationIdx in escapeComputations) {
        computeEscapeSpeed = escapeComputations[escapeComputationIdx];
        test(createMandelSet);
      }
    }
  }

  // Test all possible storage strategies. Note that we already tested
  // doImageData == true with avoidSparseArray == true.
  escape = escapeAbsDiff;
  colorMap = fuzzyColors; // This part doesn't really matter too much here
  computeEscapeSpeed = computeEscapeSpeedDoubles;

  doImageData = true;
  avoidSparseArray = false;
  test(createMandelSet);

  escape = escapeNorm2;
  doImageData = false;  // avoidSparseArray doesn't matter here
  test(createMandelSet);
//}
//testMandelbrotAll();
} /* if (gDoMandelbrotTest) */
// END MANDELBROT STUFF

/*****************************************************************************
 *  _   _  ____     _   __  ____  _____  ______                              *
 * | \ | |/ __ \   |  \/  |/ __ \|  __ \|  ____|                             *
 * |  \| | |  | |  | \  / | |  | | |__) | |__                                *
 * | . ` | |  | |  | |\/| | |  | |  _  /|  __|                               *
 * | |\  | |__| |  | |  | | |__| | | \ \| |____                              *
 * |_| \_|\____/   |_|  |_|\____/|_|  \_\______|                             *
 *                                                                           *
 *  _______ ______  _____ _______ _____                                      *
 * |__   __|  ____|/ ____|__   __/ ____|                                     *
 *    | |  | |__  | (___    | | | (___                                       *
 *    | |  |  __|  \___ \   | |  \___ \                                      *
 *    | |  | |____ ____) |  | |  ____) |                                     *
 *    |_|  |______|_____/   |_| |_____/                                      *
 *                                                                           *
 *           ______ _______ ______ _____     _    _ ______ _____  ______ _   *
 *     /\   |  ____|__   __|  ____|  __ \   | |  | |  ____|  __ \|  ____| |  *
 *    /  \  | |__     | |  | |__  | |__) |  | |__| | |__  | |__) | |__  | |  *
 *   / /\ \ |  __|    | |  |  __| |  _  /   |  __  |  __| |  _  /|  __| | |  *
 *  / ____ \| |       | |  | |____| | \ \   | |  | | |____| | \ \| |____|_|  *
 * /_/    \_\_|       |_|  |______|_|  \_\  |_|  |_|______|_|  \_\______(_)  *
 *                                                                           *
 *****************************************************************************/

/* NOTE: Keep this test last, since it screws up all for...in loops after it. */
function testGlobalProtoAccess() {
    return "ok";
}
this.__proto__.a = 3; for (var j = 0; j < 4; ++j) { [a]; }
testGlobalProtoAccess.expected = "ok";
test(testGlobalProtoAccess);

/* Keep these at the end so that we can see the summary after the trace-debug spew. */
if (gReportSummary) {
    print("\npassed:", passes.length && passes.join(","));
    print("\nFAILED:", fails.length && fails.join(","));
}
