text: var crypto = require("crypto");
  var U = require("..");
  var List = U.List;
  var os = require("os");
  var sandbox = require("./sandbox");
  
  // Reduce a test case by iteratively replacing AST nodes with various
  // permutations. Each AST_Statement in the tree is also speculatively dropped
  // to determine whether it is needed.  If the altered tree and the last known
  // good tree produce the same output after being run, then the permutation
  // survives to the next generation and is the basis for subsequent iterations.
  // The test case is reduced as a consequence of complex expressions being
  // replaced with simpler ones.  Note that a reduced test case will have
  // different runtime output - it is not functionally equivalent to the
  // original. The only criteria is that once the generated reduced test case is
  // run without minification, it will produce different output from the code
  // minified with `minify_options`.  Returns a `minify` result object.
  
  Error.stackTraceLimit = Infinity;
  module.exports = function reduce_test(testcase, minify_options, reduce_options) {
      minify_options = minify_options || {};
      reduce_options = reduce_options || {};
      var print_options = {};
      [
          "ie",
          "v8",
          "webkit",
      ].forEach(function(name) {
          var value = minify_options[name] || minify_options.output && minify_options.output[name];
          if (value) print_options[name] = value;
      });
      if (testcase instanceof U.AST_Node) testcase = testcase.print_to_string(print_options);
      var max_iterations = reduce_options.max_iterations || 1000;
      var max_timeout = reduce_options.max_timeout || 10000;
      var warnings = [];
      var log = reduce_options.log || function(msg) {
          warnings.push(msg);
      };
      var verbose = reduce_options.verbose;
      var minify_options_json = JSON.stringify(minify_options, null, 2);
      var result_cache = Object.create(null);
      var test_for_diff = compare_run_code;
      // the initial timeout to assess the viability of the test case must be large
      var differs = test_for_diff(testcase, minify_options, result_cache, max_timeout);
  
      if (verbose) {
          log("// Node.js " + process.version + " on " + os.platform() + " " + os.arch());
      }
      if (differs && differs.error && [ "DefaultsError", "SyntaxError" ].indexOf(differs.error.name) < 0) {
          test_for_diff = test_minify;
          differs = test_for_diff(testcase, minify_options, result_cache, max_timeout);
      }
      if (!differs) {
          // same stdout result produced when minified
          return {
              code: [
                  "// Can't reproduce test failure",
                  "// minify options: " + to_comment(minify_options_json)
              ].join("\n"),
              warnings: warnings,
          };
      } else if (differs.timed_out) {
          return {
              code: [
                  "// Can't reproduce test failure within " + max_timeout + "ms",
                  "// minify options: " + to_comment(minify_options_json)
              ].join("\n"),
              warnings: warnings,
          };
      } else if (differs.error) {
          differs.warnings = warnings;
          return differs;
      } else if (sandbox.is_error(differs.unminified_result)
          && sandbox.is_error(differs.minified_result)
          && differs.unminified_result.name == differs.minified_result.name) {
          return {
              code: [
                  "// No differences except in error message",
                  "// minify options: " + to_comment(minify_options_json)
              ].join("\n"),
              warnings: warnings,
          };
      } else {
          max_timeout = Math.min(100 * differs.elapsed, max_timeout);
          // Replace expressions with constants that will be parsed into
          // AST_Nodes as required.  Each AST_Node has its own permutation count,
          // so these replacements can't be shared.
          // Although simpler replacements are generally faster and better,
          // feel free to experiment with a different replacement set.
          var REPLACEMENTS = [
              // "null", "''", "false", "'foo'", "undefined", "9",
              "1", "0",
          ];
  
          // There's a relationship between each node's _permute counter and
          // REPLACEMENTS.length which is why fractional _permutes were needed.
          // One could scale all _permute operations by a factor of `steps`
          // to only deal with integer operations, but this works well enough.
          var steps = 4;        // must be a power of 2
          var step = 1 / steps; // 0.25 is exactly representable in floating point
  
          var tt = new U.TreeTransformer(function(node, descend, in_list) {
              if (CHANGED) return;
  
              // quick ignores
              if (node instanceof U.AST_Accessor) return;
              if (node instanceof U.AST_Directive) return;
              if (!in_list && node instanceof U.AST_EmptyStatement) return;
              if (node instanceof U.AST_Label) return;
              if (node instanceof U.AST_LabelRef) return;
              if (node instanceof U.AST_Toplevel) return;
              var parent = tt.parent();
              if (node instanceof U.AST_SymbolFunarg && parent instanceof U.AST_Accessor) return;
              if (!in_list && parent.rest !== node && node instanceof U.AST_SymbolDeclaration) return;
  
              // ensure that the _permute prop is a number.
              // can not use `node.start._permute |= 0;` as it will erase fractional part.
              if (typeof node.start._permute === "undefined") node.start._permute = 0;
  
              // if node reached permutation limit - skip over it.
              // no structural AST changes before this point.
              if (node.start._permute >= REPLACEMENTS.length) return;
  
              // ignore lvalues
              if (parent instanceof U.AST_Assign && parent.left === node) return;
              if (parent instanceof U.AST_DefaultValue && parent.name === node) return;
              if (parent instanceof U.AST_DestructuredKeyVal && parent.value === node) return;
              if (parent instanceof U.AST_Unary && parent.expression === node) switch (parent.operator) {
                case "++":
                case "--":
                case "delete":
                  return;
              }
              if (parent instanceof U.AST_VarDef && parent.name === node) return;
              // preserve class methods
              if (parent instanceof U.AST_ClassMethod && parent.value === node) return;
              // preserve exports
              if (parent instanceof U.AST_ExportDeclaration) return;
              if (parent instanceof U.AST_ExportDefault) return;
              if (parent instanceof U.AST_ExportForeign) return;
              if (parent instanceof U.AST_ExportReferences) return;
              // preserve sole definition of an export statement
              if (node instanceof U.AST_VarDef
                  && parent.definitions.length == 1
                  && tt.parent(1) instanceof U.AST_ExportDeclaration) {
                  return;
              }
              // preserve for (var xxx; ...)
              if (parent instanceof U.AST_For && parent.init === node && node instanceof U.AST_Definitions) return node;
              // preserve for (xxx in/of ...)
              if (parent instanceof U.AST_ForEnumeration && parent.init === node) return node;
              // preserve super(...)
              if (node.TYPE == "Call" && node.expression instanceof U.AST_Super) return;
              if (node instanceof U.AST_Super && parent.TYPE == "Call" && parent.expression === node) return node;
  
              // node specific permutations with no parent logic
  
              if (node instanceof U.AST_Array) {
                  var expr = node.elements[0];
                  if (expr && !(expr instanceof U.AST_Hole)) {
                      node.start._permute++;
                      CHANGED = true;
                      return expr instanceof U.AST_Spread ? expr.expression : expr;
                  }
              }
              else if (node instanceof U.AST_Binary) {
                  var permute = ((node.start._permute += step) * steps | 0) % 4;
                  var expr = [
                      node.left,
                      node.right,
                  ][ permute & 1 ];
                  if (expr instanceof U.AST_Destructured) expr = expr.transform(new U.TreeTransformer(function(node, descend) {
                      if (node instanceof U.AST_DefaultValue) return new U.AST_Assign({
                          operator: "=",
                          left: node.name.transform(this),
                          right: node.value,
                          start: {},
                      });
                      if (node instanceof U.AST_DestructuredKeyVal) return new U.AST_ObjectKeyVal(node);
                      if (node instanceof U.AST_Destructured) {
                          node = new (node instanceof U.AST_DestructuredArray ? U.AST_Array : U.AST_Object)(node);
                          descend(node, this);
                      }
                      return node;
                  }));
                  CHANGED = true;
                  return permute < 2 ? expr : wrap_with_console_log(expr);
              }
              else if (node instanceof U.AST_BlockStatement) {
                  if (in_list && node.body.filter(function(node) {
                      return node instanceof U.AST_Const;
                  }).length == 0) {
                      node.start._permute++;
                      CHANGED = true;
                      return List.splice(node.body);
                  }
              }
              else if (node instanceof U.AST_Call) {
                  var expr = [
                      !(node.expression instanceof U.AST_Super) && node.expression,
                      node.args[0],
                      null,  // intentional
                  ][ ((node.start._permute += step) * steps | 0) % 3 ];
                  if (expr) {
                      CHANGED = true;
                      return expr instanceof U.AST_Spread ? expr.expression : expr;
                  }
                  if (node.expression instanceof U.AST_Arrow && node.expression.value) {
                      var seq = node.args.slice();
                      seq.push(node.expression.value);
                      CHANGED = true;
                      return to_sequence(seq);
                  }
                  if (node.expression instanceof U.AST_Function) {
                      // hoist and return expressions from the IIFE function expression
                      var seq = [];
                      node.expression.body.forEach(function(node) {
                          var expr = expr instanceof U.AST_Exit ? node.value : node.body;
                          if (expr instanceof U.AST_Node && !U.is_statement(expr) && can_hoist(expr)) {
                              // collect expressions from each statements' body
                              seq.push(expr);
                          }
                      });
                      CHANGED = true;
                      return to_sequence(seq);
                  }
              }
              else if (node instanceof U.AST_Catch) {
                  // drop catch block
                  node.start._permute++;
                  CHANGED = true;
                  return null;
              }
              else if (node instanceof U.AST_Conditional) {
                  CHANGED = true;
                  return [
                      node.condition,
                      node.consequent,
                      node.alternative,
                  ][ ((node.start._permute += step) * steps | 0) % 3 ];
              }
              else if (node instanceof U.AST_DefaultValue) {
                  node.start._permute++;
                  CHANGED = true;
                  return node.name;
              }
              else if (node instanceof U.AST_DestructuredArray) {
                  var expr = node.elements[0];
                  if (expr && !(expr instanceof U.AST_Hole)) {
                      node.start._permute++;
                      CHANGED = true;
                      return expr;
                  }
              }
              else if (node instanceof U.AST_DestructuredObject) {
                  // first property's value
                  var expr = node.properties[0];
                  if (expr) {
                      node.start._permute++;
                      CHANGED = true;
                      return expr.value;
                  }
              }
              else if (node instanceof U.AST_Defun) {
                  switch (((node.start._permute += step) * steps | 0) % 2) {
                    case 0:
                      CHANGED = true;
                      return List.skip;
                    default:
                      if (!has_exit(node) && can_hoist(node)) {
                          // hoist function declaration body
                          var body = node.body;
                          node.body = [];
                          body.push(node); // retain function with empty body to be dropped later
                          CHANGED = true;
                          return List.splice(body);
                      }
                  }
              }
              else if (node instanceof U.AST_DWLoop) {
                  var expr = [
                      node.condition,
                      node.body,
                      null,  // intentional
                  ][ (node.start._permute * steps | 0) % 3 ];
                  node.start._permute += step;
                  if (!expr) {
                      if (node.body[0] instanceof U.AST_Break) {
                          if (node instanceof U.AST_Do) {
                              CHANGED = true;
                              return List.skip;
                          }
                          expr = node.condition; // AST_While - fall through
                      }
                  }
                  if (expr && (expr !== node.body || !has_loopcontrol(expr, node, parent))) {
                      CHANGED = true;
                      return to_statement(expr);
                  }
              }
              else if (node instanceof U.AST_Finally) {
                  // drop finally block
                  node.start._permute++;
                  CHANGED = true;
                  return null;
              }
              else if (node instanceof U.AST_For) {
                  var expr = [
                      node.init,
                      node.condition,
                      node.step,
                      node.body,
                  ][ (node.start._permute * steps | 0) % 4 ];
                  node.start._permute += step;
                  if (expr && (expr !== node.body || !has_loopcontrol(expr, node, parent))) {
                      CHANGED = true;
                      return to_statement_init(expr);
                  }
              }
              else if (node instanceof U.AST_ForEnumeration) {
                  var expr;
                  switch ((node.start._permute * steps | 0) % 3) {
                    case 0:
                      if (node.init instanceof U.AST_Definitions) {
                          if (node.init instanceof U.AST_Const) break;
                          if (node.init.definitions[0].name instanceof U.AST_Destructured) break;
                      }
                      expr = node.init;
                      break;
                    case 1:
                      expr = node.object;
                      break;
                    case 2:
                      if (!has_loopcontrol(node.body, node, parent)) expr = node.body;
                      break;
                  }
                  node.start._permute += step;
                  if (expr) {
                      CHANGED = true;
                      return to_statement_init(expr);
                  }
              }
              else if (node instanceof U.AST_If) {
                  var expr = [
                      node.condition,
                      node.body,
                      node.alternative,
                  ][ (node.start._permute * steps | 0) % 3 ];
                  node.start._permute += step;
                  if (expr) {
                      // replace if statement with its condition, then block or else block
                      CHANGED = true;
                      return to_statement(expr);
                  }
              }
              else if (node instanceof U.AST_Object) {
                  // first property's value
                  var expr = node.properties[0];
                  if (expr instanceof U.AST_ObjectKeyVal) {
                      expr = expr.value;
                  } else if (expr instanceof U.AST_Spread) {
                      expr = expr.expression;
                  } else if (expr && expr.key instanceof U.AST_Node) {
                      expr = expr.key;
                  } else {
                      expr = null;
                  }
                  if (expr) {
                      node.start._permute++;
                      CHANGED = true;
                      return expr;
                  }
              }
              else if (node instanceof U.AST_PropAccess) {
                  var expr = [
                      !(node.expression instanceof U.AST_Super) && node.expression,
                      node.property instanceof U.AST_Node && !(parent instanceof U.AST_Destructured) && node.property,
                  ][ node.start._permute++ % 2 ];
                  if (expr) {
                      CHANGED = true;
                      return expr;
                  }
              }
              else if (node instanceof U.AST_SimpleStatement) {
                  if (node.body instanceof U.AST_Call && node.body.expression instanceof U.AST_Function) {
                      // hoist simple statement IIFE function expression body
                      node.start._permute++;
                      if (!has_exit(node.body.expression) && can_hoist(node.body.expression)) {
                          CHANGED = true;
                          return List.splice(node.body.expression.body);
                      }
                  }
              }
              else if (node instanceof U.AST_Switch) {
                  var expr = [
                      node.expression,                         // switch expression
                      node.body[0] && node.body[0].expression, // first case expression or undefined
                      node.body[0] && node.body[0],            // first case body or undefined
                  ][ (node.start._permute * steps | 0) % 4 ];
                  node.start._permute += step;
                  if (expr && (!(expr instanceof U.AST_Statement) || !has_loopcontrol(expr, node, parent))) {
                      CHANGED = true;
                      return expr instanceof U.AST_SwitchBranch ? new U.AST_BlockStatement({
                          body: expr.body.slice(),
                          start: {},
                      }) : to_statement(expr);
                  }
              }
              else if (node instanceof U.AST_Try) {
                  var body = [
                      node.body,
                      node.bcatch && node.bcatch.body,
                      node.bfinally && node.bfinally.body,
                      null,  // intentional
                  ][ (node.start._permute * steps | 0) % 4 ];
                  node.start._permute += step;
                  if (body) {
                      // replace try statement with try block, catch block, or finally block
                      CHANGED = true;
                      return new U.AST_BlockStatement({
                          body: body,
                          start: {},
                      });
                  } else {
                      // replace try with a break or return if first in try statement
                      if (node.body[0] instanceof U.AST_Break
                          || node.body[0] instanceof U.AST_Return) {
                          CHANGED = true;
                          return node.body[0];
                      }
                  }
              }
              else if (node instanceof U.AST_Unary) {
                  node.start._permute++;
                  CHANGED = true;
                  return node.expression;
              }
              else if (node instanceof U.AST_Var) {
                  if (node.definitions.length == 1 && node.definitions[0].value) {
                      // first declaration value
                      node.start._permute++;
                      CHANGED = true;
                      return to_statement(node.definitions[0].value);
                  }
              }
              else if (node instanceof U.AST_LabeledStatement) {
                  if (node.body instanceof U.AST_Statement
                      && !has_loopcontrol(node.body, node.body, node)) {
                      // replace labelled statement with its non-labelled body
                      node.start._permute = REPLACEMENTS.length;
                      CHANGED = true;
                      return node.body;
                  }
              }
  
              if (in_list) {
                  // drop switch branches
                  if (parent instanceof U.AST_Switch && parent.expression != node) {
                      node.start._permute++;
                      CHANGED = true;
                      return List.skip;
                  }
  
                  // replace or skip statement
                  if (node instanceof U.AST_Statement) {
                      node.start._permute++;
                      CHANGED = true;
                      return List.skip;
                  }
  
                  // remove this node unless its the sole element of a (transient) sequence
                  if (!(parent instanceof U.AST_Sequence) || parent.expressions.length > 1) {
                      node.start._permute++;
                      CHANGED = true;
                      return List.skip;
                  }
              } else if (parent.rest === node) {
                  node.start._permute++;
                  CHANGED = true;
                  return null;
              }
  
              // replace this node
              var newNode = U.is_statement(node) ? new U.AST_EmptyStatement({
                  start: {},
              }) : U.parse(REPLACEMENTS[node.start._permute % REPLACEMENTS.length | 0], {
                  expression: true,
              });
              newNode.start._permute = ++node.start._permute;
              CHANGED = true;
              return newNode;
          }, function(node, in_list) {
              if (node instanceof U.AST_Definitions) {
                  // remove empty var statement
                  if (node.definitions.length == 0) return in_list ? List.skip : new U.AST_EmptyStatement({
                      start: {},
                  });
              } else if (node instanceof U.AST_ObjectMethod) {
                  if (!/Function$/.test(node.value.TYPE)) return new U.AST_ObjectKeyVal({
                      key: node.key,
                      value: node.value,
                      start: {},
                  });
              } else if (node instanceof U.AST_Sequence) {
                  // expand single-element sequence
                  if (node.expressions.length == 1) return node.expressions[0];
              } else if (node instanceof U.AST_Try) {
                  // expand orphaned try block
                  if (!node.bcatch && !node.bfinally) return new U.AST_BlockStatement({
                      body: node.body,
                      start: {},
                  });
              }
          });
  
          var diff_error_message;
          for (var pass = 1; pass <= 3; ++pass) {
              var testcase_ast = U.parse(testcase);
              if (diff_error_message === testcase) {
                  // only difference detected is in error message, so expose that and try again
                  testcase_ast.transform(new U.TreeTransformer(function(node, descend) {
                      if (node.TYPE == "Call" && node.expression.print_to_string() == "console.log") {
                          return to_sequence(node.args);
                      }
                      if (node instanceof U.AST_Catch && node.argname instanceof U.AST_SymbolCatch) {
                          descend(node, this);
                          node.body.unshift(new U.AST_SimpleStatement({
                              body: wrap_with_console_log(new U.AST_SymbolRef(node.argname)),
                              start: {},
                          }));
                          return node;
                      }
                  }));
                  var code = testcase_ast.print_to_string(print_options);
                  var diff = test_for_diff(code, minify_options, result_cache, max_timeout);
                  if (diff && !diff.timed_out && !diff.error) {
                      testcase = code;
                      differs = diff;
                  } else {
                      testcase_ast = U.parse(testcase);
                  }
              }
              diff_error_message = null;
              testcase_ast.walk(new U.TreeWalker(function(node) {
                  // unshare start props to retain visit data between iterations
                  node.start = JSON.parse(JSON.stringify(node.start));
                  node.start._permute = 0;
              }));
              var before_iterations = testcase;
              for (var c = 0; c < max_iterations; ++c) {
                  if (verbose && pass == 1 && c % 25 == 0) {
                      log("// reduce test pass " + pass + ", iteration " + c + ": " + testcase.length + " bytes");
                  }
                  var CHANGED = false;
                  var code_ast = testcase_ast.clone(true).transform(tt);
                  if (!CHANGED) break;
                  try {
                      var code = code_ast.print_to_string(print_options);
                  } catch (ex) {
                      // AST is not well formed.
                      // no harm done - just log the error, ignore latest change and continue iterating.
                      log("*** Error generating code from AST.");
                      log(ex.stack);
                      log("*** Discarding permutation and continuing.");
                      continue;
                  }
                  var diff = test_for_diff(code, minify_options, result_cache, max_timeout);
                  if (diff) {
                      if (diff.timed_out) {
                          // can't trust the validity of `code_ast` and `code` when timed out.
                          // no harm done - just ignore latest change and continue iterating.
                      } else if (diff.error) {
                          // something went wrong during minify() - could be malformed AST or genuine bug.
                          // no harm done - just log code & error, ignore latest change and continue iterating.
                          log("*** Error during minification.");
                          log(code);
                          log(diff.error.stack);
                          log("*** Discarding permutation and continuing.");
                      } else if (sandbox.is_error(diff.unminified_result)
                          && sandbox.is_error(diff.minified_result)
                          && diff.unminified_result.name == diff.minified_result.name) {
                          // ignore difference in error messages caused by minification
                          diff_error_message = testcase;
                      } else {
                          // latest permutation is valid, so use it as the basis of new changes
                          testcase_ast = code_ast;
                          testcase = code;
                          differs = diff;
                      }
                  }
              }
              if (before_iterations === testcase) break;
              if (verbose) {
                  log("// reduce test pass " + pass + ": " + testcase.length + " bytes");
              }
          }
          var beautified = U.minify(testcase, {
              compress: false,
              mangle: false,
              output: function() {
                  var options = JSON.parse(JSON.stringify(print_options));
                  options.beautify = true;
                  options.braces = true;
                  options.comments = true;
                  return options;
              }(),
          });
          testcase = {
              code: testcase,
          };
          if (!beautified.error) {
              diff = test_for_diff(beautified.code, minify_options, result_cache, max_timeout);
              if (diff && !diff.timed_out && !diff.error) {
                  testcase = beautified;
                  testcase.code = "// (beautified)\n" + testcase.code;
                  differs = diff;
              }
          }
          var lines = [ "" ];
          if (isNaN(max_timeout)) {
              lines.push("// minify error: " + to_comment(differs.minified_result.stack));
          } else {
              var unminified_result = differs.unminified_result;
              var minified_result = differs.minified_result;
              if (trim_trailing_whitespace(unminified_result) == trim_trailing_whitespace(minified_result)) {
                  lines.push(
                      "// (stringified)",
                      "// output: " + JSON.stringify(unminified_result),
                      "// minify: " + JSON.stringify(minified_result)
                  );
              } else {
                  lines.push(
                      "// output: " + to_comment(unminified_result),
                      "// minify: " + to_comment(minified_result)
                  );
              }
          }
          lines.push("// options: " + to_comment(minify_options_json));
          testcase.code += lines.join("\n");
          testcase.warnings = warnings;
          return testcase;
      }
  };
  
  function to_comment(value) {
      return ("" + value).replace(/\n/g, "\n// ");
  }
  
  function trim_trailing_whitespace(value) {
      return ("" + value).replace(/\s+$/, "");
  }
  
  function has_exit(fn) {
      var found = false;
      var tw = new U.TreeWalker(function(node) {
          if (found) return found;
          if (node instanceof U.AST_Exit) {
              return found = true;
          }
          if (node instanceof U.AST_Scope && node !== fn) {
              return true; // don't descend into nested functions
          }
      });
      fn.walk(tw);
      return found;
  }
  
  function has_loopcontrol(body, loop, label) {
      var found = false;
      var tw = new U.TreeWalker(function(node) {
          if (found) return true;
          if (node instanceof U.AST_LoopControl && this.loopcontrol_target(node) === loop) {
              return found = true;
          }
      });
      if (label instanceof U.AST_LabeledStatement) tw.push(label);
      tw.push(loop);
      body.walk(tw);
      return found;
  }
  
  function can_hoist(body) {
      var found = false;
      body.walk(new U.TreeWalker(function(node) {
          if (found) return true;
          if (node instanceof U.AST_NewTarget) return found = true;
          if (node instanceof U.AST_Scope) {
              if (node === body) return;
              return true;
          }
          if (node instanceof U.AST_Super) return found = true;
      }));
      return !found;
  }
  
  function is_timed_out(result) {
      return sandbox.is_error(result) && /timed out/.test(result.message);
  }
  
  function merge_sequence(array, node) {
      if (node instanceof U.AST_Sequence) {
          array.push.apply(array, node.expressions);
      } else {
          array.push(node);
      }
      return array;
  }
  
  function to_sequence(expressions) {
      if (expressions.length == 0) return new U.AST_Number({value: 0, start: {}});
      if (expressions.length == 1) return expressions[0];
      return new U.AST_Sequence({
          expressions: expressions.reduce(merge_sequence, []),
          start: {},
      });
  }
  
  function to_statement(node) {
      return U.is_statement(node) ? node : new U.AST_SimpleStatement({
          body: node,
          start: {},
      });
  }
  
  function to_statement_init(node) {
      return node instanceof U.AST_Const || node instanceof U.AST_Let ? new U.AST_BlockStatement({
          body: [ node ],
          start: {},
      }) : to_statement(node);
  }
  
  function wrap_with_console_log(node) {
      // wrap with console.log()
      return new U.AST_Call({
          expression: new U.AST_Dot({
              expression: new U.AST_SymbolRef({
                  name: "console",
                  start: {},
              }),
              property: "log",
              start: {},
          }),
          args: [ node ],
          start: {},
      });
  }
  
  function run_code(code, toplevel, result_cache, timeout) {
      var key = crypto.createHash("sha1").update(code).digest("base64");
      var value = result_cache[key];
      if (!value) {
          var start = Date.now();
          result_cache[key] = value = {
              result: sandbox.run_code(sandbox.patch_module_statements(code), toplevel, timeout),
              elapsed: Date.now() - start,
          };
      }
      return value;
  }
  
  function compare_run_code(code, minify_options, result_cache, max_timeout) {
      var minified = U.minify(code, minify_options);
      if (minified.error) return minified;
  
      var toplevel = sandbox.has_toplevel(minify_options);
      var unminified = run_code(code, toplevel, result_cache, max_timeout);
      var timeout = Math.min(100 * unminified.elapsed, max_timeout);
      var minified_result = run_code(minified.code, toplevel, result_cache, timeout).result;
  
      if (sandbox.same_stdout(unminified.result, minified_result)) {
          return is_timed_out(unminified.result) && is_timed_out(minified_result) && {
              timed_out: true,
          };
      }
      return {
          unminified_result: unminified.result,
          minified_result: minified_result,
          elapsed: unminified.elapsed,
      };
  }
  
  function test_minify(code, minify_options) {
      var minified = U.minify(code, minify_options);
      return minified.error && {
          minified_result: minified.error,
      };
  }
  