// |jit-test| slow;

// 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.
load(libdir + "asserts.js");
load(libdir + "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;
    }
    assertDeepEq(points, result);
  }

  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];
        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;
  createMandelSet();

  escape = escapeNorm2;
  doImageData = false;  // avoidSparseArray doesn't matter here
  createMandelSet();
//}
//testMandelbrotAll();
